gdharness 1.3.47 → 1.3.49
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 +589 -229
- package/build/godot/addons/gdharness_editor/animation_reads.gd +20 -0
- package/build/godot/addons/gdharness_editor/declared_values.gd +37 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd +6 -0
- package/build/godot/addons/gdharness_editor/property_values.gd +8 -11
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +19 -0
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +42 -0
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +1 -1
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +8 -1
- package/build/godot/addons/gdharness_editor/value_shapes.gd +9 -0
- package/build/godot/addons/gdharness_runtime/declared_values.gd +37 -0
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +32 -9
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +3 -2
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +9 -7
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +72 -11
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +21 -1
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +21 -8
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +3 -2
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +1 -1
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +9 -0
- package/build/godot/operations/animation_reads.gd +20 -0
- package/build/godot/operations/declared_values.gd +37 -0
- package/build/godot/operations/dependencies.gd +65 -9
- package/build/godot/operations/file_reads.gd +6 -0
- package/build/godot/operations/import_pipeline.gd +10 -2
- package/build/godot/operations/project_config.gd +35 -0
- package/build/godot/operations/property_values.gd +8 -11
- package/build/godot/operations/resource_files.gd +1 -2
- package/build/godot/operations/value_shapes.gd +9 -0
- package/build/godot/tests/listed.gd +25 -0
- package/build/keeper.js +1312 -1026
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -10095,6 +10095,12 @@ function uncachedFor(projectPath, messagesPer) {
|
|
|
10095
10095
|
const cached = cachedClasses(projectPath);
|
|
10096
10096
|
return messagesPer.map((messages) => unloadedTypes(messages, declared, cached).filter((type) => !type.inTheClassCache));
|
|
10097
10097
|
}
|
|
10098
|
+
function declaredButUncached(projectPath, name) {
|
|
10099
|
+
if (name === "" || cachedClasses(projectPath)?.has(name) === true) {
|
|
10100
|
+
return null;
|
|
10101
|
+
}
|
|
10102
|
+
return declaredClasses(projectPath).get(name) ?? null;
|
|
10103
|
+
}
|
|
10098
10104
|
function classesNamedIn(source, known) {
|
|
10099
10105
|
const named = new Set;
|
|
10100
10106
|
for (const [word] of source.matchAll(/\b[A-Z][A-Za-z0-9_]*\b/g)) {
|
|
@@ -10178,16 +10184,15 @@ function instanceRemedy(entries, dependsOn, besideOthers) {
|
|
|
10178
10184
|
}
|
|
10179
10185
|
const declaring = declaringScripts(entries);
|
|
10180
10186
|
const lever = dependsOn.length === 0 ? ", and the named types depend on no other global class, so that lever is not available here. " + "That is the common case rather than the awkward one: an edit near a type that depends on " + "something refreshes it before the fault is noticed, which is why a type with no " + "dependencies is where this turns up" : `, so changing ${[...dependsOn].sort().join(" or ")} and then rescanning is worth a try`;
|
|
10181
|
-
return `${besideOthers ? `For ${entries.map(written).join(", ")}, run` : "Run"} editor_rescan first: ` + "it costs about half a second, and it has cleared this in every reproduction measured since the " + "scan timing was fixed: both in one project, the scan returning in 243ms and 275ms, and one in " + "another where the same call also rebuilt the copy. " + "The fault behind it is that a script the editor has loaded keeps the copy it built, and nothing " + `rebuilds that copy when the file changes; ${declaring.length === 1 ? `editor_rescan with reloadScript set to ${declaring[0]} recompiles that copy` : `
|
|
10187
|
+
return `${besideOthers ? `For ${entries.map(written).join(", ")}, run` : "Run"} editor_rescan first: ` + "it costs about half a second, and it has cleared this in every reproduction measured since the " + "scan timing was fixed: both in one project, the scan returning in 243ms and 275ms, and one in " + "another where the same call also rebuilt the copy. " + "The fault behind it is that a script the editor has loaded keeps the copy it built, and nothing " + `rebuilds that copy when the file changes; ${declaring.length === 1 ? `editor_rescan with reloadScript set to ${declaring[0]} recompiles that copy` : `editor_rescan with reloadScript set to ${JSON.stringify(declaring)} recompiles those copies`} from the file into the same ` + "object and answers with what that added under methodsAdded and constantsAdded. Read that and the " + "next diagnostics as two readings, because they are of two things: measured in one window on " + "one editor, the analyser resolved a call to a newly added method while the built copy of that " + "same script did not have it. So the reload is worth doing and is not what clears these; the " + "scan is. The other lever measured is that a held type is refreshed when something it " + `depends on changes rather than when it changes itself${lever}. editor_launch restart has always ` + "worked and costs a window. project_import refresh_classes does not, and answers added: [] while " + "this is happening.";
|
|
10182
10188
|
}
|
|
10183
10189
|
function classRemedy(entries) {
|
|
10184
10190
|
if (entries.length === 0) {
|
|
10185
10191
|
return "";
|
|
10186
10192
|
}
|
|
10187
10193
|
const declaring = declaringScripts(entries);
|
|
10188
|
-
const reload = declaring.length === 1 ? `editor_rescan with reloadScript set to ${declaring[0]}` : `editor_rescan with reloadScript
|
|
10189
|
-
|
|
10190
|
-
return `For ${entries.map(written).join(", ")}, run ${reload} first.${oneAtATime} In the one project ` + "that has reproduced a stale enum member, three times, the reload cleared it at once each time, " + "and the one plain editor_rescan tried left the diagnostic standing.";
|
|
10194
|
+
const reload = declaring.length === 1 ? `editor_rescan with reloadScript set to ${declaring[0]}` : `editor_rescan with reloadScript set to ${JSON.stringify(declaring)}`;
|
|
10195
|
+
return `For ${entries.map(written).join(", ")}, run ${reload} first. In the one project ` + "that has reproduced a stale enum member, three times, the reload cleared it at once each time, " + "and the one plain editor_rescan tried left the diagnostic standing.";
|
|
10191
10196
|
}
|
|
10192
10197
|
function notReloadedNote(broughtIn, notReloaded) {
|
|
10193
10198
|
const tried = notReloaded.filter((one) => one.tool !== true).map((one) => one.scriptPath);
|
|
@@ -11170,12 +11175,13 @@ function readRun(printed) {
|
|
|
11170
11175
|
}
|
|
11171
11176
|
const text = found[1] ?? "";
|
|
11172
11177
|
const warning = error === null;
|
|
11178
|
+
const script = /^\s*SCRIPT /.test(line);
|
|
11173
11179
|
if (open === null) {
|
|
11174
|
-
errors.push({ text, at, suite: null, during: null, warning });
|
|
11180
|
+
errors.push({ text, at, suite: null, during: null, warning, script });
|
|
11175
11181
|
} else if (during === null) {
|
|
11176
|
-
afterCase.push({ text, at, warning });
|
|
11182
|
+
afterCase.push({ text, at, warning, script });
|
|
11177
11183
|
} else {
|
|
11178
|
-
errors.push({ text, at, suite: open.path, during, warning });
|
|
11184
|
+
errors.push({ text, at, suite: open.path, during, warning, script });
|
|
11179
11185
|
}
|
|
11180
11186
|
}
|
|
11181
11187
|
const last = open;
|
|
@@ -11289,6 +11295,25 @@ function rowsAcross(engines) {
|
|
|
11289
11295
|
}
|
|
11290
11296
|
return faults;
|
|
11291
11297
|
}
|
|
11298
|
+
function crashIn(lines) {
|
|
11299
|
+
const trimmed = lines.map((line) => line.trim());
|
|
11300
|
+
const at = trimmed.findIndex((line) => CRASH_HEADER.test(line));
|
|
11301
|
+
const said = trimmed.some((line) => line.includes(QUEUE_OVERFLOW_LINE));
|
|
11302
|
+
if (at === -1) {
|
|
11303
|
+
return said ? { signal: "", overflowed: true } : null;
|
|
11304
|
+
}
|
|
11305
|
+
const report = trimmed.slice(at);
|
|
11306
|
+
const build = report.map((line) => CRASH_BUILD.exec(line)?.[1]).find((commit) => commit !== undefined);
|
|
11307
|
+
const frames = build === undefined ? undefined : QUEUE_OVERFLOW_FRAMES.get(build);
|
|
11308
|
+
const framed = frames?.every((frame) => report.some((line) => line.includes(`(${frame})`))) === true;
|
|
11309
|
+
return { signal: CRASH_HEADER.exec(trimmed[at] ?? "")?.[1] ?? "", overflowed: said || framed };
|
|
11310
|
+
}
|
|
11311
|
+
function whileRunning(running) {
|
|
11312
|
+
if (running === null) {
|
|
11313
|
+
return "";
|
|
11314
|
+
}
|
|
11315
|
+
return running.during === null ? ` in ${running.suite}` : ` while ${running.suite} > ${running.during} ran`;
|
|
11316
|
+
}
|
|
11292
11317
|
function named(items) {
|
|
11293
11318
|
const shown = items.slice(0, MOST_NAMED).join(", ");
|
|
11294
11319
|
return items.length > MOST_NAMED ? `${shown} and ${String(items.length - MOST_NAMED)} more` : shown;
|
|
@@ -11304,11 +11329,15 @@ function judgeEngine(printed, handed, end) {
|
|
|
11304
11329
|
const failing = new Set;
|
|
11305
11330
|
const shownAt = new Set;
|
|
11306
11331
|
const explained = end.code === GDUNIT_FAILED && [...printed.suites.values()].some((suite) => suite.cases.some((ran) => ran.verdict === "FAILED")) || end.code === GDUNIT_ORPHANS && printed.orphansLeft.length > 0;
|
|
11332
|
+
const crashed = crashIn(printed.lines);
|
|
11307
11333
|
if (end.ended !== null) {
|
|
11308
11334
|
faults.push(end.ended);
|
|
11309
11335
|
} else if (end.code !== 0 && !explained) {
|
|
11310
11336
|
const free = end.freeBytes === undefined ? "" : `, with ${(end.freeBytes / 1024 ** 3).toFixed(1)} GB of memory free as it ended`;
|
|
11311
|
-
faults.push(end.code === null ? `the engine was ended by ${end.signal ?? "a signal"}${free}` : `the engine exited ${exitNamed(end.code)}${free}`);
|
|
11337
|
+
faults.push(crashed !== null && crashed.signal !== "" ? `the engine crashed${whileRunning(printed.running)}, its crash handler reporting signal ${crashed.signal}${free}` : end.code === null ? `the engine was ended by ${end.signal ?? "a signal"}${free}` : `the engine exited ${exitNamed(end.code)}${free}`);
|
|
11338
|
+
}
|
|
11339
|
+
if (crashed?.overflowed === true) {
|
|
11340
|
+
faults.push(`the engine's call queue overflowed${whileRunning(printed.running)}: something queued deferred calls without end within one frame, most often a layout that changes the size it is laid out from in a deferred handler, so the frame never ends${crashed.signal === "" ? "" : ", and the engine crashed reporting the overflow"}`);
|
|
11312
11341
|
}
|
|
11313
11342
|
if (printed.lines.every((line) => line.trim() === "")) {
|
|
11314
11343
|
faults.push("the engine printed nothing at all");
|
|
@@ -11391,6 +11420,10 @@ function judgeEngine(printed, handed, end) {
|
|
|
11391
11420
|
}
|
|
11392
11421
|
const selfTyped = selfTypedSentence(handed.selfTyped ?? []);
|
|
11393
11422
|
const notes = [];
|
|
11423
|
+
const pushErrorsFailed = handed.failsOnPushErrors === true ? pushErrorsFailedSentence(failedOnPushErrors(printed)) : undefined;
|
|
11424
|
+
if (pushErrorsFailed !== undefined) {
|
|
11425
|
+
notes.push(pushErrorsFailed);
|
|
11426
|
+
}
|
|
11394
11427
|
if (printed.leaks.length > 0 && walkStoppedPartWay(printed.lines)) {
|
|
11395
11428
|
notes.push(`${WALK_STOPPED_SENTENCE} It said: ${printed.leaks.join(" ")}`);
|
|
11396
11429
|
} else {
|
|
@@ -11447,6 +11480,25 @@ function linesShown(lines, picked, cutShort) {
|
|
|
11447
11480
|
function declaringAdvice(during, warning = false) {
|
|
11448
11481
|
return `if the suite prints it on purpose, list its text under "${funcOf(during)}" in the suite's ${warning ? "GDHARNESS_EXPECTED_WARNINGS" : "GDHARNESS_EXPECTED_ERRORS"}`;
|
|
11449
11482
|
}
|
|
11483
|
+
function failedOnPushErrors(printed) {
|
|
11484
|
+
const failed = new Set;
|
|
11485
|
+
for (const suite of printed.suites.values()) {
|
|
11486
|
+
for (const ran of suite.cases) {
|
|
11487
|
+
if (ran.verdict === "FAILED") {
|
|
11488
|
+
failed.add(`${suite.path} > ${ran.name}`);
|
|
11489
|
+
}
|
|
11490
|
+
}
|
|
11491
|
+
}
|
|
11492
|
+
return [
|
|
11493
|
+
...new Set(printed.errors.filter((error) => error.script !== true && error.suite !== null && error.during !== null).map((error) => `${error.suite ?? ""} > ${error.during ?? ""}`).filter((ran) => failed.has(ran)))
|
|
11494
|
+
];
|
|
11495
|
+
}
|
|
11496
|
+
function pushErrorsFailedSentence(cases) {
|
|
11497
|
+
if (cases.length === 0) {
|
|
11498
|
+
return;
|
|
11499
|
+
}
|
|
11500
|
+
return "The project turns on gdUnit4's report/godot/push_error, so gdUnit4 fails a case on every error " + `and warning the engine prints while it runs, declared or not, which failed ` + `${cases.length === 1 ? "this case" : "these cases"}: ${named(cases)}. A case that prints one on purpose ` + "does it inside assert_error(...).is_push_error(...) or is_push_warning(...), which gdUnit4 passes, " + "and declares it for gdharness all the same.";
|
|
11501
|
+
}
|
|
11450
11502
|
function unexpectedErrors(printed, expected, paths) {
|
|
11451
11503
|
const owed = new Map;
|
|
11452
11504
|
for (const path of paths) {
|
|
@@ -11490,7 +11542,7 @@ ${String(error.warning)}`);
|
|
|
11490
11542
|
const neverPrinted = [...owed.values()].flatMap((debt) => debt.texts.map((text) => ({ suite: debt.suite, during: debt.during, text, warning: debt.warning })));
|
|
11491
11543
|
return { printed: unexpected, neverPrinted };
|
|
11492
11544
|
}
|
|
11493
|
-
var SUITE_OPENED, TIME, CASE_LINE, STATISTICS, ERROR_LINE, WARNING_LINE, EXECUTED, MISMATCHED_ROW, UNRUNNABLE_CASE, BANNER, LEFT_OUT, DRIVER_FALLBACK, MACHINE_WARNED, ORPHANS_REPORTED, STARVED, NESTED_ERROR = "While attempting to print an error, another error was printed:", WINDOWS_EXITS, MEMORY_EXITS, GDUNIT_FAILED = 100, GDUNIT_ORPHANS = 101, GDUNIT_EXITS, GODOT_EXITS, TAIL = 60, MOST_NAMED = 8;
|
|
11545
|
+
var SUITE_OPENED, TIME, CASE_LINE, STATISTICS, ERROR_LINE, WARNING_LINE, EXECUTED, MISMATCHED_ROW, UNRUNNABLE_CASE, BANNER, LEFT_OUT, DRIVER_FALLBACK, MACHINE_WARNED, ORPHANS_REPORTED, STARVED, NESTED_ERROR = "While attempting to print an error, another error was printed:", WINDOWS_EXITS, MEMORY_EXITS, GDUNIT_FAILED = 100, GDUNIT_ORPHANS = 101, GDUNIT_EXITS, GODOT_EXITS, CRASH_HEADER, CRASH_BUILD, QUEUE_OVERFLOW_LINE = "Object was deleted while awaiting a callback.", QUEUE_OVERFLOW_FRAMES, TAIL = 60, MOST_NAMED = 8;
|
|
11494
11546
|
var init_suite_judge = __esm(() => {
|
|
11495
11547
|
init_engine_leaks();
|
|
11496
11548
|
init_junit();
|
|
@@ -11530,6 +11582,11 @@ var init_suite_judge = __esm(() => {
|
|
|
11530
11582
|
[105, "gdUnit4's code for scripts that did not compile"]
|
|
11531
11583
|
]);
|
|
11532
11584
|
GODOT_EXITS = new Set([1, 255]);
|
|
11585
|
+
CRASH_HEADER = /^(?:CrashHandlerException|handle_crash): Program crashed with signal (\d+)/;
|
|
11586
|
+
CRASH_BUILD = /^Engine version: .*\(([0-9a-f]{40})\)$/;
|
|
11587
|
+
QUEUE_OVERFLOW_FRAMES = new Map([
|
|
11588
|
+
["ed1daf0bf001b61586d9930840f2f1394092c079", ["main+3e1082a", "main+3e0f4aa"]]
|
|
11589
|
+
]);
|
|
11533
11590
|
});
|
|
11534
11591
|
|
|
11535
11592
|
// src/engine-launch.ts
|
|
@@ -11952,6 +12009,7 @@ import {
|
|
|
11952
12009
|
closeSync as closeSync3,
|
|
11953
12010
|
mkdirSync as mkdirSync3,
|
|
11954
12011
|
openSync as openSync3,
|
|
12012
|
+
readdirSync as readdirSync4,
|
|
11955
12013
|
readFileSync as readFileSync8,
|
|
11956
12014
|
rmSync as rmSync6,
|
|
11957
12015
|
statSync as statSync6,
|
|
@@ -11989,6 +12047,18 @@ function machineSlots(asked = {
|
|
|
11989
12047
|
function nonEmpty(value) {
|
|
11990
12048
|
return value === undefined || value === "" ? undefined : value;
|
|
11991
12049
|
}
|
|
12050
|
+
function slotsDrawnOn(count, inAGate, gateQueued) {
|
|
12051
|
+
const gated = Math.max(1, count - KEPT_SLOTS);
|
|
12052
|
+
if (inAGate) {
|
|
12053
|
+
return { numbers: Array.from({ length: gated }, (_, slot) => slot), limit: "gate" };
|
|
12054
|
+
}
|
|
12055
|
+
const every = Array.from({ length: count }, (_, slot) => count - 1 - slot);
|
|
12056
|
+
const kept = every.slice(0, count - gated);
|
|
12057
|
+
return gateQueued && kept.length > 0 ? { numbers: kept, limit: "kept" } : { numbers: every };
|
|
12058
|
+
}
|
|
12059
|
+
function inAGate(env = process7.env) {
|
|
12060
|
+
return nonEmpty(env[SHARE_VARIABLE]) !== undefined && nonEmpty(env[SHARE_COUNT_VARIABLE]) !== undefined;
|
|
12061
|
+
}
|
|
11992
12062
|
|
|
11993
12063
|
class FolderChanges {
|
|
11994
12064
|
changed = false;
|
|
@@ -12069,16 +12139,20 @@ class EngineSlots {
|
|
|
12069
12139
|
starts = new Map;
|
|
12070
12140
|
held = new Set;
|
|
12071
12141
|
marks = new Set;
|
|
12142
|
+
drawn = { numbers: [] };
|
|
12072
12143
|
saidAt = Number.NEGATIVE_INFINITY;
|
|
12073
12144
|
directory;
|
|
12074
12145
|
count;
|
|
12075
12146
|
holder;
|
|
12147
|
+
inAGate;
|
|
12076
12148
|
say;
|
|
12077
|
-
constructor(directory, count, holder, say) {
|
|
12149
|
+
constructor(directory, count, holder, say, gated = inAGate()) {
|
|
12078
12150
|
this.directory = directory;
|
|
12079
12151
|
this.count = count;
|
|
12080
12152
|
this.holder = holder;
|
|
12081
12153
|
this.say = say;
|
|
12154
|
+
this.inAGate = gated;
|
|
12155
|
+
this.drawn = slotsDrawnOn(count, gated, false);
|
|
12082
12156
|
}
|
|
12083
12157
|
async take(signal, waiting, blocked) {
|
|
12084
12158
|
mkdirSync3(this.directory, { recursive: true });
|
|
@@ -12105,13 +12179,14 @@ class EngineSlots {
|
|
|
12105
12179
|
}
|
|
12106
12180
|
} else if (this.say !== undefined && this.held.size === 0 && now - began >= COMMAND_WAIT_SAID_MS && now - this.saidAt >= WAIT_SAID_MS) {
|
|
12107
12181
|
this.saidAt = now;
|
|
12108
|
-
this.say(slotWaitSaid(now - began, this.count, this.directory, this.holders()));
|
|
12182
|
+
this.say(slotWaitSaid(now - began, this.count, this.directory, this.holders(), this.drawn));
|
|
12109
12183
|
}
|
|
12110
12184
|
if (changes === undefined) {
|
|
12111
12185
|
changes = new FolderChanges(this.directory);
|
|
12112
12186
|
continue;
|
|
12113
12187
|
}
|
|
12114
|
-
|
|
12188
|
+
const sayAt = waiting !== undefined ? said + WAIT_SAID_MS : this.say !== undefined && this.held.size === 0 ? Math.max(began + COMMAND_WAIT_SAID_MS, this.saidAt + WAIT_SAID_MS) : Number.POSITIVE_INFINITY;
|
|
12189
|
+
await changes.next(Math.max(0, Math.min(SLOT_LOOK_MS, sayAt - Date.now())), SLOT_SETTLE_MS, signal);
|
|
12115
12190
|
}
|
|
12116
12191
|
} finally {
|
|
12117
12192
|
changes?.close();
|
|
@@ -12122,7 +12197,8 @@ class EngineSlots {
|
|
|
12122
12197
|
}
|
|
12123
12198
|
async takeFree() {
|
|
12124
12199
|
mkdirSync3(this.directory, { recursive: true });
|
|
12125
|
-
|
|
12200
|
+
this.drawn = slotsDrawnOn(this.count, this.inAGate, !this.inAGate && await this.aGateIsQueued());
|
|
12201
|
+
for (const slot of this.drawn.numbers) {
|
|
12126
12202
|
const lock = join9(this.directory, `${String(slot)}.lock`);
|
|
12127
12203
|
if (this.made(lock) || await this.takenOver(lock)) {
|
|
12128
12204
|
this.held.add(lock);
|
|
@@ -12146,9 +12222,32 @@ class EngineSlots {
|
|
|
12146
12222
|
this.marks.add(mark);
|
|
12147
12223
|
return mark;
|
|
12148
12224
|
}
|
|
12225
|
+
async aGateIsQueued() {
|
|
12226
|
+
const marks = join9(this.directory, QUEUED_FOLDER);
|
|
12227
|
+
let names;
|
|
12228
|
+
try {
|
|
12229
|
+
names = readdirSync4(marks).filter((name) => name.endsWith(".json"));
|
|
12230
|
+
} catch {
|
|
12231
|
+
return false;
|
|
12232
|
+
}
|
|
12233
|
+
let live = false;
|
|
12234
|
+
for (const name of names) {
|
|
12235
|
+
const mark = join9(marks, name);
|
|
12236
|
+
const content = readWhole(mark);
|
|
12237
|
+
if (content === null || holderOf(content) === null) {
|
|
12238
|
+
continue;
|
|
12239
|
+
}
|
|
12240
|
+
if (await this.free(mark)) {
|
|
12241
|
+
removeWhole(mark);
|
|
12242
|
+
} else {
|
|
12243
|
+
live = true;
|
|
12244
|
+
}
|
|
12245
|
+
}
|
|
12246
|
+
return live;
|
|
12247
|
+
}
|
|
12149
12248
|
holders() {
|
|
12150
12249
|
const found = [];
|
|
12151
|
-
for (
|
|
12250
|
+
for (const slot of this.drawn.numbers) {
|
|
12152
12251
|
try {
|
|
12153
12252
|
const holder = holderOf(readFileSync8(join9(this.directory, `${String(slot)}.lock`), "utf8"));
|
|
12154
12253
|
if (holder !== null) {
|
|
@@ -12169,11 +12268,17 @@ class EngineSlots {
|
|
|
12169
12268
|
}
|
|
12170
12269
|
release(lock) {
|
|
12171
12270
|
this.held.delete(lock);
|
|
12172
|
-
|
|
12173
|
-
|
|
12174
|
-
|
|
12175
|
-
|
|
12176
|
-
|
|
12271
|
+
giveBackSlot(lock, this.holder.pid);
|
|
12272
|
+
}
|
|
12273
|
+
forget(lock) {
|
|
12274
|
+
this.held.delete(lock);
|
|
12275
|
+
}
|
|
12276
|
+
hand(lock, to) {
|
|
12277
|
+
const handed = handSlot(lock, this.holder.pid, to);
|
|
12278
|
+
if (handed) {
|
|
12279
|
+
this.held.delete(lock);
|
|
12280
|
+
}
|
|
12281
|
+
return handed;
|
|
12177
12282
|
}
|
|
12178
12283
|
made(path) {
|
|
12179
12284
|
let made;
|
|
@@ -12240,6 +12345,22 @@ class EngineSlots {
|
|
|
12240
12345
|
this.starts.set(pid, { started, readAt: Date.now() });
|
|
12241
12346
|
}
|
|
12242
12347
|
}
|
|
12348
|
+
function handSlot(lock, from, to) {
|
|
12349
|
+
let content;
|
|
12350
|
+
try {
|
|
12351
|
+
content = readWhole(lock);
|
|
12352
|
+
} catch {
|
|
12353
|
+
return false;
|
|
12354
|
+
}
|
|
12355
|
+
return content !== null && holderOf(content)?.pid === from && writeWhole(lock, JSON.stringify(to));
|
|
12356
|
+
}
|
|
12357
|
+
function giveBackSlot(lock, pid) {
|
|
12358
|
+
try {
|
|
12359
|
+
if (holderOf(readFileSync8(lock, "utf8"))?.pid === pid) {
|
|
12360
|
+
removeWhole(lock);
|
|
12361
|
+
}
|
|
12362
|
+
} catch {}
|
|
12363
|
+
}
|
|
12243
12364
|
function sayWaitsTo(say) {
|
|
12244
12365
|
saidTo = say;
|
|
12245
12366
|
}
|
|
@@ -12256,26 +12377,40 @@ function processSlots() {
|
|
|
12256
12377
|
function nameSlotTaker(by) {
|
|
12257
12378
|
takenBy = by;
|
|
12258
12379
|
}
|
|
12259
|
-
function slotWaitSaid(waitedMs, count, directory, holders = []) {
|
|
12260
|
-
const
|
|
12261
|
-
const
|
|
12380
|
+
function slotWaitSaid(waitedMs, count, directory, holders = [], drawn = { numbers: [] }) {
|
|
12381
|
+
const taking = drawn.limit === undefined ? count : drawn.numbers.length;
|
|
12382
|
+
const slots = `${slotsSaid(taking, count, directory, drawn.limit)} ${taking === 1 ? "is" : "are"} in use`;
|
|
12383
|
+
const named = holdersNamed(taking, holders);
|
|
12262
12384
|
const waiting = `Waiting${waitedMs >= 1000 ? ` ${String(Math.round(waitedMs / 1000))} s so far` : ""} for an engine: ${slots}`;
|
|
12385
|
+
const back = `the engine starts when ${taking === 1 ? "it" : "one"} is given back`;
|
|
12386
|
+
const starts = drawn.limit === "kept" ? `${back} or no gate is queued` : back;
|
|
12263
12387
|
if (named.length === 0) {
|
|
12264
|
-
return `${waiting}, by a gate or another tool, and
|
|
12388
|
+
return `${waiting}, by a gate or another tool, and ${starts}.`;
|
|
12265
12389
|
}
|
|
12266
|
-
if (
|
|
12267
|
-
return `${waiting}, by ${named.join(", ")}, and
|
|
12390
|
+
if (taking === 1) {
|
|
12391
|
+
return `${waiting}, by ${named.join(", ")}, and ${starts}.`;
|
|
12268
12392
|
}
|
|
12269
|
-
return `${waiting}: ${listed(named)}.
|
|
12393
|
+
return `${waiting}: ${listed(named)}. ${starts.charAt(0).toUpperCase()}${starts.slice(1)}.`;
|
|
12270
12394
|
}
|
|
12271
|
-
function
|
|
12395
|
+
function slotsSaid(taking, count, directory, limit) {
|
|
12396
|
+
const where = `on this machine (${directory})`;
|
|
12397
|
+
if (limit === "kept") {
|
|
12398
|
+
return `${taking === 1 ? "the one engine slot" : `the ${String(taking)} engine slots`} of the ${String(count)} ${where} kept for engines outside a gate while a gate is queued`;
|
|
12399
|
+
}
|
|
12400
|
+
if (limit === "gate") {
|
|
12401
|
+
return `${taking === 1 ? "the one engine slot" : `the ${String(taking)} engine slots`} of the ${String(count)} ${where} a gate's engines draw on`;
|
|
12402
|
+
}
|
|
12403
|
+
return count === 1 ? `the one engine slot ${where}` : `all ${String(count)} engine slots ${where}`;
|
|
12404
|
+
}
|
|
12405
|
+
function slotWaitedSaid(waitedMs, count, directory, holders, drawn = { numbers: [] }) {
|
|
12272
12406
|
const waited = `This call waited ${String(Math.round(waitedMs / 1000))} s for an engine slot in all.`;
|
|
12273
|
-
const
|
|
12274
|
-
const
|
|
12407
|
+
const taking = drawn.limit === undefined ? count : drawn.numbers.length;
|
|
12408
|
+
const slots = `${slotsSaid(taking, count, directory, drawn.limit)} ${taking === 1 ? "was" : "were"} in use`;
|
|
12409
|
+
const named = holdersNamed(taking, holders);
|
|
12275
12410
|
if (named.length === 0) {
|
|
12276
12411
|
return `${waited} When it first waited, ${slots}, by a gate or another tool.`;
|
|
12277
12412
|
}
|
|
12278
|
-
return
|
|
12413
|
+
return taking === 1 ? `${waited} When it first waited, ${slots}, by ${named.join(", ")}.` : `${waited} When it first waited, ${slots}: ${listed(named)}.`;
|
|
12279
12414
|
}
|
|
12280
12415
|
function holdersNamed(count, holders) {
|
|
12281
12416
|
const held = new Map;
|
|
@@ -12297,19 +12432,28 @@ async function slotForCall() {
|
|
|
12297
12432
|
const waits = callSlotWaits();
|
|
12298
12433
|
const began = Date.now();
|
|
12299
12434
|
let blockedBy;
|
|
12435
|
+
let blockedOn;
|
|
12300
12436
|
const lock = await slots.take(callSignal(), progress === undefined ? undefined : (waitedMs) => {
|
|
12301
|
-
progress(waitedMs / 1000, slotWaitSaid(waitedMs, slots.count, slots.directory, slots.holders()));
|
|
12437
|
+
progress(waitedMs / 1000, slotWaitSaid(waitedMs, slots.count, slots.directory, slots.holders(), slots.drawn));
|
|
12302
12438
|
}, () => {
|
|
12303
12439
|
blockedBy = slots.holders();
|
|
12440
|
+
blockedOn = slots.drawn;
|
|
12304
12441
|
});
|
|
12305
12442
|
if (waits !== undefined && blockedBy !== undefined) {
|
|
12306
12443
|
waits.ms += Date.now() - began;
|
|
12307
12444
|
const heldBy = blockedBy;
|
|
12308
|
-
|
|
12445
|
+
const heldOn = blockedOn;
|
|
12446
|
+
waits.said ??= (waitedMs) => slotWaitedSaid(waitedMs, slots.count, slots.directory, heldBy, heldOn);
|
|
12309
12447
|
}
|
|
12310
12448
|
return {
|
|
12449
|
+
lock,
|
|
12450
|
+
from: slots.holder.pid,
|
|
12311
12451
|
release: () => {
|
|
12312
12452
|
slots.release(lock);
|
|
12453
|
+
},
|
|
12454
|
+
handTo: (holder) => slots.hand(lock, holder),
|
|
12455
|
+
leave: () => {
|
|
12456
|
+
slots.forget(lock);
|
|
12313
12457
|
}
|
|
12314
12458
|
};
|
|
12315
12459
|
}
|
|
@@ -12321,7 +12465,7 @@ async function inASlot(body) {
|
|
|
12321
12465
|
held.release();
|
|
12322
12466
|
}
|
|
12323
12467
|
}
|
|
12324
|
-
var SLOTS_VARIABLE = "GODOT_ENGINE_SLOTS", SLOT_COUNT_VARIABLE = "GODOT_ENGINE_SLOT_COUNT", PROCESSORS_A_SLOT = 2, START_TOLERANCE_MS = 2000, UNWRITTEN_MS = 1e4, SLOT_LOOK_MS = 1000, SLOT_SETTLE_MS = 50, WAIT_SAID_MS = 20000, COMMAND_WAIT_SAID_MS = 2000, START_READ_MS = 1e4, WAITING_FOLDER = "waiting", waitsMarked = 0, ours, saidTo, takenBy;
|
|
12468
|
+
var SLOTS_VARIABLE = "GODOT_ENGINE_SLOTS", SLOT_COUNT_VARIABLE = "GODOT_ENGINE_SLOT_COUNT", PROCESSORS_A_SLOT = 2, START_TOLERANCE_MS = 2000, UNWRITTEN_MS = 1e4, SLOT_LOOK_MS = 1000, SLOT_SETTLE_MS = 50, WAIT_SAID_MS = 20000, COMMAND_WAIT_SAID_MS = 2000, START_READ_MS = 1e4, WAITING_FOLDER = "waiting", QUEUED_FOLDER = "queued", SHARE_VARIABLE = "GODOT_ENGINE_SHARE", SHARE_COUNT_VARIABLE = "GODOT_ENGINE_SHARE_COUNT", KEPT_SLOTS = 2, waitsMarked = 0, ours, saidTo, takenBy;
|
|
12325
12469
|
var init_engine_slots = __esm(() => {
|
|
12326
12470
|
init_alive();
|
|
12327
12471
|
init_cache_home();
|
|
@@ -12332,7 +12476,7 @@ var init_engine_slots = __esm(() => {
|
|
|
12332
12476
|
});
|
|
12333
12477
|
|
|
12334
12478
|
// src/detection.ts
|
|
12335
|
-
import { existsSync as existsSync6, readdirSync as
|
|
12479
|
+
import { existsSync as existsSync6, readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
|
|
12336
12480
|
import { homedir as homedir4 } from "node:os";
|
|
12337
12481
|
import { join as join10 } from "node:path";
|
|
12338
12482
|
function resolveHomeDirectory() {
|
|
@@ -12348,7 +12492,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
12348
12492
|
}
|
|
12349
12493
|
let entries;
|
|
12350
12494
|
try {
|
|
12351
|
-
entries =
|
|
12495
|
+
entries = readdirSync5(directory);
|
|
12352
12496
|
} catch {
|
|
12353
12497
|
return [];
|
|
12354
12498
|
}
|
|
@@ -12431,6 +12575,10 @@ function resolveHeadless(requested, host) {
|
|
|
12431
12575
|
}
|
|
12432
12576
|
return envValue("DISPLAY", host.variables) === undefined && envValue("WAYLAND_DISPLAY", host.variables) === undefined;
|
|
12433
12577
|
}
|
|
12578
|
+
function hiddenWindowFlags(engineArgs, projectCapsFrames) {
|
|
12579
|
+
const capped = projectCapsFrames || engineArgs.includes("--max-fps");
|
|
12580
|
+
return ["--disable-vsync", ...capped ? [] : ["--max-fps", String(HIDDEN_WINDOW_FPS)]];
|
|
12581
|
+
}
|
|
12434
12582
|
function resolveSilent(requested) {
|
|
12435
12583
|
return requested !== false;
|
|
12436
12584
|
}
|
|
@@ -12475,6 +12623,9 @@ function runArguments(options) {
|
|
|
12475
12623
|
if (options.quitAfter !== undefined && options.quitAfter !== null) {
|
|
12476
12624
|
args.push("--quit-after", String(options.quitAfter));
|
|
12477
12625
|
}
|
|
12626
|
+
if (options.hiddenWindow !== undefined && !options.headless) {
|
|
12627
|
+
args.push(...hiddenWindowFlags(options.engineArgs ?? [], options.hiddenWindow.projectCapsFrames));
|
|
12628
|
+
}
|
|
12478
12629
|
if (options.engineArgs !== undefined) {
|
|
12479
12630
|
args.push(...options.engineArgs);
|
|
12480
12631
|
}
|
|
@@ -12552,7 +12703,7 @@ function environmentFor2(options, variables = process.env) {
|
|
|
12552
12703
|
const base = options.savesIn === undefined ? { ...variables } : userDataIn(options.savesIn, variables);
|
|
12553
12704
|
return { ...base, ...options.env, GDHARNESS_RUNTIME_DIR: options.runtimeDirectory };
|
|
12554
12705
|
}
|
|
12555
|
-
var ENGINE_FLAGS, VALUE_KIND, SET_BY_ARGUMENT, OPENED_BY_A_SERVER = "GDHARNESS_OPENED_BY_A_SERVER", EDITOR_PID_VARIABLE = "GDHARNESS_EDITOR_PID", RESERVED_VARIABLE_PREFIX = "GDHARNESS_", SAVES_NOT_MOVED_NOTE = "On macOS the engine reads user:// off HOME and ignores the variables that move it elsewhere, measured on Godot 4.7.2, so this run wrote its saves where the player keeps theirs. A suite that saves a game has written into the same folder as the copy being played, and one that tidies up after itself may have removed real saves.";
|
|
12706
|
+
var HIDDEN_WINDOW_FPS = 60, ENGINE_FLAGS, VALUE_KIND, SET_BY_ARGUMENT, OPENED_BY_A_SERVER = "GDHARNESS_OPENED_BY_A_SERVER", EDITOR_PID_VARIABLE = "GDHARNESS_EDITOR_PID", RESERVED_VARIABLE_PREFIX = "GDHARNESS_", SAVES_NOT_MOVED_NOTE = "On macOS the engine reads user:// off HOME and ignores the variables that move it elsewhere, measured on Godot 4.7.2, so this run wrote its saves where the player keeps theirs. A suite that saves a game has written into the same folder as the copy being played, and one that tidies up after itself may have removed real saves.";
|
|
12556
12707
|
var init_launch = __esm(() => {
|
|
12557
12708
|
ENGINE_FLAGS = Object.freeze({
|
|
12558
12709
|
"--verbose": "none",
|
|
@@ -12848,7 +12999,7 @@ class GameLog {
|
|
|
12848
12999
|
return;
|
|
12849
13000
|
}
|
|
12850
13001
|
const owner = this.lastHeadline[source];
|
|
12851
|
-
if (owner !== null && /^\s/.test(line)) {
|
|
13002
|
+
if (owner !== null && /^\s/.test(line) && !GDUNIT_CASE.test(line)) {
|
|
12852
13003
|
owner.detail.push(line.trim());
|
|
12853
13004
|
owner.continues = false;
|
|
12854
13005
|
return;
|
|
@@ -12925,10 +13076,11 @@ function answersTo(entry, severity, contains) {
|
|
|
12925
13076
|
const needle = contains.toLowerCase();
|
|
12926
13077
|
return [entry.text, ...entry.detail].some((line) => line.toLowerCase().includes(needle));
|
|
12927
13078
|
}
|
|
12928
|
-
var HEADLINE, CRASH, CRASH_FILLER, COLOUR_CODE, STREAMS, ADMITTED, LOCATION2, FRAME, LONGEST_CYCLE = 64;
|
|
13079
|
+
var HEADLINE, CRASH, GDUNIT_CASE, CRASH_FILLER, COLOUR_CODE, STREAMS, ADMITTED, LOCATION2, FRAME, LONGEST_CYCLE = 64;
|
|
12929
13080
|
var init_game_log = __esm(() => {
|
|
12930
13081
|
HEADLINE = /^(USER )?(SCRIPT ERROR|SHADER ERROR|ERROR|WARNING):\s?(.*)$/;
|
|
12931
13082
|
CRASH = /^(?:CrashHandlerException|handle_crash): Program crashed with signal \d+/;
|
|
13083
|
+
GDUNIT_CASE = /^\s+res:\/\/.+?\.(?:gd|cs) > .+? (?:STARTED|PASSED|FAILED|ERROR|SKIPPED|FLAKY)(?:\s|$)/;
|
|
12932
13084
|
CRASH_FILLER = /^(?:=+|-- END OF .* BACKTRACE --|\[\d+\] .* - no debug info in .*)$/;
|
|
12933
13085
|
COLOUR_CODE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
12934
13086
|
STREAMS = ["stdout", "stderr", "transcript"];
|
|
@@ -13021,7 +13173,7 @@ var init_engine_run = __esm(() => {
|
|
|
13021
13173
|
});
|
|
13022
13174
|
|
|
13023
13175
|
// src/doc-cache.ts
|
|
13024
|
-
import { existsSync as existsSync8, readdirSync as
|
|
13176
|
+
import { existsSync as existsSync8, readdirSync as readdirSync6, statSync as statSync8 } from "node:fs";
|
|
13025
13177
|
import { homedir as homedir5 } from "node:os";
|
|
13026
13178
|
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute5, join as join11 } from "node:path";
|
|
13027
13179
|
import process9 from "node:process";
|
|
@@ -13047,7 +13199,7 @@ function docCachesWritten(directory) {
|
|
|
13047
13199
|
const written = new Map;
|
|
13048
13200
|
let names;
|
|
13049
13201
|
try {
|
|
13050
|
-
names =
|
|
13202
|
+
names = readdirSync6(directory);
|
|
13051
13203
|
} catch {
|
|
13052
13204
|
return written;
|
|
13053
13205
|
}
|
|
@@ -13077,7 +13229,7 @@ var init_doc_cache = __esm(() => {
|
|
|
13077
13229
|
});
|
|
13078
13230
|
|
|
13079
13231
|
// src/project-scan.ts
|
|
13080
|
-
import { existsSync as existsSync9, readdirSync as
|
|
13232
|
+
import { existsSync as existsSync9, readdirSync as readdirSync7, readFileSync as readFileSync9, statSync as statSync9 } from "node:fs";
|
|
13081
13233
|
import { basename as basename5, extname, join as join12, relative as relative4, sep as sep3 } from "node:path";
|
|
13082
13234
|
function skipped(name) {
|
|
13083
13235
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
@@ -13097,7 +13249,7 @@ function projectStructure(projectPath) {
|
|
|
13097
13249
|
if (steppedOver(directory)) {
|
|
13098
13250
|
return;
|
|
13099
13251
|
}
|
|
13100
|
-
for (const entry of
|
|
13252
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13101
13253
|
if (skipped(entry.name)) {
|
|
13102
13254
|
continue;
|
|
13103
13255
|
}
|
|
@@ -13144,7 +13296,7 @@ function steppedOverIn(directory) {
|
|
|
13144
13296
|
}
|
|
13145
13297
|
const found = [];
|
|
13146
13298
|
const visit = (at, prefix) => {
|
|
13147
|
-
for (const entry of
|
|
13299
|
+
for (const entry of readdirSync7(at, { withFileTypes: true })) {
|
|
13148
13300
|
if (!entry.isDirectory() || skipped(entry.name)) {
|
|
13149
13301
|
continue;
|
|
13150
13302
|
}
|
|
@@ -13165,7 +13317,7 @@ function steppedOverIn(directory) {
|
|
|
13165
13317
|
function filesBelow(root, extensions) {
|
|
13166
13318
|
const found = [];
|
|
13167
13319
|
const visit = (directory, prefix) => {
|
|
13168
|
-
for (const entry of
|
|
13320
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13169
13321
|
if (skipped(entry.name)) {
|
|
13170
13322
|
continue;
|
|
13171
13323
|
}
|
|
@@ -13189,7 +13341,7 @@ function scriptsWithoutUid(projectPath) {
|
|
|
13189
13341
|
if (steppedOver(directory)) {
|
|
13190
13342
|
return;
|
|
13191
13343
|
}
|
|
13192
|
-
for (const entry of
|
|
13344
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13193
13345
|
if (skipped(entry.name)) {
|
|
13194
13346
|
continue;
|
|
13195
13347
|
}
|
|
@@ -13261,7 +13413,7 @@ function projectTexts(projectPath) {
|
|
|
13261
13413
|
if (steppedOver(directory)) {
|
|
13262
13414
|
return;
|
|
13263
13415
|
}
|
|
13264
|
-
for (const entry of
|
|
13416
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13265
13417
|
if (skipped(entry.name)) {
|
|
13266
13418
|
continue;
|
|
13267
13419
|
}
|
|
@@ -13291,7 +13443,7 @@ function ignoredTexts(projectPath) {
|
|
|
13291
13443
|
return;
|
|
13292
13444
|
}
|
|
13293
13445
|
const under = ignored || gdignored(directory);
|
|
13294
|
-
for (const entry of
|
|
13446
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13295
13447
|
if (skipped(entry.name)) {
|
|
13296
13448
|
continue;
|
|
13297
13449
|
}
|
|
@@ -13389,7 +13541,7 @@ function searchProject(projectPath, options) {
|
|
|
13389
13541
|
}
|
|
13390
13542
|
return;
|
|
13391
13543
|
}
|
|
13392
|
-
for (const entry of
|
|
13544
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13393
13545
|
if (skipped(entry.name)) {
|
|
13394
13546
|
continue;
|
|
13395
13547
|
}
|
|
@@ -13469,7 +13621,7 @@ var init_project_scan = __esm(() => {
|
|
|
13469
13621
|
|
|
13470
13622
|
// src/import-freshness.ts
|
|
13471
13623
|
import { createHash as createHash2 } from "node:crypto";
|
|
13472
|
-
import { existsSync as existsSync10, readdirSync as
|
|
13624
|
+
import { existsSync as existsSync10, readdirSync as readdirSync8, readFileSync as readFileSync10, statSync as statSync10 } from "node:fs";
|
|
13473
13625
|
import { join as join13 } from "node:path";
|
|
13474
13626
|
function md5Of(file) {
|
|
13475
13627
|
let stats;
|
|
@@ -13564,7 +13716,7 @@ function staleImports(projectPath) {
|
|
|
13564
13716
|
}
|
|
13565
13717
|
let entries;
|
|
13566
13718
|
try {
|
|
13567
|
-
entries =
|
|
13719
|
+
entries = readdirSync8(directory, { withFileTypes: true });
|
|
13568
13720
|
} catch {
|
|
13569
13721
|
return;
|
|
13570
13722
|
}
|
|
@@ -13651,7 +13803,7 @@ var init_import_freshness = __esm(() => {
|
|
|
13651
13803
|
import {
|
|
13652
13804
|
existsSync as existsSync11,
|
|
13653
13805
|
mkdirSync as mkdirSync4,
|
|
13654
|
-
readdirSync as
|
|
13806
|
+
readdirSync as readdirSync9,
|
|
13655
13807
|
readFileSync as readFileSync11,
|
|
13656
13808
|
renameSync as renameSync2,
|
|
13657
13809
|
unlinkSync,
|
|
@@ -13676,7 +13828,7 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
13676
13828
|
}
|
|
13677
13829
|
let names;
|
|
13678
13830
|
try {
|
|
13679
|
-
names =
|
|
13831
|
+
names = readdirSync9(folder);
|
|
13680
13832
|
} catch {
|
|
13681
13833
|
continue;
|
|
13682
13834
|
}
|
|
@@ -13703,7 +13855,7 @@ function seedExtensionList(projectPath) {
|
|
|
13703
13855
|
}
|
|
13704
13856
|
let entries;
|
|
13705
13857
|
try {
|
|
13706
|
-
entries =
|
|
13858
|
+
entries = readdirSync9(directory, { withFileTypes: true });
|
|
13707
13859
|
} catch {
|
|
13708
13860
|
return;
|
|
13709
13861
|
}
|
|
@@ -13797,7 +13949,7 @@ function importRecord(projectPath, resourcePath) {
|
|
|
13797
13949
|
var init_reimport = () => {};
|
|
13798
13950
|
|
|
13799
13951
|
// src/scratch.ts
|
|
13800
|
-
import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, readdirSync as
|
|
13952
|
+
import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, readdirSync as readdirSync10, rmSync as rmSync8 } from "node:fs";
|
|
13801
13953
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
13802
13954
|
import { join as join16 } from "node:path";
|
|
13803
13955
|
import process10 from "node:process";
|
|
@@ -13807,7 +13959,7 @@ function scratchDirectory(kind) {
|
|
|
13807
13959
|
function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
13808
13960
|
let entries;
|
|
13809
13961
|
try {
|
|
13810
|
-
entries =
|
|
13962
|
+
entries = readdirSync10(directory);
|
|
13811
13963
|
} catch {
|
|
13812
13964
|
return;
|
|
13813
13965
|
}
|
|
@@ -14225,7 +14377,7 @@ async function operationOutcome(engine, operation, params, projectPath) {
|
|
|
14225
14377
|
let ran;
|
|
14226
14378
|
let answer;
|
|
14227
14379
|
try {
|
|
14228
|
-
ran = await runEngine(engine.godotPath, args);
|
|
14380
|
+
ran = await runEngine(engine.godotPath, args, { timeout: engine.ceiling ?? OPERATION_CEILING });
|
|
14229
14381
|
answer = answerIn(answerFile);
|
|
14230
14382
|
} finally {
|
|
14231
14383
|
discard(paramsDir);
|
|
@@ -14315,7 +14467,7 @@ function answerIn(file) {
|
|
|
14315
14467
|
};
|
|
14316
14468
|
}
|
|
14317
14469
|
}
|
|
14318
|
-
var AFTER_ANSWER_NOTE = "Printed after this operation had written its answer, while the engine ran the project's autoloads and shut down: Godot starts a project's autoloads in every script run and cannot be told not to. An error raised in a project script here is that autoload's own, and none of these changes the answer.", BEFORE_OPERATION_NOTE = "Printed while the engine started the project's autoloads, before this operation ran: an operation that compiles the project's scripts waits for them, because the engine knows an autoload's name only once it is in the tree. An error raised in a project script here is that autoload's own.", OWN_ERROR = "[ERROR] ", ANSWERED = "[gdharness] answered", WAITING = "[gdharness] waiting for the autoloads", BEGUN = "[gdharness] begun", ASIDE = "[gdharness] aside", RESUMED = "[gdharness] resumed", MESSAGES_KEPT = 200, ERRORS_NAMED = 3, NO_BLENDER = "Blender path is invalid or not set", BLENDER_STOPPED = "the import pass stopped at a .blend file and imported nothing: the engine imports .blend files through Blender, which the editor setting filesystem/import/blender/blender_path names, and here it names none. Set it in an editor, or set the project setting filesystem/import/blender/enabled to false with project_settings set, which has the engine leave .blend files unimported.";
|
|
14470
|
+
var AFTER_ANSWER_NOTE = "Printed after this operation had written its answer, while the engine ran the project's autoloads and shut down: Godot starts a project's autoloads in every script run and cannot be told not to. An error raised in a project script here is that autoload's own, and none of these changes the answer.", BEFORE_OPERATION_NOTE = "Printed while the engine started the project's autoloads, before this operation ran: an operation that compiles the project's scripts waits for them, because the engine knows an autoload's name only once it is in the tree. An error raised in a project script here is that autoload's own.", OWN_ERROR = "[ERROR] ", ANSWERED = "[gdharness] answered", WAITING = "[gdharness] waiting for the autoloads", BEGUN = "[gdharness] begun", ASIDE = "[gdharness] aside", RESUMED = "[gdharness] resumed", MESSAGES_KEPT = 200, OPERATION_CEILING, ERRORS_NAMED = 3, NO_BLENDER = "Blender path is invalid or not set", BLENDER_STOPPED = "the import pass stopped at a .blend file and imported nothing: the engine imports .blend files through Blender, which the editor setting filesystem/import/blender/blender_path names, and here it names none. Set it in an editor, or set the project setting filesystem/import/blender/enabled to false with project_settings set, which has the engine leave .blend files unimported.";
|
|
14319
14471
|
var init_headless = __esm(() => {
|
|
14320
14472
|
init_doc_cache();
|
|
14321
14473
|
init_engine_leaks();
|
|
@@ -14326,6 +14478,7 @@ var init_headless = __esm(() => {
|
|
|
14326
14478
|
init_reimport();
|
|
14327
14479
|
init_scratch();
|
|
14328
14480
|
init_script_origins();
|
|
14481
|
+
OPERATION_CEILING = { ms: 600000, said: "ten minutes" };
|
|
14329
14482
|
});
|
|
14330
14483
|
|
|
14331
14484
|
// src/update-check.ts
|
|
@@ -16048,7 +16201,7 @@ import {
|
|
|
16048
16201
|
cpSync,
|
|
16049
16202
|
existsSync as existsSync18,
|
|
16050
16203
|
mkdirSync as mkdirSync6,
|
|
16051
|
-
readdirSync as
|
|
16204
|
+
readdirSync as readdirSync12,
|
|
16052
16205
|
readFileSync as readFileSync18,
|
|
16053
16206
|
rmdirSync,
|
|
16054
16207
|
rmSync as rmSync9,
|
|
@@ -16072,7 +16225,7 @@ function runtimeAddonDigest(from = shippedAddonsDirectory()) {
|
|
|
16072
16225
|
function addonsDigest(names, from) {
|
|
16073
16226
|
const hash = createHash5("sha256");
|
|
16074
16227
|
const walk = (directory, relative) => {
|
|
16075
|
-
for (const name of
|
|
16228
|
+
for (const name of readdirSync12(directory).sort()) {
|
|
16076
16229
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
16077
16230
|
continue;
|
|
16078
16231
|
}
|
|
@@ -16171,7 +16324,7 @@ function removeLoader(projectPath) {
|
|
|
16171
16324
|
rmSync9(copy);
|
|
16172
16325
|
rmSync9(`${copy}.uid`, { force: true });
|
|
16173
16326
|
const folder = dirname9(copy);
|
|
16174
|
-
if (
|
|
16327
|
+
if (readdirSync12(folder).length === 0) {
|
|
16175
16328
|
rmdirSync(folder);
|
|
16176
16329
|
}
|
|
16177
16330
|
return true;
|
|
@@ -16232,7 +16385,7 @@ function removeAddons(projectPath) {
|
|
|
16232
16385
|
}
|
|
16233
16386
|
}
|
|
16234
16387
|
const addons = join22(projectPath, "addons");
|
|
16235
|
-
if (removed.length > 0 && existsSync18(addons) &&
|
|
16388
|
+
if (removed.length > 0 && existsSync18(addons) && readdirSync12(addons).length === 0) {
|
|
16236
16389
|
rmdirSync(addons);
|
|
16237
16390
|
removed.push(addons);
|
|
16238
16391
|
}
|
|
@@ -16565,7 +16718,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16565
16718
|
};
|
|
16566
16719
|
PROPERTIES = {
|
|
16567
16720
|
type: "object",
|
|
16568
|
-
description: `Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {"x": 1, "y": 2} or tagged {"_type": "Vector2", "x": 1, "y": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. One carrying a field its type has not, such as a z for a Vector2, is refused naming it. A colour may also be given as its html code, "#ff8000" or "#ff800080". A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: "position:x", "modulate:a", "region_rect:size:y"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as "../Player". A property holding a Resource takes the res:// path of a file, which a read answers with its class and, when its script declares one, scriptClass, or a resource built into the scene as {"_type": "CircleShape2D", "radius": 8}, its class (an engine class or a script's class_name) beside its properties. A typed list or map of resources or nodes, such as Array[Item], takes each in the same forms, a node by its path; a read answers a resource built into the scene in that form, with what it holds away from the defaults, so it too can be written to another node. script takes the res:// path of a script to attach, one with no class_name included, and is attached before the rest are set, so the properties it declares can be given beside it. Each value is read back once set, and one the engine changed or refused is refused with what it holds. An enum property takes its number or the name of a value: as the script spells it, as the inspector shows it, or for an engine enum as
|
|
16721
|
+
description: `Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {"x": 1, "y": 2} or tagged {"_type": "Vector2", "x": 1, "y": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. One carrying a field its type has not, such as a z for a Vector2, is refused naming it. A colour may also be given as its html code, "#ff8000" or "#ff800080". A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: "position:x", "modulate:a", "region_rect:size:y"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as "../Player". A property holding a Resource takes the res:// path of a file, which a read answers with its class and, when its script declares one, scriptClass, or a resource built into the scene as {"_type": "CircleShape2D", "radius": 8}, its class (an engine class or a script's class_name) beside its properties. A typed list or map of resources or nodes, such as Array[Item], takes each in the same forms, a node by its path; a read answers a resource built into the scene in that form, with what it holds away from the defaults, so it too can be written to another node. script takes the res:// path of a script to attach, one with no class_name included, and is attached before the rest are set, so the properties it declares can be given beside it. Each value is read back once set, and one the engine changed or refused is refused with what it holds. An enum property takes its number or the name of a value: as the script spells it, as the inspector shows it, or for an engine enum as its constant, so Add or BLEND_MODE_ADD, and Bottom or VERTICAL_ALIGNMENT_BOTTOM for a global one; a value past an export range, or a number no value of an enum has, is written as given and named under outsideDeclared. A key may reach into a resource built into this one: the property holding it, a slash, then one of its own, so a TileSet's first atlas source takes "sources/0/0:0/0/probability". A list of such resources is stepped into by an element's index, so "lines/1/text" is the text of the second resource in lines and the others are left as they were. A resource kept in a file of its own, or built into the scene an instance comes from, is written in that file, or replaced here by one of this file's own. The next element of a list is made by writing to it, as the inspector's Add Element makes it: physics_layer_0/collision_layer on a TileSet with no physics layer makes its first, and "sources/0/0:0/0/physics_layer_0/polygon_0/points" a tile's first collision polygon on that layer. ${WHOLE_NUMBERS}`,
|
|
16569
16722
|
additionalProperties: true
|
|
16570
16723
|
};
|
|
16571
16724
|
XY = {
|
|
@@ -16593,13 +16746,25 @@ var init_tool_definitions = __esm(() => {
|
|
|
16593
16746
|
],
|
|
16594
16747
|
description: "add_track makes property and method tracks; remove_track takes any type read answers."
|
|
16595
16748
|
},
|
|
16596
|
-
nodePath: {
|
|
16749
|
+
nodePath: {
|
|
16750
|
+
type: "string",
|
|
16751
|
+
description: "Target node, relative to the AnimationPlayer's root_node, which is the player's parent unless the scene sets it."
|
|
16752
|
+
},
|
|
16597
16753
|
property: { type: "string", description: "For property tracks: the property to animate." },
|
|
16598
16754
|
update: {
|
|
16599
16755
|
type: "string",
|
|
16600
16756
|
enum: ["continuous", "discrete", "capture"],
|
|
16601
16757
|
description: "For property tracks: how the value moves between keys. continuous blends toward the next key, so a bool or an int changes halfway to it; discrete holds each key's value until the next; capture blends from the value the property holds when playing starts. Without it a new track is made as the editor makes one: as the RESET animation's track for the same property, otherwise continuous for a float, vector, colour or transform and discrete for anything else. The answer gives the track's update."
|
|
16602
16758
|
},
|
|
16759
|
+
interpolation: {
|
|
16760
|
+
type: "string",
|
|
16761
|
+
enum: ["nearest", "linear", "cubic", "linear_angle", "cubic_angle"],
|
|
16762
|
+
description: "For property tracks: how a continuous track blends between keys. Without it a new track is made linear, or as the RESET track for the same property is, and an existing track keeps its own."
|
|
16763
|
+
},
|
|
16764
|
+
loopWrap: {
|
|
16765
|
+
type: "boolean",
|
|
16766
|
+
description: "For property tracks: whether a looping animation blends its last key into its first. Without it a new track wraps, or as the RESET track for the same property does, and an existing track keeps its own."
|
|
16767
|
+
},
|
|
16603
16768
|
method: {
|
|
16604
16769
|
type: "string",
|
|
16605
16770
|
description: "For method tracks: the method to call. A method track holds every method key for its node, so a key calling another method joins the node's track, with existingTrack true. The answer's methodFound says whether the target node has it; a method it lacks is still added, while a nodePath that reaches no node is refused, since it may be written next, and until it exists each call the track makes fails. A key whose args the method cannot take (too few or too many, or an object where it takes a value or the reverse) is written too, and answers argumentsFit false with misfit saying why. remove_track: only the keys calling this method."
|
|
@@ -16923,7 +17088,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16923
17088
|
name: "project_search",
|
|
16924
17089
|
title: "Search the project",
|
|
16925
17090
|
effect: "read",
|
|
16926
|
-
description: "Searches text or a regular expression across project files and returns file paths with line numbers, one entry per line that holds the query, however often it holds it; total_matches counts those lines. What the engine steps over is not searched: directories spelled with a dot, node_modules, and any directory holding a .gdignore, which not_searched names when the search passed one. A vendored engine or an export directory is therefore absent from the results rather than matching the code you are looking for in a copy nobody runs. The addons gdharness installed are not searched either, since nobody on the project wrote them. A path naming one of those folders, or a place inside one, is searched as asked, and note says the whole-project search steps over it. Each line is searched on its own, so a query holding a line break is refused.",
|
|
17091
|
+
description: "Searches text or a regular expression across project files and returns file paths with line numbers, one entry per line that holds the query, however often it holds it; total_matches counts those lines. What the engine steps over is not searched: files and directories spelled with a dot, node_modules, and any directory holding a .gdignore, which not_searched names when the search passed one. A vendored engine or an export directory is therefore absent from the results rather than matching the code you are looking for in a copy nobody runs. The addons gdharness installed are not searched either, since nobody on the project wrote them. A path naming one of those folders, or a place inside one, is searched as asked, and note says the whole-project search steps over it. Each line is searched on its own, so a query holding a line break is refused.",
|
|
16927
17092
|
parameters: {
|
|
16928
17093
|
projectPath: PROJECT_PATH,
|
|
16929
17094
|
query: { type: "string", description: "The text or pattern to find." },
|
|
@@ -16957,7 +17122,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16957
17122
|
direction: {
|
|
16958
17123
|
type: "string",
|
|
16959
17124
|
enum: ["forward", "reverse"],
|
|
16960
|
-
description: "forward: what this resource loads,
|
|
17125
|
+
description: "forward: what this resource loads or names. Each file named by its path carries kind, as reverse words it: ext_resource, extends, preload, load, include, or path for a path only written in a string, which the file itself may never load. Each reference closing a cycle is marked circular, with the cycle under circular_references. A script's dependencies include every script whose class_name it uses, marked by_class with the name, and the script of every autoload it names, marked by_autoload with the autoload's name. An ext_resource whose uid names another file than its path is followed to that file, as the engine loads it, with the path written under stale_path; one whose uid names no file the project has is followed by its path, as the engine does after warning, with the uid under unknown_uid; and a load or preload of a uid the project does not know is listed under that uid as missing. A shader's #include is followed as the preprocessor reads it, relative to the shader. A file the depth stops at carries beyond_depth, how many files it names that were not walked. A file's dependencies are listed at its first entry, and again further down only where the depth cut that list short and more is left; every other entry for a file naming any carries dependencies_listed_above. summary.total_resources is the file asked about, so 1; total_dependencies counts each file reached, directly or through another, once however many places load it; total_references counts the entries in the tree, and missing names every file referenced that does not exist, with the files referring to it. reverse: every file that refers to it and how, for a file that is gone as well while anything still refers to it, answered with exists: false (a missing file nothing refers to is refused, naming the nearest files), a scene instancing it, a script extending, preloading or loading it, and for a script with a class_name every use of that name, and for an autoload's script every use of the autoload's name, with autoload naming it. Each reference carries a kind: ext_resource, extends, preload, load, include or path for a reference by path, with stale_path when an ext_resource reaches it by its uid under another path, and unknown_uid when one names it by path beside a uid naming no file; setting, plugin, export_preset and import for one in project.godot, a plugin.cfg, export_presets.cfg or an import sidecar; class_name for a use of the declared name, the declaration itself being the script asked about; autoload for code naming the autoload. Three kinds are not uses: doc is a name inside a ## documentation comment, which renaming would break in the generated docs; comment is one in ordinary prose, which it would only make stale; and stale_path is an ext_resource naming it by a path the engine does not load, since its uid leads to the file under loads. summary.in_code is the count with those left out, which is the number to read when the question is whether anything still uses this. Default forward."
|
|
16961
17126
|
},
|
|
16962
17127
|
depth: {
|
|
16963
17128
|
type: "integer",
|
|
@@ -16992,7 +17157,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16992
17157
|
options: {
|
|
16993
17158
|
type: "object",
|
|
16994
17159
|
ops: ["set_options"],
|
|
16995
|
-
description: `set_options: import options keyed as the .import file spells them, {"compress/mode": 1}. A dictionary option whose keys are numbers, a font's opentype_features or any option already holding number keys, takes them written as strings of digits, and opentype_features also takes an OpenType tag by its four-letter name: {"opentype_features": {"liga": 0}} turns ligatures off. Both are written as the numbers the engine reads. One already holding the value given is answered under unchanged_options rather than updated_options, and when none changed nothing is written or reimported. changed gives each option written as {was, now}
|
|
17160
|
+
description: `set_options: import options keyed as the .import file spells them, {"compress/mode": 1}. A dictionary option whose keys are numbers, a font's opentype_features or any option already holding number keys, takes them written as strings of digits, and opentype_features also takes an OpenType tag by its four-letter name: {"opentype_features": {"liga": 0}} turns ligatures off. Both are written as the numbers the engine reads. One already holding the value given is answered under unchanged_options rather than updated_options, and when none changed nothing is written or reimported. changed gives each option written as {was, now}.`,
|
|
16996
17161
|
additionalProperties: true
|
|
16997
17162
|
},
|
|
16998
17163
|
reimport: {
|
|
@@ -17032,7 +17197,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17032
17197
|
},
|
|
17033
17198
|
uid: {
|
|
17034
17199
|
effect: "read",
|
|
17035
|
-
summary: "the UID of one file and where it was read: a script's or shader's .uid file, an imported file's .import, or a scene's or resource's own header",
|
|
17200
|
+
summary: "the UID of one file and where it was read: a script's or shader's .uid file, an imported file's .import, or a scene's or resource's own header. A file with none is answered with uid null and what would give it one",
|
|
17036
17201
|
requires: ["resourcePath"]
|
|
17037
17202
|
},
|
|
17038
17203
|
refresh_uids: {
|
|
@@ -17050,7 +17215,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17050
17215
|
{
|
|
17051
17216
|
name: "project_export",
|
|
17052
17217
|
title: "Export",
|
|
17053
|
-
description: "Export presets and exports. add_preset writes a preset to export_presets.cfg with the keys the editor's Project > Export writes for a new one and every option a new preset of its platform shows, each at its default, and makes it the platform's runnable preset when the platform has none. set_preset changes a preset's export path, its resource filter and the files it lists, its include and exclude filters, its custom features and its platform's options, and answers under changed what each held before and holds now, and under unchanged what already held the value given, with saved false when that is all of it, since the file is then not written; nothing is written when any of it is refused. The option names are the exporter's own, as the Export dialog's Options tab shows them by their paths, such as binary_format/embed_pck, and a name the platform does not have is refused with the nearest it has, or all of them when none is near. An option the dialog shows only while another is on, such as codesign/identity behind codesign/enable, is taken when the preset has that one on or the same call turns it on, and otherwise refused naming what shows it. A secret option, such as a signing password, is written to .godot/export_credentials.cfg, where the editor keeps secrets out of the committed file, and its value is never answered. A file an option names that is not there is written and listed under missing_files, or for a secret its option under secretsNamingNoFile. remove_preset takes a preset out, and when it was its platform's runnable preset the next of that platform becomes it, under runnableNow, which is null when the platform has no other; a secret it held is taken out with it. It answers where this engine's export templates belong and whether the preset's platform has them, since an export needs them; list says the same for every preset. An editor open on the project does not see a preset added this way until it restarts. run names, as alsoWritten, what else the export wrote beside its output, such as the .pck a Windows or Linux build cannot start without. Its entries are the errors and warnings the export printed, the newest two hundred distinct ones, one printed more than once carrying times, and errors and warnings count them; exported is true only when this run wrote the output and printed no error.",
|
|
17218
|
+
description: "Export presets and exports. add_preset writes a preset to export_presets.cfg with the keys the editor's Project > Export writes for a new one and every option a new preset of its platform shows, each at its default, and makes it the platform's runnable preset when the platform has none. set_preset changes a preset's export path, its resource filter and the files it lists, its include and exclude filters, its custom features and its platform's options, and answers under changed what each held before and holds now, and under unchanged what already held the value given, with saved false when that is all of it, since the file is then not written; nothing is written when any of it is refused. The option names are the exporter's own, as the Export dialog's Options tab shows them by their paths, such as binary_format/embed_pck, and a name the platform does not have is refused with the nearest it has, or all of them when none is near. An option the dialog shows only while another is on, such as codesign/identity behind codesign/enable, is taken when the preset has that one on or the same call turns it on, and otherwise refused naming what shows it. A secret option, such as a signing password, is written to .godot/export_credentials.cfg, where the editor keeps secrets out of the committed file, and its value is never answered. A file an option names that is not there is written and listed under missing_files, or for a secret its option under secretsNamingNoFile. remove_preset takes a preset out, and when it was its platform's runnable preset the next of that platform becomes it, under runnableNow, which is null when the platform has no other; a secret it held is taken out with it. It answers where this engine's export templates belong and whether the preset's platform has them, since an export needs them; list says the same for every preset. An editor open on the project does not see a preset added this way until it restarts. run answers bytes, the size of the output it wrote, and names, as alsoWritten, what else the export wrote beside its output, such as the .pck a Windows or Linux build cannot start without. Its entries are the errors and warnings the export printed, the newest two hundred distinct ones, one printed more than once carrying times, and errors and warnings count them; exported is true only when this run wrote the output and printed no error.",
|
|
17054
17219
|
parameters: {
|
|
17055
17220
|
projectPath: PROJECT_PATH,
|
|
17056
17221
|
preset: {
|
|
@@ -17079,7 +17244,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17079
17244
|
type: "string",
|
|
17080
17245
|
ops: ["set_preset"],
|
|
17081
17246
|
enum: ["all_resources", "scenes", "resources", "exclude"],
|
|
17082
|
-
description: "set_preset: what the preset exports: every resource, the scenes listed in files and what they use, the resources listed, or every resource but those listed."
|
|
17247
|
+
description: "set_preset: what the preset exports: every resource, the scenes listed in files and what they use, the resources listed, or every resource but those listed. What a scene or a resource uses is what the engine counts as its dependencies, so a file named only in a string, such as a scene path held in an exported property, or a script reached only by its class_name, is left out of the export, though project_dependencies lists both."
|
|
17083
17248
|
},
|
|
17084
17249
|
files: {
|
|
17085
17250
|
type: "array",
|
|
@@ -17134,7 +17299,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17134
17299
|
title: "Run tests",
|
|
17135
17300
|
effect: "additive",
|
|
17136
17301
|
description: "Runs the project's gdUnit4 tests and answers with every case that did not pass: where it is, and what the assertion said. Each entry under failed carries its first report as message and detail, and, when it has more than one, its reports under reports in the order the case met them: gdUnit4 goes on past a failed assertion, and a case can fail several and then stop on a runtime error, which makes its status error. Identical reports fold into one with times, at most twenty different ones are kept (reportsLeftOut counts the rest), and past about a megabyte the largest cases keep only their first message (failedWithoutDetail). failures counts the cases that failed an assertion and errors the cases that hit an error, so such a case counts in both. Suites where everything passed are counted, not listed, and an engine message keeps the frames above gdUnit4, not the twenty inside it; identical engine messages come back once under engineEntries, with times saying how often they were printed, and with script, line and function for the project script one points at, as editor_output's entries have. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes. What changed on disk since it was imported is then imported before any suite runs, and named under importedFirst. A suite it ran with no .uid beside it is named under uidsMissing, with uidsNote. On Windows and Linux the run gets a user:// of its own, away from a player's saves; on macOS the engine reads user:// off HOME, so it writes where the player keeps theirs, and savesNote says so. The run is headless unless a suite in it needs a window as gdharness test reads it, and then has one, on a desktop of its own on Windows. A run that found nothing to run is never called a pass, and names the path it looked in. A script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. An autoload that does not compile stops the run at once, its errors listed the same way. A client sending a progress token is told as each suite finishes, and at least every twenty seconds. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. A run where nothing failed but orphan nodes were left comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. A run whose every case passed is still not called passed when it printed a script error that no case failed on: gdUnit4 fails a case on the script errors its logger sees, and it cannot see a stack overflow, so the cases pass on whatever the faulted calls returned. Those errors come back under uncaughtScriptErrors, and under engineEntries by index with detailUnder rather than with their backtrace a second time; a backtrace that repeats, as a recursion's does, has each repeated frame or cycle of frames written once with the frames it covers and how many times. One raised inside assert_error(...).is_runtime_error is the test's own and is not listed there, and none are when the project turns off gdUnit4's report/godot/script_error setting; a suite declares them all the same. Needs gdUnit4 under addons/gdUnit4.",
|
|
17137
|
-
answer: "passed is the verdict to read. exitCode is the engine's own exit, gdUnit4's code for what it saw (100 a case failed, 101 only warnings, 105 script errors it detected), and is 0 beside passed false when the run is failed for something gdUnit4 does not judge, such as an engine error no suite declared. time is gdUnit4's own sum of the seconds its cases took, from its report. tookMs is the whole call in milliseconds, from the class rebuild to the engine's exit, which is what a caller waits for, and tookMsBy splits it into the class rebuild (classes), the import after it (import) and the engine running the suites (engine), each with any wait for an engine slot or the editor's scan in it; a call that waited a second or more for slots in all is answered with an engine_slot_wait block giving waitedMs and who held them. The run is judged as gdharness test judges it: undeclaredErrors names each engine error that no suite declared in its GDHARNESS_EXPECTED_ERRORS constant, and each engine warning a suite printed and did not declare in its GDHARNESS_EXPECTED_WARNINGS, marked warning true, with the suite and the case that printed it, and declaredNotPrinted each declared error or warning its case did not print. gdUnit4 does not fail a case on push_error or
|
|
17302
|
+
answer: "passed is the verdict to read. exitCode is the engine's own exit, gdUnit4's code for what it saw (100 a case failed, 101 only warnings, 105 script errors it detected), and is 0 beside passed false when the run is failed for something gdUnit4 does not judge, such as an engine error no suite declared. time is gdUnit4's own sum of the seconds its cases took, from its report. tookMs is the whole call in milliseconds, from the class rebuild to the engine's exit, which is what a caller waits for, and tookMsBy splits it into the class rebuild (classes), the import after it (import) and the engine running the suites (engine), each with any wait for an engine slot or the editor's scan in it; a call that waited a second or more for slots in all is answered with an engine_slot_wait block giving waitedMs and who held them. The run is judged as gdharness test judges it: undeclaredErrors names each engine error that no suite declared in its GDHARNESS_EXPECTED_ERRORS constant, and each engine warning a suite printed and did not declare in its GDHARNESS_EXPECTED_WARNINGS, marked warning true, with the suite and the case that printed it, and declaredNotPrinted each declared error or warning its case did not print. gdUnit4 does not fail a case on push_error, push_warning or an engine error unless the project turns on its report/godot/push_error setting, so a case that printed one undeclared is otherwise in neither failed nor errors: it is named here, and the run is failed for it. With that setting on, gdUnit4 fails the case on each one, declared or not, and the note names those cases; one printed inside assert_error(...).is_push_error(...) or is_push_warning(...) passes there, and is declared all the same. A declared text must be the whole line after ERROR: or WARNING: and its space, so a longer one its case printed holding the text is named under printedInstead. Either fails the run, and each suite holding one is listed with its count. leakedAtExit holds the engine's lines saying it exited still holding objects, resources, memory pages or a server's RIDs, which fail the run too, and the note names any script of the project Godot itself never frees and, on a run with engineArgs [\"--verbose\"], what the engine held. engineErrors counts every engine error the run printed but those a suite declared. orphans totals the orphan nodes gdUnit4 counted, and each suite that left some has its count, and a warning saying how to free them, under warnings, with left naming each case that left some (before or after for one of the suite's hooks, or the one value \"before or after\" where no case began and the two cannot be told apart) and the nodes, by class, name and script. suites lists only the suites with something to say (a case that failed, erred or was skipped, a hook that failed, orphans, or an error undeclared or declared and not printed), and suitesPassed counts the rest. A before() or after() that failed comes back under hookFailures with its suite, line and whole message; failures and errors count cases and leave it out, so the verdict names it as failed hooks, and the cases after a failed before() still run and are counted. Under classes, the classes each addon declares are counted under addedInAddons and changedInAddons rather than named, and a class the rebuild puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. A suite needs a window when it declares const GDHARNESS_NEEDS_WINDOW: bool = true, or calls scene_runner and declares nothing; a run holding one has a window, at the size project.godot gives its viewport and with no sound, and windowed is true and windowNote names those suites and any saying it runs headless that shared the window. On a machine with no display such a run is headless, windowed is false, and windowNote says so. " + IMPORTED_FIRST,
|
|
17138
17303
|
parameters: {
|
|
17139
17304
|
projectPath: PROJECT_PATH,
|
|
17140
17305
|
path: {
|
|
@@ -17183,7 +17348,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17183
17348
|
rootNodeType: {
|
|
17184
17349
|
type: "string",
|
|
17185
17350
|
ops: ["create"],
|
|
17186
|
-
description: "create: the root node class, an engine class or a class_name a project script declares, which the root is made as with that script attached. Default Node. The answer names the root as scene_node get does: rootNodeType its engine class, with scriptClass for a class_name."
|
|
17351
|
+
description: "create: the root node class, an engine class or a class_name a project script declares, which the root is made as with that script attached. Default Node. The answer names the root as scene_node get does: rootNodeType its engine class, with scriptClass for a class_name. With no editor open, a class_name the class cache does not list yet is put in it first, and classCacheRebuilt says so."
|
|
17187
17352
|
},
|
|
17188
17353
|
rootNodeName: {
|
|
17189
17354
|
type: "string",
|
|
@@ -17214,7 +17379,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17214
17379
|
name: "scene_tree",
|
|
17215
17380
|
title: "Scene tree",
|
|
17216
17381
|
effect: "read",
|
|
17217
|
-
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. A node that instances another scene names that file under instanceOf, and the nodes listed under it come from that file rather than this one; its editableChildren says whether this scene, or a scene it inherits, keeps values of its own for them (scene_node set editableChildren). The root of a scene inheriting another names the scene it inherits under inherits, and every node that scene or one further down makes names its file under inheritedFrom; this file holds only the values it changes on them. A node in any group answers groups, sorted, from this file and every scene it comes from. Needs no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without making its nodes, so no script of theirs runs (autoloads still start once it has answered, as in every headless call). For the nodes of a game that is running, runtime_inspect tree.",
|
|
17382
|
+
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. type is the engine class, and a node whose script declares a class_name names it under scriptClass, as scene_node get does. A node that instances another scene names that file under instanceOf, and the nodes listed under it come from that file rather than this one; its editableChildren says whether this scene, or a scene it inherits, keeps values of its own for them (scene_node set editableChildren). The root of a scene inheriting another names the scene it inherits under inherits, and every node that scene or one further down makes names its file under inheritedFrom; this file holds only the values it changes on them. A node in any group answers groups, sorted, from this file and every scene it comes from. Needs no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without making its nodes, so no script of theirs runs (autoloads still start once it has answered, as in every headless call). For the nodes of a game that is running, runtime_inspect tree.",
|
|
17218
17383
|
parameters: {
|
|
17219
17384
|
projectPath: PROJECT_PATH,
|
|
17220
17385
|
scenePath: SCENE_PATH,
|
|
@@ -17254,7 +17419,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17254
17419
|
nodeType: {
|
|
17255
17420
|
type: "string",
|
|
17256
17421
|
ops: ["add"],
|
|
17257
|
-
description: `add: the node class, such as "CharacterBody2D", or a class_name a project script declares, which the node is made as with that script attached. Give this or instancePath. The answer names the node made as get does: nodeType its engine class, for an instance the class of its scene's root, with scriptClass when its script declares a class_name.`
|
|
17422
|
+
description: `add: the node class, such as "CharacterBody2D", or a class_name a project script declares, which the node is made as with that script attached. Give this or instancePath. The answer names the node made as get does: nodeType its engine class, for an instance the class of its scene's root, with scriptClass when its script declares a class_name. With no editor open, a class_name the class cache does not list yet is put in it first, and classCacheRebuilt says so.`
|
|
17258
17423
|
},
|
|
17259
17424
|
instancePath: {
|
|
17260
17425
|
type: "string",
|
|
@@ -17657,7 +17822,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17657
17822
|
},
|
|
17658
17823
|
symbols: {
|
|
17659
17824
|
effect: "read",
|
|
17660
|
-
summary:
|
|
17825
|
+
summary: `document symbols from the language server, each with its kind, the line and column of its name, and the lastLine of its declaration. detail is the language server's own text, which writes a default through the engine (Callable() as "null::null", a Color as a quoted tuple) and leaves out a signal's argument types; structure gives each declaration as the file writes it`,
|
|
17661
17826
|
requires: []
|
|
17662
17827
|
},
|
|
17663
17828
|
completion: {
|
|
@@ -17697,7 +17862,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17697
17862
|
resourceType: {
|
|
17698
17863
|
type: "string",
|
|
17699
17864
|
ops: ["create"],
|
|
17700
|
-
description: 'create: the resource class, such as "PhysicsMaterial" or "StandardMaterial3D", or a class_name a project script declares, which the resource is made as with that script attached.'
|
|
17865
|
+
description: 'create: the resource class, such as "PhysicsMaterial" or "StandardMaterial3D", or a class_name a project script declares, which the resource is made as with that script attached. With no editor open, a class_name the class cache does not list yet is put in it first, and classCacheRebuilt says so.'
|
|
17701
17866
|
},
|
|
17702
17867
|
properties: { ...PROPERTIES, ops: ["create", "modify"] },
|
|
17703
17868
|
script: {
|
|
@@ -17876,7 +18041,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17876
18041
|
visible: {
|
|
17877
18042
|
type: "boolean",
|
|
17878
18043
|
ops: ["start"],
|
|
17879
|
-
description:
|
|
18044
|
+
description: `start: on Windows, show the window on the desktop in use. By default a windowed run this server starts goes on a desktop of its own, where it renders and runtime_capture works but the window never shows and never takes the keyboard or the pointer. That desktop has no display, so there is no refresh for vsync to wait on: a Vulkan game waiting on one drew two frames a second, and kept to that after turning vsync off as it ran. A game there, whether this server starts it or a hidden editor plays it, is given --disable-vsync, and --max-fps ${String(HIDDEN_WINDOW_FPS)} unless engineArgs, the project's editor/run/main_run_args or its application/run/max_fps cap its frames, so it reads its vsync mode as disabled; a game that turns vsync on as it runs slows to two frames a second again. A game a visible editor plays opens on the desktop in use, and one a hidden editor (editor_launch hidden: true) plays goes on its desktop. Elsewhere than Windows the window shows either way. Default false.`
|
|
17880
18045
|
},
|
|
17881
18046
|
silent: {
|
|
17882
18047
|
type: "boolean",
|
|
@@ -18070,8 +18235,9 @@ var init_tool_definitions = __esm(() => {
|
|
|
18070
18235
|
description: "How long to wait for the scan. Default 30000."
|
|
18071
18236
|
},
|
|
18072
18237
|
reloadScript: {
|
|
18073
|
-
type: "string",
|
|
18074
|
-
|
|
18238
|
+
type: ["string", "array"],
|
|
18239
|
+
items: { type: "string" },
|
|
18240
|
+
description: "A script to recompile in the editor after the scan, or a list of them recompiled in that order, for the half a scan does not reach: a script the editor has loaded keeps the copy it built, that copy is refreshed when one of its dependencies changes rather than when it changes itself, and a scan leaves it alone. Measured here, a built copy comes back without a method added to it while script_diagnostics on the same script reads clean in the same window, so the copy the editor built and whatever the analyser resolves types against are not known to be one thing: reload for the copy, and read the diagnostics as a separate answer rather than as this one confirmed. This recompiles into the same object, so the holders that kept the stale copy alive see the new one. What the reload changed comes back under methodsAdded, methodsRemoved, constantsAdded and constantsRemoved, with each enum value as Kind.SHORT among the constants, since an enum member is held as a constant and never shows among the methods; methodsHeld and constantsHeld count what the copy holds afterwards, because a reload that compiled nothing and answered OK is the failure worth catching. listMembers adds the whole lists. A script that does not compile is not reloaded: reloadProblem gives Godot's error, says whether the copy the editor holds survived, and names the classes the file uses, whose held copies being behind is what failed one before. A @tool script is not reloaded either, since it runs inside the editor and reloading one from inside a call can take the editor down: reloadProblem says so, and editor_launch restart recompiles it. A path spelled in another case than the file is reloaded under the spelling on disk, which is the one the editor holds its copy under, and that spelling comes back under reloadedAs. A script open in a script tab with unsaved edits is reloaded from the file all the same, and unsavedInEditorNote says that saving the tab writes its text back over the file; open tabs with no unsaved edits reread what changed, as they do when the editor's window takes the focus. A list is answered under reloads, one entry for each script with its scriptPath and ok and these fields, in place of them beside the scan."
|
|
18075
18241
|
},
|
|
18076
18242
|
listMembers: {
|
|
18077
18243
|
type: "boolean",
|
|
@@ -18225,7 +18391,11 @@ var init_tool_definitions = __esm(() => {
|
|
|
18225
18391
|
},
|
|
18226
18392
|
requires: [],
|
|
18227
18393
|
operations: {
|
|
18228
|
-
tree: {
|
|
18394
|
+
tree: {
|
|
18395
|
+
effect: "read",
|
|
18396
|
+
summary: "the live scene tree, with current_scene giving the path of the scene the game is showing and the file it was loaded from, or null when it shows none",
|
|
18397
|
+
requires: []
|
|
18398
|
+
},
|
|
18229
18399
|
text: {
|
|
18230
18400
|
effect: "read",
|
|
18231
18401
|
summary: "every line of text under nodePath, the values in its fields included, in reading order, leaving out what is hidden and everything under it, and a control drawn wholly outside the viewport or scrolled out of a ScrollContainer, which the player cannot read. Every line reads as drawn: rich text without tags, text only as far as it is typed out, a translation rather than its key, an upper-case label in capitals and a secret field as its mask. Tab titles, menu bar titles, list items, a tree's column titles and rows, and an open menu's items and the titles of its separators read a line each. A line drawn as a copy under a node saying the same words, as an outline or a glow is, reads once, counted under copies",
|
|
@@ -18238,7 +18408,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18238
18408
|
},
|
|
18239
18409
|
rect: {
|
|
18240
18410
|
effect: "read",
|
|
18241
|
-
summary: "one node's rectangle or position, in canvas and in window pixels, and for a Control whether it is in view. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 2D node answers where it stands, and a sprite, collision shape, polygon or line also the rectangle it covers under covers. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one",
|
|
18411
|
+
summary: "one node's rectangle or position, in canvas and in window pixels, and for a Control whether it is in view, with drawn_over naming what a pointer at its centre would reach instead, such as a dialog over the screen. Canvas is the node's own viewport; window is where it is drawn in the game's window, through any embedded dialog or SubViewport it sits in, which is what runtime_input mouse_click and mouse_motion take. A 2D node answers where it stands, and a sprite, collision shape, polygon or line also the rectangle it covers under covers. A 3D node answers with the point to aim at, which is the middle of what it draws rather than the origin it stands on, the rectangle it covers under covers, the camera that drew it, and behind_camera when it is not in front of one",
|
|
18242
18412
|
requires: ["nodePath"]
|
|
18243
18413
|
},
|
|
18244
18414
|
property: {
|
|
@@ -18279,7 +18449,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18279
18449
|
value: {
|
|
18280
18450
|
ops: ["set"],
|
|
18281
18451
|
blank: true,
|
|
18282
|
-
description: `set: the value, fitted to the property's type. ${WHOLE_NUMBERS} A number the property would have to cut is refused rather than truncated: a fraction into an int, a number other than 0 or 1 into a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}, which is the form a read answers with, and one carrying a field its type has not, such as a z for a Vector2, is refused naming it. A colour may also be given as its html code, "#ff8000" or "#ff800080". Text is written as sent: the JSON string "" writes an empty string, and quote marks inside the string are written into the text. An enum property takes the name of a value as well as its number, as the script or the inspector spells it, and a name none of its values has is refused listing them. A number past the range a property is exported with, or one none of its enum values has, is written, as the engine writes it, and the answer says so under outside_declared. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent, a fraction into one holding an int included. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name.`
|
|
18452
|
+
description: `set: the value, fitted to the property's type. ${WHOLE_NUMBERS} A number the property would have to cut is refused rather than truncated: a fraction into an int, a number other than 0 or 1 into a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}, which is the form a read answers with, and one carrying a field its type has not, such as a z for a Vector2, is refused naming it. A colour may also be given as its html code, "#ff8000" or "#ff800080". Text is written as sent: the JSON string "" writes an empty string, and quote marks inside the string are written into the text. An enum property takes the name of a value as well as its number, as the script or the inspector spells it, and a name none of its values has is refused listing them. A number past the range a property is exported with, or one none of its enum values has, is written, as the engine writes it, and the answer says so under outside_declared. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. A resource is named by the res:// or uid:// path of its file, "res://art/hero.png", and loaded as the game loads one, so a resource it already holds is that same instance; the record a read answers a node or a filed resource with names it too. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent, a fraction into one holding an int included. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name.`
|
|
18283
18453
|
},
|
|
18284
18454
|
method: {
|
|
18285
18455
|
type: "string",
|
|
@@ -18289,7 +18459,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18289
18459
|
args: {
|
|
18290
18460
|
type: "array",
|
|
18291
18461
|
ops: ["call"],
|
|
18292
|
-
description: `call: the arguments, fitted to the method's parameter types. An RID is passed as the {"_type": "RID", "id": ...} a read answers it with, such as a viewport's for RenderingServer's render timing. ${WHOLE_NUMBERS} A number a parameter would have to cut is refused rather than truncated: a fraction for an int, a number other than 0 or 1 for a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, "_game:run:wares:3", or one starting at a node, "/root/Main/Hud" or "/root/Main:_game:run". A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], or as a packed array, such as PackedVector2Array, is built from a JSON list or object element by element, points as their fields and objects named by their paths, and an element that cannot become what it holds is refused naming it, as a fraction where whole numbers go is. A parameter typed as an enum takes a name of its values as well as a number, Mood's ANGRY or Node's PROCESS_MODE_DISABLED, and a number none of its values has is passed on and named under outside_declared. The method itself takes its arguments here only: brackets on it holding arguments are refused.`
|
|
18462
|
+
description: `call: the arguments, fitted to the method's parameter types. An RID is passed as the {"_type": "RID", "id": ...} a read answers it with, such as a viewport's for RenderingServer's render timing. ${WHOLE_NUMBERS} A number a parameter would have to cut is refused rather than truncated: a fraction for an int, a number other than 0 or 1 for a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}. A parameter typed as an object takes a path naming one the game holds and is handed that instance: a colon path read from nodePath, "_game:run:wares:3", or one starting at a node, "/root/Main/Hud" or "/root/Main:_game:run". A resource is named by the res:// or uid:// path of its file, as set takes one. A path that reaches no object, or an object of another class than the parameter declares, is refused, and so are too few or too many arguments. The engine names a parameter typed as a script class without class_name by the class the script extends, so an object of that class with another script passes that check and the engine refuses the call: the answer says the method did not run, with the engine's reason. A parameter typed as a list or map, such as Array[int] or Array[Gear], or as a packed array, such as PackedVector2Array, is built from a JSON list or object element by element, points as their fields and objects named by their paths, and an element that cannot become what it holds is refused naming it, as a fraction where whole numbers go is. A parameter typed as an enum takes a name of its values as well as a number, Mood's ANGRY or Node's PROCESS_MODE_DISABLED, and a number none of its values has is passed on and named under outside_declared. The method itself takes its arguments here only: brackets on it holding arguments are refused.`
|
|
18293
18463
|
},
|
|
18294
18464
|
properties: {
|
|
18295
18465
|
type: ["boolean", "array"],
|
|
@@ -18635,7 +18805,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18635
18805
|
includeHidden: {
|
|
18636
18806
|
type: "boolean",
|
|
18637
18807
|
ops: ["until"],
|
|
18638
|
-
description: "until, with says: count words on a hidden node too, and on one drawn outside the viewport or scrolled out of a ScrollContainer. Default false, since a wait for words is a wait for them to be shown, and a wait that runs out while such a node holds them says so under unseen_note: a button that exists hidden through a whole animation carries its words the whole time and satisfied the wait at once, and every wait on that screen fell back to counting frames."
|
|
18808
|
+
description: "until, with says: count words on a hidden node too, and on one drawn outside the viewport or scrolled out of a ScrollContainer. Default false, since a wait for words is a wait for them to be shown, and a wait that runs out while such a node holds them says so under unseen_note: a button that exists hidden through a whole animation carries its words the whole time and satisfied the wait at once, and every wait on that screen fell back to counting frames. What is drawn over a node is not looked at, so words under a dialog or a panel count either way."
|
|
18639
18809
|
},
|
|
18640
18810
|
timeoutMs: {
|
|
18641
18811
|
type: "integer",
|
|
@@ -18711,7 +18881,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18711
18881
|
},
|
|
18712
18882
|
step_out: {
|
|
18713
18883
|
effect: "additive",
|
|
18714
|
-
summary: "run to the end of the current function and stop at the next line run at its caller's depth: in its caller, or, when the call was the last thing its caller did, wherever script runs next, such as the caller's next call; a game this server started
|
|
18884
|
+
summary: "run to the end of the current function and stop at the next line run at its caller's depth: in its caller, or, when the call was the last thing its caller did, wherever script runs next, such as the caller's next call; only for a game this server started",
|
|
18715
18885
|
requires: []
|
|
18716
18886
|
}
|
|
18717
18887
|
}
|
|
@@ -18729,7 +18899,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18729
18899
|
expression: {
|
|
18730
18900
|
type: "string",
|
|
18731
18901
|
ops: ["evaluate"],
|
|
18732
|
-
description: "evaluate: a GDScript expression, as Godot's Expression class parses it: members of self by name, locals, autoloads and global classes, operators, indexing and calls, such as wave.spawned[0] or Ledger.entries.size(). An expression that does not parse, or fails as it runs, comes to null, and the game
|
|
18902
|
+
description: "evaluate: a GDScript expression, as Godot's Expression class parses it: members of self by name, locals, autoloads and global classes, operators, indexing and calls, such as wave.spawned[0] or Ledger.entries.size(). An expression that does not parse, or fails as it runs, comes to null with failed saying why, as the engine words it, and a null with no failed is the value; for a game the editor plays, the reason reaches only the editor's Debugger panel, so a null there carries a note saying it may be either. The game reports what an expression raises while it is held inside a runtime_invoke call as raised by that call, and the call's answer says so under errorsNote."
|
|
18733
18903
|
}
|
|
18734
18904
|
},
|
|
18735
18905
|
requires: [],
|
|
@@ -19330,6 +19500,21 @@ function windowNeedsOf(scripts, paths) {
|
|
|
19330
19500
|
headless: sorted.filter((suite) => suite.headless).map((suite) => suite.path)
|
|
19331
19501
|
};
|
|
19332
19502
|
}
|
|
19503
|
+
function capsFrames(projectPath) {
|
|
19504
|
+
const file = `${projectPath}/project.godot`;
|
|
19505
|
+
if (!existsSync22(file)) {
|
|
19506
|
+
return false;
|
|
19507
|
+
}
|
|
19508
|
+
const cap = parseProjectGodot(readFileSync21(file, "utf8"))["application"]?.["run/max_fps"];
|
|
19509
|
+
return typeof cap === "number" && cap > 0;
|
|
19510
|
+
}
|
|
19511
|
+
function gdUnitFailsOnPushErrors(projectPath) {
|
|
19512
|
+
const file = `${projectPath}/project.godot`;
|
|
19513
|
+
if (!existsSync22(file)) {
|
|
19514
|
+
return false;
|
|
19515
|
+
}
|
|
19516
|
+
return parseProjectGodot(readFileSync21(file, "utf8"))["gdunit4"]?.["report/godot/push_error"] === true;
|
|
19517
|
+
}
|
|
19333
19518
|
function viewportOf(projectPath) {
|
|
19334
19519
|
const file = `${projectPath}/project.godot`;
|
|
19335
19520
|
if (!existsSync22(file)) {
|
|
@@ -40993,7 +41178,7 @@ var init_autoload_failure = __esm(() => {
|
|
|
40993
41178
|
});
|
|
40994
41179
|
|
|
40995
41180
|
// src/runtime-client.ts
|
|
40996
|
-
import { existsSync as existsSync25, readdirSync as
|
|
41181
|
+
import { existsSync as existsSync25, readdirSync as readdirSync16, readFileSync as readFileSync25, statSync as statSync15, unlinkSync as unlinkSync3 } from "node:fs";
|
|
40997
41182
|
import { createConnection as createConnection2 } from "node:net";
|
|
40998
41183
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
40999
41184
|
import { dirname as dirname14, join as join30, resolve as resolve11 } from "node:path";
|
|
@@ -41169,7 +41354,7 @@ function announcedIn(directory) {
|
|
|
41169
41354
|
const found = [];
|
|
41170
41355
|
const candidates = [];
|
|
41171
41356
|
const entries = [];
|
|
41172
|
-
for (const entry of
|
|
41357
|
+
for (const entry of readdirSync16(directory)) {
|
|
41173
41358
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
41174
41359
|
if (report) {
|
|
41175
41360
|
sweepErrorReport(join30(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
@@ -42562,6 +42747,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
42562
42747
|
const frameId = typeof safeArgs.frameId === "number" ? safeArgs.frameId : undefined;
|
|
42563
42748
|
const expression = typeof safeArgs.expression === "string" ? safeArgs.expression : "";
|
|
42564
42749
|
const evaluated = await client.evaluate(expression, frameId);
|
|
42750
|
+
const nil = evaluated.type === "Nil" || evaluated.value === "null" || evaluated.value === "<null>";
|
|
42565
42751
|
return {
|
|
42566
42752
|
content: [
|
|
42567
42753
|
{
|
|
@@ -42570,7 +42756,10 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
42570
42756
|
frame: evaluated.frame,
|
|
42571
42757
|
expression,
|
|
42572
42758
|
value: evaluated.value,
|
|
42573
|
-
...evaluated.type === undefined ? {} : { type: evaluated.type }
|
|
42759
|
+
...evaluated.type === undefined ? {} : { type: evaluated.type },
|
|
42760
|
+
...nil ? {
|
|
42761
|
+
note: "null is also what an expression comes to when it does not parse or fails as it runs. For a game the editor plays, why reaches only the editor's Debugger panel, so this cannot say which it was."
|
|
42762
|
+
} : {}
|
|
42574
42763
|
})
|
|
42575
42764
|
}
|
|
42576
42765
|
]
|
|
@@ -43507,19 +43696,35 @@ function planMove(args, plan) {
|
|
|
43507
43696
|
}
|
|
43508
43697
|
const lines = script?.lines ?? new Lines(file.text);
|
|
43509
43698
|
for (const path of named) {
|
|
43510
|
-
const
|
|
43511
|
-
|
|
43512
|
-
|
|
43513
|
-
|
|
43514
|
-
|
|
43515
|
-
|
|
43516
|
-
|
|
43517
|
-
|
|
43518
|
-
|
|
43519
|
-
|
|
43520
|
-
|
|
43521
|
-
why: `names ${path}
|
|
43522
|
-
}
|
|
43699
|
+
const after = String.raw`(?![\w-]|\.\w)`;
|
|
43700
|
+
const escaped = (text) => text.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
|
43701
|
+
const fromRoot = path.startsWith("res://") ? path.slice("res://".length) : "";
|
|
43702
|
+
const spellings = [
|
|
43703
|
+
{
|
|
43704
|
+
pattern: new RegExp(`${escaped(path)}${after}`, "g"),
|
|
43705
|
+
why: `names ${path} outside a string the engine reads as a path`
|
|
43706
|
+
},
|
|
43707
|
+
...fromRoot === "" ? [] : [
|
|
43708
|
+
{
|
|
43709
|
+
pattern: new RegExp(`(?<![\\w./-])${escaped(fromRoot)}${after}`, "g"),
|
|
43710
|
+
why: `names ${path} as ${fromRoot}, a path from the project's root without res://, which the engine does not read as one`
|
|
43711
|
+
}
|
|
43712
|
+
]
|
|
43713
|
+
];
|
|
43714
|
+
for (const { pattern, why } of spellings) {
|
|
43715
|
+
for (const match of file.text.matchAll(pattern)) {
|
|
43716
|
+
const offset = match.index;
|
|
43717
|
+
const covered = edits.some((one) => one.file === file.path && one.offset <= offset && offset < one.offset + one.length);
|
|
43718
|
+
if (!covered) {
|
|
43719
|
+
const region = script?.regions.find((one) => one.start <= offset && offset < one.end);
|
|
43720
|
+
mentions.push({
|
|
43721
|
+
file: file.path,
|
|
43722
|
+
line: lines.positionOf(offset).line + 1,
|
|
43723
|
+
kind: region !== undefined ? region.kind : /\.(tscn|tres)$/.test(file.path) ? "scene" : "text",
|
|
43724
|
+
text: lines.lineAt(offset).trim(),
|
|
43725
|
+
why
|
|
43726
|
+
});
|
|
43727
|
+
}
|
|
43523
43728
|
}
|
|
43524
43729
|
}
|
|
43525
43730
|
}
|
|
@@ -43656,7 +43861,7 @@ var init_rename = __esm(() => {
|
|
|
43656
43861
|
});
|
|
43657
43862
|
|
|
43658
43863
|
// src/rename-symbol.ts
|
|
43659
|
-
import { existsSync as existsSync30, mkdirSync as mkdirSync17, readdirSync as
|
|
43864
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync17, readdirSync as readdirSync17, realpathSync as realpathSync4, rmSync as rmSync16, writeFileSync as writeFileSync18 } from "node:fs";
|
|
43660
43865
|
import { basename as basename9, dirname as dirname21, join as join37, relative as relative7, resolve as resolve12 } from "node:path";
|
|
43661
43866
|
function resourcePathOf(projectPath, absolute) {
|
|
43662
43867
|
const inside = relative7(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
@@ -43906,7 +44111,7 @@ async function renameSymbol(request, services) {
|
|
|
43906
44111
|
}
|
|
43907
44112
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? [] : [declaringPath]);
|
|
43908
44113
|
const rewrites = rewritesOf(plan, texts, moveTo === null || request.newScriptPath === null ? null : { from: declaringPath, to: moveTo, absoluteTo: request.newScriptPath });
|
|
43909
|
-
const left = leftAloneReport(asWritten(plan.mentions, rewrites));
|
|
44114
|
+
const left = leftAloneReport(asWritten(plan.mentions, rewrites), [symbol]);
|
|
43910
44115
|
const graph = new ClassGraph(scripts, declared);
|
|
43911
44116
|
const rank = (path) => (declaringFiles.includes(path) ? 0 : 1000) + graph.ancestors(`${path}#`).length;
|
|
43912
44117
|
const scriptsInOrder = rewrites.map((rewrite) => rewrite.path).filter((path) => path.endsWith(".gd")).sort((a, b) => rank(a) - rank(b) || a.localeCompare(b)).map((path) => path === declaringPath && moveTo !== null ? moveTo : path);
|
|
@@ -43990,6 +44195,7 @@ function changedReport(edits, rewrites) {
|
|
|
43990
44195
|
const lines = rewrite.after.split(`
|
|
43991
44196
|
`);
|
|
43992
44197
|
const byLine = new Map;
|
|
44198
|
+
const firstAt = new Map;
|
|
43993
44199
|
const starts = [0];
|
|
43994
44200
|
for (let at = rewrite.before.indexOf(`
|
|
43995
44201
|
`);at !== -1; at = rewrite.before.indexOf(`
|
|
@@ -44007,6 +44213,8 @@ function changedReport(edits, rewrites) {
|
|
|
44007
44213
|
line += 1;
|
|
44008
44214
|
}
|
|
44009
44215
|
byLine.set(line, (byLine.get(line) ?? new Set).add(edit.kind));
|
|
44216
|
+
const column = edit.offset - (starts[line] ?? 0);
|
|
44217
|
+
firstAt.set(line, Math.min(firstAt.get(line) ?? column, column));
|
|
44010
44218
|
}
|
|
44011
44219
|
return {
|
|
44012
44220
|
file: rewrite.path,
|
|
@@ -44014,7 +44222,11 @@ function changedReport(edits, rewrites) {
|
|
|
44014
44222
|
lines: [...byLine.entries()].sort(([a], [b]) => a - b).map(([line, kinds]) => ({
|
|
44015
44223
|
line: line + 1,
|
|
44016
44224
|
kinds: [...kinds],
|
|
44017
|
-
text: (
|
|
44225
|
+
text: (() => {
|
|
44226
|
+
const whole = (lines[line] ?? "").replace(/\r$/, "");
|
|
44227
|
+
const indent = whole.length - whole.trimStart().length;
|
|
44228
|
+
return quoted2(whole.trim(), (firstAt.get(line) ?? 0) - indent);
|
|
44229
|
+
})()
|
|
44018
44230
|
}))
|
|
44019
44231
|
};
|
|
44020
44232
|
});
|
|
@@ -44027,7 +44239,7 @@ function asWritten(mentions, rewrites) {
|
|
|
44027
44239
|
return line === undefined ? mention : { ...mention, text: line.replace(/\r$/, "").trim() };
|
|
44028
44240
|
});
|
|
44029
44241
|
}
|
|
44030
|
-
function leftAloneReport(mentions) {
|
|
44242
|
+
function leftAloneReport(mentions, terms) {
|
|
44031
44243
|
const full = [];
|
|
44032
44244
|
const prose = new Map;
|
|
44033
44245
|
const others = new Map;
|
|
@@ -44057,7 +44269,7 @@ ${mention.kind}`;
|
|
|
44057
44269
|
others.set(mention.why, files);
|
|
44058
44270
|
counts.otherSymbols += 1;
|
|
44059
44271
|
} else {
|
|
44060
|
-
full.push(mention);
|
|
44272
|
+
full.push({ ...mention, text: quotedAround(mention.text, terms) });
|
|
44061
44273
|
counts.quoted += 1;
|
|
44062
44274
|
}
|
|
44063
44275
|
}
|
|
@@ -44114,12 +44326,24 @@ function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
|
44114
44326
|
const path = join37(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
44115
44327
|
writeFileSync18(path, `${JSON.stringify(detail, null, 2)}
|
|
44116
44328
|
`, "utf8");
|
|
44117
|
-
const kept =
|
|
44329
|
+
const kept = readdirSync17(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
44118
44330
|
for (const old of kept.slice(10)) {
|
|
44119
44331
|
rmSync16(join37(directory, old), { force: true });
|
|
44120
44332
|
}
|
|
44121
44333
|
return path;
|
|
44122
44334
|
}
|
|
44335
|
+
function quoted2(line, at) {
|
|
44336
|
+
if (line.length <= QUOTED_CHARS) {
|
|
44337
|
+
return line;
|
|
44338
|
+
}
|
|
44339
|
+
const start = Math.max(0, Math.min(at - Math.floor(QUOTED_CHARS / 3), line.length - QUOTED_CHARS));
|
|
44340
|
+
const end = start + QUOTED_CHARS;
|
|
44341
|
+
return `${start > 0 ? "..." : ""}${line.slice(start, end)}${end < line.length ? "..." : ""}`;
|
|
44342
|
+
}
|
|
44343
|
+
function quotedAround(text, terms) {
|
|
44344
|
+
const at = terms.map((term) => text.indexOf(term)).filter((found) => found >= 0);
|
|
44345
|
+
return quoted2(text, at.length > 0 ? Math.min(...at) : 0);
|
|
44346
|
+
}
|
|
44123
44347
|
function summaryOf(edits, mentions, rewrites, left) {
|
|
44124
44348
|
const count = (values) => {
|
|
44125
44349
|
const counted = {};
|
|
@@ -44136,7 +44360,7 @@ function summaryOf(edits, mentions, rewrites, left) {
|
|
|
44136
44360
|
leftAloneByKind: count(mentions.map((mention) => mention.kind))
|
|
44137
44361
|
};
|
|
44138
44362
|
}
|
|
44139
|
-
var WHAT, LEFT_ALONE_ANSWER_LINES = 300, detailWrites = 0, PROSE_KINDS;
|
|
44363
|
+
var WHAT, LEFT_ALONE_ANSWER_LINES = 300, detailWrites = 0, PROSE_KINDS, QUOTED_CHARS = 240;
|
|
44140
44364
|
var init_rename_symbol = __esm(() => {
|
|
44141
44365
|
init_gdscript_source();
|
|
44142
44366
|
init_lsp_client();
|
|
@@ -44159,7 +44383,7 @@ var init_rename_symbol = __esm(() => {
|
|
|
44159
44383
|
import {
|
|
44160
44384
|
existsSync as existsSync31,
|
|
44161
44385
|
mkdirSync as mkdirSync18,
|
|
44162
|
-
readdirSync as
|
|
44386
|
+
readdirSync as readdirSync18,
|
|
44163
44387
|
readFileSync as readFileSync33,
|
|
44164
44388
|
renameSync as renameSync4,
|
|
44165
44389
|
statSync as statSync17,
|
|
@@ -44167,7 +44391,7 @@ import {
|
|
|
44167
44391
|
} from "node:fs";
|
|
44168
44392
|
import { basename as basename10, dirname as dirname22, extname as extname4, join as join38, relative as relative8 } from "node:path";
|
|
44169
44393
|
function filesUnder(directory) {
|
|
44170
|
-
return
|
|
44394
|
+
return readdirSync18(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
44171
44395
|
const path = join38(directory, entry.name);
|
|
44172
44396
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
44173
44397
|
});
|
|
@@ -44210,7 +44434,7 @@ function binaryResources(projectPath) {
|
|
|
44210
44434
|
if (existsSync31(join38(directory, ".gdignore"))) {
|
|
44211
44435
|
return;
|
|
44212
44436
|
}
|
|
44213
|
-
for (const entry of
|
|
44437
|
+
for (const entry of readdirSync18(directory, { withFileTypes: true })) {
|
|
44214
44438
|
if (entry.name.startsWith(".")) {
|
|
44215
44439
|
continue;
|
|
44216
44440
|
}
|
|
@@ -44338,7 +44562,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
44338
44562
|
...mention,
|
|
44339
44563
|
file: afterwards(mention.file)
|
|
44340
44564
|
}));
|
|
44341
|
-
const left = leftAloneReport(mentions);
|
|
44565
|
+
const left = leftAloneReport(mentions, [from, ...moves.keys()].flatMap((path) => [path, path.replace(/^res:\/\//, "")]));
|
|
44342
44566
|
const absoluteOf = (path) => join38(projectPath, path.slice("res://".length));
|
|
44343
44567
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
44344
44568
|
const moved = [...moves].map(([source, target]) => {
|
|
@@ -48522,7 +48746,7 @@ var init_restart_note = __esm(() => {
|
|
|
48522
48746
|
|
|
48523
48747
|
// src/run-record.ts
|
|
48524
48748
|
import { createHash as createHash7 } from "node:crypto";
|
|
48525
|
-
import { mkdirSync as mkdirSync21, openSync as openSync5, readdirSync as
|
|
48749
|
+
import { mkdirSync as mkdirSync21, openSync as openSync5, readdirSync as readdirSync19, readFileSync as readFileSync35, rmSync as rmSync18, statSync as statSync19 } from "node:fs";
|
|
48526
48750
|
import { basename as basename11, join as join41, resolve as resolve13 } from "node:path";
|
|
48527
48751
|
function runsDirectory() {
|
|
48528
48752
|
return join41(runtimeDirectory(), "runs");
|
|
@@ -48627,7 +48851,7 @@ function everyRunRecord() {
|
|
|
48627
48851
|
for (const directory of runtimeDirectories()) {
|
|
48628
48852
|
let entries;
|
|
48629
48853
|
try {
|
|
48630
|
-
entries =
|
|
48854
|
+
entries = readdirSync19(join41(directory, "runs"));
|
|
48631
48855
|
} catch {
|
|
48632
48856
|
continue;
|
|
48633
48857
|
}
|
|
@@ -48892,7 +49116,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
48892
49116
|
]));
|
|
48893
49117
|
let entries;
|
|
48894
49118
|
try {
|
|
48895
|
-
entries =
|
|
49119
|
+
entries = readdirSync19(directory);
|
|
48896
49120
|
} catch {
|
|
48897
49121
|
return;
|
|
48898
49122
|
}
|
|
@@ -49314,7 +49538,7 @@ import {
|
|
|
49314
49538
|
fstatSync as fstatSync2,
|
|
49315
49539
|
mkdirSync as mkdirSync23,
|
|
49316
49540
|
openSync as openSync6,
|
|
49317
|
-
readdirSync as
|
|
49541
|
+
readdirSync as readdirSync20,
|
|
49318
49542
|
readFileSync as readFileSync37,
|
|
49319
49543
|
readSync as readSync3,
|
|
49320
49544
|
realpathSync as realpathSync5,
|
|
@@ -49518,6 +49742,13 @@ function restartsOf(run) {
|
|
|
49518
49742
|
endedAt: new Date(one.endedAt).toISOString()
|
|
49519
49743
|
}));
|
|
49520
49744
|
}
|
|
49745
|
+
async function handToTheGame(slot, pid, projectPath) {
|
|
49746
|
+
const started = (await startTimesOf([pid])).get(pid);
|
|
49747
|
+
const by = `a game${projectPath === null ? "" : ` of ${projectPath}`} the editor played for editor_run`;
|
|
49748
|
+
if (started === undefined || !slot.handTo({ pid, started, by })) {
|
|
49749
|
+
slot.release();
|
|
49750
|
+
}
|
|
49751
|
+
}
|
|
49521
49752
|
function followTheRestart(run) {
|
|
49522
49753
|
if (run.throughEditor || run.pid === null || run.projectPath === null) {
|
|
49523
49754
|
return false;
|
|
@@ -50044,6 +50275,13 @@ function presetsIn(projectPath) {
|
|
|
50044
50275
|
function presetPlatform(projectPath, preset) {
|
|
50045
50276
|
return presetsIn(projectPath)?.find((held) => held.name === preset)?.platform ?? null;
|
|
50046
50277
|
}
|
|
50278
|
+
function engineEndSaid(ending) {
|
|
50279
|
+
if (ending.failure === null && ending.exitSignal === null) {
|
|
50280
|
+
return `the engine ended with ${exitCodeText(ending.exitCode)}`;
|
|
50281
|
+
}
|
|
50282
|
+
const how = howItEnded(ending);
|
|
50283
|
+
return how.startsWith("ended by") ? `the engine was ${how}` : how;
|
|
50284
|
+
}
|
|
50047
50285
|
function exportPresetRefusal(projectPath, preset) {
|
|
50048
50286
|
const presets = presetsIn(projectPath);
|
|
50049
50287
|
if (presets === null) {
|
|
@@ -50055,7 +50293,7 @@ function exportPresetRefusal(projectPath, preset) {
|
|
|
50055
50293
|
}
|
|
50056
50294
|
return names.length === 0 ? `export_presets.cfg names no preset, so there is no '${preset}' to export with. project_export add_preset adds one.` : `This project has no export preset named '${preset}'. Its presets are ${names.map((name) => `'${name}'`).join(", ")}.`;
|
|
50057
50295
|
}
|
|
50058
|
-
function exportAnswer(ending, asked, before, after, beside = [], docCacheNote = null) {
|
|
50296
|
+
function exportAnswer(ending, asked, before, after, beside = [], docCacheNote = null, bytes = null) {
|
|
50059
50297
|
const { log } = ending;
|
|
50060
50298
|
const written = after !== null && after !== before;
|
|
50061
50299
|
const notCopied = librariesNotCopied(log.all);
|
|
@@ -50067,6 +50305,7 @@ function exportAnswer(ending, asked, before, after, beside = [], docCacheNote =
|
|
|
50067
50305
|
failure: ending.failure ?? undefined,
|
|
50068
50306
|
errors: log.count("error"),
|
|
50069
50307
|
warnings: log.count("warning"),
|
|
50308
|
+
...written && bytes !== null ? { bytes } : {},
|
|
50070
50309
|
...beside.length === 0 ? {} : { alsoWritten: beside },
|
|
50071
50310
|
...notCopied.length === 0 ? {} : {
|
|
50072
50311
|
librariesNotLoaded: notCopied,
|
|
@@ -50078,7 +50317,24 @@ function exportAnswer(ending, asked, before, after, beside = [], docCacheNote =
|
|
|
50078
50317
|
if (verdict.exported) {
|
|
50079
50318
|
return { content: [{ type: "text", text: answerJson(verdict) }] };
|
|
50080
50319
|
}
|
|
50081
|
-
|
|
50320
|
+
if (written) {
|
|
50321
|
+
const errors = log.count("error");
|
|
50322
|
+
const because = [
|
|
50323
|
+
...ending.exitCode === 0 && ending.failure === null && ending.exitSignal === null ? [] : [engineEndSaid(ending)],
|
|
50324
|
+
...errors === 0 ? [] : [`it printed ${String(errors)} ${errors === 1 ? "error" : "errors"}, under entries`]
|
|
50325
|
+
];
|
|
50326
|
+
return {
|
|
50327
|
+
content: [
|
|
50328
|
+
{
|
|
50329
|
+
type: "text",
|
|
50330
|
+
text: `Export with preset '${asked.preset}' wrote ${asked.outputPath}, but it is not counted as exported, as ${because.join(" and ")}.`
|
|
50331
|
+
},
|
|
50332
|
+
{ type: "text", text: answerJson(verdict) }
|
|
50333
|
+
],
|
|
50334
|
+
isError: true
|
|
50335
|
+
};
|
|
50336
|
+
}
|
|
50337
|
+
const stale = after === null ? "" : ` The ${asked.outputPath} there is from before this export, which did not write it.`;
|
|
50082
50338
|
const why = ending.exitSignal === null ? stale : ` The engine was ${howItEnded(ending)}, so it has no exit code.${stale}`;
|
|
50083
50339
|
return {
|
|
50084
50340
|
content: [
|
|
@@ -50097,7 +50353,7 @@ function writtenBeside(before, after, output, within) {
|
|
|
50097
50353
|
function filesIn(folder) {
|
|
50098
50354
|
const found = new Map;
|
|
50099
50355
|
try {
|
|
50100
|
-
for (const entry of
|
|
50356
|
+
for (const entry of readdirSync20(folder, { withFileTypes: true })) {
|
|
50101
50357
|
if (entry.isFile()) {
|
|
50102
50358
|
found.set(entry.name, statSync20(join44(folder, entry.name)).mtimeMs);
|
|
50103
50359
|
}
|
|
@@ -50689,7 +50945,7 @@ function elsewhereIn(projectPath, asked) {
|
|
|
50689
50945
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
50690
50946
|
let near = [];
|
|
50691
50947
|
try {
|
|
50692
|
-
near =
|
|
50948
|
+
near = readdirSync20(projectPath, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.toLowerCase() !== root).map((entry) => entry.name).filter((name) => name.toLowerCase().startsWith("test") || root.startsWith(name.toLowerCase()));
|
|
50693
50949
|
} catch {
|
|
50694
50950
|
return "";
|
|
50695
50951
|
}
|
|
@@ -50833,6 +51089,20 @@ function ownArguments(params) {
|
|
|
50833
51089
|
function needsItsOwnProcess(asked) {
|
|
50834
51090
|
return asked.savesIn !== undefined || asked.env !== undefined;
|
|
50835
51091
|
}
|
|
51092
|
+
function staleCopyNote(problem) {
|
|
51093
|
+
return `The editor serving the project still holds the copy of the script it compiled before, since reloading it failed: ${problem.replace(/\.$/, "")}.`;
|
|
51094
|
+
}
|
|
51095
|
+
function unheldNote(changed) {
|
|
51096
|
+
if (!isPlainObject3(changed)) {
|
|
51097
|
+
return "";
|
|
51098
|
+
}
|
|
51099
|
+
const unheld = Object.entries(changed).filter(([, change]) => isPlainObject3(change) && change["was"] === null).map(([name]) => name);
|
|
51100
|
+
if (unheld.length === 0) {
|
|
51101
|
+
return "";
|
|
51102
|
+
}
|
|
51103
|
+
const one = unheld.length === 1;
|
|
51104
|
+
return ` ${unheld.join(", ")} ${one ? "was" : "were"} not in the import file before, so only that reimport can tell whether the importer has ${one ? "it" : "them"}: one it lacks is dropped from the file then, and the reimport through set_options says which.`;
|
|
51105
|
+
}
|
|
50836
51106
|
|
|
50837
51107
|
class GodotServer {
|
|
50838
51108
|
stdio = null;
|
|
@@ -52464,7 +52734,7 @@ class GodotServer {
|
|
|
52464
52734
|
...written,
|
|
52465
52735
|
payload: {
|
|
52466
52736
|
...written.payload,
|
|
52467
|
-
note:
|
|
52737
|
+
note: `Not reimported: the editor imports it with these options on its next scan, or project_import reimport does now.${unheldNote(written.payload["changed"])}`
|
|
52468
52738
|
}
|
|
52469
52739
|
} : written);
|
|
52470
52740
|
}
|
|
@@ -52825,7 +53095,7 @@ class GodotServer {
|
|
|
52825
53095
|
return this.createErrorResponse(place.reason);
|
|
52826
53096
|
}
|
|
52827
53097
|
if (place !== null && !existsSync34(place.absolutePath)) {
|
|
52828
|
-
const folders =
|
|
53098
|
+
const folders = readdirSync20(project.value.path, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort();
|
|
52829
53099
|
return this.createErrorResponse(`There is nothing at res://${place.relativePath} to search.${folders.length === 0 ? "" : ` The project's folders are ${folders.join(", ")}.`}`);
|
|
52830
53100
|
}
|
|
52831
53101
|
if (place !== null && !isWithinRoot(realpathSync5.native(project.value.path), realpathSync5.native(place.absolutePath))) {
|
|
@@ -53044,7 +53314,7 @@ ${platform}`;
|
|
|
53044
53314
|
return this.createErrorResponse(`Export could not be run: ${ending.failure ?? "the engine gave no exit status"}`);
|
|
53045
53315
|
}
|
|
53046
53316
|
const beside = writtenBeside(listedBefore, filesIn(folder), basename13(output.absolutePath), dirname25(output.relativePath).replaceAll("\\", "/"));
|
|
53047
|
-
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug, ...pack ? { pack } : {} }, before, writtenAt(), beside, docCacheNote);
|
|
53317
|
+
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug, ...pack ? { pack } : {} }, before, writtenAt(), beside, docCacheNote, existsSync34(output.absolutePath) ? statSync20(output.absolutePath).size : null);
|
|
53048
53318
|
}
|
|
53049
53319
|
reportTestProgress(log) {
|
|
53050
53320
|
const progress = callProgress();
|
|
@@ -53316,7 +53586,7 @@ ${platform}`;
|
|
|
53316
53586
|
const casePrinted = withNestedErrors(readRun(existsSync34(engineLog) ? readFileSync37(engineLog, "utf8") : ""), run.log.all);
|
|
53317
53587
|
const declaredErrors = readExpected(expectedFile);
|
|
53318
53588
|
try {
|
|
53319
|
-
const written = existsSync34(reportsDir) && notCompiled === null ?
|
|
53589
|
+
const written = existsSync34(reportsDir) && notCompiled === null ? readdirSync20(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
53320
53590
|
const newest = written.at(-1);
|
|
53321
53591
|
if (newest !== undefined) {
|
|
53322
53592
|
report = parseJUnit(readFileSync37(join44(reportsDir, newest, "results.xml"), "utf8"));
|
|
@@ -53589,6 +53859,7 @@ ${error.text}`;
|
|
|
53589
53859
|
uncaughtNote(uncaught),
|
|
53590
53860
|
undeclaredErrors.length > 0 ? `An engine error or warning a suite does not declare fails the run, as gdharness test judges it; ${declaringAdvice(undeclaredErrors[0]?.case ?? "test_name", undeclaredErrors[0]?.warning === true)}, once for each time it prints it${undeclaredErrors.some((error) => error.warning !== undeclaredErrors[0]?.warning) ? undeclaredErrors[0]?.warning === true ? ", and an error in its GDHARNESS_EXPECTED_ERRORS" : ", and a warning in its GDHARNESS_EXPECTED_WARNINGS" : ""}.${declaredAs}` : undefined,
|
|
53591
53861
|
declaredNotPrinted.length > 0 ? "A declared error or warning the case did not print fails the run too, since the case no longer reaches what it was written to test." : undefined,
|
|
53862
|
+
gdUnitFailsOnPushErrors(project.value.path) ? pushErrorsFailedSentence(failedOnPushErrors(casePrinted)) : undefined,
|
|
53592
53863
|
walkStopped ? WALK_STOPPED_SENTENCE : undefined,
|
|
53593
53864
|
countedLeaks.length > 0 ? [
|
|
53594
53865
|
heldNamed === undefined ? 'What the engine still held as it exited fails the run, as gdharness test judges it; engineArgs ["--verbose"] has the engine name what it held.' : `What the engine still held as it exited fails the run, as gdharness test judges it. ${heldNamed}`,
|
|
@@ -53732,7 +54003,7 @@ ${error.text}`;
|
|
|
53732
54003
|
const wanted = `res://${relativePath}`;
|
|
53733
54004
|
const folder = dirname25(relativePath.replaceAll("\\", "/"));
|
|
53734
54005
|
const inFolder = join44(projectPath, folder);
|
|
53735
|
-
const beside = existsSync34(inFolder) ?
|
|
54006
|
+
const beside = existsSync34(inFolder) ? readdirSync20(inFolder, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => `res://${folder === "." ? "" : `${folder}/`}${entry.name}`) : [];
|
|
53736
54007
|
return this.createErrorResponse(withNearestFiles(`Script file does not exist: ${wanted}`, wanted, scripts, beside));
|
|
53737
54008
|
}
|
|
53738
54009
|
async compileScripts(projectPath, paths) {
|
|
@@ -54945,16 +55216,18 @@ ${error.text}`;
|
|
|
54945
55216
|
if (exported === null && this.godotBridge.isConnected() && editorWouldPlay && given.length === 0 && flags.value.length === 0 && !needsItsOwnProcess(asked.value) && !silentOnlyHere) {
|
|
54946
55217
|
return await this.playThroughEditor(sceneArgument, refreshed.value, scanned, project.value.path, alreadyPlaying, runtimeWaitMs, ended, profiled);
|
|
54947
55218
|
}
|
|
55219
|
+
const onTheHiddenDesktop = !headless && args["visible"] !== true;
|
|
54948
55220
|
const cmdArgs = runArguments({
|
|
54949
55221
|
projectPath: exported === null ? project.value.path : null,
|
|
54950
55222
|
headless,
|
|
54951
55223
|
scene: sceneArgument,
|
|
54952
55224
|
userArgs: given,
|
|
54953
55225
|
engineArgs: flags.value,
|
|
54954
|
-
silent
|
|
55226
|
+
silent,
|
|
55227
|
+
...onTheHiddenDesktop && process.platform === "win32" ? { hiddenWindow: { projectCapsFrames: exported === null && capsFrames(project.value.path) } } : {}
|
|
54955
55228
|
});
|
|
54956
55229
|
this.logDebug(`Running Godot project: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
54957
|
-
const started = await this.launchKeptGame(engine.value, cmdArgs, project.value.path, needsItsOwnProcess(asked.value) ? environmentFor2({ ...asked.value, runtimeDirectory: runtimeDirectory() }) : undefined,
|
|
55230
|
+
const started = await this.launchKeptGame(engine.value, cmdArgs, project.value.path, needsItsOwnProcess(asked.value) ? environmentFor2({ ...asked.value, runtimeDirectory: runtimeDirectory() }) : undefined, onTheHiddenDesktop ? HIDDEN_DESKTOP : undefined, profiled, exported === null);
|
|
54958
55231
|
if ("error" in started) {
|
|
54959
55232
|
return this.createErrorResponse(`The game could not be started: ${started.error}`, [
|
|
54960
55233
|
"editor_status says which Godot this server runs and whether it answers"
|
|
@@ -55131,17 +55404,20 @@ ${error.text}`;
|
|
|
55131
55404
|
const sendingFrom = Date.now();
|
|
55132
55405
|
const breakpoints = await this.dapHoldingBreakpointsOf(projectPath).reapplyBreakpoints();
|
|
55133
55406
|
this.logDebug(`Breakpoints were sent again after ${Date.now() - sendingFrom}ms`);
|
|
55407
|
+
const slot = await slotForCall();
|
|
55134
55408
|
if (profiled) {
|
|
55135
55409
|
const armed = await this.handleViaBridge("profile_start", {
|
|
55136
55410
|
arm: true,
|
|
55137
55411
|
frameFunctions: profileFrameFunctions(projectPath)
|
|
55138
55412
|
});
|
|
55139
55413
|
if (armed.isError === true) {
|
|
55414
|
+
slot.release();
|
|
55140
55415
|
return armed;
|
|
55141
55416
|
}
|
|
55142
55417
|
}
|
|
55143
55418
|
const answer = await this.handleViaBridge("play_scene", scene === null ? {} : { scenePath: `res://${scene}` });
|
|
55144
55419
|
if (answer.isError === true) {
|
|
55420
|
+
slot.release();
|
|
55145
55421
|
return answer;
|
|
55146
55422
|
}
|
|
55147
55423
|
const startedAt = Date.now();
|
|
@@ -55171,7 +55447,8 @@ ${error.text}`;
|
|
|
55171
55447
|
announcedBefore: alreadyPlaying,
|
|
55172
55448
|
seenPlaying: readBoolean(playAnswer, "playing") === true,
|
|
55173
55449
|
...which === null ? {} : { play: which },
|
|
55174
|
-
bootScene: runtimeWaitMs.scene
|
|
55450
|
+
bootScene: runtimeWaitMs.scene,
|
|
55451
|
+
slot
|
|
55175
55452
|
};
|
|
55176
55453
|
this.activeProcess = played;
|
|
55177
55454
|
this.noteTheLatestRun({ projectPath, pid: null, startedAt });
|
|
@@ -55187,6 +55464,11 @@ ${error.text}`;
|
|
|
55187
55464
|
sweepTranscripts();
|
|
55188
55465
|
step("sweep");
|
|
55189
55466
|
this.logDebug(`After the play: ${steps.join(", ")}`);
|
|
55467
|
+
const runtime = await this.runtimeUp(projectPath, alreadyPlaying, runtimeWaitMs, false);
|
|
55468
|
+
if (played.slot !== undefined) {
|
|
55469
|
+
played.slot.release();
|
|
55470
|
+
delete played.slot;
|
|
55471
|
+
}
|
|
55190
55472
|
return this.jsonTextResponse({
|
|
55191
55473
|
started: true,
|
|
55192
55474
|
through: "editor",
|
|
@@ -55203,7 +55485,7 @@ ${error.text}`;
|
|
|
55203
55485
|
scriptPath: projectSpelling(projectPath, one.scriptPath),
|
|
55204
55486
|
reason: one.reason
|
|
55205
55487
|
})),
|
|
55206
|
-
runtime
|
|
55488
|
+
runtime,
|
|
55207
55489
|
message: "The editor is playing it, so its debugger holds it: the debug_* tools can reach it, " + "editor_output reads its console through the debug adapter and the runtime's report of it, and editor_run stop ends it." + endedToStartThis(ended)
|
|
55208
55490
|
});
|
|
55209
55491
|
}
|
|
@@ -55359,6 +55641,12 @@ ${error.text}`;
|
|
|
55359
55641
|
if (spawned) {
|
|
55360
55642
|
clearTheRecord();
|
|
55361
55643
|
}
|
|
55644
|
+
if (running.throughEditor) {
|
|
55645
|
+
const told = await this.handleViaBridge("stop_playing", {});
|
|
55646
|
+
if (told.isError === true) {
|
|
55647
|
+
this.logDebug(`The editor was not told its closed game is over: ${told.content[0]?.text ?? ""}`);
|
|
55648
|
+
}
|
|
55649
|
+
}
|
|
55362
55650
|
running.endedAt ??= Date.now();
|
|
55363
55651
|
running.log.record("info", `gdharness asked pid ${announced ?? "unknown"} to close, as a player's close asks it, and it exited.`);
|
|
55364
55652
|
return "gone";
|
|
@@ -55584,20 +55872,33 @@ ${error.text}`;
|
|
|
55584
55872
|
closeSync5(transcript.fd);
|
|
55585
55873
|
const gameEnv = profiled ? { ...env ?? process.env, [PROFILE_FROM_START]: "1" } : env;
|
|
55586
55874
|
const profile = profiled ? profilePath(startedAt) : undefined;
|
|
55587
|
-
const
|
|
55588
|
-
|
|
55589
|
-
|
|
55590
|
-
|
|
55591
|
-
|
|
55592
|
-
|
|
55593
|
-
|
|
55594
|
-
|
|
55595
|
-
|
|
55596
|
-
|
|
55597
|
-
|
|
55598
|
-
|
|
55599
|
-
|
|
55600
|
-
|
|
55875
|
+
const slot = await slotForCall();
|
|
55876
|
+
let launched;
|
|
55877
|
+
try {
|
|
55878
|
+
launched = await launchOutsideTheTree({
|
|
55879
|
+
command: godotPath,
|
|
55880
|
+
args: cmdArgs,
|
|
55881
|
+
...gameEnv === undefined ? {} : { env: gameEnv },
|
|
55882
|
+
run: {
|
|
55883
|
+
transcript: transcript.path,
|
|
55884
|
+
startedAt,
|
|
55885
|
+
projectPath,
|
|
55886
|
+
...this.servedBy(),
|
|
55887
|
+
...profile === undefined ? {} : { profile },
|
|
55888
|
+
...debugged ? { breakpoints: this.breakpointsForAGame(projectPath) } : { debugged: false },
|
|
55889
|
+
slot: { lock: slot.lock, from: slot.from, by: `a game of ${projectPath} editor_run started` }
|
|
55890
|
+
},
|
|
55891
|
+
...desktop === undefined ? {} : { desktop }
|
|
55892
|
+
});
|
|
55893
|
+
} catch (error) {
|
|
55894
|
+
slot.release();
|
|
55895
|
+
throw error;
|
|
55896
|
+
}
|
|
55897
|
+
if ("error" in launched) {
|
|
55898
|
+
slot.release();
|
|
55899
|
+
} else {
|
|
55900
|
+
slot.leave();
|
|
55901
|
+
}
|
|
55601
55902
|
if ("error" in launched) {
|
|
55602
55903
|
return launched;
|
|
55603
55904
|
}
|
|
@@ -55819,6 +56120,11 @@ ${error.text}`;
|
|
|
55819
56120
|
tie(run, endpoint) {
|
|
55820
56121
|
run.announcedPid = endpoint.pid;
|
|
55821
56122
|
this.noteBootOf(run, endpoint);
|
|
56123
|
+
const slot = run.slot;
|
|
56124
|
+
if (slot !== undefined) {
|
|
56125
|
+
delete run.slot;
|
|
56126
|
+
handToTheGame(slot, endpoint.pid, run.projectPath);
|
|
56127
|
+
}
|
|
55822
56128
|
}
|
|
55823
56129
|
playedByOurEditor(endpoint) {
|
|
55824
56130
|
const editor = this.godotBridge.getStatus().editorPid;
|
|
@@ -56782,73 +57088,15 @@ ${error.text}`;
|
|
|
56782
57088
|
const rebuilt = atMissingPaths.length > 0 ? cachedClasses(projectPath) : null;
|
|
56783
57089
|
const dropped = rebuilt === null ? [] : atMissingPaths.filter((name) => !cachedAtMissingPaths(rebuilt, projectPath).includes(name));
|
|
56784
57090
|
const stillAtMissingPaths = atMissingPaths.filter((name) => !dropped.includes(name));
|
|
56785
|
-
const
|
|
56786
|
-
|
|
57091
|
+
const asked = args["reloadScript"];
|
|
57092
|
+
const reloads = [];
|
|
56787
57093
|
const reloadedFirst = [];
|
|
56788
|
-
|
|
56789
|
-
|
|
56790
|
-
|
|
56791
|
-
|
|
56792
|
-
reloaded = {
|
|
56793
|
-
problem: `${reloading} is a @tool script, which runs inside the editor, so it is not reloaded from inside a call: editor_launch restart recompiles it.`
|
|
56794
|
-
};
|
|
56795
|
-
} else if (reloading !== undefined) {
|
|
56796
|
-
try {
|
|
56797
|
-
const reloadAsked = async () => asParams(await this.godotBridge.invokeTool("reload_script", { ...args, scriptPath: reloading }));
|
|
56798
|
-
let answer = await reloadAsked();
|
|
56799
|
-
const failedFirst = answer["failed"];
|
|
56800
|
-
if (typeof failedFirst === "number" && failedFirst !== 0 && projectPath !== "") {
|
|
56801
|
-
const first = await this.reloadWhatItIsBuiltOn(projectPath, readString(answer, "script") ?? reloading);
|
|
56802
|
-
reloadedFirst.push(...first.reloaded);
|
|
56803
|
-
if (first.reloaded.length > 0) {
|
|
56804
|
-
answer = await reloadAsked();
|
|
56805
|
-
}
|
|
56806
|
-
builtOn = first.said;
|
|
56807
|
-
builtOnUnchecked = first.unchecked === true;
|
|
56808
|
-
}
|
|
56809
|
-
const methods = readArray(answer, "methods");
|
|
56810
|
-
const listed = (field) => {
|
|
56811
|
-
const found = readArray(answer, field);
|
|
56812
|
-
return found ? { [field]: found.map(String) } : {};
|
|
56813
|
-
};
|
|
56814
|
-
const asResource = (path) => path.startsWith("res://") ? path : `res://${path.replace(/^\/+/, "")}`;
|
|
56815
|
-
const script = asResource(readString(answer, "script") ?? reloading);
|
|
56816
|
-
const spelling = script === asResource(reloading) ? {} : { as: script };
|
|
56817
|
-
const failed = answer["failed"];
|
|
56818
|
-
if (typeof failed === "number" && failed !== 0) {
|
|
56819
|
-
reloaded = {
|
|
56820
|
-
...spelling,
|
|
56821
|
-
...listed("heldBefore"),
|
|
56822
|
-
...listed("heldConstantsBefore"),
|
|
56823
|
-
problem: failedReloadNote({
|
|
56824
|
-
script,
|
|
56825
|
-
code: failed,
|
|
56826
|
-
codeName: readString(answer, "failedAs") ?? "",
|
|
56827
|
-
heldBefore: listed("heldBefore")["heldBefore"] ?? [],
|
|
56828
|
-
heldAfter: methods?.map(String) ?? [],
|
|
56829
|
-
constantsBefore: listed("heldConstantsBefore")["heldConstantsBefore"] ?? [],
|
|
56830
|
-
constantsAfter: listed("constants")["constants"] ?? [],
|
|
56831
|
-
names: projectPath === "" ? [] : this.classesAScriptNames(projectPath, script),
|
|
56832
|
-
restored: answer["restored"] === true,
|
|
56833
|
-
restoredAs: readString(answer, "restoredAs") ?? "",
|
|
56834
|
-
...builtOn === undefined ? {} : { builtOn },
|
|
56835
|
-
...builtOnUnchecked ? { builtOnUnchecked } : {}
|
|
56836
|
-
})
|
|
56837
|
-
};
|
|
56838
|
-
} else {
|
|
56839
|
-
reloaded = methods ? {
|
|
56840
|
-
...spelling,
|
|
56841
|
-
methods: methods.map(String),
|
|
56842
|
-
...listed("heldBefore"),
|
|
56843
|
-
...listed("constants"),
|
|
56844
|
-
...listed("heldConstantsBefore"),
|
|
56845
|
-
...answer["unsavedInEditor"] === true ? { unsaved: unsavedInEditorNote([script]) } : {}
|
|
56846
|
-
} : { problem: `the editor would not say what ${script} has after reloading it` };
|
|
56847
|
-
}
|
|
56848
|
-
} catch (error) {
|
|
56849
|
-
reloaded = { problem: `${reloading} could not be reloaded: ${errorMessage(error)}` };
|
|
56850
|
-
}
|
|
57094
|
+
for (const scriptPath of Array.isArray(asked) ? asked.map(String) : typeof asked === "string" ? [asked] : []) {
|
|
57095
|
+
const one = await this.reloadAsked(projectPath, args, scriptPath);
|
|
57096
|
+
reloads.push({ scriptPath, reloaded: one.reloaded });
|
|
57097
|
+
reloadedFirst.push(...one.reloadedFirst);
|
|
56851
57098
|
}
|
|
57099
|
+
const reloaded = Array.isArray(asked) ? {} : reloads[0]?.reloaded ?? {};
|
|
56852
57100
|
const checked = busy ? { unseen: [] } : await this.classesTheEditorCannotSee(args);
|
|
56853
57101
|
const unseen = checked.unseen;
|
|
56854
57102
|
const carried = projectPath === "" ? [] : this.unseenBeforeAnUnfinishedScan(projectPath);
|
|
@@ -56887,7 +57135,7 @@ ${error.text}`;
|
|
|
56887
57135
|
notes.push(`The scan wrote the class cache from the list this editor is holding, and that list still has ${dropped.join(", ")} for a script that is gone, so the cache was rebuilt from the files without it. The editor goes on holding it and writes it back on every scan until it is restarted with editor_launch restart; until then a scan is followed by this same rebuild.`);
|
|
56888
57136
|
}
|
|
56889
57137
|
return this.jsonTextResponse({
|
|
56890
|
-
ok: !busy && unseen.length === 0 && stillGone.length === 0 && stillAtMissingPaths.length === 0 && (dependents === null || dependents.notReloaded.length === 0) && reloaded.problem === undefined && checked.unchecked === undefined,
|
|
57138
|
+
ok: !busy && unseen.length === 0 && stillGone.length === 0 && stillAtMissingPaths.length === 0 && (dependents === null || dependents.notReloaded.length === 0) && reloads.every((one) => one.reloaded.problem === undefined) && checked.unchecked === undefined,
|
|
56891
57139
|
stillWorking: busy,
|
|
56892
57140
|
waitedMs: Date.now() - started,
|
|
56893
57141
|
waitedForEditorScanMs: waitedForEditorMs > 0 ? waitedForEditorMs : undefined,
|
|
@@ -56906,6 +57154,20 @@ ${error.text}`;
|
|
|
56906
57154
|
return all.length > 0 ? all : undefined;
|
|
56907
57155
|
})(),
|
|
56908
57156
|
unsavedInEditorNote: reloaded.unsaved,
|
|
57157
|
+
reloads: Array.isArray(asked) ? reloads.map(({ scriptPath, reloaded: one }) => ({
|
|
57158
|
+
scriptPath,
|
|
57159
|
+
ok: one.problem === undefined,
|
|
57160
|
+
reloadedAs: one.as,
|
|
57161
|
+
...reloadDifference(one),
|
|
57162
|
+
...args["listMembers"] === true ? {
|
|
57163
|
+
heldBeforeReload: one.heldBefore,
|
|
57164
|
+
reloadedMethods: one.methods,
|
|
57165
|
+
heldConstantsBeforeReload: one.heldConstantsBefore,
|
|
57166
|
+
reloadedConstants: one.constants
|
|
57167
|
+
} : {},
|
|
57168
|
+
reloadProblem: one.problem,
|
|
57169
|
+
unsavedInEditorNote: one.unsaved
|
|
57170
|
+
})) : undefined,
|
|
56909
57171
|
unseenByEditor: unseen.length > 0 ? unseen : undefined,
|
|
56910
57172
|
broughtIn: broughtIn.length > 0 ? broughtIn : undefined,
|
|
56911
57173
|
dependentsReloaded: dependents !== null && dependents.reloaded.length > 0 ? dependents.reloaded : undefined,
|
|
@@ -56920,6 +57182,75 @@ ${error.text}`;
|
|
|
56920
57182
|
note: notes.length > 0 ? notes.join(" ") : undefined
|
|
56921
57183
|
});
|
|
56922
57184
|
}
|
|
57185
|
+
async reloadAsked(projectPath, args, reloading) {
|
|
57186
|
+
let reloaded = {};
|
|
57187
|
+
const reloadedFirst = [];
|
|
57188
|
+
let builtOn;
|
|
57189
|
+
let builtOnUnchecked = false;
|
|
57190
|
+
const reloadingSource = projectPath === "" ? null : this.sourceOfClass(projectPath, reloading);
|
|
57191
|
+
if (reloadingSource !== null && isToolScript(reloadingSource)) {
|
|
57192
|
+
reloaded = {
|
|
57193
|
+
problem: `${reloading} is a @tool script, which runs inside the editor, so it is not reloaded from inside a call: editor_launch restart recompiles it.`
|
|
57194
|
+
};
|
|
57195
|
+
} else {
|
|
57196
|
+
try {
|
|
57197
|
+
const reloadAsked = async () => asParams(await this.godotBridge.invokeTool("reload_script", { ...args, scriptPath: reloading }));
|
|
57198
|
+
let answer = await reloadAsked();
|
|
57199
|
+
const failedFirst = answer["failed"];
|
|
57200
|
+
if (typeof failedFirst === "number" && failedFirst !== 0 && projectPath !== "") {
|
|
57201
|
+
const first = await this.reloadWhatItIsBuiltOn(projectPath, readString(answer, "script") ?? reloading);
|
|
57202
|
+
reloadedFirst.push(...first.reloaded);
|
|
57203
|
+
if (first.reloaded.length > 0) {
|
|
57204
|
+
answer = await reloadAsked();
|
|
57205
|
+
}
|
|
57206
|
+
builtOn = first.said;
|
|
57207
|
+
builtOnUnchecked = first.unchecked === true;
|
|
57208
|
+
}
|
|
57209
|
+
const methods = readArray(answer, "methods");
|
|
57210
|
+
const listed = (field) => {
|
|
57211
|
+
const found = readArray(answer, field);
|
|
57212
|
+
return found ? { [field]: found.map(String) } : {};
|
|
57213
|
+
};
|
|
57214
|
+
const asResource = (path) => path.startsWith("res://") ? path : `res://${path.replace(/^\/+/, "")}`;
|
|
57215
|
+
const script = asResource(readString(answer, "script") ?? reloading);
|
|
57216
|
+
const spelling = script === asResource(reloading) ? {} : { as: script };
|
|
57217
|
+
const failed = answer["failed"];
|
|
57218
|
+
if (typeof failed === "number" && failed !== 0) {
|
|
57219
|
+
reloaded = {
|
|
57220
|
+
...spelling,
|
|
57221
|
+
...listed("heldBefore"),
|
|
57222
|
+
...listed("heldConstantsBefore"),
|
|
57223
|
+
problem: failedReloadNote({
|
|
57224
|
+
script,
|
|
57225
|
+
code: failed,
|
|
57226
|
+
codeName: readString(answer, "failedAs") ?? "",
|
|
57227
|
+
heldBefore: listed("heldBefore")["heldBefore"] ?? [],
|
|
57228
|
+
heldAfter: methods?.map(String) ?? [],
|
|
57229
|
+
constantsBefore: listed("heldConstantsBefore")["heldConstantsBefore"] ?? [],
|
|
57230
|
+
constantsAfter: listed("constants")["constants"] ?? [],
|
|
57231
|
+
names: projectPath === "" ? [] : this.classesAScriptNames(projectPath, script),
|
|
57232
|
+
restored: answer["restored"] === true,
|
|
57233
|
+
restoredAs: readString(answer, "restoredAs") ?? "",
|
|
57234
|
+
...builtOn === undefined ? {} : { builtOn },
|
|
57235
|
+
...builtOnUnchecked ? { builtOnUnchecked } : {}
|
|
57236
|
+
})
|
|
57237
|
+
};
|
|
57238
|
+
} else {
|
|
57239
|
+
reloaded = methods ? {
|
|
57240
|
+
...spelling,
|
|
57241
|
+
methods: methods.map(String),
|
|
57242
|
+
...listed("heldBefore"),
|
|
57243
|
+
...listed("constants"),
|
|
57244
|
+
...listed("heldConstantsBefore"),
|
|
57245
|
+
...answer["unsavedInEditor"] === true ? { unsaved: unsavedInEditorNote([script]) } : {}
|
|
57246
|
+
} : { problem: `the editor would not say what ${script} has after reloading it` };
|
|
57247
|
+
}
|
|
57248
|
+
} catch (error) {
|
|
57249
|
+
reloaded = { problem: `${reloading} could not be reloaded: ${errorMessage(error)}` };
|
|
57250
|
+
}
|
|
57251
|
+
}
|
|
57252
|
+
return { reloaded, reloadedFirst };
|
|
57253
|
+
}
|
|
56923
57254
|
async reloadDependents(projectPath, classes) {
|
|
56924
57255
|
for (const { script, tool } of scriptsDeclaring(projectPath, classes)) {
|
|
56925
57256
|
if (!tool) {
|
|
@@ -57251,9 +57582,7 @@ ${error.text}`;
|
|
|
57251
57582
|
const done = await this.reloadInEditor([{ script: scriptPath, tool: source !== null && isToolScript(source) }], project.value.path);
|
|
57252
57583
|
const problem = done.notReloaded[0]?.problem;
|
|
57253
57584
|
const notes = [
|
|
57254
|
-
...problem === undefined ? [] : [
|
|
57255
|
-
`The editor serving the project was not given the script as written, so it answers from the copy it compiled before: ${problem}.`
|
|
57256
|
-
],
|
|
57585
|
+
...problem === undefined ? [] : [staleCopyNote(problem)],
|
|
57257
57586
|
...done.unsavedInEditor.length > 0 ? [unsavedInEditorNote(done.unsavedInEditor)] : []
|
|
57258
57587
|
];
|
|
57259
57588
|
return this.jsonTextResponse({
|
|
@@ -57434,9 +57763,15 @@ ${error.text}`;
|
|
|
57434
57763
|
return project.response;
|
|
57435
57764
|
}
|
|
57436
57765
|
const projectPath = project.value.path;
|
|
57766
|
+
const typeArgument = TYPE_ARGUMENTS[command];
|
|
57767
|
+
const named = typeArgument === undefined ? "" : readString(contained.value, typeArgument) ?? "";
|
|
57437
57768
|
const waited = await this.waitForTheEditorOpenedHere(contained.value, []);
|
|
57438
57769
|
if (this.editorHasOpen(projectPath)) {
|
|
57439
|
-
|
|
57770
|
+
const answered = await this.handleViaBridge(command, contained.value, askedAs);
|
|
57771
|
+
if (answered.isError === true && declaredButUncached(projectPath, named) !== null) {
|
|
57772
|
+
answered.content.push({ type: "text", text: uncachedClassNote([named]) });
|
|
57773
|
+
}
|
|
57774
|
+
return answered;
|
|
57440
57775
|
}
|
|
57441
57776
|
if (effect === "writes") {
|
|
57442
57777
|
if (waited !== null) {
|
|
@@ -57451,12 +57786,22 @@ ${error.text}`;
|
|
|
57451
57786
|
]);
|
|
57452
57787
|
}
|
|
57453
57788
|
}
|
|
57789
|
+
const declaring = declaredButUncached(projectPath, named);
|
|
57790
|
+
if (declaring !== null) {
|
|
57791
|
+
const rebuilt = await this.rebuildClassCache(projectPath);
|
|
57792
|
+
if (!rebuilt.ok) {
|
|
57793
|
+
return this.answer(rebuilt);
|
|
57794
|
+
}
|
|
57795
|
+
}
|
|
57454
57796
|
const outcome = await this.operation("run_editor_tool", { command, arguments: contained.value }, projectPath);
|
|
57455
57797
|
if (!outcome.ok) {
|
|
57456
57798
|
return this.answer(outcome);
|
|
57457
57799
|
}
|
|
57458
57800
|
return this.jsonTextResponse({
|
|
57459
57801
|
...outcome.payload,
|
|
57802
|
+
...declaring === null ? {} : {
|
|
57803
|
+
classCacheRebuilt: `${named} is declared in ${declaring} and .godot/global_script_class_cache.cfg did not list it, which is where an engine resolves a class name from, so the cache was rebuilt from the declarations on disk before the write.`
|
|
57804
|
+
},
|
|
57460
57805
|
...effect === "reads" ? {
|
|
57461
57806
|
readBy: "a headless engine, since no editor serves this project; unsaved changes in an editor are not seen"
|
|
57462
57807
|
} : { writtenBy: "a headless engine, since no editor serves this project" },
|
|
@@ -57692,7 +58037,7 @@ ${error.text}`;
|
|
|
57692
58037
|
if (behind.length > 0) {
|
|
57693
58038
|
const one = behind.length === 1;
|
|
57694
58039
|
const scripts = behind.map((entry) => entry.scriptPath);
|
|
57695
|
-
notes.push(`The editor holds ${behind.map((entry) => entry.className).join(", ")} compiled from ${one ? "a text its file no longer has" : "texts their files no longer have"}, so a script it compiles against ${one ? "that class" : "them"} fails on whatever the file has gained, as a member not found or an identifier not declared, while the files are right and a new engine compiles them. editor_rescan with reloadScript
|
|
58040
|
+
notes.push(`The editor holds ${behind.map((entry) => entry.className).join(", ")} compiled from ${one ? "a text its file no longer has" : "texts their files no longer have"}, so a script it compiles against ${one ? "that class" : "them"} fails on whatever the file has gained, as a member not found or an identifier not declared, while the files are right and a new engine compiles them. editor_rescan with reloadScript set to ${one ? scripts[0] : JSON.stringify(scripts)} reloads ${one ? "it" : "each"}, and a rescan reloads such a class before a dependent compiled against it.`);
|
|
57696
58041
|
}
|
|
57697
58042
|
return this.jsonTextResponse({
|
|
57698
58043
|
...payload,
|
|
@@ -58221,7 +58566,7 @@ async function runGodotServer() {
|
|
|
58221
58566
|
const server = new GodotServer;
|
|
58222
58567
|
await server.run();
|
|
58223
58568
|
}
|
|
58224
|
-
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, SUCCESSOR_SOUGHT_WITHIN_MS = 20000, CLOSE_ASK_MS = 3000, 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_SETTLED_MS = 500, SLOW_ANSWER_MS = 1000, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_TIMEOUT_MS = 600000, 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, DESTINATION_ARGUMENTS, 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, MOST_PRINTED_LINES = 50, MOST_UNDECLARED_ERRORS = 20, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", RAISED_WARNING = " (Warning treated as error.)", PARSE_ERROR2 = "Parse Error: ", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
58569
|
+
var TYPE_ARGUMENTS, 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, SUCCESSOR_SOUGHT_WITHIN_MS = 20000, CLOSE_ASK_MS = 3000, 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_SETTLED_MS = 500, SLOW_ANSWER_MS = 1000, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_TIMEOUT_MS = 600000, 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, DESTINATION_ARGUMENTS, 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, MOST_PRINTED_LINES = 50, MOST_UNDECLARED_ERRORS = 20, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", RAISED_WARNING = " (Warning treated as error.)", PARSE_ERROR2 = "Parse Error: ", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
58225
58570
|
var init_server = __esm(() => {
|
|
58226
58571
|
init_dist2();
|
|
58227
58572
|
init_stdio();
|
|
@@ -58288,6 +58633,11 @@ var init_server = __esm(() => {
|
|
|
58288
58633
|
init_tool_definitions();
|
|
58289
58634
|
init_tool_reference();
|
|
58290
58635
|
init_update_check();
|
|
58636
|
+
TYPE_ARGUMENTS = {
|
|
58637
|
+
add_node: "nodeType",
|
|
58638
|
+
create_resource: "resourceType",
|
|
58639
|
+
create_scene: "rootNodeType"
|
|
58640
|
+
};
|
|
58291
58641
|
__dirname2 = dirname25(fileURLToPath5(import.meta.url));
|
|
58292
58642
|
KEEPER_DEBUG_MS = PAUSE_MS + STEP_MS + 2 * READ_MS;
|
|
58293
58643
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
@@ -58393,7 +58743,7 @@ init_alive();
|
|
|
58393
58743
|
init_engine_launch();
|
|
58394
58744
|
init_lsp_client();
|
|
58395
58745
|
init_process_children();
|
|
58396
|
-
import { existsSync as existsSync17, readdirSync as
|
|
58746
|
+
import { existsSync as existsSync17, readdirSync as readdirSync11, readFileSync as readFileSync16, unlinkSync as unlinkSync2 } from "node:fs";
|
|
58397
58747
|
import { connect as connect2 } from "node:net";
|
|
58398
58748
|
import { join as join21, resolve as resolve7 } from "node:path";
|
|
58399
58749
|
var EDITOR_RECORD_PROTOCOL = 1;
|
|
@@ -58404,7 +58754,7 @@ function editorRecordsIn(projectPath) {
|
|
|
58404
58754
|
const directory = join21(projectPath, ".godot", "gdharness-editors");
|
|
58405
58755
|
let names;
|
|
58406
58756
|
try {
|
|
58407
|
-
names =
|
|
58757
|
+
names = readdirSync11(directory).filter((name) => /^\d+\.json$/.test(name));
|
|
58408
58758
|
} catch {
|
|
58409
58759
|
return [];
|
|
58410
58760
|
}
|
|
@@ -58612,7 +58962,7 @@ init_setup();
|
|
|
58612
58962
|
init_setup();
|
|
58613
58963
|
init_tool_definitions();
|
|
58614
58964
|
init_tool_reference();
|
|
58615
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync7, readdirSync as
|
|
58965
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync7, readdirSync as readdirSync13, rmSync as rmSync10, writeFileSync as writeFileSync11 } from "node:fs";
|
|
58616
58966
|
import { basename as basename8, dirname as dirname10, join as join23 } from "node:path";
|
|
58617
58967
|
var SKILL_NAME = "gdharness";
|
|
58618
58968
|
var SHARED_SKILLS = join23(".agents", "skills");
|
|
@@ -58896,7 +59246,7 @@ function pruneEmpty(directory, projectPath) {
|
|
|
58896
59246
|
const shared = join23(projectPath, ".agents");
|
|
58897
59247
|
let path = directory;
|
|
58898
59248
|
while (path !== projectPath && (basename8(path) === "skills" || path === shared)) {
|
|
58899
|
-
if (
|
|
59249
|
+
if (readdirSync13(path).length > 0) {
|
|
58900
59250
|
return;
|
|
58901
59251
|
}
|
|
58902
59252
|
rmSync10(path, { recursive: true, force: true });
|
|
@@ -59212,13 +59562,14 @@ init_engine_launch();
|
|
|
59212
59562
|
init_engine_leaks();
|
|
59213
59563
|
init_engine_slots();
|
|
59214
59564
|
init_import_freshness();
|
|
59565
|
+
init_launch();
|
|
59215
59566
|
init_library_copies();
|
|
59216
59567
|
init_setup();
|
|
59217
59568
|
import {
|
|
59218
59569
|
copyFileSync,
|
|
59219
59570
|
existsSync as existsSync23,
|
|
59220
59571
|
mkdirSync as mkdirSync10,
|
|
59221
|
-
readdirSync as
|
|
59572
|
+
readdirSync as readdirSync15,
|
|
59222
59573
|
readFileSync as readFileSync23,
|
|
59223
59574
|
rmSync as rmSync12,
|
|
59224
59575
|
statSync as statSync14,
|
|
@@ -59231,7 +59582,7 @@ import process14 from "node:process";
|
|
|
59231
59582
|
// src/suite-fingerprint.ts
|
|
59232
59583
|
init_class_cache();
|
|
59233
59584
|
import { createHash as createHash6 } from "node:crypto";
|
|
59234
|
-
import { existsSync as existsSync21, readdirSync as
|
|
59585
|
+
import { existsSync as existsSync21, readdirSync as readdirSync14, readFileSync as readFileSync20, statSync as statSync13 } from "node:fs";
|
|
59235
59586
|
import { join as join25, posix as posix2 } from "node:path";
|
|
59236
59587
|
var RES_LITERAL = /res:\/\/[^"'\s)\]<>]*/g;
|
|
59237
59588
|
var RELATIVE_LOAD = /\b(?:pre)?load\(\s*["']((?!res:\/\/|uid:\/\/)[^"']+)["']/g;
|
|
@@ -59290,7 +59641,7 @@ ${this.read(path).digest}
|
|
|
59290
59641
|
addon(root) {
|
|
59291
59642
|
const hash = createHash6("sha256");
|
|
59292
59643
|
const walk = (directory, under) => {
|
|
59293
|
-
for (const entry of
|
|
59644
|
+
for (const entry of readdirSync14(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
59294
59645
|
const file = join25(directory, entry.name);
|
|
59295
59646
|
if (entry.isDirectory()) {
|
|
59296
59647
|
walk(file, `${under}${entry.name}/`);
|
|
@@ -59315,7 +59666,7 @@ ${this.read(path).digest}
|
|
|
59315
59666
|
return { digest: "missing", reaches: [], unreadable: null };
|
|
59316
59667
|
}
|
|
59317
59668
|
if (statSync13(file).isDirectory()) {
|
|
59318
|
-
const inside =
|
|
59669
|
+
const inside = readdirSync14(file, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".")).map((entry) => `${path.endsWith("/") ? path : `${path}/`}${entry.name}`).sort();
|
|
59319
59670
|
return { digest: inside.join(`
|
|
59320
59671
|
`), reaches: inside, unreadable: null };
|
|
59321
59672
|
}
|
|
@@ -59567,7 +59918,7 @@ class RunLogs {
|
|
|
59567
59918
|
if (this.kept === null) {
|
|
59568
59919
|
return;
|
|
59569
59920
|
}
|
|
59570
|
-
for (const file of
|
|
59921
|
+
for (const file of readdirSync15(this.directory, { recursive: true, encoding: "utf8" })) {
|
|
59571
59922
|
if (file.endsWith(".log") && file.split(sep4).length === 2) {
|
|
59572
59923
|
mkdirSync10(dirname13(join28(this.kept, file)), { recursive: true });
|
|
59573
59924
|
copyFileSync(join28(this.directory, file), join28(this.kept, file));
|
|
@@ -59627,10 +59978,15 @@ async function runTestCommand(options, godotPath) {
|
|
|
59627
59978
|
selfTyped: selfTypedExports(scripts.sources),
|
|
59628
59979
|
godotVersion: options.godotVersion,
|
|
59629
59980
|
cores: options.cores,
|
|
59630
|
-
verbose: options.verbose
|
|
59981
|
+
verbose: options.verbose,
|
|
59982
|
+
failsOnPushErrors: gdUnitFailsOnPushErrors(options.projectPath)
|
|
59631
59983
|
};
|
|
59632
59984
|
try {
|
|
59633
|
-
|
|
59985
|
+
const neverImported = cachedClasses(options.projectPath) === null;
|
|
59986
|
+
if (neverImported && !options.importFirst) {
|
|
59987
|
+
console.log("The project has never been imported, so it is imported first.");
|
|
59988
|
+
}
|
|
59989
|
+
if (options.importFirst || neverImported) {
|
|
59634
59990
|
const imported = await importProject(launch);
|
|
59635
59991
|
if (imported !== null) {
|
|
59636
59992
|
logs.keep();
|
|
@@ -59894,7 +60250,10 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59894
60250
|
`)}
|
|
59895
60251
|
`);
|
|
59896
60252
|
const viewport = viewportOf(launch.projectPath);
|
|
59897
|
-
const display = plan.windowed ?
|
|
60253
|
+
const display = plan.windowed ? [
|
|
60254
|
+
...viewport === null ? [] : ["--resolution", `${String(viewport.width)}x${String(viewport.height)}`],
|
|
60255
|
+
...process14.platform === "win32" ? hiddenWindowFlags([], capsFrames(launch.projectPath)) : []
|
|
60256
|
+
] : ["--headless"];
|
|
59898
60257
|
const args = [
|
|
59899
60258
|
...display,
|
|
59900
60259
|
"--audio-driver",
|
|
@@ -59939,7 +60298,8 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59939
60298
|
suites: plan.suites,
|
|
59940
60299
|
expected: readExpected(expectedFile),
|
|
59941
60300
|
selfTyped: launch.selfTyped,
|
|
59942
|
-
godotVersion: launch.godotVersion
|
|
60301
|
+
godotVersion: launch.godotVersion,
|
|
60302
|
+
failsOnPushErrors: launch.failsOnPushErrors
|
|
59943
60303
|
};
|
|
59944
60304
|
const judged = judgeEngine(printed, assignment, end);
|
|
59945
60305
|
const written = reportsFault(join28(directory, "reports"));
|
|
@@ -59956,7 +60316,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59956
60316
|
}
|
|
59957
60317
|
var REPORT_FILES_NAMED = 3;
|
|
59958
60318
|
function reportsFault(reports) {
|
|
59959
|
-
const files = existsSync23(reports) ?
|
|
60319
|
+
const files = existsSync23(reports) ? readdirSync15(reports, { recursive: true, encoding: "utf8" }).filter((file) => statSync14(join28(reports, file)).isFile()).sort() : [];
|
|
59960
60320
|
if (files.length === 0) {
|
|
59961
60321
|
return null;
|
|
59962
60322
|
}
|