gdharness 1.3.48 → 1.3.50
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 +482 -201
- package/build/godot/addons/gdharness_editor/declared_values.gd +37 -0
- package/build/godot/addons/gdharness_editor/property_values.gd +8 -11
- 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_paths.gd +72 -11
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +14 -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/value_shapes.gd +9 -0
- package/build/godot/operations/declared_values.gd +37 -0
- package/build/godot/operations/property_values.gd +8 -11
- package/build/godot/operations/value_shapes.gd +9 -0
- package/build/keeper.js +6 -2
- 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,7 +12179,7 @@ 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);
|
|
@@ -12123,7 +12197,8 @@ class EngineSlots {
|
|
|
12123
12197
|
}
|
|
12124
12198
|
async takeFree() {
|
|
12125
12199
|
mkdirSync3(this.directory, { recursive: true });
|
|
12126
|
-
|
|
12200
|
+
this.drawn = slotsDrawnOn(this.count, this.inAGate, !this.inAGate && await this.aGateIsQueued());
|
|
12201
|
+
for (const slot of this.drawn.numbers) {
|
|
12127
12202
|
const lock = join9(this.directory, `${String(slot)}.lock`);
|
|
12128
12203
|
if (this.made(lock) || await this.takenOver(lock)) {
|
|
12129
12204
|
this.held.add(lock);
|
|
@@ -12147,9 +12222,32 @@ class EngineSlots {
|
|
|
12147
12222
|
this.marks.add(mark);
|
|
12148
12223
|
return mark;
|
|
12149
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
|
+
}
|
|
12150
12248
|
holders() {
|
|
12151
12249
|
const found = [];
|
|
12152
|
-
for (
|
|
12250
|
+
for (const slot of this.drawn.numbers) {
|
|
12153
12251
|
try {
|
|
12154
12252
|
const holder = holderOf(readFileSync8(join9(this.directory, `${String(slot)}.lock`), "utf8"));
|
|
12155
12253
|
if (holder !== null) {
|
|
@@ -12279,26 +12377,40 @@ function processSlots() {
|
|
|
12279
12377
|
function nameSlotTaker(by) {
|
|
12280
12378
|
takenBy = by;
|
|
12281
12379
|
}
|
|
12282
|
-
function slotWaitSaid(waitedMs, count, directory, holders = []) {
|
|
12283
|
-
const
|
|
12284
|
-
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);
|
|
12285
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;
|
|
12286
12387
|
if (named.length === 0) {
|
|
12287
|
-
return `${waiting}, by a gate or another tool, and
|
|
12388
|
+
return `${waiting}, by a gate or another tool, and ${starts}.`;
|
|
12389
|
+
}
|
|
12390
|
+
if (taking === 1) {
|
|
12391
|
+
return `${waiting}, by ${named.join(", ")}, and ${starts}.`;
|
|
12392
|
+
}
|
|
12393
|
+
return `${waiting}: ${listed(named)}. ${starts.charAt(0).toUpperCase()}${starts.slice(1)}.`;
|
|
12394
|
+
}
|
|
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`;
|
|
12288
12399
|
}
|
|
12289
|
-
if (
|
|
12290
|
-
return `${
|
|
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`;
|
|
12291
12402
|
}
|
|
12292
|
-
return
|
|
12403
|
+
return count === 1 ? `the one engine slot ${where}` : `all ${String(count)} engine slots ${where}`;
|
|
12293
12404
|
}
|
|
12294
|
-
function slotWaitedSaid(waitedMs, count, directory, holders) {
|
|
12405
|
+
function slotWaitedSaid(waitedMs, count, directory, holders, drawn = { numbers: [] }) {
|
|
12295
12406
|
const waited = `This call waited ${String(Math.round(waitedMs / 1000))} s for an engine slot in all.`;
|
|
12296
|
-
const
|
|
12297
|
-
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);
|
|
12298
12410
|
if (named.length === 0) {
|
|
12299
12411
|
return `${waited} When it first waited, ${slots}, by a gate or another tool.`;
|
|
12300
12412
|
}
|
|
12301
|
-
return
|
|
12413
|
+
return taking === 1 ? `${waited} When it first waited, ${slots}, by ${named.join(", ")}.` : `${waited} When it first waited, ${slots}: ${listed(named)}.`;
|
|
12302
12414
|
}
|
|
12303
12415
|
function holdersNamed(count, holders) {
|
|
12304
12416
|
const held = new Map;
|
|
@@ -12320,15 +12432,18 @@ async function slotForCall() {
|
|
|
12320
12432
|
const waits = callSlotWaits();
|
|
12321
12433
|
const began = Date.now();
|
|
12322
12434
|
let blockedBy;
|
|
12435
|
+
let blockedOn;
|
|
12323
12436
|
const lock = await slots.take(callSignal(), progress === undefined ? undefined : (waitedMs) => {
|
|
12324
|
-
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));
|
|
12325
12438
|
}, () => {
|
|
12326
12439
|
blockedBy = slots.holders();
|
|
12440
|
+
blockedOn = slots.drawn;
|
|
12327
12441
|
});
|
|
12328
12442
|
if (waits !== undefined && blockedBy !== undefined) {
|
|
12329
12443
|
waits.ms += Date.now() - began;
|
|
12330
12444
|
const heldBy = blockedBy;
|
|
12331
|
-
|
|
12445
|
+
const heldOn = blockedOn;
|
|
12446
|
+
waits.said ??= (waitedMs) => slotWaitedSaid(waitedMs, slots.count, slots.directory, heldBy, heldOn);
|
|
12332
12447
|
}
|
|
12333
12448
|
return {
|
|
12334
12449
|
lock,
|
|
@@ -12350,7 +12465,7 @@ async function inASlot(body) {
|
|
|
12350
12465
|
held.release();
|
|
12351
12466
|
}
|
|
12352
12467
|
}
|
|
12353
|
-
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;
|
|
12354
12469
|
var init_engine_slots = __esm(() => {
|
|
12355
12470
|
init_alive();
|
|
12356
12471
|
init_cache_home();
|
|
@@ -12361,7 +12476,7 @@ var init_engine_slots = __esm(() => {
|
|
|
12361
12476
|
});
|
|
12362
12477
|
|
|
12363
12478
|
// src/detection.ts
|
|
12364
|
-
import { existsSync as existsSync6, readdirSync as
|
|
12479
|
+
import { existsSync as existsSync6, readdirSync as readdirSync5, statSync as statSync7 } from "node:fs";
|
|
12365
12480
|
import { homedir as homedir4 } from "node:os";
|
|
12366
12481
|
import { join as join10 } from "node:path";
|
|
12367
12482
|
function resolveHomeDirectory() {
|
|
@@ -12377,7 +12492,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
12377
12492
|
}
|
|
12378
12493
|
let entries;
|
|
12379
12494
|
try {
|
|
12380
|
-
entries =
|
|
12495
|
+
entries = readdirSync5(directory);
|
|
12381
12496
|
} catch {
|
|
12382
12497
|
return [];
|
|
12383
12498
|
}
|
|
@@ -12460,6 +12575,10 @@ function resolveHeadless(requested, host) {
|
|
|
12460
12575
|
}
|
|
12461
12576
|
return envValue("DISPLAY", host.variables) === undefined && envValue("WAYLAND_DISPLAY", host.variables) === undefined;
|
|
12462
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
|
+
}
|
|
12463
12582
|
function resolveSilent(requested) {
|
|
12464
12583
|
return requested !== false;
|
|
12465
12584
|
}
|
|
@@ -12504,6 +12623,9 @@ function runArguments(options) {
|
|
|
12504
12623
|
if (options.quitAfter !== undefined && options.quitAfter !== null) {
|
|
12505
12624
|
args.push("--quit-after", String(options.quitAfter));
|
|
12506
12625
|
}
|
|
12626
|
+
if (options.hiddenWindow !== undefined && !options.headless) {
|
|
12627
|
+
args.push(...hiddenWindowFlags(options.engineArgs ?? [], options.hiddenWindow.projectCapsFrames));
|
|
12628
|
+
}
|
|
12507
12629
|
if (options.engineArgs !== undefined) {
|
|
12508
12630
|
args.push(...options.engineArgs);
|
|
12509
12631
|
}
|
|
@@ -12581,7 +12703,7 @@ function environmentFor2(options, variables = process.env) {
|
|
|
12581
12703
|
const base = options.savesIn === undefined ? { ...variables } : userDataIn(options.savesIn, variables);
|
|
12582
12704
|
return { ...base, ...options.env, GDHARNESS_RUNTIME_DIR: options.runtimeDirectory };
|
|
12583
12705
|
}
|
|
12584
|
-
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.";
|
|
12585
12707
|
var init_launch = __esm(() => {
|
|
12586
12708
|
ENGINE_FLAGS = Object.freeze({
|
|
12587
12709
|
"--verbose": "none",
|
|
@@ -12635,10 +12757,35 @@ var init_launch = __esm(() => {
|
|
|
12635
12757
|
// src/godot-path.ts
|
|
12636
12758
|
import { existsSync as existsSync7 } from "node:fs";
|
|
12637
12759
|
import { isAbsolute as isAbsolute4, normalize, resolve as resolve5 } from "node:path";
|
|
12760
|
+
function versionStartOf(error) {
|
|
12761
|
+
const failed = error ?? {};
|
|
12762
|
+
const printed = [failed.stdout, failed.stderr].filter((part) => typeof part === "string").join(`
|
|
12763
|
+
`).trim();
|
|
12764
|
+
const quoted = printed.length > QUOTED_CHARACTERS ? `${printed.slice(0, QUOTED_CHARACTERS)}...` : printed;
|
|
12765
|
+
const output = quoted === "" ? " and printed nothing" : ` and printed ${JSON.stringify(quoted)}`;
|
|
12766
|
+
if (typeof failed.signal === "string") {
|
|
12767
|
+
return { kind: "crashed", said: `it was ended by ${failed.signal}${output}` };
|
|
12768
|
+
}
|
|
12769
|
+
if (typeof failed.code !== "number") {
|
|
12770
|
+
return { kind: "refused", said: `it could not be started: ${errorMessage(error)}` };
|
|
12771
|
+
}
|
|
12772
|
+
const code = failed.code >>> 0;
|
|
12773
|
+
const exception = code >= 3221225472;
|
|
12774
|
+
const said = `it exited ${exception ? `0x${code.toString(16).toUpperCase()}` : String(failed.code)}${output}`;
|
|
12775
|
+
return exception || printed === "" ? { kind: "crashed", said } : { kind: "refused", said };
|
|
12776
|
+
}
|
|
12638
12777
|
|
|
12639
12778
|
class GodotLocator {
|
|
12640
12779
|
answered = new Map;
|
|
12641
12780
|
found = null;
|
|
12781
|
+
start;
|
|
12782
|
+
sleep;
|
|
12783
|
+
constructor(start = (path) => runHidden(path, ["--version"]), sleep = (ms) => new Promise((done) => {
|
|
12784
|
+
setTimeout(done, ms);
|
|
12785
|
+
})) {
|
|
12786
|
+
this.start = start;
|
|
12787
|
+
this.sleep = sleep;
|
|
12788
|
+
}
|
|
12642
12789
|
async find() {
|
|
12643
12790
|
if (this.found !== null && await this.whyNot(this.found) === null) {
|
|
12644
12791
|
return { ok: true, path: this.found };
|
|
@@ -12686,21 +12833,37 @@ class GodotLocator {
|
|
|
12686
12833
|
if (known !== undefined) {
|
|
12687
12834
|
return known;
|
|
12688
12835
|
}
|
|
12689
|
-
let reason;
|
|
12690
12836
|
if (path !== "godot" && !existsSync7(path)) {
|
|
12691
|
-
|
|
12692
|
-
|
|
12837
|
+
this.answered.set(path, "does not exist");
|
|
12838
|
+
return "does not exist";
|
|
12839
|
+
}
|
|
12840
|
+
let crashed = "";
|
|
12841
|
+
for (let started = 1;started <= VERSION_STARTS; started += 1) {
|
|
12842
|
+
if (started > 1) {
|
|
12843
|
+
await this.sleep(FIRST_RETRY_MS * 2 ** (started - 2));
|
|
12844
|
+
}
|
|
12845
|
+
let went;
|
|
12693
12846
|
try {
|
|
12694
|
-
await
|
|
12695
|
-
|
|
12847
|
+
await this.start(path);
|
|
12848
|
+
went = { kind: "answered" };
|
|
12696
12849
|
} catch (error) {
|
|
12697
|
-
|
|
12850
|
+
went = versionStartOf(error);
|
|
12851
|
+
}
|
|
12852
|
+
if (went.kind === "answered") {
|
|
12853
|
+
this.answered.set(path, null);
|
|
12854
|
+
return null;
|
|
12855
|
+
}
|
|
12856
|
+
if (went.kind === "refused") {
|
|
12857
|
+
const reason = `does not answer --version: ${went.said}`;
|
|
12858
|
+
this.answered.set(path, reason);
|
|
12859
|
+
return reason;
|
|
12698
12860
|
}
|
|
12861
|
+
crashed = went.said;
|
|
12699
12862
|
}
|
|
12700
|
-
|
|
12701
|
-
return reason;
|
|
12863
|
+
return `crashed on each of ${String(VERSION_STARTS)} starts with --version; the last time ${crashed}`;
|
|
12702
12864
|
}
|
|
12703
12865
|
}
|
|
12866
|
+
var VERSION_STARTS = 5, FIRST_RETRY_MS = 500, QUOTED_CHARACTERS = 300;
|
|
12704
12867
|
var init_godot_path = __esm(() => {
|
|
12705
12868
|
init_detection();
|
|
12706
12869
|
init_errors();
|
|
@@ -13051,7 +13214,7 @@ var init_engine_run = __esm(() => {
|
|
|
13051
13214
|
});
|
|
13052
13215
|
|
|
13053
13216
|
// src/doc-cache.ts
|
|
13054
|
-
import { existsSync as existsSync8, readdirSync as
|
|
13217
|
+
import { existsSync as existsSync8, readdirSync as readdirSync6, statSync as statSync8 } from "node:fs";
|
|
13055
13218
|
import { homedir as homedir5 } from "node:os";
|
|
13056
13219
|
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute5, join as join11 } from "node:path";
|
|
13057
13220
|
import process9 from "node:process";
|
|
@@ -13077,7 +13240,7 @@ function docCachesWritten(directory) {
|
|
|
13077
13240
|
const written = new Map;
|
|
13078
13241
|
let names;
|
|
13079
13242
|
try {
|
|
13080
|
-
names =
|
|
13243
|
+
names = readdirSync6(directory);
|
|
13081
13244
|
} catch {
|
|
13082
13245
|
return written;
|
|
13083
13246
|
}
|
|
@@ -13107,7 +13270,7 @@ var init_doc_cache = __esm(() => {
|
|
|
13107
13270
|
});
|
|
13108
13271
|
|
|
13109
13272
|
// src/project-scan.ts
|
|
13110
|
-
import { existsSync as existsSync9, readdirSync as
|
|
13273
|
+
import { existsSync as existsSync9, readdirSync as readdirSync7, readFileSync as readFileSync9, statSync as statSync9 } from "node:fs";
|
|
13111
13274
|
import { basename as basename5, extname, join as join12, relative as relative4, sep as sep3 } from "node:path";
|
|
13112
13275
|
function skipped(name) {
|
|
13113
13276
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
@@ -13127,7 +13290,7 @@ function projectStructure(projectPath) {
|
|
|
13127
13290
|
if (steppedOver(directory)) {
|
|
13128
13291
|
return;
|
|
13129
13292
|
}
|
|
13130
|
-
for (const entry of
|
|
13293
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13131
13294
|
if (skipped(entry.name)) {
|
|
13132
13295
|
continue;
|
|
13133
13296
|
}
|
|
@@ -13174,7 +13337,7 @@ function steppedOverIn(directory) {
|
|
|
13174
13337
|
}
|
|
13175
13338
|
const found = [];
|
|
13176
13339
|
const visit = (at, prefix) => {
|
|
13177
|
-
for (const entry of
|
|
13340
|
+
for (const entry of readdirSync7(at, { withFileTypes: true })) {
|
|
13178
13341
|
if (!entry.isDirectory() || skipped(entry.name)) {
|
|
13179
13342
|
continue;
|
|
13180
13343
|
}
|
|
@@ -13195,7 +13358,7 @@ function steppedOverIn(directory) {
|
|
|
13195
13358
|
function filesBelow(root, extensions) {
|
|
13196
13359
|
const found = [];
|
|
13197
13360
|
const visit = (directory, prefix) => {
|
|
13198
|
-
for (const entry of
|
|
13361
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13199
13362
|
if (skipped(entry.name)) {
|
|
13200
13363
|
continue;
|
|
13201
13364
|
}
|
|
@@ -13219,7 +13382,7 @@ function scriptsWithoutUid(projectPath) {
|
|
|
13219
13382
|
if (steppedOver(directory)) {
|
|
13220
13383
|
return;
|
|
13221
13384
|
}
|
|
13222
|
-
for (const entry of
|
|
13385
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13223
13386
|
if (skipped(entry.name)) {
|
|
13224
13387
|
continue;
|
|
13225
13388
|
}
|
|
@@ -13291,7 +13454,7 @@ function projectTexts(projectPath) {
|
|
|
13291
13454
|
if (steppedOver(directory)) {
|
|
13292
13455
|
return;
|
|
13293
13456
|
}
|
|
13294
|
-
for (const entry of
|
|
13457
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13295
13458
|
if (skipped(entry.name)) {
|
|
13296
13459
|
continue;
|
|
13297
13460
|
}
|
|
@@ -13321,7 +13484,7 @@ function ignoredTexts(projectPath) {
|
|
|
13321
13484
|
return;
|
|
13322
13485
|
}
|
|
13323
13486
|
const under = ignored || gdignored(directory);
|
|
13324
|
-
for (const entry of
|
|
13487
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13325
13488
|
if (skipped(entry.name)) {
|
|
13326
13489
|
continue;
|
|
13327
13490
|
}
|
|
@@ -13419,7 +13582,7 @@ function searchProject(projectPath, options) {
|
|
|
13419
13582
|
}
|
|
13420
13583
|
return;
|
|
13421
13584
|
}
|
|
13422
|
-
for (const entry of
|
|
13585
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
13423
13586
|
if (skipped(entry.name)) {
|
|
13424
13587
|
continue;
|
|
13425
13588
|
}
|
|
@@ -13499,7 +13662,7 @@ var init_project_scan = __esm(() => {
|
|
|
13499
13662
|
|
|
13500
13663
|
// src/import-freshness.ts
|
|
13501
13664
|
import { createHash as createHash2 } from "node:crypto";
|
|
13502
|
-
import { existsSync as existsSync10, readdirSync as
|
|
13665
|
+
import { existsSync as existsSync10, readdirSync as readdirSync8, readFileSync as readFileSync10, statSync as statSync10 } from "node:fs";
|
|
13503
13666
|
import { join as join13 } from "node:path";
|
|
13504
13667
|
function md5Of(file) {
|
|
13505
13668
|
let stats;
|
|
@@ -13594,7 +13757,7 @@ function staleImports(projectPath) {
|
|
|
13594
13757
|
}
|
|
13595
13758
|
let entries;
|
|
13596
13759
|
try {
|
|
13597
|
-
entries =
|
|
13760
|
+
entries = readdirSync8(directory, { withFileTypes: true });
|
|
13598
13761
|
} catch {
|
|
13599
13762
|
return;
|
|
13600
13763
|
}
|
|
@@ -13681,7 +13844,7 @@ var init_import_freshness = __esm(() => {
|
|
|
13681
13844
|
import {
|
|
13682
13845
|
existsSync as existsSync11,
|
|
13683
13846
|
mkdirSync as mkdirSync4,
|
|
13684
|
-
readdirSync as
|
|
13847
|
+
readdirSync as readdirSync9,
|
|
13685
13848
|
readFileSync as readFileSync11,
|
|
13686
13849
|
renameSync as renameSync2,
|
|
13687
13850
|
unlinkSync,
|
|
@@ -13706,7 +13869,7 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
13706
13869
|
}
|
|
13707
13870
|
let names;
|
|
13708
13871
|
try {
|
|
13709
|
-
names =
|
|
13872
|
+
names = readdirSync9(folder);
|
|
13710
13873
|
} catch {
|
|
13711
13874
|
continue;
|
|
13712
13875
|
}
|
|
@@ -13733,7 +13896,7 @@ function seedExtensionList(projectPath) {
|
|
|
13733
13896
|
}
|
|
13734
13897
|
let entries;
|
|
13735
13898
|
try {
|
|
13736
|
-
entries =
|
|
13899
|
+
entries = readdirSync9(directory, { withFileTypes: true });
|
|
13737
13900
|
} catch {
|
|
13738
13901
|
return;
|
|
13739
13902
|
}
|
|
@@ -13827,7 +13990,7 @@ function importRecord(projectPath, resourcePath) {
|
|
|
13827
13990
|
var init_reimport = () => {};
|
|
13828
13991
|
|
|
13829
13992
|
// src/scratch.ts
|
|
13830
|
-
import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, readdirSync as
|
|
13993
|
+
import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, readdirSync as readdirSync10, rmSync as rmSync8 } from "node:fs";
|
|
13831
13994
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
13832
13995
|
import { join as join16 } from "node:path";
|
|
13833
13996
|
import process10 from "node:process";
|
|
@@ -13837,7 +14000,7 @@ function scratchDirectory(kind) {
|
|
|
13837
14000
|
function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
13838
14001
|
let entries;
|
|
13839
14002
|
try {
|
|
13840
|
-
entries =
|
|
14003
|
+
entries = readdirSync10(directory);
|
|
13841
14004
|
} catch {
|
|
13842
14005
|
return;
|
|
13843
14006
|
}
|
|
@@ -14482,7 +14645,7 @@ class UpdateCheck {
|
|
|
14482
14645
|
asked = false;
|
|
14483
14646
|
checkedAt = 0;
|
|
14484
14647
|
retryAt = 0;
|
|
14485
|
-
backoffMs =
|
|
14648
|
+
backoffMs = FIRST_RETRY_MS2;
|
|
14486
14649
|
enabled;
|
|
14487
14650
|
current;
|
|
14488
14651
|
cachePath;
|
|
@@ -14511,7 +14674,7 @@ class UpdateCheck {
|
|
|
14511
14674
|
}
|
|
14512
14675
|
this.latest = version;
|
|
14513
14676
|
this.checkedAt = Date.now();
|
|
14514
|
-
this.backoffMs =
|
|
14677
|
+
this.backoffMs = FIRST_RETRY_MS2;
|
|
14515
14678
|
this.retryAt = 0;
|
|
14516
14679
|
writeCache(this.cachePath, { checkedAt: this.checkedAt, latest: version });
|
|
14517
14680
|
}).catch(() => {
|
|
@@ -14543,7 +14706,7 @@ class UpdateCheck {
|
|
|
14543
14706
|
};
|
|
14544
14707
|
}
|
|
14545
14708
|
}
|
|
14546
|
-
var REGISTRY_ROOT = "https://registry.npmjs.org", RELEASES = "https://github.com/Aureliolo/gdharness/releases/tag", CACHE_MS, REQUEST_TIMEOUT_MS = 1e4, MAX_BODY_BYTES,
|
|
14709
|
+
var REGISTRY_ROOT = "https://registry.npmjs.org", RELEASES = "https://github.com/Aureliolo/gdharness/releases/tag", CACHE_MS, REQUEST_TIMEOUT_MS = 1e4, MAX_BODY_BYTES, FIRST_RETRY_MS2 = 30000, MAX_RETRY_MS, VERSION;
|
|
14547
14710
|
var init_update_check = __esm(() => {
|
|
14548
14711
|
init_runner();
|
|
14549
14712
|
CACHE_MS = 10 * 60 * 1000;
|
|
@@ -16079,7 +16242,7 @@ import {
|
|
|
16079
16242
|
cpSync,
|
|
16080
16243
|
existsSync as existsSync18,
|
|
16081
16244
|
mkdirSync as mkdirSync6,
|
|
16082
|
-
readdirSync as
|
|
16245
|
+
readdirSync as readdirSync12,
|
|
16083
16246
|
readFileSync as readFileSync18,
|
|
16084
16247
|
rmdirSync,
|
|
16085
16248
|
rmSync as rmSync9,
|
|
@@ -16103,7 +16266,7 @@ function runtimeAddonDigest(from = shippedAddonsDirectory()) {
|
|
|
16103
16266
|
function addonsDigest(names, from) {
|
|
16104
16267
|
const hash = createHash5("sha256");
|
|
16105
16268
|
const walk = (directory, relative) => {
|
|
16106
|
-
for (const name of
|
|
16269
|
+
for (const name of readdirSync12(directory).sort()) {
|
|
16107
16270
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
16108
16271
|
continue;
|
|
16109
16272
|
}
|
|
@@ -16202,7 +16365,7 @@ function removeLoader(projectPath) {
|
|
|
16202
16365
|
rmSync9(copy);
|
|
16203
16366
|
rmSync9(`${copy}.uid`, { force: true });
|
|
16204
16367
|
const folder = dirname9(copy);
|
|
16205
|
-
if (
|
|
16368
|
+
if (readdirSync12(folder).length === 0) {
|
|
16206
16369
|
rmdirSync(folder);
|
|
16207
16370
|
}
|
|
16208
16371
|
return true;
|
|
@@ -16263,7 +16426,7 @@ function removeAddons(projectPath) {
|
|
|
16263
16426
|
}
|
|
16264
16427
|
}
|
|
16265
16428
|
const addons = join22(projectPath, "addons");
|
|
16266
|
-
if (removed.length > 0 && existsSync18(addons) &&
|
|
16429
|
+
if (removed.length > 0 && existsSync18(addons) && readdirSync12(addons).length === 0) {
|
|
16267
16430
|
rmdirSync(addons);
|
|
16268
16431
|
removed.push(addons);
|
|
16269
16432
|
}
|
|
@@ -16596,7 +16759,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16596
16759
|
};
|
|
16597
16760
|
PROPERTIES = {
|
|
16598
16761
|
type: "object",
|
|
16599
|
-
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
|
|
16762
|
+
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}`,
|
|
16600
16763
|
additionalProperties: true
|
|
16601
16764
|
};
|
|
16602
16765
|
XY = {
|
|
@@ -16966,7 +17129,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16966
17129
|
name: "project_search",
|
|
16967
17130
|
title: "Search the project",
|
|
16968
17131
|
effect: "read",
|
|
16969
|
-
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.",
|
|
17132
|
+
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.",
|
|
16970
17133
|
parameters: {
|
|
16971
17134
|
projectPath: PROJECT_PATH,
|
|
16972
17135
|
query: { type: "string", description: "The text or pattern to find." },
|
|
@@ -17093,7 +17256,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17093
17256
|
{
|
|
17094
17257
|
name: "project_export",
|
|
17095
17258
|
title: "Export",
|
|
17096
|
-
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.",
|
|
17259
|
+
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.",
|
|
17097
17260
|
parameters: {
|
|
17098
17261
|
projectPath: PROJECT_PATH,
|
|
17099
17262
|
preset: {
|
|
@@ -17177,7 +17340,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17177
17340
|
title: "Run tests",
|
|
17178
17341
|
effect: "additive",
|
|
17179
17342
|
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.",
|
|
17180
|
-
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
|
|
17343
|
+
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,
|
|
17181
17344
|
parameters: {
|
|
17182
17345
|
projectPath: PROJECT_PATH,
|
|
17183
17346
|
path: {
|
|
@@ -17226,7 +17389,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17226
17389
|
rootNodeType: {
|
|
17227
17390
|
type: "string",
|
|
17228
17391
|
ops: ["create"],
|
|
17229
|
-
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."
|
|
17392
|
+
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."
|
|
17230
17393
|
},
|
|
17231
17394
|
rootNodeName: {
|
|
17232
17395
|
type: "string",
|
|
@@ -17297,7 +17460,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17297
17460
|
nodeType: {
|
|
17298
17461
|
type: "string",
|
|
17299
17462
|
ops: ["add"],
|
|
17300
|
-
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.`
|
|
17463
|
+
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.`
|
|
17301
17464
|
},
|
|
17302
17465
|
instancePath: {
|
|
17303
17466
|
type: "string",
|
|
@@ -17700,7 +17863,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17700
17863
|
},
|
|
17701
17864
|
symbols: {
|
|
17702
17865
|
effect: "read",
|
|
17703
|
-
summary:
|
|
17866
|
+
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`,
|
|
17704
17867
|
requires: []
|
|
17705
17868
|
},
|
|
17706
17869
|
completion: {
|
|
@@ -17740,7 +17903,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17740
17903
|
resourceType: {
|
|
17741
17904
|
type: "string",
|
|
17742
17905
|
ops: ["create"],
|
|
17743
|
-
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.'
|
|
17906
|
+
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.'
|
|
17744
17907
|
},
|
|
17745
17908
|
properties: { ...PROPERTIES, ops: ["create", "modify"] },
|
|
17746
17909
|
script: {
|
|
@@ -17919,7 +18082,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17919
18082
|
visible: {
|
|
17920
18083
|
type: "boolean",
|
|
17921
18084
|
ops: ["start"],
|
|
17922
|
-
description:
|
|
18085
|
+
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.`
|
|
17923
18086
|
},
|
|
17924
18087
|
silent: {
|
|
17925
18088
|
type: "boolean",
|
|
@@ -18113,8 +18276,9 @@ var init_tool_definitions = __esm(() => {
|
|
|
18113
18276
|
description: "How long to wait for the scan. Default 30000."
|
|
18114
18277
|
},
|
|
18115
18278
|
reloadScript: {
|
|
18116
|
-
type: "string",
|
|
18117
|
-
|
|
18279
|
+
type: ["string", "array"],
|
|
18280
|
+
items: { type: "string" },
|
|
18281
|
+
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."
|
|
18118
18282
|
},
|
|
18119
18283
|
listMembers: {
|
|
18120
18284
|
type: "boolean",
|
|
@@ -18285,7 +18449,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18285
18449
|
},
|
|
18286
18450
|
rect: {
|
|
18287
18451
|
effect: "read",
|
|
18288
|
-
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",
|
|
18452
|
+
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",
|
|
18289
18453
|
requires: ["nodePath"]
|
|
18290
18454
|
},
|
|
18291
18455
|
property: {
|
|
@@ -18326,7 +18490,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18326
18490
|
value: {
|
|
18327
18491
|
ops: ["set"],
|
|
18328
18492
|
blank: true,
|
|
18329
|
-
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.`
|
|
18493
|
+
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.`
|
|
18330
18494
|
},
|
|
18331
18495
|
method: {
|
|
18332
18496
|
type: "string",
|
|
@@ -18336,7 +18500,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18336
18500
|
args: {
|
|
18337
18501
|
type: "array",
|
|
18338
18502
|
ops: ["call"],
|
|
18339
|
-
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.`
|
|
18503
|
+
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.`
|
|
18340
18504
|
},
|
|
18341
18505
|
properties: {
|
|
18342
18506
|
type: ["boolean", "array"],
|
|
@@ -19377,6 +19541,21 @@ function windowNeedsOf(scripts, paths) {
|
|
|
19377
19541
|
headless: sorted.filter((suite) => suite.headless).map((suite) => suite.path)
|
|
19378
19542
|
};
|
|
19379
19543
|
}
|
|
19544
|
+
function capsFrames(projectPath) {
|
|
19545
|
+
const file = `${projectPath}/project.godot`;
|
|
19546
|
+
if (!existsSync22(file)) {
|
|
19547
|
+
return false;
|
|
19548
|
+
}
|
|
19549
|
+
const cap = parseProjectGodot(readFileSync21(file, "utf8"))["application"]?.["run/max_fps"];
|
|
19550
|
+
return typeof cap === "number" && cap > 0;
|
|
19551
|
+
}
|
|
19552
|
+
function gdUnitFailsOnPushErrors(projectPath) {
|
|
19553
|
+
const file = `${projectPath}/project.godot`;
|
|
19554
|
+
if (!existsSync22(file)) {
|
|
19555
|
+
return false;
|
|
19556
|
+
}
|
|
19557
|
+
return parseProjectGodot(readFileSync21(file, "utf8"))["gdunit4"]?.["report/godot/push_error"] === true;
|
|
19558
|
+
}
|
|
19380
19559
|
function viewportOf(projectPath) {
|
|
19381
19560
|
const file = `${projectPath}/project.godot`;
|
|
19382
19561
|
if (!existsSync22(file)) {
|
|
@@ -41040,7 +41219,7 @@ var init_autoload_failure = __esm(() => {
|
|
|
41040
41219
|
});
|
|
41041
41220
|
|
|
41042
41221
|
// src/runtime-client.ts
|
|
41043
|
-
import { existsSync as existsSync25, readdirSync as
|
|
41222
|
+
import { existsSync as existsSync25, readdirSync as readdirSync16, readFileSync as readFileSync25, statSync as statSync15, unlinkSync as unlinkSync3 } from "node:fs";
|
|
41044
41223
|
import { createConnection as createConnection2 } from "node:net";
|
|
41045
41224
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
41046
41225
|
import { dirname as dirname14, join as join30, resolve as resolve11 } from "node:path";
|
|
@@ -41216,7 +41395,7 @@ function announcedIn(directory) {
|
|
|
41216
41395
|
const found = [];
|
|
41217
41396
|
const candidates = [];
|
|
41218
41397
|
const entries = [];
|
|
41219
|
-
for (const entry of
|
|
41398
|
+
for (const entry of readdirSync16(directory)) {
|
|
41220
41399
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
41221
41400
|
if (report) {
|
|
41222
41401
|
sweepErrorReport(join30(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
@@ -43558,19 +43737,35 @@ function planMove(args, plan) {
|
|
|
43558
43737
|
}
|
|
43559
43738
|
const lines = script?.lines ?? new Lines(file.text);
|
|
43560
43739
|
for (const path of named) {
|
|
43561
|
-
const
|
|
43562
|
-
|
|
43563
|
-
|
|
43564
|
-
|
|
43565
|
-
|
|
43566
|
-
|
|
43567
|
-
|
|
43568
|
-
|
|
43569
|
-
|
|
43570
|
-
|
|
43571
|
-
|
|
43572
|
-
why: `names ${path}
|
|
43573
|
-
}
|
|
43740
|
+
const after = String.raw`(?![\w-]|\.\w)`;
|
|
43741
|
+
const escaped = (text) => text.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
|
|
43742
|
+
const fromRoot = path.startsWith("res://") ? path.slice("res://".length) : "";
|
|
43743
|
+
const spellings = [
|
|
43744
|
+
{
|
|
43745
|
+
pattern: new RegExp(`${escaped(path)}${after}`, "g"),
|
|
43746
|
+
why: `names ${path} outside a string the engine reads as a path`
|
|
43747
|
+
},
|
|
43748
|
+
...fromRoot === "" ? [] : [
|
|
43749
|
+
{
|
|
43750
|
+
pattern: new RegExp(`(?<![\\w./-])${escaped(fromRoot)}${after}`, "g"),
|
|
43751
|
+
why: `names ${path} as ${fromRoot}, a path from the project's root without res://, which the engine does not read as one`
|
|
43752
|
+
}
|
|
43753
|
+
]
|
|
43754
|
+
];
|
|
43755
|
+
for (const { pattern, why } of spellings) {
|
|
43756
|
+
for (const match of file.text.matchAll(pattern)) {
|
|
43757
|
+
const offset = match.index;
|
|
43758
|
+
const covered = edits.some((one) => one.file === file.path && one.offset <= offset && offset < one.offset + one.length);
|
|
43759
|
+
if (!covered) {
|
|
43760
|
+
const region = script?.regions.find((one) => one.start <= offset && offset < one.end);
|
|
43761
|
+
mentions.push({
|
|
43762
|
+
file: file.path,
|
|
43763
|
+
line: lines.positionOf(offset).line + 1,
|
|
43764
|
+
kind: region !== undefined ? region.kind : /\.(tscn|tres)$/.test(file.path) ? "scene" : "text",
|
|
43765
|
+
text: lines.lineAt(offset).trim(),
|
|
43766
|
+
why
|
|
43767
|
+
});
|
|
43768
|
+
}
|
|
43574
43769
|
}
|
|
43575
43770
|
}
|
|
43576
43771
|
}
|
|
@@ -43707,7 +43902,7 @@ var init_rename = __esm(() => {
|
|
|
43707
43902
|
});
|
|
43708
43903
|
|
|
43709
43904
|
// src/rename-symbol.ts
|
|
43710
|
-
import { existsSync as existsSync30, mkdirSync as mkdirSync17, readdirSync as
|
|
43905
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync17, readdirSync as readdirSync17, realpathSync as realpathSync4, rmSync as rmSync16, writeFileSync as writeFileSync18 } from "node:fs";
|
|
43711
43906
|
import { basename as basename9, dirname as dirname21, join as join37, relative as relative7, resolve as resolve12 } from "node:path";
|
|
43712
43907
|
function resourcePathOf(projectPath, absolute) {
|
|
43713
43908
|
const inside = relative7(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
@@ -43957,7 +44152,7 @@ async function renameSymbol(request, services) {
|
|
|
43957
44152
|
}
|
|
43958
44153
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? [] : [declaringPath]);
|
|
43959
44154
|
const rewrites = rewritesOf(plan, texts, moveTo === null || request.newScriptPath === null ? null : { from: declaringPath, to: moveTo, absoluteTo: request.newScriptPath });
|
|
43960
|
-
const left = leftAloneReport(asWritten(plan.mentions, rewrites));
|
|
44155
|
+
const left = leftAloneReport(asWritten(plan.mentions, rewrites), [symbol]);
|
|
43961
44156
|
const graph = new ClassGraph(scripts, declared);
|
|
43962
44157
|
const rank = (path) => (declaringFiles.includes(path) ? 0 : 1000) + graph.ancestors(`${path}#`).length;
|
|
43963
44158
|
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);
|
|
@@ -44041,6 +44236,7 @@ function changedReport(edits, rewrites) {
|
|
|
44041
44236
|
const lines = rewrite.after.split(`
|
|
44042
44237
|
`);
|
|
44043
44238
|
const byLine = new Map;
|
|
44239
|
+
const firstAt = new Map;
|
|
44044
44240
|
const starts = [0];
|
|
44045
44241
|
for (let at = rewrite.before.indexOf(`
|
|
44046
44242
|
`);at !== -1; at = rewrite.before.indexOf(`
|
|
@@ -44058,6 +44254,8 @@ function changedReport(edits, rewrites) {
|
|
|
44058
44254
|
line += 1;
|
|
44059
44255
|
}
|
|
44060
44256
|
byLine.set(line, (byLine.get(line) ?? new Set).add(edit.kind));
|
|
44257
|
+
const column = edit.offset - (starts[line] ?? 0);
|
|
44258
|
+
firstAt.set(line, Math.min(firstAt.get(line) ?? column, column));
|
|
44061
44259
|
}
|
|
44062
44260
|
return {
|
|
44063
44261
|
file: rewrite.path,
|
|
@@ -44065,7 +44263,11 @@ function changedReport(edits, rewrites) {
|
|
|
44065
44263
|
lines: [...byLine.entries()].sort(([a], [b]) => a - b).map(([line, kinds]) => ({
|
|
44066
44264
|
line: line + 1,
|
|
44067
44265
|
kinds: [...kinds],
|
|
44068
|
-
text: (
|
|
44266
|
+
text: (() => {
|
|
44267
|
+
const whole = (lines[line] ?? "").replace(/\r$/, "");
|
|
44268
|
+
const indent = whole.length - whole.trimStart().length;
|
|
44269
|
+
return quoted2(whole.trim(), (firstAt.get(line) ?? 0) - indent);
|
|
44270
|
+
})()
|
|
44069
44271
|
}))
|
|
44070
44272
|
};
|
|
44071
44273
|
});
|
|
@@ -44078,7 +44280,7 @@ function asWritten(mentions, rewrites) {
|
|
|
44078
44280
|
return line === undefined ? mention : { ...mention, text: line.replace(/\r$/, "").trim() };
|
|
44079
44281
|
});
|
|
44080
44282
|
}
|
|
44081
|
-
function leftAloneReport(mentions) {
|
|
44283
|
+
function leftAloneReport(mentions, terms) {
|
|
44082
44284
|
const full = [];
|
|
44083
44285
|
const prose = new Map;
|
|
44084
44286
|
const others = new Map;
|
|
@@ -44108,7 +44310,7 @@ ${mention.kind}`;
|
|
|
44108
44310
|
others.set(mention.why, files);
|
|
44109
44311
|
counts.otherSymbols += 1;
|
|
44110
44312
|
} else {
|
|
44111
|
-
full.push(mention);
|
|
44313
|
+
full.push({ ...mention, text: quotedAround(mention.text, terms) });
|
|
44112
44314
|
counts.quoted += 1;
|
|
44113
44315
|
}
|
|
44114
44316
|
}
|
|
@@ -44165,12 +44367,24 @@ function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
|
44165
44367
|
const path = join37(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
44166
44368
|
writeFileSync18(path, `${JSON.stringify(detail, null, 2)}
|
|
44167
44369
|
`, "utf8");
|
|
44168
|
-
const kept =
|
|
44370
|
+
const kept = readdirSync17(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
44169
44371
|
for (const old of kept.slice(10)) {
|
|
44170
44372
|
rmSync16(join37(directory, old), { force: true });
|
|
44171
44373
|
}
|
|
44172
44374
|
return path;
|
|
44173
44375
|
}
|
|
44376
|
+
function quoted2(line, at) {
|
|
44377
|
+
if (line.length <= QUOTED_CHARS) {
|
|
44378
|
+
return line;
|
|
44379
|
+
}
|
|
44380
|
+
const start = Math.max(0, Math.min(at - Math.floor(QUOTED_CHARS / 3), line.length - QUOTED_CHARS));
|
|
44381
|
+
const end = start + QUOTED_CHARS;
|
|
44382
|
+
return `${start > 0 ? "..." : ""}${line.slice(start, end)}${end < line.length ? "..." : ""}`;
|
|
44383
|
+
}
|
|
44384
|
+
function quotedAround(text, terms) {
|
|
44385
|
+
const at = terms.map((term) => text.indexOf(term)).filter((found) => found >= 0);
|
|
44386
|
+
return quoted2(text, at.length > 0 ? Math.min(...at) : 0);
|
|
44387
|
+
}
|
|
44174
44388
|
function summaryOf(edits, mentions, rewrites, left) {
|
|
44175
44389
|
const count = (values) => {
|
|
44176
44390
|
const counted = {};
|
|
@@ -44187,7 +44401,7 @@ function summaryOf(edits, mentions, rewrites, left) {
|
|
|
44187
44401
|
leftAloneByKind: count(mentions.map((mention) => mention.kind))
|
|
44188
44402
|
};
|
|
44189
44403
|
}
|
|
44190
|
-
var WHAT, LEFT_ALONE_ANSWER_LINES = 300, detailWrites = 0, PROSE_KINDS;
|
|
44404
|
+
var WHAT, LEFT_ALONE_ANSWER_LINES = 300, detailWrites = 0, PROSE_KINDS, QUOTED_CHARS = 240;
|
|
44191
44405
|
var init_rename_symbol = __esm(() => {
|
|
44192
44406
|
init_gdscript_source();
|
|
44193
44407
|
init_lsp_client();
|
|
@@ -44210,7 +44424,7 @@ var init_rename_symbol = __esm(() => {
|
|
|
44210
44424
|
import {
|
|
44211
44425
|
existsSync as existsSync31,
|
|
44212
44426
|
mkdirSync as mkdirSync18,
|
|
44213
|
-
readdirSync as
|
|
44427
|
+
readdirSync as readdirSync18,
|
|
44214
44428
|
readFileSync as readFileSync33,
|
|
44215
44429
|
renameSync as renameSync4,
|
|
44216
44430
|
statSync as statSync17,
|
|
@@ -44218,7 +44432,7 @@ import {
|
|
|
44218
44432
|
} from "node:fs";
|
|
44219
44433
|
import { basename as basename10, dirname as dirname22, extname as extname4, join as join38, relative as relative8 } from "node:path";
|
|
44220
44434
|
function filesUnder(directory) {
|
|
44221
|
-
return
|
|
44435
|
+
return readdirSync18(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
44222
44436
|
const path = join38(directory, entry.name);
|
|
44223
44437
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
44224
44438
|
});
|
|
@@ -44261,7 +44475,7 @@ function binaryResources(projectPath) {
|
|
|
44261
44475
|
if (existsSync31(join38(directory, ".gdignore"))) {
|
|
44262
44476
|
return;
|
|
44263
44477
|
}
|
|
44264
|
-
for (const entry of
|
|
44478
|
+
for (const entry of readdirSync18(directory, { withFileTypes: true })) {
|
|
44265
44479
|
if (entry.name.startsWith(".")) {
|
|
44266
44480
|
continue;
|
|
44267
44481
|
}
|
|
@@ -44389,7 +44603,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
44389
44603
|
...mention,
|
|
44390
44604
|
file: afterwards(mention.file)
|
|
44391
44605
|
}));
|
|
44392
|
-
const left = leftAloneReport(mentions);
|
|
44606
|
+
const left = leftAloneReport(mentions, [from, ...moves.keys()].flatMap((path) => [path, path.replace(/^res:\/\//, "")]));
|
|
44393
44607
|
const absoluteOf = (path) => join38(projectPath, path.slice("res://".length));
|
|
44394
44608
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
44395
44609
|
const moved = [...moves].map(([source, target]) => {
|
|
@@ -48573,7 +48787,7 @@ var init_restart_note = __esm(() => {
|
|
|
48573
48787
|
|
|
48574
48788
|
// src/run-record.ts
|
|
48575
48789
|
import { createHash as createHash7 } from "node:crypto";
|
|
48576
|
-
import { mkdirSync as mkdirSync21, openSync as openSync5, readdirSync as
|
|
48790
|
+
import { mkdirSync as mkdirSync21, openSync as openSync5, readdirSync as readdirSync19, readFileSync as readFileSync35, rmSync as rmSync18, statSync as statSync19 } from "node:fs";
|
|
48577
48791
|
import { basename as basename11, join as join41, resolve as resolve13 } from "node:path";
|
|
48578
48792
|
function runsDirectory() {
|
|
48579
48793
|
return join41(runtimeDirectory(), "runs");
|
|
@@ -48678,7 +48892,7 @@ function everyRunRecord() {
|
|
|
48678
48892
|
for (const directory of runtimeDirectories()) {
|
|
48679
48893
|
let entries;
|
|
48680
48894
|
try {
|
|
48681
|
-
entries =
|
|
48895
|
+
entries = readdirSync19(join41(directory, "runs"));
|
|
48682
48896
|
} catch {
|
|
48683
48897
|
continue;
|
|
48684
48898
|
}
|
|
@@ -48943,7 +49157,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
48943
49157
|
]));
|
|
48944
49158
|
let entries;
|
|
48945
49159
|
try {
|
|
48946
|
-
entries =
|
|
49160
|
+
entries = readdirSync19(directory);
|
|
48947
49161
|
} catch {
|
|
48948
49162
|
return;
|
|
48949
49163
|
}
|
|
@@ -49365,7 +49579,7 @@ import {
|
|
|
49365
49579
|
fstatSync as fstatSync2,
|
|
49366
49580
|
mkdirSync as mkdirSync23,
|
|
49367
49581
|
openSync as openSync6,
|
|
49368
|
-
readdirSync as
|
|
49582
|
+
readdirSync as readdirSync20,
|
|
49369
49583
|
readFileSync as readFileSync37,
|
|
49370
49584
|
readSync as readSync3,
|
|
49371
49585
|
realpathSync as realpathSync5,
|
|
@@ -50120,7 +50334,7 @@ function exportPresetRefusal(projectPath, preset) {
|
|
|
50120
50334
|
}
|
|
50121
50335
|
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(", ")}.`;
|
|
50122
50336
|
}
|
|
50123
|
-
function exportAnswer(ending, asked, before, after, beside = [], docCacheNote = null) {
|
|
50337
|
+
function exportAnswer(ending, asked, before, after, beside = [], docCacheNote = null, bytes = null) {
|
|
50124
50338
|
const { log } = ending;
|
|
50125
50339
|
const written = after !== null && after !== before;
|
|
50126
50340
|
const notCopied = librariesNotCopied(log.all);
|
|
@@ -50132,6 +50346,7 @@ function exportAnswer(ending, asked, before, after, beside = [], docCacheNote =
|
|
|
50132
50346
|
failure: ending.failure ?? undefined,
|
|
50133
50347
|
errors: log.count("error"),
|
|
50134
50348
|
warnings: log.count("warning"),
|
|
50349
|
+
...written && bytes !== null ? { bytes } : {},
|
|
50135
50350
|
...beside.length === 0 ? {} : { alsoWritten: beside },
|
|
50136
50351
|
...notCopied.length === 0 ? {} : {
|
|
50137
50352
|
librariesNotLoaded: notCopied,
|
|
@@ -50179,7 +50394,7 @@ function writtenBeside(before, after, output, within) {
|
|
|
50179
50394
|
function filesIn(folder) {
|
|
50180
50395
|
const found = new Map;
|
|
50181
50396
|
try {
|
|
50182
|
-
for (const entry of
|
|
50397
|
+
for (const entry of readdirSync20(folder, { withFileTypes: true })) {
|
|
50183
50398
|
if (entry.isFile()) {
|
|
50184
50399
|
found.set(entry.name, statSync20(join44(folder, entry.name)).mtimeMs);
|
|
50185
50400
|
}
|
|
@@ -50771,7 +50986,7 @@ function elsewhereIn(projectPath, asked) {
|
|
|
50771
50986
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
50772
50987
|
let near = [];
|
|
50773
50988
|
try {
|
|
50774
|
-
near =
|
|
50989
|
+
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()));
|
|
50775
50990
|
} catch {
|
|
50776
50991
|
return "";
|
|
50777
50992
|
}
|
|
@@ -50915,6 +51130,9 @@ function ownArguments(params) {
|
|
|
50915
51130
|
function needsItsOwnProcess(asked) {
|
|
50916
51131
|
return asked.savesIn !== undefined || asked.env !== undefined;
|
|
50917
51132
|
}
|
|
51133
|
+
function staleCopyNote(problem) {
|
|
51134
|
+
return `The editor serving the project still holds the copy of the script it compiled before, since reloading it failed: ${problem.replace(/\.$/, "")}.`;
|
|
51135
|
+
}
|
|
50918
51136
|
function unheldNote(changed) {
|
|
50919
51137
|
if (!isPlainObject3(changed)) {
|
|
50920
51138
|
return "";
|
|
@@ -52918,7 +53136,7 @@ class GodotServer {
|
|
|
52918
53136
|
return this.createErrorResponse(place.reason);
|
|
52919
53137
|
}
|
|
52920
53138
|
if (place !== null && !existsSync34(place.absolutePath)) {
|
|
52921
|
-
const folders =
|
|
53139
|
+
const folders = readdirSync20(project.value.path, { withFileTypes: true }).filter((entry) => entry.isDirectory() && !entry.name.startsWith(".")).map((entry) => entry.name).sort();
|
|
52922
53140
|
return this.createErrorResponse(`There is nothing at res://${place.relativePath} to search.${folders.length === 0 ? "" : ` The project's folders are ${folders.join(", ")}.`}`);
|
|
52923
53141
|
}
|
|
52924
53142
|
if (place !== null && !isWithinRoot(realpathSync5.native(project.value.path), realpathSync5.native(place.absolutePath))) {
|
|
@@ -53137,7 +53355,7 @@ ${platform}`;
|
|
|
53137
53355
|
return this.createErrorResponse(`Export could not be run: ${ending.failure ?? "the engine gave no exit status"}`);
|
|
53138
53356
|
}
|
|
53139
53357
|
const beside = writtenBeside(listedBefore, filesIn(folder), basename13(output.absolutePath), dirname25(output.relativePath).replaceAll("\\", "/"));
|
|
53140
|
-
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug, ...pack ? { pack } : {} }, before, writtenAt(), beside, docCacheNote);
|
|
53358
|
+
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug, ...pack ? { pack } : {} }, before, writtenAt(), beside, docCacheNote, existsSync34(output.absolutePath) ? statSync20(output.absolutePath).size : null);
|
|
53141
53359
|
}
|
|
53142
53360
|
reportTestProgress(log) {
|
|
53143
53361
|
const progress = callProgress();
|
|
@@ -53409,7 +53627,7 @@ ${platform}`;
|
|
|
53409
53627
|
const casePrinted = withNestedErrors(readRun(existsSync34(engineLog) ? readFileSync37(engineLog, "utf8") : ""), run.log.all);
|
|
53410
53628
|
const declaredErrors = readExpected(expectedFile);
|
|
53411
53629
|
try {
|
|
53412
|
-
const written = existsSync34(reportsDir) && notCompiled === null ?
|
|
53630
|
+
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))) : [];
|
|
53413
53631
|
const newest = written.at(-1);
|
|
53414
53632
|
if (newest !== undefined) {
|
|
53415
53633
|
report = parseJUnit(readFileSync37(join44(reportsDir, newest, "results.xml"), "utf8"));
|
|
@@ -53682,6 +53900,7 @@ ${error.text}`;
|
|
|
53682
53900
|
uncaughtNote(uncaught),
|
|
53683
53901
|
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,
|
|
53684
53902
|
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,
|
|
53903
|
+
gdUnitFailsOnPushErrors(project.value.path) ? pushErrorsFailedSentence(failedOnPushErrors(casePrinted)) : undefined,
|
|
53685
53904
|
walkStopped ? WALK_STOPPED_SENTENCE : undefined,
|
|
53686
53905
|
countedLeaks.length > 0 ? [
|
|
53687
53906
|
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}`,
|
|
@@ -53825,7 +54044,7 @@ ${error.text}`;
|
|
|
53825
54044
|
const wanted = `res://${relativePath}`;
|
|
53826
54045
|
const folder = dirname25(relativePath.replaceAll("\\", "/"));
|
|
53827
54046
|
const inFolder = join44(projectPath, folder);
|
|
53828
|
-
const beside = existsSync34(inFolder) ?
|
|
54047
|
+
const beside = existsSync34(inFolder) ? readdirSync20(inFolder, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => `res://${folder === "." ? "" : `${folder}/`}${entry.name}`) : [];
|
|
53829
54048
|
return this.createErrorResponse(withNearestFiles(`Script file does not exist: ${wanted}`, wanted, scripts, beside));
|
|
53830
54049
|
}
|
|
53831
54050
|
async compileScripts(projectPath, paths) {
|
|
@@ -55038,16 +55257,18 @@ ${error.text}`;
|
|
|
55038
55257
|
if (exported === null && this.godotBridge.isConnected() && editorWouldPlay && given.length === 0 && flags.value.length === 0 && !needsItsOwnProcess(asked.value) && !silentOnlyHere) {
|
|
55039
55258
|
return await this.playThroughEditor(sceneArgument, refreshed.value, scanned, project.value.path, alreadyPlaying, runtimeWaitMs, ended, profiled);
|
|
55040
55259
|
}
|
|
55260
|
+
const onTheHiddenDesktop = !headless && args["visible"] !== true;
|
|
55041
55261
|
const cmdArgs = runArguments({
|
|
55042
55262
|
projectPath: exported === null ? project.value.path : null,
|
|
55043
55263
|
headless,
|
|
55044
55264
|
scene: sceneArgument,
|
|
55045
55265
|
userArgs: given,
|
|
55046
55266
|
engineArgs: flags.value,
|
|
55047
|
-
silent
|
|
55267
|
+
silent,
|
|
55268
|
+
...onTheHiddenDesktop && process.platform === "win32" ? { hiddenWindow: { projectCapsFrames: exported === null && capsFrames(project.value.path) } } : {}
|
|
55048
55269
|
});
|
|
55049
55270
|
this.logDebug(`Running Godot project: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
55050
|
-
const started = await this.launchKeptGame(engine.value, cmdArgs, project.value.path, needsItsOwnProcess(asked.value) ? environmentFor2({ ...asked.value, runtimeDirectory: runtimeDirectory() }) : undefined,
|
|
55271
|
+
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);
|
|
55051
55272
|
if ("error" in started) {
|
|
55052
55273
|
return this.createErrorResponse(`The game could not be started: ${started.error}`, [
|
|
55053
55274
|
"editor_status says which Godot this server runs and whether it answers"
|
|
@@ -55461,6 +55682,12 @@ ${error.text}`;
|
|
|
55461
55682
|
if (spawned) {
|
|
55462
55683
|
clearTheRecord();
|
|
55463
55684
|
}
|
|
55685
|
+
if (running.throughEditor) {
|
|
55686
|
+
const told = await this.handleViaBridge("stop_playing", {});
|
|
55687
|
+
if (told.isError === true) {
|
|
55688
|
+
this.logDebug(`The editor was not told its closed game is over: ${told.content[0]?.text ?? ""}`);
|
|
55689
|
+
}
|
|
55690
|
+
}
|
|
55464
55691
|
running.endedAt ??= Date.now();
|
|
55465
55692
|
running.log.record("info", `gdharness asked pid ${announced ?? "unknown"} to close, as a player's close asks it, and it exited.`);
|
|
55466
55693
|
return "gone";
|
|
@@ -56902,73 +57129,15 @@ ${error.text}`;
|
|
|
56902
57129
|
const rebuilt = atMissingPaths.length > 0 ? cachedClasses(projectPath) : null;
|
|
56903
57130
|
const dropped = rebuilt === null ? [] : atMissingPaths.filter((name) => !cachedAtMissingPaths(rebuilt, projectPath).includes(name));
|
|
56904
57131
|
const stillAtMissingPaths = atMissingPaths.filter((name) => !dropped.includes(name));
|
|
56905
|
-
const
|
|
56906
|
-
|
|
57132
|
+
const asked = args["reloadScript"];
|
|
57133
|
+
const reloads = [];
|
|
56907
57134
|
const reloadedFirst = [];
|
|
56908
|
-
|
|
56909
|
-
|
|
56910
|
-
|
|
56911
|
-
|
|
56912
|
-
reloaded = {
|
|
56913
|
-
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.`
|
|
56914
|
-
};
|
|
56915
|
-
} else if (reloading !== undefined) {
|
|
56916
|
-
try {
|
|
56917
|
-
const reloadAsked = async () => asParams(await this.godotBridge.invokeTool("reload_script", { ...args, scriptPath: reloading }));
|
|
56918
|
-
let answer = await reloadAsked();
|
|
56919
|
-
const failedFirst = answer["failed"];
|
|
56920
|
-
if (typeof failedFirst === "number" && failedFirst !== 0 && projectPath !== "") {
|
|
56921
|
-
const first = await this.reloadWhatItIsBuiltOn(projectPath, readString(answer, "script") ?? reloading);
|
|
56922
|
-
reloadedFirst.push(...first.reloaded);
|
|
56923
|
-
if (first.reloaded.length > 0) {
|
|
56924
|
-
answer = await reloadAsked();
|
|
56925
|
-
}
|
|
56926
|
-
builtOn = first.said;
|
|
56927
|
-
builtOnUnchecked = first.unchecked === true;
|
|
56928
|
-
}
|
|
56929
|
-
const methods = readArray(answer, "methods");
|
|
56930
|
-
const listed = (field) => {
|
|
56931
|
-
const found = readArray(answer, field);
|
|
56932
|
-
return found ? { [field]: found.map(String) } : {};
|
|
56933
|
-
};
|
|
56934
|
-
const asResource = (path) => path.startsWith("res://") ? path : `res://${path.replace(/^\/+/, "")}`;
|
|
56935
|
-
const script = asResource(readString(answer, "script") ?? reloading);
|
|
56936
|
-
const spelling = script === asResource(reloading) ? {} : { as: script };
|
|
56937
|
-
const failed = answer["failed"];
|
|
56938
|
-
if (typeof failed === "number" && failed !== 0) {
|
|
56939
|
-
reloaded = {
|
|
56940
|
-
...spelling,
|
|
56941
|
-
...listed("heldBefore"),
|
|
56942
|
-
...listed("heldConstantsBefore"),
|
|
56943
|
-
problem: failedReloadNote({
|
|
56944
|
-
script,
|
|
56945
|
-
code: failed,
|
|
56946
|
-
codeName: readString(answer, "failedAs") ?? "",
|
|
56947
|
-
heldBefore: listed("heldBefore")["heldBefore"] ?? [],
|
|
56948
|
-
heldAfter: methods?.map(String) ?? [],
|
|
56949
|
-
constantsBefore: listed("heldConstantsBefore")["heldConstantsBefore"] ?? [],
|
|
56950
|
-
constantsAfter: listed("constants")["constants"] ?? [],
|
|
56951
|
-
names: projectPath === "" ? [] : this.classesAScriptNames(projectPath, script),
|
|
56952
|
-
restored: answer["restored"] === true,
|
|
56953
|
-
restoredAs: readString(answer, "restoredAs") ?? "",
|
|
56954
|
-
...builtOn === undefined ? {} : { builtOn },
|
|
56955
|
-
...builtOnUnchecked ? { builtOnUnchecked } : {}
|
|
56956
|
-
})
|
|
56957
|
-
};
|
|
56958
|
-
} else {
|
|
56959
|
-
reloaded = methods ? {
|
|
56960
|
-
...spelling,
|
|
56961
|
-
methods: methods.map(String),
|
|
56962
|
-
...listed("heldBefore"),
|
|
56963
|
-
...listed("constants"),
|
|
56964
|
-
...listed("heldConstantsBefore"),
|
|
56965
|
-
...answer["unsavedInEditor"] === true ? { unsaved: unsavedInEditorNote([script]) } : {}
|
|
56966
|
-
} : { problem: `the editor would not say what ${script} has after reloading it` };
|
|
56967
|
-
}
|
|
56968
|
-
} catch (error) {
|
|
56969
|
-
reloaded = { problem: `${reloading} could not be reloaded: ${errorMessage(error)}` };
|
|
56970
|
-
}
|
|
57135
|
+
for (const scriptPath of Array.isArray(asked) ? asked.map(String) : typeof asked === "string" ? [asked] : []) {
|
|
57136
|
+
const one = await this.reloadAsked(projectPath, args, scriptPath);
|
|
57137
|
+
reloads.push({ scriptPath, reloaded: one.reloaded });
|
|
57138
|
+
reloadedFirst.push(...one.reloadedFirst);
|
|
56971
57139
|
}
|
|
57140
|
+
const reloaded = Array.isArray(asked) ? {} : reloads[0]?.reloaded ?? {};
|
|
56972
57141
|
const checked = busy ? { unseen: [] } : await this.classesTheEditorCannotSee(args);
|
|
56973
57142
|
const unseen = checked.unseen;
|
|
56974
57143
|
const carried = projectPath === "" ? [] : this.unseenBeforeAnUnfinishedScan(projectPath);
|
|
@@ -57007,7 +57176,7 @@ ${error.text}`;
|
|
|
57007
57176
|
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.`);
|
|
57008
57177
|
}
|
|
57009
57178
|
return this.jsonTextResponse({
|
|
57010
|
-
ok: !busy && unseen.length === 0 && stillGone.length === 0 && stillAtMissingPaths.length === 0 && (dependents === null || dependents.notReloaded.length === 0) && reloaded.problem === undefined && checked.unchecked === undefined,
|
|
57179
|
+
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,
|
|
57011
57180
|
stillWorking: busy,
|
|
57012
57181
|
waitedMs: Date.now() - started,
|
|
57013
57182
|
waitedForEditorScanMs: waitedForEditorMs > 0 ? waitedForEditorMs : undefined,
|
|
@@ -57026,6 +57195,20 @@ ${error.text}`;
|
|
|
57026
57195
|
return all.length > 0 ? all : undefined;
|
|
57027
57196
|
})(),
|
|
57028
57197
|
unsavedInEditorNote: reloaded.unsaved,
|
|
57198
|
+
reloads: Array.isArray(asked) ? reloads.map(({ scriptPath, reloaded: one }) => ({
|
|
57199
|
+
scriptPath,
|
|
57200
|
+
ok: one.problem === undefined,
|
|
57201
|
+
reloadedAs: one.as,
|
|
57202
|
+
...reloadDifference(one),
|
|
57203
|
+
...args["listMembers"] === true ? {
|
|
57204
|
+
heldBeforeReload: one.heldBefore,
|
|
57205
|
+
reloadedMethods: one.methods,
|
|
57206
|
+
heldConstantsBeforeReload: one.heldConstantsBefore,
|
|
57207
|
+
reloadedConstants: one.constants
|
|
57208
|
+
} : {},
|
|
57209
|
+
reloadProblem: one.problem,
|
|
57210
|
+
unsavedInEditorNote: one.unsaved
|
|
57211
|
+
})) : undefined,
|
|
57029
57212
|
unseenByEditor: unseen.length > 0 ? unseen : undefined,
|
|
57030
57213
|
broughtIn: broughtIn.length > 0 ? broughtIn : undefined,
|
|
57031
57214
|
dependentsReloaded: dependents !== null && dependents.reloaded.length > 0 ? dependents.reloaded : undefined,
|
|
@@ -57040,6 +57223,75 @@ ${error.text}`;
|
|
|
57040
57223
|
note: notes.length > 0 ? notes.join(" ") : undefined
|
|
57041
57224
|
});
|
|
57042
57225
|
}
|
|
57226
|
+
async reloadAsked(projectPath, args, reloading) {
|
|
57227
|
+
let reloaded = {};
|
|
57228
|
+
const reloadedFirst = [];
|
|
57229
|
+
let builtOn;
|
|
57230
|
+
let builtOnUnchecked = false;
|
|
57231
|
+
const reloadingSource = projectPath === "" ? null : this.sourceOfClass(projectPath, reloading);
|
|
57232
|
+
if (reloadingSource !== null && isToolScript(reloadingSource)) {
|
|
57233
|
+
reloaded = {
|
|
57234
|
+
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.`
|
|
57235
|
+
};
|
|
57236
|
+
} else {
|
|
57237
|
+
try {
|
|
57238
|
+
const reloadAsked = async () => asParams(await this.godotBridge.invokeTool("reload_script", { ...args, scriptPath: reloading }));
|
|
57239
|
+
let answer = await reloadAsked();
|
|
57240
|
+
const failedFirst = answer["failed"];
|
|
57241
|
+
if (typeof failedFirst === "number" && failedFirst !== 0 && projectPath !== "") {
|
|
57242
|
+
const first = await this.reloadWhatItIsBuiltOn(projectPath, readString(answer, "script") ?? reloading);
|
|
57243
|
+
reloadedFirst.push(...first.reloaded);
|
|
57244
|
+
if (first.reloaded.length > 0) {
|
|
57245
|
+
answer = await reloadAsked();
|
|
57246
|
+
}
|
|
57247
|
+
builtOn = first.said;
|
|
57248
|
+
builtOnUnchecked = first.unchecked === true;
|
|
57249
|
+
}
|
|
57250
|
+
const methods = readArray(answer, "methods");
|
|
57251
|
+
const listed = (field) => {
|
|
57252
|
+
const found = readArray(answer, field);
|
|
57253
|
+
return found ? { [field]: found.map(String) } : {};
|
|
57254
|
+
};
|
|
57255
|
+
const asResource = (path) => path.startsWith("res://") ? path : `res://${path.replace(/^\/+/, "")}`;
|
|
57256
|
+
const script = asResource(readString(answer, "script") ?? reloading);
|
|
57257
|
+
const spelling = script === asResource(reloading) ? {} : { as: script };
|
|
57258
|
+
const failed = answer["failed"];
|
|
57259
|
+
if (typeof failed === "number" && failed !== 0) {
|
|
57260
|
+
reloaded = {
|
|
57261
|
+
...spelling,
|
|
57262
|
+
...listed("heldBefore"),
|
|
57263
|
+
...listed("heldConstantsBefore"),
|
|
57264
|
+
problem: failedReloadNote({
|
|
57265
|
+
script,
|
|
57266
|
+
code: failed,
|
|
57267
|
+
codeName: readString(answer, "failedAs") ?? "",
|
|
57268
|
+
heldBefore: listed("heldBefore")["heldBefore"] ?? [],
|
|
57269
|
+
heldAfter: methods?.map(String) ?? [],
|
|
57270
|
+
constantsBefore: listed("heldConstantsBefore")["heldConstantsBefore"] ?? [],
|
|
57271
|
+
constantsAfter: listed("constants")["constants"] ?? [],
|
|
57272
|
+
names: projectPath === "" ? [] : this.classesAScriptNames(projectPath, script),
|
|
57273
|
+
restored: answer["restored"] === true,
|
|
57274
|
+
restoredAs: readString(answer, "restoredAs") ?? "",
|
|
57275
|
+
...builtOn === undefined ? {} : { builtOn },
|
|
57276
|
+
...builtOnUnchecked ? { builtOnUnchecked } : {}
|
|
57277
|
+
})
|
|
57278
|
+
};
|
|
57279
|
+
} else {
|
|
57280
|
+
reloaded = methods ? {
|
|
57281
|
+
...spelling,
|
|
57282
|
+
methods: methods.map(String),
|
|
57283
|
+
...listed("heldBefore"),
|
|
57284
|
+
...listed("constants"),
|
|
57285
|
+
...listed("heldConstantsBefore"),
|
|
57286
|
+
...answer["unsavedInEditor"] === true ? { unsaved: unsavedInEditorNote([script]) } : {}
|
|
57287
|
+
} : { problem: `the editor would not say what ${script} has after reloading it` };
|
|
57288
|
+
}
|
|
57289
|
+
} catch (error) {
|
|
57290
|
+
reloaded = { problem: `${reloading} could not be reloaded: ${errorMessage(error)}` };
|
|
57291
|
+
}
|
|
57292
|
+
}
|
|
57293
|
+
return { reloaded, reloadedFirst };
|
|
57294
|
+
}
|
|
57043
57295
|
async reloadDependents(projectPath, classes) {
|
|
57044
57296
|
for (const { script, tool } of scriptsDeclaring(projectPath, classes)) {
|
|
57045
57297
|
if (!tool) {
|
|
@@ -57371,9 +57623,7 @@ ${error.text}`;
|
|
|
57371
57623
|
const done = await this.reloadInEditor([{ script: scriptPath, tool: source !== null && isToolScript(source) }], project.value.path);
|
|
57372
57624
|
const problem = done.notReloaded[0]?.problem;
|
|
57373
57625
|
const notes = [
|
|
57374
|
-
...problem === undefined ? [] : [
|
|
57375
|
-
`The editor serving the project was not given the script as written, so it answers from the copy it compiled before: ${problem}.`
|
|
57376
|
-
],
|
|
57626
|
+
...problem === undefined ? [] : [staleCopyNote(problem)],
|
|
57377
57627
|
...done.unsavedInEditor.length > 0 ? [unsavedInEditorNote(done.unsavedInEditor)] : []
|
|
57378
57628
|
];
|
|
57379
57629
|
return this.jsonTextResponse({
|
|
@@ -57554,9 +57804,15 @@ ${error.text}`;
|
|
|
57554
57804
|
return project.response;
|
|
57555
57805
|
}
|
|
57556
57806
|
const projectPath = project.value.path;
|
|
57807
|
+
const typeArgument = TYPE_ARGUMENTS[command];
|
|
57808
|
+
const named = typeArgument === undefined ? "" : readString(contained.value, typeArgument) ?? "";
|
|
57557
57809
|
const waited = await this.waitForTheEditorOpenedHere(contained.value, []);
|
|
57558
57810
|
if (this.editorHasOpen(projectPath)) {
|
|
57559
|
-
|
|
57811
|
+
const answered = await this.handleViaBridge(command, contained.value, askedAs);
|
|
57812
|
+
if (answered.isError === true && declaredButUncached(projectPath, named) !== null) {
|
|
57813
|
+
answered.content.push({ type: "text", text: uncachedClassNote([named]) });
|
|
57814
|
+
}
|
|
57815
|
+
return answered;
|
|
57560
57816
|
}
|
|
57561
57817
|
if (effect === "writes") {
|
|
57562
57818
|
if (waited !== null) {
|
|
@@ -57571,12 +57827,22 @@ ${error.text}`;
|
|
|
57571
57827
|
]);
|
|
57572
57828
|
}
|
|
57573
57829
|
}
|
|
57830
|
+
const declaring = declaredButUncached(projectPath, named);
|
|
57831
|
+
if (declaring !== null) {
|
|
57832
|
+
const rebuilt = await this.rebuildClassCache(projectPath);
|
|
57833
|
+
if (!rebuilt.ok) {
|
|
57834
|
+
return this.answer(rebuilt);
|
|
57835
|
+
}
|
|
57836
|
+
}
|
|
57574
57837
|
const outcome = await this.operation("run_editor_tool", { command, arguments: contained.value }, projectPath);
|
|
57575
57838
|
if (!outcome.ok) {
|
|
57576
57839
|
return this.answer(outcome);
|
|
57577
57840
|
}
|
|
57578
57841
|
return this.jsonTextResponse({
|
|
57579
57842
|
...outcome.payload,
|
|
57843
|
+
...declaring === null ? {} : {
|
|
57844
|
+
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.`
|
|
57845
|
+
},
|
|
57580
57846
|
...effect === "reads" ? {
|
|
57581
57847
|
readBy: "a headless engine, since no editor serves this project; unsaved changes in an editor are not seen"
|
|
57582
57848
|
} : { writtenBy: "a headless engine, since no editor serves this project" },
|
|
@@ -57812,7 +58078,7 @@ ${error.text}`;
|
|
|
57812
58078
|
if (behind.length > 0) {
|
|
57813
58079
|
const one = behind.length === 1;
|
|
57814
58080
|
const scripts = behind.map((entry) => entry.scriptPath);
|
|
57815
|
-
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
|
|
58081
|
+
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.`);
|
|
57816
58082
|
}
|
|
57817
58083
|
return this.jsonTextResponse({
|
|
57818
58084
|
...payload,
|
|
@@ -58341,7 +58607,7 @@ async function runGodotServer() {
|
|
|
58341
58607
|
const server = new GodotServer;
|
|
58342
58608
|
await server.run();
|
|
58343
58609
|
}
|
|
58344
|
-
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;
|
|
58610
|
+
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;
|
|
58345
58611
|
var init_server = __esm(() => {
|
|
58346
58612
|
init_dist2();
|
|
58347
58613
|
init_stdio();
|
|
@@ -58408,6 +58674,11 @@ var init_server = __esm(() => {
|
|
|
58408
58674
|
init_tool_definitions();
|
|
58409
58675
|
init_tool_reference();
|
|
58410
58676
|
init_update_check();
|
|
58677
|
+
TYPE_ARGUMENTS = {
|
|
58678
|
+
add_node: "nodeType",
|
|
58679
|
+
create_resource: "resourceType",
|
|
58680
|
+
create_scene: "rootNodeType"
|
|
58681
|
+
};
|
|
58411
58682
|
__dirname2 = dirname25(fileURLToPath5(import.meta.url));
|
|
58412
58683
|
KEEPER_DEBUG_MS = PAUSE_MS + STEP_MS + 2 * READ_MS;
|
|
58413
58684
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
@@ -58513,7 +58784,7 @@ init_alive();
|
|
|
58513
58784
|
init_engine_launch();
|
|
58514
58785
|
init_lsp_client();
|
|
58515
58786
|
init_process_children();
|
|
58516
|
-
import { existsSync as existsSync17, readdirSync as
|
|
58787
|
+
import { existsSync as existsSync17, readdirSync as readdirSync11, readFileSync as readFileSync16, unlinkSync as unlinkSync2 } from "node:fs";
|
|
58517
58788
|
import { connect as connect2 } from "node:net";
|
|
58518
58789
|
import { join as join21, resolve as resolve7 } from "node:path";
|
|
58519
58790
|
var EDITOR_RECORD_PROTOCOL = 1;
|
|
@@ -58524,7 +58795,7 @@ function editorRecordsIn(projectPath) {
|
|
|
58524
58795
|
const directory = join21(projectPath, ".godot", "gdharness-editors");
|
|
58525
58796
|
let names;
|
|
58526
58797
|
try {
|
|
58527
|
-
names =
|
|
58798
|
+
names = readdirSync11(directory).filter((name) => /^\d+\.json$/.test(name));
|
|
58528
58799
|
} catch {
|
|
58529
58800
|
return [];
|
|
58530
58801
|
}
|
|
@@ -58732,7 +59003,7 @@ init_setup();
|
|
|
58732
59003
|
init_setup();
|
|
58733
59004
|
init_tool_definitions();
|
|
58734
59005
|
init_tool_reference();
|
|
58735
|
-
import { existsSync as existsSync19, mkdirSync as mkdirSync7, readdirSync as
|
|
59006
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync7, readdirSync as readdirSync13, rmSync as rmSync10, writeFileSync as writeFileSync11 } from "node:fs";
|
|
58736
59007
|
import { basename as basename8, dirname as dirname10, join as join23 } from "node:path";
|
|
58737
59008
|
var SKILL_NAME = "gdharness";
|
|
58738
59009
|
var SHARED_SKILLS = join23(".agents", "skills");
|
|
@@ -59016,7 +59287,7 @@ function pruneEmpty(directory, projectPath) {
|
|
|
59016
59287
|
const shared = join23(projectPath, ".agents");
|
|
59017
59288
|
let path = directory;
|
|
59018
59289
|
while (path !== projectPath && (basename8(path) === "skills" || path === shared)) {
|
|
59019
|
-
if (
|
|
59290
|
+
if (readdirSync13(path).length > 0) {
|
|
59020
59291
|
return;
|
|
59021
59292
|
}
|
|
59022
59293
|
rmSync10(path, { recursive: true, force: true });
|
|
@@ -59332,13 +59603,14 @@ init_engine_launch();
|
|
|
59332
59603
|
init_engine_leaks();
|
|
59333
59604
|
init_engine_slots();
|
|
59334
59605
|
init_import_freshness();
|
|
59606
|
+
init_launch();
|
|
59335
59607
|
init_library_copies();
|
|
59336
59608
|
init_setup();
|
|
59337
59609
|
import {
|
|
59338
59610
|
copyFileSync,
|
|
59339
59611
|
existsSync as existsSync23,
|
|
59340
59612
|
mkdirSync as mkdirSync10,
|
|
59341
|
-
readdirSync as
|
|
59613
|
+
readdirSync as readdirSync15,
|
|
59342
59614
|
readFileSync as readFileSync23,
|
|
59343
59615
|
rmSync as rmSync12,
|
|
59344
59616
|
statSync as statSync14,
|
|
@@ -59351,7 +59623,7 @@ import process14 from "node:process";
|
|
|
59351
59623
|
// src/suite-fingerprint.ts
|
|
59352
59624
|
init_class_cache();
|
|
59353
59625
|
import { createHash as createHash6 } from "node:crypto";
|
|
59354
|
-
import { existsSync as existsSync21, readdirSync as
|
|
59626
|
+
import { existsSync as existsSync21, readdirSync as readdirSync14, readFileSync as readFileSync20, statSync as statSync13 } from "node:fs";
|
|
59355
59627
|
import { join as join25, posix as posix2 } from "node:path";
|
|
59356
59628
|
var RES_LITERAL = /res:\/\/[^"'\s)\]<>]*/g;
|
|
59357
59629
|
var RELATIVE_LOAD = /\b(?:pre)?load\(\s*["']((?!res:\/\/|uid:\/\/)[^"']+)["']/g;
|
|
@@ -59410,7 +59682,7 @@ ${this.read(path).digest}
|
|
|
59410
59682
|
addon(root) {
|
|
59411
59683
|
const hash = createHash6("sha256");
|
|
59412
59684
|
const walk = (directory, under) => {
|
|
59413
|
-
for (const entry of
|
|
59685
|
+
for (const entry of readdirSync14(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
59414
59686
|
const file = join25(directory, entry.name);
|
|
59415
59687
|
if (entry.isDirectory()) {
|
|
59416
59688
|
walk(file, `${under}${entry.name}/`);
|
|
@@ -59435,7 +59707,7 @@ ${this.read(path).digest}
|
|
|
59435
59707
|
return { digest: "missing", reaches: [], unreadable: null };
|
|
59436
59708
|
}
|
|
59437
59709
|
if (statSync13(file).isDirectory()) {
|
|
59438
|
-
const inside =
|
|
59710
|
+
const inside = readdirSync14(file, { withFileTypes: true }).filter((entry) => !entry.name.startsWith(".")).map((entry) => `${path.endsWith("/") ? path : `${path}/`}${entry.name}`).sort();
|
|
59439
59711
|
return { digest: inside.join(`
|
|
59440
59712
|
`), reaches: inside, unreadable: null };
|
|
59441
59713
|
}
|
|
@@ -59687,7 +59959,7 @@ class RunLogs {
|
|
|
59687
59959
|
if (this.kept === null) {
|
|
59688
59960
|
return;
|
|
59689
59961
|
}
|
|
59690
|
-
for (const file of
|
|
59962
|
+
for (const file of readdirSync15(this.directory, { recursive: true, encoding: "utf8" })) {
|
|
59691
59963
|
if (file.endsWith(".log") && file.split(sep4).length === 2) {
|
|
59692
59964
|
mkdirSync10(dirname13(join28(this.kept, file)), { recursive: true });
|
|
59693
59965
|
copyFileSync(join28(this.directory, file), join28(this.kept, file));
|
|
@@ -59747,10 +60019,15 @@ async function runTestCommand(options, godotPath) {
|
|
|
59747
60019
|
selfTyped: selfTypedExports(scripts.sources),
|
|
59748
60020
|
godotVersion: options.godotVersion,
|
|
59749
60021
|
cores: options.cores,
|
|
59750
|
-
verbose: options.verbose
|
|
60022
|
+
verbose: options.verbose,
|
|
60023
|
+
failsOnPushErrors: gdUnitFailsOnPushErrors(options.projectPath)
|
|
59751
60024
|
};
|
|
59752
60025
|
try {
|
|
59753
|
-
|
|
60026
|
+
const neverImported = cachedClasses(options.projectPath) === null;
|
|
60027
|
+
if (neverImported && !options.importFirst) {
|
|
60028
|
+
console.log("The project has never been imported, so it is imported first.");
|
|
60029
|
+
}
|
|
60030
|
+
if (options.importFirst || neverImported) {
|
|
59754
60031
|
const imported = await importProject(launch);
|
|
59755
60032
|
if (imported !== null) {
|
|
59756
60033
|
logs.keep();
|
|
@@ -60014,7 +60291,10 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
60014
60291
|
`)}
|
|
60015
60292
|
`);
|
|
60016
60293
|
const viewport = viewportOf(launch.projectPath);
|
|
60017
|
-
const display = plan.windowed ?
|
|
60294
|
+
const display = plan.windowed ? [
|
|
60295
|
+
...viewport === null ? [] : ["--resolution", `${String(viewport.width)}x${String(viewport.height)}`],
|
|
60296
|
+
...process14.platform === "win32" ? hiddenWindowFlags([], capsFrames(launch.projectPath)) : []
|
|
60297
|
+
] : ["--headless"];
|
|
60018
60298
|
const args = [
|
|
60019
60299
|
...display,
|
|
60020
60300
|
"--audio-driver",
|
|
@@ -60059,7 +60339,8 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
60059
60339
|
suites: plan.suites,
|
|
60060
60340
|
expected: readExpected(expectedFile),
|
|
60061
60341
|
selfTyped: launch.selfTyped,
|
|
60062
|
-
godotVersion: launch.godotVersion
|
|
60342
|
+
godotVersion: launch.godotVersion,
|
|
60343
|
+
failsOnPushErrors: launch.failsOnPushErrors
|
|
60063
60344
|
};
|
|
60064
60345
|
const judged = judgeEngine(printed, assignment, end);
|
|
60065
60346
|
const written = reportsFault(join28(directory, "reports"));
|
|
@@ -60076,7 +60357,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
60076
60357
|
}
|
|
60077
60358
|
var REPORT_FILES_NAMED = 3;
|
|
60078
60359
|
function reportsFault(reports) {
|
|
60079
|
-
const files = existsSync23(reports) ?
|
|
60360
|
+
const files = existsSync23(reports) ? readdirSync15(reports, { recursive: true, encoding: "utf8" }).filter((file) => statSync14(join28(reports, file)).isFile()).sort() : [];
|
|
60080
60361
|
if (files.length === 0) {
|
|
60081
60362
|
return null;
|
|
60082
60363
|
}
|