gdharness 1.3.43 → 1.3.45
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 +607 -511
- package/build/godot/addons/gdharness_editor/reading.gd +14 -5
- package/build/godot/addons/gdharness_editor/scene_file.gd +18 -1
- package/build/godot/addons/gdharness_editor/setting_reads.gd +42 -1
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +31 -12
- package/build/godot/addons/gdharness_runtime/reading.gd +14 -5
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +13 -0
- package/build/godot/operations/dependencies.gd +154 -85
- package/build/godot/operations/editor_tools.gd +5 -4
- package/build/godot/operations/gdscript_analysis.gd +45 -8
- package/build/godot/operations/patterns.gd +6 -0
- package/build/godot/operations/project_config.gd +8 -3
- package/build/godot/operations/project_validation.gd +3 -4
- package/build/godot/operations/reading.gd +14 -5
- package/build/godot/operations/setting_reads.gd +42 -1
- package/build/keeper.js +43 -43
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -9723,8 +9723,71 @@ var init_gdscript_source = __esm(() => {
|
|
|
9723
9723
|
]);
|
|
9724
9724
|
});
|
|
9725
9725
|
|
|
9726
|
+
// src/whole-file.ts
|
|
9727
|
+
import { readFileSync as readFileSync5, renameSync, rmSync as rmSync4, writeFileSync as writeFileSync4 } from "node:fs";
|
|
9728
|
+
function readWhole(path) {
|
|
9729
|
+
const until = Date.now() + HELD_FILE_MS;
|
|
9730
|
+
for (;; ) {
|
|
9731
|
+
try {
|
|
9732
|
+
return readFileSync5(path, "utf8");
|
|
9733
|
+
} catch (error) {
|
|
9734
|
+
const code = error.code ?? "";
|
|
9735
|
+
if (code === "ENOENT") {
|
|
9736
|
+
return null;
|
|
9737
|
+
}
|
|
9738
|
+
if (!HELD_BY_A_READER.has(code) || Date.now() >= until) {
|
|
9739
|
+
throw error;
|
|
9740
|
+
}
|
|
9741
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, HELD_FILE_RETRY_MS);
|
|
9742
|
+
}
|
|
9743
|
+
}
|
|
9744
|
+
}
|
|
9745
|
+
function writeWhole(path, text) {
|
|
9746
|
+
const partial = `${path}.${process.pid}.partial`;
|
|
9747
|
+
try {
|
|
9748
|
+
writeFileSync4(partial, text, "utf8");
|
|
9749
|
+
} catch {
|
|
9750
|
+
return false;
|
|
9751
|
+
}
|
|
9752
|
+
const until = Date.now() + HELD_FILE_MS;
|
|
9753
|
+
for (;; ) {
|
|
9754
|
+
try {
|
|
9755
|
+
renameSync(partial, path);
|
|
9756
|
+
return true;
|
|
9757
|
+
} catch (error) {
|
|
9758
|
+
const code = error.code ?? "";
|
|
9759
|
+
if (!HELD_BY_A_READER.has(code) || Date.now() >= until) {
|
|
9760
|
+
try {
|
|
9761
|
+
rmSync4(partial, { force: true });
|
|
9762
|
+
} catch {}
|
|
9763
|
+
return false;
|
|
9764
|
+
}
|
|
9765
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, HELD_FILE_RETRY_MS);
|
|
9766
|
+
}
|
|
9767
|
+
}
|
|
9768
|
+
}
|
|
9769
|
+
function removeWhole(path) {
|
|
9770
|
+
const until = Date.now() + HELD_FILE_MS;
|
|
9771
|
+
for (;; ) {
|
|
9772
|
+
try {
|
|
9773
|
+
rmSync4(path, { force: true });
|
|
9774
|
+
return true;
|
|
9775
|
+
} catch (error) {
|
|
9776
|
+
const code = error.code ?? "";
|
|
9777
|
+
if (!HELD_BY_A_READER.has(code) || Date.now() >= until) {
|
|
9778
|
+
return false;
|
|
9779
|
+
}
|
|
9780
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, HELD_FILE_RETRY_MS);
|
|
9781
|
+
}
|
|
9782
|
+
}
|
|
9783
|
+
}
|
|
9784
|
+
var HELD_FILE_MS = 1000, HELD_FILE_RETRY_MS = 10, HELD_BY_A_READER;
|
|
9785
|
+
var init_whole_file = __esm(() => {
|
|
9786
|
+
HELD_BY_A_READER = new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
9787
|
+
});
|
|
9788
|
+
|
|
9726
9789
|
// src/class-cache.ts
|
|
9727
|
-
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as
|
|
9790
|
+
import { existsSync as existsSync4, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync4 } from "node:fs";
|
|
9728
9791
|
import { join as join6, posix } from "node:path";
|
|
9729
9792
|
function isToolScript(source) {
|
|
9730
9793
|
return TOOL.test(codeOf(source));
|
|
@@ -9799,7 +9862,7 @@ function eachScript(projectPath, each) {
|
|
|
9799
9862
|
if (entry.isDirectory()) {
|
|
9800
9863
|
visit(path, `${prefix}${entry.name}/`);
|
|
9801
9864
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
9802
|
-
each(`res://${prefix}${entry.name}`,
|
|
9865
|
+
each(`res://${prefix}${entry.name}`, readFileSync6(path, "utf8"));
|
|
9803
9866
|
}
|
|
9804
9867
|
}
|
|
9805
9868
|
};
|
|
@@ -9832,12 +9895,11 @@ function scriptsDeclaring(projectPath, classes) {
|
|
|
9832
9895
|
return declaring.sort((a, b) => a.script.localeCompare(b.script));
|
|
9833
9896
|
}
|
|
9834
9897
|
function cachedClasses(projectPath) {
|
|
9835
|
-
const
|
|
9836
|
-
if (
|
|
9898
|
+
const text = readWhole(join6(projectPath, ".godot", "global_script_class_cache.cfg"));
|
|
9899
|
+
if (text === null) {
|
|
9837
9900
|
return null;
|
|
9838
9901
|
}
|
|
9839
9902
|
const listed = new Map;
|
|
9840
|
-
const text = readFileSync5(cache, "utf8");
|
|
9841
9903
|
for (const entry of text.matchAll(/"class":\s*&"([^"]+)"[\s\S]*?"path":\s*"([^"]+)"/g)) {
|
|
9842
9904
|
listed.set(entry[1] ?? "", entry[2] ?? "");
|
|
9843
9905
|
}
|
|
@@ -10126,7 +10188,8 @@ function failedReloadNote(failed) {
|
|
|
10126
10188
|
} else {
|
|
10127
10189
|
copy = `The copy the editor holds was compiled again from the text it was built from and changed even so${lost.length === 0 ? "" : `, and has lost ${lost.join(", ")}`}: anything diagnosed against it now is answered from what is left, and reloading it once it compiles rebuilds it.`;
|
|
10128
10190
|
}
|
|
10129
|
-
const
|
|
10191
|
+
const advice = failed.names.length === 0 ? "" : ` If they deny a member of ${failed.names.join(" or ")} that its file declares, the editor's copy of that class is behind: reload that class first with reloadScript and then this script, the order that cleared it in the one project that has met this.`;
|
|
10192
|
+
const order = failed.builtOn === undefined ? advice : ` ${failed.builtOn}${failed.builtOnUnchecked === true ? advice : ""}`;
|
|
10130
10193
|
return `${failed.script} did not compile: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
|
|
10131
10194
|
}
|
|
10132
10195
|
function uncachedClassNote(types) {
|
|
@@ -10188,6 +10251,7 @@ function unseenByEditor(projectPath, editorHolds) {
|
|
|
10188
10251
|
var DECLARATION, TOOL, ABSTRACT_HEADER, UNKNOWN_CLASS;
|
|
10189
10252
|
var init_class_cache = __esm(() => {
|
|
10190
10253
|
init_gdscript_source();
|
|
10254
|
+
init_whole_file();
|
|
10191
10255
|
DECLARATION = /^(?:@[a-z_]+(?:\([^)\n]*\))?\s+)*class_name\s+([A-Za-z_][A-Za-z0-9_]*)/m;
|
|
10192
10256
|
TOOL = /^[ \t]*(?:@[a-z_]+(?:\([^)\n]*\))?[ \t]+)*@tool\b/m;
|
|
10193
10257
|
ABSTRACT_HEADER = /^((?:@[\p{L}_][\p{L}\p{N}_]*(?:\([^)]*\))?\s+)*)(?:class_name|extends)\s/mu;
|
|
@@ -10894,7 +10958,7 @@ var init_junit = __esm(() => {
|
|
|
10894
10958
|
});
|
|
10895
10959
|
|
|
10896
10960
|
// src/suite-judge.ts
|
|
10897
|
-
import { readFileSync as
|
|
10961
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
10898
10962
|
import { stripVTControlCharacters } from "node:util";
|
|
10899
10963
|
function secondsOf(time) {
|
|
10900
10964
|
let ms = 0;
|
|
@@ -11095,7 +11159,7 @@ function versionFault(said, wanted) {
|
|
|
11095
11159
|
function readExpected(file) {
|
|
11096
11160
|
const declared = (value) => typeof value === "object" && value !== null ? value : {};
|
|
11097
11161
|
try {
|
|
11098
|
-
const parsed = JSON.parse(
|
|
11162
|
+
const parsed = JSON.parse(readFileSync7(file, "utf8"));
|
|
11099
11163
|
const read = typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
11100
11164
|
return { errors: declared(read["errors"]), warnings: declared(read["warnings"]) };
|
|
11101
11165
|
} catch {
|
|
@@ -11417,9 +11481,9 @@ import {
|
|
|
11417
11481
|
openSync as openSync2,
|
|
11418
11482
|
readdirSync as readdirSync3,
|
|
11419
11483
|
readSync,
|
|
11420
|
-
rmSync as
|
|
11484
|
+
rmSync as rmSync5,
|
|
11421
11485
|
statSync as statSync5,
|
|
11422
|
-
writeFileSync as
|
|
11486
|
+
writeFileSync as writeFileSync5,
|
|
11423
11487
|
writeSync as writeSync2
|
|
11424
11488
|
} from "node:fs";
|
|
11425
11489
|
import { availableParallelism, freemem, totalmem } from "node:os";
|
|
@@ -11495,14 +11559,14 @@ function sweepRunDirectories(runs, kept = KEPT_RUN_DIRECTORIES) {
|
|
|
11495
11559
|
const named = /^(\d+)-(\d+)$/.exec(entry);
|
|
11496
11560
|
const pid = Number(named?.[2]);
|
|
11497
11561
|
if (!Number.isInteger(pid)) {
|
|
11498
|
-
|
|
11562
|
+
rmSync5(join7(runs, entry), { recursive: true, force: true });
|
|
11499
11563
|
} else if (!alive(pid)) {
|
|
11500
11564
|
ended.push({ entry, started: Number(named?.[1]) });
|
|
11501
11565
|
}
|
|
11502
11566
|
}
|
|
11503
11567
|
ended.sort((a, b) => b.started - a.started);
|
|
11504
11568
|
for (const { entry } of ended.slice(kept)) {
|
|
11505
|
-
|
|
11569
|
+
rmSync5(join7(runs, entry), { recursive: true, force: true });
|
|
11506
11570
|
}
|
|
11507
11571
|
}
|
|
11508
11572
|
function roomFor(most, processors, available, said = "of memory free", processorsSaid = `the machine has ${plural(processors, "processor")}`) {
|
|
@@ -11607,7 +11671,7 @@ async function watchedEngine(launch, args, directory, log, silenceSeconds, silen
|
|
|
11607
11671
|
piped = [helped.stdout, helped.stderr];
|
|
11608
11672
|
child = helped;
|
|
11609
11673
|
stop = () => {
|
|
11610
|
-
|
|
11674
|
+
writeFileSync5(stopFile, "");
|
|
11611
11675
|
};
|
|
11612
11676
|
} else {
|
|
11613
11677
|
const held = heldToCores(launch.cores, launch.godotPath, args);
|
|
@@ -11754,6 +11818,16 @@ var init_engine_launch = __esm(() => {
|
|
|
11754
11818
|
MEMORY_PER_ENGINE = 1.5 * 1024 ** 3;
|
|
11755
11819
|
});
|
|
11756
11820
|
|
|
11821
|
+
// src/cache-home.ts
|
|
11822
|
+
import { homedir as homedir2 } from "node:os";
|
|
11823
|
+
import { join as join8 } from "node:path";
|
|
11824
|
+
function cacheHome(env = process.env, platform = process.platform, home = homedir2()) {
|
|
11825
|
+
const set = (name) => env[name] === "" ? undefined : env[name];
|
|
11826
|
+
const local = platform === "win32" ? set("LOCALAPPDATA") : undefined;
|
|
11827
|
+
return join8(set("XDG_CACHE_HOME") ?? local ?? join8(home, ".cache"), "gdharness");
|
|
11828
|
+
}
|
|
11829
|
+
var init_cache_home = () => {};
|
|
11830
|
+
|
|
11757
11831
|
// src/call-signal.ts
|
|
11758
11832
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
11759
11833
|
function stillGoingSaid(tool, op, elapsedMs) {
|
|
@@ -11761,9 +11835,9 @@ function stillGoingSaid(tool, op, elapsedMs) {
|
|
|
11761
11835
|
const took = seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
11762
11836
|
return `${op === null || op === "" ? tool : `${tool} ${op}`} is still going; ${took} in.`;
|
|
11763
11837
|
}
|
|
11764
|
-
async function withCallSignal(signal, answer, progress, still) {
|
|
11838
|
+
async function withCallSignal(signal, answer, progress, still, slotWaits) {
|
|
11765
11839
|
if (progress === undefined) {
|
|
11766
|
-
return signal === undefined ? answer() : current.run({ signal, progress: undefined }, answer);
|
|
11840
|
+
return signal === undefined && slotWaits === undefined ? answer() : current.run({ signal, progress: undefined, slotWaits }, answer);
|
|
11767
11841
|
}
|
|
11768
11842
|
const began = Date.now();
|
|
11769
11843
|
const rising = risingOnly(progress);
|
|
@@ -11787,7 +11861,7 @@ async function withCallSignal(signal, answer, progress, still) {
|
|
|
11787
11861
|
};
|
|
11788
11862
|
rearm();
|
|
11789
11863
|
try {
|
|
11790
|
-
return await current.run({ signal, progress: told }, answer);
|
|
11864
|
+
return await current.run({ signal, progress: told, slotWaits }, answer);
|
|
11791
11865
|
} finally {
|
|
11792
11866
|
going = undefined;
|
|
11793
11867
|
clearTimeout(quiet);
|
|
@@ -11806,70 +11880,27 @@ function callSignal() {
|
|
|
11806
11880
|
function callProgress() {
|
|
11807
11881
|
return current.getStore()?.progress;
|
|
11808
11882
|
}
|
|
11883
|
+
function callSlotWaits() {
|
|
11884
|
+
return current.getStore()?.slotWaits;
|
|
11885
|
+
}
|
|
11809
11886
|
var current, CALL_HEARTBEAT_MS = 20000;
|
|
11810
11887
|
var init_call_signal = __esm(() => {
|
|
11811
11888
|
current = new AsyncLocalStorage;
|
|
11812
11889
|
});
|
|
11813
11890
|
|
|
11814
|
-
// src/whole-file.ts
|
|
11815
|
-
import { renameSync, rmSync as rmSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
11816
|
-
function writeWhole(path, text) {
|
|
11817
|
-
const partial = `${path}.${process.pid}.partial`;
|
|
11818
|
-
try {
|
|
11819
|
-
writeFileSync5(partial, text, "utf8");
|
|
11820
|
-
} catch {
|
|
11821
|
-
return false;
|
|
11822
|
-
}
|
|
11823
|
-
const until = Date.now() + HELD_FILE_MS;
|
|
11824
|
-
for (;; ) {
|
|
11825
|
-
try {
|
|
11826
|
-
renameSync(partial, path);
|
|
11827
|
-
return true;
|
|
11828
|
-
} catch (error) {
|
|
11829
|
-
const code = error.code ?? "";
|
|
11830
|
-
if (!HELD_BY_A_READER.has(code) || Date.now() >= until) {
|
|
11831
|
-
try {
|
|
11832
|
-
rmSync5(partial, { force: true });
|
|
11833
|
-
} catch {}
|
|
11834
|
-
return false;
|
|
11835
|
-
}
|
|
11836
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, HELD_FILE_RETRY_MS);
|
|
11837
|
-
}
|
|
11838
|
-
}
|
|
11839
|
-
}
|
|
11840
|
-
function removeWhole(path) {
|
|
11841
|
-
const until = Date.now() + HELD_FILE_MS;
|
|
11842
|
-
for (;; ) {
|
|
11843
|
-
try {
|
|
11844
|
-
rmSync5(path, { force: true });
|
|
11845
|
-
return true;
|
|
11846
|
-
} catch (error) {
|
|
11847
|
-
const code = error.code ?? "";
|
|
11848
|
-
if (!HELD_BY_A_READER.has(code) || Date.now() >= until) {
|
|
11849
|
-
return false;
|
|
11850
|
-
}
|
|
11851
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, HELD_FILE_RETRY_MS);
|
|
11852
|
-
}
|
|
11853
|
-
}
|
|
11854
|
-
}
|
|
11855
|
-
var HELD_FILE_MS = 1000, HELD_FILE_RETRY_MS = 10, HELD_BY_A_READER;
|
|
11856
|
-
var init_whole_file = __esm(() => {
|
|
11857
|
-
HELD_BY_A_READER = new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
11858
|
-
});
|
|
11859
|
-
|
|
11860
11891
|
// src/engine-slots.ts
|
|
11861
11892
|
import {
|
|
11862
11893
|
closeSync as closeSync3,
|
|
11863
11894
|
mkdirSync as mkdirSync3,
|
|
11864
11895
|
openSync as openSync3,
|
|
11865
|
-
readFileSync as
|
|
11896
|
+
readFileSync as readFileSync8,
|
|
11866
11897
|
rmSync as rmSync6,
|
|
11867
11898
|
statSync as statSync6,
|
|
11868
11899
|
watch,
|
|
11869
11900
|
writeSync as writeSync3
|
|
11870
11901
|
} from "node:fs";
|
|
11871
|
-
import { availableParallelism as availableParallelism2, homedir as
|
|
11872
|
-
import { join as
|
|
11902
|
+
import { availableParallelism as availableParallelism2, homedir as homedir3 } from "node:os";
|
|
11903
|
+
import { join as join9, resolve as resolve4 } from "node:path";
|
|
11873
11904
|
import process7 from "node:process";
|
|
11874
11905
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
11875
11906
|
function machineSlots(asked = {
|
|
@@ -11879,15 +11910,13 @@ function machineSlots(asked = {
|
|
|
11879
11910
|
env: process7.env,
|
|
11880
11911
|
platform: process7.platform,
|
|
11881
11912
|
processors: availableParallelism2(),
|
|
11882
|
-
home:
|
|
11913
|
+
home: homedir3(),
|
|
11883
11914
|
cwd: process7.cwd()
|
|
11884
11915
|
}) {
|
|
11885
11916
|
const { env } = machine;
|
|
11886
11917
|
const named = nonEmpty(env[SLOTS_VARIABLE]);
|
|
11887
|
-
const
|
|
11888
|
-
const
|
|
11889
|
-
const cache = join8(xdg ?? local ?? join8(machine.home, ".cache"), "gdharness");
|
|
11890
|
-
const directory = asked.directory ?? (named === undefined ? join8(cache, "slots") : resolve4(machine.cwd, named));
|
|
11918
|
+
const cache = cacheHome(env, machine.platform, machine.home);
|
|
11919
|
+
const directory = asked.directory ?? (named === undefined ? join9(cache, "slots") : resolve4(machine.cwd, named));
|
|
11891
11920
|
const counted = nonEmpty(env[SLOT_COUNT_VARIABLE]);
|
|
11892
11921
|
let count = asked.count;
|
|
11893
11922
|
if (count === null && counted !== undefined) {
|
|
@@ -11992,7 +12021,7 @@ class EngineSlots {
|
|
|
11992
12021
|
this.holder = holder;
|
|
11993
12022
|
this.say = say;
|
|
11994
12023
|
}
|
|
11995
|
-
async take(signal, waiting) {
|
|
12024
|
+
async take(signal, waiting, blocked) {
|
|
11996
12025
|
mkdirSync3(this.directory, { recursive: true });
|
|
11997
12026
|
const began = Date.now();
|
|
11998
12027
|
let said = Number.NEGATIVE_INFINITY;
|
|
@@ -12005,7 +12034,10 @@ class EngineSlots {
|
|
|
12005
12034
|
if (lock !== null) {
|
|
12006
12035
|
return lock;
|
|
12007
12036
|
}
|
|
12008
|
-
mark
|
|
12037
|
+
if (mark === undefined) {
|
|
12038
|
+
mark = this.waitMarked();
|
|
12039
|
+
blocked?.();
|
|
12040
|
+
}
|
|
12009
12041
|
const now = Date.now();
|
|
12010
12042
|
if (waiting !== undefined) {
|
|
12011
12043
|
if (now - said >= WAIT_SAID_MS) {
|
|
@@ -12032,7 +12064,7 @@ class EngineSlots {
|
|
|
12032
12064
|
async takeFree() {
|
|
12033
12065
|
mkdirSync3(this.directory, { recursive: true });
|
|
12034
12066
|
for (let slot = 0;slot < this.count; slot += 1) {
|
|
12035
|
-
const lock =
|
|
12067
|
+
const lock = join9(this.directory, `${String(slot)}.lock`);
|
|
12036
12068
|
if (this.made(lock) || await this.takenOver(lock)) {
|
|
12037
12069
|
this.held.add(lock);
|
|
12038
12070
|
return lock;
|
|
@@ -12041,9 +12073,9 @@ class EngineSlots {
|
|
|
12041
12073
|
return null;
|
|
12042
12074
|
}
|
|
12043
12075
|
waitMarked() {
|
|
12044
|
-
const marks =
|
|
12076
|
+
const marks = join9(this.directory, WAITING_FOLDER);
|
|
12045
12077
|
waitsMarked += 1;
|
|
12046
|
-
const mark =
|
|
12078
|
+
const mark = join9(marks, `${String(this.holder.pid)}-${String(waitsMarked)}.json`);
|
|
12047
12079
|
try {
|
|
12048
12080
|
mkdirSync3(marks, { recursive: true });
|
|
12049
12081
|
} catch {
|
|
@@ -12059,7 +12091,7 @@ class EngineSlots {
|
|
|
12059
12091
|
const found = [];
|
|
12060
12092
|
for (let slot = 0;slot < this.count; slot += 1) {
|
|
12061
12093
|
try {
|
|
12062
|
-
const holder = holderOf(
|
|
12094
|
+
const holder = holderOf(readFileSync8(join9(this.directory, `${String(slot)}.lock`), "utf8"));
|
|
12063
12095
|
if (holder !== null) {
|
|
12064
12096
|
found.push(holder);
|
|
12065
12097
|
}
|
|
@@ -12079,7 +12111,7 @@ class EngineSlots {
|
|
|
12079
12111
|
release(lock) {
|
|
12080
12112
|
this.held.delete(lock);
|
|
12081
12113
|
try {
|
|
12082
|
-
if (holderOf(
|
|
12114
|
+
if (holderOf(readFileSync8(lock, "utf8"))?.pid === this.holder.pid) {
|
|
12083
12115
|
removeWhole(lock);
|
|
12084
12116
|
}
|
|
12085
12117
|
} catch {}
|
|
@@ -12129,7 +12161,7 @@ class EngineSlots {
|
|
|
12129
12161
|
let content;
|
|
12130
12162
|
let writtenAt;
|
|
12131
12163
|
try {
|
|
12132
|
-
content =
|
|
12164
|
+
content = readFileSync8(lock, "utf8");
|
|
12133
12165
|
writtenAt = statSync6(lock).mtimeMs;
|
|
12134
12166
|
} catch {
|
|
12135
12167
|
return false;
|
|
@@ -12167,31 +12199,55 @@ function nameSlotTaker(by) {
|
|
|
12167
12199
|
}
|
|
12168
12200
|
function slotWaitSaid(waitedMs, count, directory, holders = []) {
|
|
12169
12201
|
const slots = count === 1 ? `the one engine slot on this machine (${directory}) is in use` : `all ${String(count)} engine slots on this machine (${directory}) are in use`;
|
|
12202
|
+
const named = holdersNamed(count, holders);
|
|
12203
|
+
const waiting = `Waiting${waitedMs >= 1000 ? ` ${String(Math.round(waitedMs / 1000))} s so far` : ""} for an engine: ${slots}`;
|
|
12204
|
+
if (named.length === 0) {
|
|
12205
|
+
return `${waiting}, by a gate or another tool, and the engine starts when one is given back.`;
|
|
12206
|
+
}
|
|
12207
|
+
if (count === 1) {
|
|
12208
|
+
return `${waiting}, by ${named.join(", ")}, and the engine starts when it is given back.`;
|
|
12209
|
+
}
|
|
12210
|
+
return `${waiting}: ${listed(named)}. The engine starts when one is given back.`;
|
|
12211
|
+
}
|
|
12212
|
+
function slotWaitedSaid(waitedMs, count, directory, holders) {
|
|
12213
|
+
const waited = `This call waited ${String(Math.round(waitedMs / 1000))} s for an engine slot in all.`;
|
|
12214
|
+
const slots = count === 1 ? `the one engine slot on this machine (${directory}) was in use` : `all ${String(count)} engine slots on this machine (${directory}) were in use`;
|
|
12215
|
+
const named = holdersNamed(count, holders);
|
|
12216
|
+
if (named.length === 0) {
|
|
12217
|
+
return `${waited} When it first waited, ${slots}, by a gate or another tool.`;
|
|
12218
|
+
}
|
|
12219
|
+
return count === 1 ? `${waited} When it first waited, ${slots}, by ${named.join(", ")}.` : `${waited} When it first waited, ${slots}: ${listed(named)}.`;
|
|
12220
|
+
}
|
|
12221
|
+
function holdersNamed(count, holders) {
|
|
12170
12222
|
const held = new Map;
|
|
12171
12223
|
for (const holder of holders) {
|
|
12172
12224
|
const known = held.get(holder.pid);
|
|
12173
12225
|
held.set(holder.pid, { by: known?.by ?? holder.by, slots: (known?.slots ?? 0) + 1 });
|
|
12174
12226
|
}
|
|
12175
|
-
|
|
12227
|
+
return [...held].map(([pid, { by, slots: taken }]) => {
|
|
12176
12228
|
const who = pid === process7.pid ? `this process (pid ${String(pid)})` : by === undefined ? `pid ${String(pid)}` : `${by} (pid ${String(pid)})`;
|
|
12177
12229
|
return count === 1 ? who : `${String(taken)} by ${who}`;
|
|
12178
12230
|
});
|
|
12179
|
-
|
|
12180
|
-
|
|
12181
|
-
|
|
12182
|
-
}
|
|
12183
|
-
if (count === 1) {
|
|
12184
|
-
return `${waiting}, by ${named.join(", ")}, and the engine starts when it is given back.`;
|
|
12185
|
-
}
|
|
12186
|
-
const list = named.length === 1 ? named.join("") : `${named.slice(0, -1).join(", ")} and ${named.at(-1) ?? ""}`;
|
|
12187
|
-
return `${waiting}: ${list}. The engine starts when one is given back.`;
|
|
12231
|
+
}
|
|
12232
|
+
function listed(named) {
|
|
12233
|
+
return named.length === 1 ? named.join("") : `${named.slice(0, -1).join(", ")} and ${named.at(-1) ?? ""}`;
|
|
12188
12234
|
}
|
|
12189
12235
|
async function slotForCall() {
|
|
12190
12236
|
const slots = await processSlots();
|
|
12191
12237
|
const progress = callProgress();
|
|
12238
|
+
const waits = callSlotWaits();
|
|
12239
|
+
const began = Date.now();
|
|
12240
|
+
let blockedBy;
|
|
12192
12241
|
const lock = await slots.take(callSignal(), progress === undefined ? undefined : (waitedMs) => {
|
|
12193
12242
|
progress(waitedMs / 1000, slotWaitSaid(waitedMs, slots.count, slots.directory, slots.holders()));
|
|
12243
|
+
}, () => {
|
|
12244
|
+
blockedBy = slots.holders();
|
|
12194
12245
|
});
|
|
12246
|
+
if (waits !== undefined && blockedBy !== undefined) {
|
|
12247
|
+
waits.ms += Date.now() - began;
|
|
12248
|
+
const heldBy = blockedBy;
|
|
12249
|
+
waits.said ??= (waitedMs) => slotWaitedSaid(waitedMs, slots.count, slots.directory, heldBy);
|
|
12250
|
+
}
|
|
12195
12251
|
return {
|
|
12196
12252
|
release: () => {
|
|
12197
12253
|
slots.release(lock);
|
|
@@ -12209,6 +12265,7 @@ async function inASlot(body) {
|
|
|
12209
12265
|
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;
|
|
12210
12266
|
var init_engine_slots = __esm(() => {
|
|
12211
12267
|
init_alive();
|
|
12268
|
+
init_cache_home();
|
|
12212
12269
|
init_call_signal();
|
|
12213
12270
|
init_engine_launch();
|
|
12214
12271
|
init_process_children();
|
|
@@ -12217,11 +12274,11 @@ var init_engine_slots = __esm(() => {
|
|
|
12217
12274
|
|
|
12218
12275
|
// src/detection.ts
|
|
12219
12276
|
import { existsSync as existsSync6, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
|
|
12220
|
-
import { homedir as
|
|
12221
|
-
import { join as
|
|
12277
|
+
import { homedir as homedir4 } from "node:os";
|
|
12278
|
+
import { join as join10 } from "node:path";
|
|
12222
12279
|
function resolveHomeDirectory() {
|
|
12223
12280
|
try {
|
|
12224
|
-
return
|
|
12281
|
+
return homedir4();
|
|
12225
12282
|
} catch {
|
|
12226
12283
|
return "";
|
|
12227
12284
|
}
|
|
@@ -12242,7 +12299,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
12242
12299
|
if (!pattern.test(name)) {
|
|
12243
12300
|
continue;
|
|
12244
12301
|
}
|
|
12245
|
-
const fullPath =
|
|
12302
|
+
const fullPath = join10(directory, name);
|
|
12246
12303
|
try {
|
|
12247
12304
|
const stat = statSync7(fullPath);
|
|
12248
12305
|
if (stat.isFile()) {
|
|
@@ -12251,7 +12308,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
12251
12308
|
} catch {}
|
|
12252
12309
|
}
|
|
12253
12310
|
matches.sort((a, b) => b.mtime - a.mtime);
|
|
12254
|
-
return matches.map((m) =>
|
|
12311
|
+
return matches.map((m) => join10(directory, m.name));
|
|
12255
12312
|
}
|
|
12256
12313
|
function conventionalPaths(platform, home) {
|
|
12257
12314
|
const paths = ["godot"];
|
|
@@ -12906,26 +12963,26 @@ var init_engine_run = __esm(() => {
|
|
|
12906
12963
|
|
|
12907
12964
|
// src/doc-cache.ts
|
|
12908
12965
|
import { existsSync as existsSync8, readdirSync as readdirSync5, statSync as statSync8 } from "node:fs";
|
|
12909
|
-
import { homedir as
|
|
12910
|
-
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute5, join as
|
|
12966
|
+
import { homedir as homedir5 } from "node:os";
|
|
12967
|
+
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute5, join as join11 } from "node:path";
|
|
12911
12968
|
import process9 from "node:process";
|
|
12912
|
-
function editorCacheDirectory(godotPath, platform = process9.platform, env = process9.env, home =
|
|
12969
|
+
function editorCacheDirectory(godotPath, platform = process9.platform, env = process9.env, home = homedir5()) {
|
|
12913
12970
|
const beside = dirname5(godotPath);
|
|
12914
12971
|
const bundle = platform === "darwin" && basename4(beside) === "MacOS" && basename4(dirname5(beside)) === "Contents" ? dirname5(dirname5(dirname5(beside))) : null;
|
|
12915
12972
|
for (const folder of bundle === null ? [beside] : [beside, bundle]) {
|
|
12916
|
-
if (existsSync8(
|
|
12917
|
-
return
|
|
12973
|
+
if (existsSync8(join11(folder, "._sc_")) || existsSync8(join11(folder, "_sc_"))) {
|
|
12974
|
+
return join11(folder, "editor_data", "cache");
|
|
12918
12975
|
}
|
|
12919
12976
|
}
|
|
12920
12977
|
if (platform === "win32") {
|
|
12921
12978
|
const set = (name) => env[name] === "" ? undefined : env[name];
|
|
12922
|
-
return
|
|
12979
|
+
return join11(set("LOCALAPPDATA") ?? set("TEMP") ?? join11(home, "AppData", "Local"), "Godot");
|
|
12923
12980
|
}
|
|
12924
12981
|
if (platform === "darwin") {
|
|
12925
|
-
return
|
|
12982
|
+
return join11(home, "Library", "Caches", "Godot");
|
|
12926
12983
|
}
|
|
12927
12984
|
const xdg = env["XDG_CACHE_HOME"];
|
|
12928
|
-
return
|
|
12985
|
+
return join11(xdg !== undefined && isAbsolute5(xdg) ? xdg : join11(home, ".cache"), "godot");
|
|
12929
12986
|
}
|
|
12930
12987
|
function docCachesWritten(directory) {
|
|
12931
12988
|
const written = new Map;
|
|
@@ -12937,7 +12994,7 @@ function docCachesWritten(directory) {
|
|
|
12937
12994
|
}
|
|
12938
12995
|
for (const name of names.filter((one) => CACHE_FILE.test(one))) {
|
|
12939
12996
|
try {
|
|
12940
|
-
written.set(
|
|
12997
|
+
written.set(join11(directory, name), statSync8(join11(directory, name)).mtimeMs);
|
|
12941
12998
|
} catch {}
|
|
12942
12999
|
}
|
|
12943
13000
|
return written;
|
|
@@ -12961,8 +13018,8 @@ var init_doc_cache = __esm(() => {
|
|
|
12961
13018
|
});
|
|
12962
13019
|
|
|
12963
13020
|
// src/project-scan.ts
|
|
12964
|
-
import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as
|
|
12965
|
-
import { basename as basename5, extname, join as
|
|
13021
|
+
import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync9 } from "node:fs";
|
|
13022
|
+
import { basename as basename5, extname, join as join12, relative as relative4, sep as sep3 } from "node:path";
|
|
12966
13023
|
function skipped(name) {
|
|
12967
13024
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
12968
13025
|
}
|
|
@@ -12970,10 +13027,10 @@ function steppedOver(directory) {
|
|
|
12970
13027
|
return gdignored(directory) || installedByUs(directory);
|
|
12971
13028
|
}
|
|
12972
13029
|
function gdignored(directory) {
|
|
12973
|
-
return existsSync9(
|
|
13030
|
+
return existsSync9(join12(directory, ".gdignore"));
|
|
12974
13031
|
}
|
|
12975
13032
|
function installedByUs(directory) {
|
|
12976
|
-
return existsSync9(
|
|
13033
|
+
return existsSync9(join12(directory, VERSION_MARKER));
|
|
12977
13034
|
}
|
|
12978
13035
|
function projectStructure(projectPath) {
|
|
12979
13036
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -12986,7 +13043,7 @@ function projectStructure(projectPath) {
|
|
|
12986
13043
|
continue;
|
|
12987
13044
|
}
|
|
12988
13045
|
if (entry.isDirectory()) {
|
|
12989
|
-
visit(
|
|
13046
|
+
visit(join12(directory, entry.name));
|
|
12990
13047
|
} else if (entry.isFile()) {
|
|
12991
13048
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
12992
13049
|
if (SIDECAR_EXTENSIONS.has(extension)) {
|
|
@@ -13016,7 +13073,7 @@ function folderFiles(directory, extensions) {
|
|
|
13016
13073
|
function underGdignore(projectPath, relative) {
|
|
13017
13074
|
const folders = relative.split("/").slice(0, -1);
|
|
13018
13075
|
for (let depth = 1;depth <= folders.length; depth += 1) {
|
|
13019
|
-
if (gdignored(
|
|
13076
|
+
if (gdignored(join12(projectPath, ...folders.slice(0, depth)))) {
|
|
13020
13077
|
return true;
|
|
13021
13078
|
}
|
|
13022
13079
|
}
|
|
@@ -13033,7 +13090,7 @@ function steppedOverIn(directory) {
|
|
|
13033
13090
|
continue;
|
|
13034
13091
|
}
|
|
13035
13092
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
13036
|
-
const path =
|
|
13093
|
+
const path = join12(at, entry.name);
|
|
13037
13094
|
if (gdignored(path)) {
|
|
13038
13095
|
found.push({ folder: spelled, why: "gdignore" });
|
|
13039
13096
|
} else if (installedByUs(path)) {
|
|
@@ -13054,7 +13111,7 @@ function filesBelow(root, extensions) {
|
|
|
13054
13111
|
continue;
|
|
13055
13112
|
}
|
|
13056
13113
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
13057
|
-
const path =
|
|
13114
|
+
const path = join12(directory, entry.name);
|
|
13058
13115
|
if (entry.isDirectory()) {
|
|
13059
13116
|
if (!steppedOver(path)) {
|
|
13060
13117
|
visit(path, spelled);
|
|
@@ -13079,14 +13136,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
13079
13136
|
}
|
|
13080
13137
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
13081
13138
|
if (entry.isDirectory()) {
|
|
13082
|
-
visit(
|
|
13139
|
+
visit(join12(directory, entry.name), spelled);
|
|
13083
13140
|
continue;
|
|
13084
13141
|
}
|
|
13085
13142
|
if (!entry.isFile()) {
|
|
13086
13143
|
continue;
|
|
13087
13144
|
}
|
|
13088
13145
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
13089
|
-
if (CARRIES_A_UID.has(extension) && !existsSync9(
|
|
13146
|
+
if (CARRIES_A_UID.has(extension) && !existsSync9(join12(directory, `${entry.name}.uid`))) {
|
|
13090
13147
|
found.push(spelled);
|
|
13091
13148
|
}
|
|
13092
13149
|
}
|
|
@@ -13097,7 +13154,7 @@ function scriptsWithoutUid(projectPath) {
|
|
|
13097
13154
|
function pathsInUidCache(projectPath) {
|
|
13098
13155
|
let data;
|
|
13099
13156
|
try {
|
|
13100
|
-
data =
|
|
13157
|
+
data = readFileSync9(join12(projectPath, ".godot", "uid_cache.bin"));
|
|
13101
13158
|
} catch {
|
|
13102
13159
|
return null;
|
|
13103
13160
|
}
|
|
@@ -13125,7 +13182,7 @@ function pathsInUidCache(projectPath) {
|
|
|
13125
13182
|
function steppedOverAround(projectPath, place) {
|
|
13126
13183
|
let at = projectPath;
|
|
13127
13184
|
for (const step of relative4(projectPath, place).split(sep3).filter((part) => part !== "")) {
|
|
13128
|
-
at =
|
|
13185
|
+
at = join12(at, step);
|
|
13129
13186
|
if (!statSync9(at).isDirectory()) {
|
|
13130
13187
|
break;
|
|
13131
13188
|
}
|
|
@@ -13149,7 +13206,7 @@ function projectTexts(projectPath) {
|
|
|
13149
13206
|
if (skipped(entry.name)) {
|
|
13150
13207
|
continue;
|
|
13151
13208
|
}
|
|
13152
|
-
const absolute =
|
|
13209
|
+
const absolute = join12(directory, entry.name);
|
|
13153
13210
|
const spelled = `${prefix}${entry.name}`;
|
|
13154
13211
|
if (entry.isDirectory()) {
|
|
13155
13212
|
visit(absolute, `${spelled}/`);
|
|
@@ -13159,7 +13216,7 @@ function projectTexts(projectPath) {
|
|
|
13159
13216
|
if (!entry.isFile() || BINARY_EXTENSIONS.has(extension)) {
|
|
13160
13217
|
continue;
|
|
13161
13218
|
}
|
|
13162
|
-
const bytes =
|
|
13219
|
+
const bytes = readFileSync9(absolute);
|
|
13163
13220
|
if (!isBinary(bytes)) {
|
|
13164
13221
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
13165
13222
|
}
|
|
@@ -13179,7 +13236,7 @@ function ignoredTexts(projectPath) {
|
|
|
13179
13236
|
if (skipped(entry.name)) {
|
|
13180
13237
|
continue;
|
|
13181
13238
|
}
|
|
13182
|
-
const absolute =
|
|
13239
|
+
const absolute = join12(directory, entry.name);
|
|
13183
13240
|
const spelled = `${prefix}${entry.name}`;
|
|
13184
13241
|
if (entry.isDirectory()) {
|
|
13185
13242
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -13189,7 +13246,7 @@ function ignoredTexts(projectPath) {
|
|
|
13189
13246
|
if (!under || !entry.isFile() || BINARY_EXTENSIONS.has(extension) || entry.name === ".gdignore") {
|
|
13190
13247
|
continue;
|
|
13191
13248
|
}
|
|
13192
|
-
const bytes =
|
|
13249
|
+
const bytes = readFileSync9(absolute);
|
|
13193
13250
|
if (!isBinary(bytes)) {
|
|
13194
13251
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
13195
13252
|
}
|
|
@@ -13226,7 +13283,7 @@ function searchProject(projectPath, options) {
|
|
|
13226
13283
|
};
|
|
13227
13284
|
const regex = options.regex ? compiledQuery(options.query, options.caseSensitive) : null;
|
|
13228
13285
|
const needle = options.caseSensitive ? options.query : options.query.toLowerCase();
|
|
13229
|
-
|
|
13286
|
+
let shown = 0;
|
|
13230
13287
|
const notSearched = [];
|
|
13231
13288
|
const met = new Set;
|
|
13232
13289
|
const searchFile = (entryPath, name) => {
|
|
@@ -13237,28 +13294,33 @@ function searchProject(projectPath, options) {
|
|
|
13237
13294
|
if (extensions !== null && !extensions.has(extension)) {
|
|
13238
13295
|
return;
|
|
13239
13296
|
}
|
|
13240
|
-
const bytes =
|
|
13297
|
+
const bytes = readFileSync9(entryPath);
|
|
13241
13298
|
if (extensions === null && isBinary(bytes)) {
|
|
13242
13299
|
return;
|
|
13243
13300
|
}
|
|
13244
13301
|
result.summary.files_searched += 1;
|
|
13245
13302
|
const matches = [];
|
|
13303
|
+
let matched = false;
|
|
13246
13304
|
for (const [index, line] of bytes.toString("utf8").split(/\r?\n/).entries()) {
|
|
13247
13305
|
const found = regex ? regex.exec(line) : plainMatch(line, needle, options.caseSensitive);
|
|
13248
13306
|
if (found === null) {
|
|
13249
13307
|
continue;
|
|
13250
13308
|
}
|
|
13251
|
-
|
|
13309
|
+
matched = true;
|
|
13310
|
+
result.summary.total_matches += 1;
|
|
13311
|
+
if (shown >= options.maxResults) {
|
|
13252
13312
|
result.summary.truncated = true;
|
|
13253
|
-
|
|
13313
|
+
continue;
|
|
13254
13314
|
}
|
|
13315
|
+
shown += 1;
|
|
13255
13316
|
matches.push({ line: index + 1, content: line.trim(), match: found[0] });
|
|
13256
|
-
|
|
13317
|
+
}
|
|
13318
|
+
if (matched) {
|
|
13319
|
+
result.summary.files_with_matches += 1;
|
|
13257
13320
|
}
|
|
13258
13321
|
if (matches.length > 0) {
|
|
13259
13322
|
const relativePath = entryPath.slice(projectPath.length + 1).replace(/\\/g, "/");
|
|
13260
13323
|
result.results.push({ file: `res://${relativePath}`, matches });
|
|
13261
|
-
result.summary.files_with_matches += 1;
|
|
13262
13324
|
}
|
|
13263
13325
|
};
|
|
13264
13326
|
const visit = (directory, named = false) => {
|
|
@@ -13269,13 +13331,10 @@ function searchProject(projectPath, options) {
|
|
|
13269
13331
|
return;
|
|
13270
13332
|
}
|
|
13271
13333
|
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
13272
|
-
if (full()) {
|
|
13273
|
-
return;
|
|
13274
|
-
}
|
|
13275
13334
|
if (skipped(entry.name)) {
|
|
13276
13335
|
continue;
|
|
13277
13336
|
}
|
|
13278
|
-
const entryPath =
|
|
13337
|
+
const entryPath = join12(directory, entry.name);
|
|
13279
13338
|
if (entry.isDirectory()) {
|
|
13280
13339
|
visit(entryPath);
|
|
13281
13340
|
} else if (entry.isFile()) {
|
|
@@ -13351,8 +13410,8 @@ var init_project_scan = __esm(() => {
|
|
|
13351
13410
|
|
|
13352
13411
|
// src/import-freshness.ts
|
|
13353
13412
|
import { createHash as createHash2 } from "node:crypto";
|
|
13354
|
-
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as
|
|
13355
|
-
import { join as
|
|
13413
|
+
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync10 } from "node:fs";
|
|
13414
|
+
import { join as join13 } from "node:path";
|
|
13356
13415
|
function md5Of(file) {
|
|
13357
13416
|
let stats;
|
|
13358
13417
|
try {
|
|
@@ -13366,7 +13425,7 @@ function md5Of(file) {
|
|
|
13366
13425
|
}
|
|
13367
13426
|
let md5;
|
|
13368
13427
|
try {
|
|
13369
|
-
md5 = createHash2("md5").update(
|
|
13428
|
+
md5 = createHash2("md5").update(readFileSync10(file)).digest("hex");
|
|
13370
13429
|
} catch {
|
|
13371
13430
|
return null;
|
|
13372
13431
|
}
|
|
@@ -13392,7 +13451,7 @@ function sectionOf(text, section) {
|
|
|
13392
13451
|
function unquoted(value) {
|
|
13393
13452
|
return value === undefined ? "" : value.replace(/^"(.*)"$/, "$1");
|
|
13394
13453
|
}
|
|
13395
|
-
function
|
|
13454
|
+
function listed2(value) {
|
|
13396
13455
|
return [...(value ?? "").matchAll(/"((?:[^"\\]|\\.)*)"/g)].map((match) => match[1] ?? "");
|
|
13397
13456
|
}
|
|
13398
13457
|
function importedIntoTheProject(projectPath, resPaths) {
|
|
@@ -13400,11 +13459,11 @@ function importedIntoTheProject(projectPath, resPaths) {
|
|
|
13400
13459
|
for (const resPath of resPaths) {
|
|
13401
13460
|
let sidecar;
|
|
13402
13461
|
try {
|
|
13403
|
-
sidecar =
|
|
13462
|
+
sidecar = readFileSync10(`${onDisk(projectPath, resPath)}.import`, "utf8");
|
|
13404
13463
|
} catch {
|
|
13405
13464
|
continue;
|
|
13406
13465
|
}
|
|
13407
|
-
const outputs =
|
|
13466
|
+
const outputs = listed2(sectionOf(sidecar, "deps").get("dest_files")).filter((output) => output.startsWith("res://") && !output.startsWith("res://.godot/"));
|
|
13408
13467
|
if (outputs.length > 0) {
|
|
13409
13468
|
wrote[resPath] = outputs;
|
|
13410
13469
|
}
|
|
@@ -13412,7 +13471,7 @@ function importedIntoTheProject(projectPath, resPaths) {
|
|
|
13412
13471
|
return wrote;
|
|
13413
13472
|
}
|
|
13414
13473
|
function onDisk(projectPath, resPath) {
|
|
13415
|
-
return
|
|
13474
|
+
return join13(projectPath, resPath.slice("res://".length));
|
|
13416
13475
|
}
|
|
13417
13476
|
function behind(projectPath, resPath, sidecar) {
|
|
13418
13477
|
const remap = sectionOf(sidecar, "remap");
|
|
@@ -13420,15 +13479,15 @@ function behind(projectPath, resPath, sidecar) {
|
|
|
13420
13479
|
if (importer === "keep" || importer === "skip") {
|
|
13421
13480
|
return null;
|
|
13422
13481
|
}
|
|
13423
|
-
const outputs =
|
|
13482
|
+
const outputs = listed2(sectionOf(sidecar, "deps").get("dest_files"));
|
|
13424
13483
|
if (outputs.some((output) => !existsSync10(onDisk(projectPath, output)))) {
|
|
13425
13484
|
return "its imported output is not on disk";
|
|
13426
13485
|
}
|
|
13427
13486
|
const name = resPath.slice(resPath.lastIndexOf("/") + 1);
|
|
13428
|
-
const record =
|
|
13487
|
+
const record = join13(projectPath, ".godot", "imported", `${name}-${createHash2("md5").update(resPath, "utf8").digest("hex")}.md5`);
|
|
13429
13488
|
let recorded = "";
|
|
13430
13489
|
try {
|
|
13431
|
-
recorded = /^source_md5="([0-9a-f]*)"/m.exec(
|
|
13490
|
+
recorded = /^source_md5="([0-9a-f]*)"/m.exec(readFileSync10(record, "utf8"))?.[1] ?? "";
|
|
13432
13491
|
} catch {
|
|
13433
13492
|
recorded = "";
|
|
13434
13493
|
}
|
|
@@ -13441,7 +13500,7 @@ function behind(projectPath, resPath, sidecar) {
|
|
|
13441
13500
|
function staleImports(projectPath) {
|
|
13442
13501
|
const stale = [];
|
|
13443
13502
|
const visit = (directory, resDirectory) => {
|
|
13444
|
-
if (existsSync10(
|
|
13503
|
+
if (existsSync10(join13(directory, ".gdignore")) || existsSync10(join13(directory, VERSION_MARKER))) {
|
|
13445
13504
|
return;
|
|
13446
13505
|
}
|
|
13447
13506
|
let entries;
|
|
@@ -13457,7 +13516,7 @@ function staleImports(projectPath) {
|
|
|
13457
13516
|
}
|
|
13458
13517
|
const resPath = `${resDirectory}${entry.name}`;
|
|
13459
13518
|
if (entry.isDirectory()) {
|
|
13460
|
-
visit(
|
|
13519
|
+
visit(join13(directory, entry.name), `${resPath}/`);
|
|
13461
13520
|
continue;
|
|
13462
13521
|
}
|
|
13463
13522
|
if (!entry.isFile()) {
|
|
@@ -13470,7 +13529,7 @@ function staleImports(projectPath) {
|
|
|
13470
13529
|
}
|
|
13471
13530
|
let sidecar;
|
|
13472
13531
|
try {
|
|
13473
|
-
sidecar =
|
|
13532
|
+
sidecar = readFileSync10(join13(directory, entry.name), "utf8");
|
|
13474
13533
|
} catch {
|
|
13475
13534
|
continue;
|
|
13476
13535
|
}
|
|
@@ -13534,12 +13593,12 @@ import {
|
|
|
13534
13593
|
existsSync as existsSync11,
|
|
13535
13594
|
mkdirSync as mkdirSync4,
|
|
13536
13595
|
readdirSync as readdirSync8,
|
|
13537
|
-
readFileSync as
|
|
13596
|
+
readFileSync as readFileSync11,
|
|
13538
13597
|
renameSync as renameSync2,
|
|
13539
13598
|
unlinkSync,
|
|
13540
13599
|
writeFileSync as writeFileSync6
|
|
13541
13600
|
} from "node:fs";
|
|
13542
|
-
import { basename as basename6, dirname as dirname6, isAbsolute as isAbsolute6, join as
|
|
13601
|
+
import { basename as basename6, dirname as dirname6, isAbsolute as isAbsolute6, join as join14 } from "node:path";
|
|
13543
13602
|
function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
13544
13603
|
const setAside = [];
|
|
13545
13604
|
const swept = [];
|
|
@@ -13549,10 +13608,10 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
13549
13608
|
for (const library of windowsLibraries(projectPath)) {
|
|
13550
13609
|
const folder = dirname6(library);
|
|
13551
13610
|
const copy = `~${basename6(library)}`;
|
|
13552
|
-
if (existsSync11(
|
|
13553
|
-
const aside =
|
|
13611
|
+
if (existsSync11(join14(folder, copy))) {
|
|
13612
|
+
const aside = join14(folder, `${copy}.aside-${process.pid}-${Date.now()}`);
|
|
13554
13613
|
try {
|
|
13555
|
-
renameSync2(
|
|
13614
|
+
renameSync2(join14(folder, copy), aside);
|
|
13556
13615
|
setAside.push(aside);
|
|
13557
13616
|
} catch {}
|
|
13558
13617
|
}
|
|
@@ -13565,8 +13624,8 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
13565
13624
|
for (const name of names) {
|
|
13566
13625
|
if (name.length > copy.length && name.toLowerCase().startsWith(copy.toLowerCase())) {
|
|
13567
13626
|
try {
|
|
13568
|
-
unlinkSync(
|
|
13569
|
-
swept.push(
|
|
13627
|
+
unlinkSync(join14(folder, name));
|
|
13628
|
+
swept.push(join14(folder, name));
|
|
13570
13629
|
} catch {}
|
|
13571
13630
|
}
|
|
13572
13631
|
}
|
|
@@ -13574,13 +13633,13 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
13574
13633
|
return { setAside, swept };
|
|
13575
13634
|
}
|
|
13576
13635
|
function seedExtensionList(projectPath) {
|
|
13577
|
-
const list =
|
|
13636
|
+
const list = join14(projectPath, ".godot", "extension_list.cfg");
|
|
13578
13637
|
if (existsSync11(list)) {
|
|
13579
13638
|
return null;
|
|
13580
13639
|
}
|
|
13581
13640
|
const found = [];
|
|
13582
13641
|
const visit = (directory, resDirectory) => {
|
|
13583
|
-
if (existsSync11(
|
|
13642
|
+
if (existsSync11(join14(directory, ".gdignore"))) {
|
|
13584
13643
|
return;
|
|
13585
13644
|
}
|
|
13586
13645
|
let entries;
|
|
@@ -13594,7 +13653,7 @@ function seedExtensionList(projectPath) {
|
|
|
13594
13653
|
continue;
|
|
13595
13654
|
}
|
|
13596
13655
|
if (entry.isDirectory()) {
|
|
13597
|
-
visit(
|
|
13656
|
+
visit(join14(directory, entry.name), `${resDirectory}${entry.name}/`);
|
|
13598
13657
|
} else if (entry.name.endsWith(".gdextension")) {
|
|
13599
13658
|
found.push(`${resDirectory}${entry.name}`);
|
|
13600
13659
|
}
|
|
@@ -13612,7 +13671,7 @@ function seedExtensionList(projectPath) {
|
|
|
13612
13671
|
return found;
|
|
13613
13672
|
}
|
|
13614
13673
|
function windowsLibraries(projectPath) {
|
|
13615
|
-
const listed = readOrNull(
|
|
13674
|
+
const listed = readOrNull(join14(projectPath, ".godot", "extension_list.cfg"));
|
|
13616
13675
|
if (listed === null) {
|
|
13617
13676
|
return [];
|
|
13618
13677
|
}
|
|
@@ -13644,13 +13703,13 @@ function resolveResource(projectPath, relativeTo, path) {
|
|
|
13644
13703
|
return null;
|
|
13645
13704
|
}
|
|
13646
13705
|
if (path.startsWith("res://")) {
|
|
13647
|
-
return
|
|
13706
|
+
return join14(projectPath, path.slice("res://".length));
|
|
13648
13707
|
}
|
|
13649
|
-
return isAbsolute6(path) ? path :
|
|
13708
|
+
return isAbsolute6(path) ? path : join14(relativeTo, path);
|
|
13650
13709
|
}
|
|
13651
13710
|
function readOrNull(path) {
|
|
13652
13711
|
try {
|
|
13653
|
-
return
|
|
13712
|
+
return readFileSync11(path, "utf8");
|
|
13654
13713
|
} catch {
|
|
13655
13714
|
return null;
|
|
13656
13715
|
}
|
|
@@ -13659,32 +13718,32 @@ var init_library_copies = () => {};
|
|
|
13659
13718
|
|
|
13660
13719
|
// src/reimport.ts
|
|
13661
13720
|
import { createHash as createHash3 } from "node:crypto";
|
|
13662
|
-
import { existsSync as existsSync12, readFileSync as
|
|
13663
|
-
import { basename as basename7, join as
|
|
13721
|
+
import { existsSync as existsSync12, readFileSync as readFileSync12, rmSync as rmSync7, statSync as statSync11, utimesSync, writeFileSync as writeFileSync7 } from "node:fs";
|
|
13722
|
+
import { basename as basename7, join as join15 } from "node:path";
|
|
13664
13723
|
function forceNextImport(projectPath, resourcePath) {
|
|
13665
13724
|
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
13666
|
-
const sidecar =
|
|
13725
|
+
const sidecar = join15(projectPath, `${relative}.import`);
|
|
13667
13726
|
if (!existsSync12(sidecar)) {
|
|
13668
13727
|
return;
|
|
13669
13728
|
}
|
|
13670
13729
|
const { atime, mtimeMs } = statSync11(sidecar);
|
|
13671
|
-
writeFileSync7(sidecar,
|
|
13730
|
+
writeFileSync7(sidecar, readFileSync12(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
13672
13731
|
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
13673
13732
|
rmSync7(importRecord(projectPath, resourcePath), { force: true });
|
|
13674
13733
|
}
|
|
13675
13734
|
function importRecord(projectPath, resourcePath) {
|
|
13676
13735
|
const digest = createHash3("md5").update(resourcePath).digest("hex");
|
|
13677
|
-
return
|
|
13736
|
+
return join15(projectPath, ".godot", "imported", `${basename7(resourcePath)}-${digest}.md5`);
|
|
13678
13737
|
}
|
|
13679
13738
|
var init_reimport = () => {};
|
|
13680
13739
|
|
|
13681
13740
|
// src/scratch.ts
|
|
13682
13741
|
import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, readdirSync as readdirSync9, rmSync as rmSync8 } from "node:fs";
|
|
13683
13742
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
13684
|
-
import { join as
|
|
13743
|
+
import { join as join16 } from "node:path";
|
|
13685
13744
|
import process10 from "node:process";
|
|
13686
13745
|
function scratchDirectory(kind) {
|
|
13687
|
-
return mkdtempSync2(
|
|
13746
|
+
return mkdtempSync2(join16(tmpdir3(), `gdharness-${kind}-${process10.pid}-`));
|
|
13688
13747
|
}
|
|
13689
13748
|
function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
13690
13749
|
let entries;
|
|
@@ -13700,7 +13759,7 @@ function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
|
13700
13759
|
continue;
|
|
13701
13760
|
}
|
|
13702
13761
|
try {
|
|
13703
|
-
remove(
|
|
13762
|
+
remove(join16(directory, entry));
|
|
13704
13763
|
} catch {}
|
|
13705
13764
|
}
|
|
13706
13765
|
}
|
|
@@ -13786,7 +13845,7 @@ var init_scratch = __esm(() => {
|
|
|
13786
13845
|
});
|
|
13787
13846
|
|
|
13788
13847
|
// src/script-origins.ts
|
|
13789
|
-
import { join as
|
|
13848
|
+
import { join as join17 } from "node:path";
|
|
13790
13849
|
function unresolvedClassIn(message) {
|
|
13791
13850
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
13792
13851
|
}
|
|
@@ -13848,7 +13907,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
13848
13907
|
const ran = await runEngine(godotPath, [
|
|
13849
13908
|
"--headless",
|
|
13850
13909
|
"--log-file",
|
|
13851
|
-
|
|
13910
|
+
join17(logDir, "engine.log"),
|
|
13852
13911
|
"--path",
|
|
13853
13912
|
projectPath,
|
|
13854
13913
|
"--check-only",
|
|
@@ -13894,8 +13953,8 @@ var init_script_origins = __esm(() => {
|
|
|
13894
13953
|
});
|
|
13895
13954
|
|
|
13896
13955
|
// src/headless.ts
|
|
13897
|
-
import { existsSync as existsSync14, readFileSync as
|
|
13898
|
-
import { dirname as dirname7, join as
|
|
13956
|
+
import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "node:fs";
|
|
13957
|
+
import { dirname as dirname7, join as join18 } from "node:path";
|
|
13899
13958
|
function snakeCased(params) {
|
|
13900
13959
|
const result = emptyRecord();
|
|
13901
13960
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -14051,7 +14110,7 @@ async function importOnce(godotPath, projectPath) {
|
|
|
14051
14110
|
ran = await runEngine(godotPath, [
|
|
14052
14111
|
"--headless",
|
|
14053
14112
|
"--log-file",
|
|
14054
|
-
|
|
14113
|
+
join18(logDir, "engine.log"),
|
|
14055
14114
|
"--path",
|
|
14056
14115
|
projectPath,
|
|
14057
14116
|
"--import"
|
|
@@ -14087,14 +14146,14 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
14087
14146
|
}
|
|
14088
14147
|
async function operationOutcome(engine, operation, params, projectPath) {
|
|
14089
14148
|
const paramsDir = scratchDirectory("params");
|
|
14090
|
-
const paramsFile =
|
|
14091
|
-
const answerFile =
|
|
14149
|
+
const paramsFile = join18(paramsDir, `${operation}.json`);
|
|
14150
|
+
const answerFile = join18(paramsDir, "answer.json");
|
|
14092
14151
|
writeFileSync8(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
14093
14152
|
const args = [
|
|
14094
14153
|
"--headless",
|
|
14095
14154
|
...engine.editor === true ? ["--editor"] : [],
|
|
14096
14155
|
"--log-file",
|
|
14097
|
-
|
|
14156
|
+
join18(paramsDir, "engine.log"),
|
|
14098
14157
|
"--path",
|
|
14099
14158
|
projectPath,
|
|
14100
14159
|
"--script",
|
|
@@ -14185,7 +14244,7 @@ function answerIn(file) {
|
|
|
14185
14244
|
if (!existsSync14(file)) {
|
|
14186
14245
|
return { kind: "none" };
|
|
14187
14246
|
}
|
|
14188
|
-
const text =
|
|
14247
|
+
const text = readFileSync13(file, "utf8");
|
|
14189
14248
|
try {
|
|
14190
14249
|
const parsed = JSON.parse(text);
|
|
14191
14250
|
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { kind: "answer", payload: parsed } : { kind: "unreadable", why: `it is JSON but not an object: ${text.slice(0, 200)}` };
|
|
@@ -14210,9 +14269,9 @@ var init_headless = __esm(() => {
|
|
|
14210
14269
|
});
|
|
14211
14270
|
|
|
14212
14271
|
// src/update-check.ts
|
|
14213
|
-
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as
|
|
14214
|
-
import { homedir as
|
|
14215
|
-
import { join as
|
|
14272
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "node:fs";
|
|
14273
|
+
import { homedir as homedir6, tmpdir as tmpdir4 } from "node:os";
|
|
14274
|
+
import { join as join19 } from "node:path";
|
|
14216
14275
|
function packageUrl(root) {
|
|
14217
14276
|
return `${root.replace(/\/+$/, "")}/gdharness/latest`;
|
|
14218
14277
|
}
|
|
@@ -14232,22 +14291,22 @@ function cacheDirectory(environment) {
|
|
|
14232
14291
|
const value = environment[name];
|
|
14233
14292
|
return value !== undefined && value !== "" ? value : null;
|
|
14234
14293
|
};
|
|
14235
|
-
const home = set("HOME") ??
|
|
14294
|
+
const home = set("HOME") ?? homedir6();
|
|
14236
14295
|
if (process.platform === "win32") {
|
|
14237
|
-
return
|
|
14296
|
+
return join19(set("LOCALAPPDATA") ?? home, "gdharness");
|
|
14238
14297
|
}
|
|
14239
14298
|
if (process.platform === "darwin") {
|
|
14240
|
-
return
|
|
14299
|
+
return join19(home, "Library", "Caches", "gdharness");
|
|
14241
14300
|
}
|
|
14242
|
-
return
|
|
14301
|
+
return join19(set("XDG_CACHE_HOME") ?? join19(home, ".cache"), "gdharness");
|
|
14243
14302
|
}
|
|
14244
14303
|
function cacheFile(environment = process.env) {
|
|
14245
14304
|
try {
|
|
14246
14305
|
const directory = cacheDirectory(environment);
|
|
14247
14306
|
mkdirSync5(directory, { recursive: true, mode: 448 });
|
|
14248
|
-
return
|
|
14307
|
+
return join19(directory, "update-check.json");
|
|
14249
14308
|
} catch {
|
|
14250
|
-
return
|
|
14309
|
+
return join19(tmpdir4(), "gdharness-update-check.json");
|
|
14251
14310
|
}
|
|
14252
14311
|
}
|
|
14253
14312
|
function readCache(path) {
|
|
@@ -14255,7 +14314,7 @@ function readCache(path) {
|
|
|
14255
14314
|
if (!existsSync15(path)) {
|
|
14256
14315
|
return null;
|
|
14257
14316
|
}
|
|
14258
|
-
const parsed = JSON.parse(
|
|
14317
|
+
const parsed = JSON.parse(readFileSync14(path, "utf8"));
|
|
14259
14318
|
if (typeof parsed !== "object" || parsed === null) {
|
|
14260
14319
|
return null;
|
|
14261
14320
|
}
|
|
@@ -14403,7 +14462,7 @@ var init_update_check = __esm(() => {
|
|
|
14403
14462
|
});
|
|
14404
14463
|
|
|
14405
14464
|
// src/server-version.ts
|
|
14406
|
-
import { readFileSync as
|
|
14465
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
14407
14466
|
function staleAddon(addonDigest, shippedDigest) {
|
|
14408
14467
|
return addonDigest !== shippedDigest;
|
|
14409
14468
|
}
|
|
@@ -14462,7 +14521,7 @@ var init_server_version = __esm(() => {
|
|
|
14462
14521
|
DEBUG_MODE = process.env["GDHARNESS_DEBUG"] === "1";
|
|
14463
14522
|
SERVER_VERSION = (() => {
|
|
14464
14523
|
try {
|
|
14465
|
-
const pkg = JSON.parse(
|
|
14524
|
+
const pkg = JSON.parse(readFileSync15(new URL("../package.json", import.meta.url), "utf8"));
|
|
14466
14525
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
14467
14526
|
} catch {
|
|
14468
14527
|
return "0.0.0";
|
|
@@ -14735,7 +14794,7 @@ var init_ports = __esm(() => {
|
|
|
14735
14794
|
import { existsSync as existsSync16, realpathSync as realpathSync2 } from "node:fs";
|
|
14736
14795
|
import { readFile, realpath } from "node:fs/promises";
|
|
14737
14796
|
import { createConnection } from "node:net";
|
|
14738
|
-
import { dirname as dirname8, join as
|
|
14797
|
+
import { dirname as dirname8, join as join20, resolve as resolve6 } from "node:path";
|
|
14739
14798
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
14740
14799
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
14741
14800
|
function initializeTimeoutMs() {
|
|
@@ -15413,7 +15472,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
15413
15472
|
} catch {
|
|
15414
15473
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
15415
15474
|
}
|
|
15416
|
-
if (!existsSync16(
|
|
15475
|
+
if (!existsSync16(join20(projectPath, "project.godot"))) {
|
|
15417
15476
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
15418
15477
|
}
|
|
15419
15478
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -15573,7 +15632,7 @@ var init_lsp_client = __esm(() => {
|
|
|
15573
15632
|
});
|
|
15574
15633
|
|
|
15575
15634
|
// src/resources.ts
|
|
15576
|
-
import { readFileSync as
|
|
15635
|
+
import { readFileSync as readFileSync17, realpathSync as realpathSync3 } from "node:fs";
|
|
15577
15636
|
import { extname as extname2, resolve as resolve8 } from "node:path";
|
|
15578
15637
|
function ensureProjectPath(getProjectPath) {
|
|
15579
15638
|
const projectPath = getProjectPath();
|
|
@@ -15769,7 +15828,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
15769
15828
|
const parsedUri = parseGodotUri(uri);
|
|
15770
15829
|
if (parsedUri.kind === "project-info") {
|
|
15771
15830
|
const projectFilePath = resolveProjectFile(projectPath, "project.godot");
|
|
15772
|
-
const rawProject =
|
|
15831
|
+
const rawProject = readFileSync17(projectFilePath, "utf-8");
|
|
15773
15832
|
const parsedProject = parseProjectGodot(rawProject);
|
|
15774
15833
|
return {
|
|
15775
15834
|
mimeType: "application/json",
|
|
@@ -15778,7 +15837,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
15778
15837
|
}
|
|
15779
15838
|
const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
|
|
15780
15839
|
ensureAllowedExtension(parsedUri.kind, filePath);
|
|
15781
|
-
const text =
|
|
15840
|
+
const text = readFileSync17(filePath, "utf-8");
|
|
15782
15841
|
return {
|
|
15783
15842
|
mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
|
|
15784
15843
|
text
|
|
@@ -15925,13 +15984,13 @@ import {
|
|
|
15925
15984
|
existsSync as existsSync18,
|
|
15926
15985
|
mkdirSync as mkdirSync6,
|
|
15927
15986
|
readdirSync as readdirSync11,
|
|
15928
|
-
readFileSync as
|
|
15987
|
+
readFileSync as readFileSync18,
|
|
15929
15988
|
rmdirSync,
|
|
15930
15989
|
rmSync as rmSync9,
|
|
15931
15990
|
statSync as statSync12,
|
|
15932
15991
|
writeFileSync as writeFileSync10
|
|
15933
15992
|
} from "node:fs";
|
|
15934
|
-
import { dirname as dirname9, join as
|
|
15993
|
+
import { dirname as dirname9, join as join22 } from "node:path";
|
|
15935
15994
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
15936
15995
|
function addonMarking(name) {
|
|
15937
15996
|
if (name === RUNTIME_ADDON) {
|
|
@@ -15952,21 +16011,21 @@ function addonsDigest(names, from) {
|
|
|
15952
16011
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
15953
16012
|
continue;
|
|
15954
16013
|
}
|
|
15955
|
-
const path =
|
|
16014
|
+
const path = join22(directory, name);
|
|
15956
16015
|
const inside = `${relative}/${name}`;
|
|
15957
16016
|
if (statSync12(path).isDirectory()) {
|
|
15958
16017
|
walk(path, inside);
|
|
15959
16018
|
} else {
|
|
15960
16019
|
hash.update(`${inside}
|
|
15961
16020
|
`);
|
|
15962
|
-
hash.update(
|
|
16021
|
+
hash.update(readFileSync18(path));
|
|
15963
16022
|
hash.update(`
|
|
15964
16023
|
`);
|
|
15965
16024
|
}
|
|
15966
16025
|
}
|
|
15967
16026
|
};
|
|
15968
16027
|
for (const name of names) {
|
|
15969
|
-
walk(
|
|
16028
|
+
walk(join22(from, name), name);
|
|
15970
16029
|
}
|
|
15971
16030
|
return hash.digest("hex");
|
|
15972
16031
|
}
|
|
@@ -15985,28 +16044,28 @@ function installedRuntimeDigest(projectPath) {
|
|
|
15985
16044
|
return digestMarker(projectPath, RUNTIME_ADDON);
|
|
15986
16045
|
}
|
|
15987
16046
|
function digestMarker(projectPath, addon) {
|
|
15988
|
-
const marker =
|
|
16047
|
+
const marker = join22(projectPath, "addons", addon, DIGEST_MARKER);
|
|
15989
16048
|
if (!existsSync18(marker)) {
|
|
15990
16049
|
return null;
|
|
15991
16050
|
}
|
|
15992
|
-
const said =
|
|
16051
|
+
const said = readFileSync18(marker, "utf8").trim();
|
|
15993
16052
|
return said === "" ? null : said;
|
|
15994
16053
|
}
|
|
15995
16054
|
function shippedAddonsDirectory() {
|
|
15996
|
-
return
|
|
16055
|
+
return join22(dirname9(fileURLToPath2(import.meta.url)), "godot", "addons");
|
|
15997
16056
|
}
|
|
15998
16057
|
function shippedOperationsScript() {
|
|
15999
|
-
return
|
|
16058
|
+
return join22(dirname9(fileURLToPath2(import.meta.url)), "godot", "operations", "godot_operations.gd");
|
|
16000
16059
|
}
|
|
16001
16060
|
function shippedLoader() {
|
|
16002
|
-
return
|
|
16061
|
+
return join22(dirname9(fileURLToPath2(import.meta.url)), "godot", "loader", "loader.gd");
|
|
16003
16062
|
}
|
|
16004
16063
|
function loaderState(projectPath) {
|
|
16005
|
-
const copy =
|
|
16064
|
+
const copy = join22(projectPath, ...RUNTIME_AUTOLOAD.path.split("/"));
|
|
16006
16065
|
if (!existsSync18(copy)) {
|
|
16007
16066
|
return "missing";
|
|
16008
16067
|
}
|
|
16009
|
-
const lines = (path) =>
|
|
16068
|
+
const lines = (path) => readFileSync18(path, "utf8").replaceAll(`\r
|
|
16010
16069
|
`, `
|
|
16011
16070
|
`);
|
|
16012
16071
|
return lines(copy) === lines(shippedLoader()) ? "current" : "differs";
|
|
@@ -16015,13 +16074,13 @@ function writeLoader(projectPath) {
|
|
|
16015
16074
|
if (loaderState(projectPath) === "current") {
|
|
16016
16075
|
return false;
|
|
16017
16076
|
}
|
|
16018
|
-
const copy =
|
|
16077
|
+
const copy = join22(projectPath, ...RUNTIME_AUTOLOAD.path.split("/"));
|
|
16019
16078
|
mkdirSync6(dirname9(copy), { recursive: true });
|
|
16020
|
-
writeFileSync10(copy,
|
|
16079
|
+
writeFileSync10(copy, readFileSync18(shippedLoader()));
|
|
16021
16080
|
return true;
|
|
16022
16081
|
}
|
|
16023
16082
|
function removeLoader(projectPath) {
|
|
16024
|
-
const copy =
|
|
16083
|
+
const copy = join22(projectPath, ...RUNTIME_AUTOLOAD.path.split("/"));
|
|
16025
16084
|
if (!existsSync18(copy)) {
|
|
16026
16085
|
return false;
|
|
16027
16086
|
}
|
|
@@ -16034,14 +16093,14 @@ function removeLoader(projectPath) {
|
|
|
16034
16093
|
return true;
|
|
16035
16094
|
}
|
|
16036
16095
|
function shippedListedRunner() {
|
|
16037
|
-
return
|
|
16096
|
+
return join22(dirname9(fileURLToPath2(import.meta.url)), "godot", "tests", "listed.gd");
|
|
16038
16097
|
}
|
|
16039
16098
|
function gdUnit4Unusable(projectPath) {
|
|
16040
16099
|
const marker = "addons/gdUnit4/plugin.cfg";
|
|
16041
|
-
if (!existsSync18(
|
|
16100
|
+
if (!existsSync18(join22(projectPath, ...marker.split("/")))) {
|
|
16042
16101
|
return { missing: true, reason: `gdUnit4 is not installed in this project: no ${marker}` };
|
|
16043
16102
|
}
|
|
16044
|
-
if (!existsSync18(
|
|
16103
|
+
if (!existsSync18(join22(projectPath, ...GDUNIT4_CI_RUNNER.split("/")))) {
|
|
16045
16104
|
return {
|
|
16046
16105
|
missing: false,
|
|
16047
16106
|
reason: `gdUnit4 in this project is older than 6.0, which the shipped runner needs: no ${GDUNIT4_CI_RUNNER}`
|
|
@@ -16052,43 +16111,43 @@ function gdUnit4Unusable(projectPath) {
|
|
|
16052
16111
|
function installAddons(projectPath, from = shippedAddonsDirectory()) {
|
|
16053
16112
|
const installed = [];
|
|
16054
16113
|
for (const name of ADDONS) {
|
|
16055
|
-
const source =
|
|
16056
|
-
if (!existsSync18(
|
|
16114
|
+
const source = join22(from, name);
|
|
16115
|
+
if (!existsSync18(join22(source, addonMarking(name)))) {
|
|
16057
16116
|
throw new Error(`The package holds no ${name} addon at ${source}.`);
|
|
16058
16117
|
}
|
|
16059
|
-
const target =
|
|
16118
|
+
const target = join22(projectPath, "addons", name);
|
|
16060
16119
|
const replaced = existsSync18(target);
|
|
16061
16120
|
rmSync9(target, { recursive: true, force: true });
|
|
16062
16121
|
mkdirSync6(dirname9(target), { recursive: true });
|
|
16063
16122
|
cpSync(source, target, { recursive: true });
|
|
16064
|
-
writeFileSync10(
|
|
16123
|
+
writeFileSync10(join22(target, VERSION_MARKER), `${SERVER_VERSION}
|
|
16065
16124
|
`);
|
|
16066
16125
|
installed.push({ name, path: target, replaced });
|
|
16067
16126
|
}
|
|
16068
|
-
writeFileSync10(
|
|
16127
|
+
writeFileSync10(join22(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
|
|
16069
16128
|
`);
|
|
16070
|
-
writeFileSync10(
|
|
16129
|
+
writeFileSync10(join22(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
|
|
16071
16130
|
`);
|
|
16072
16131
|
return installed;
|
|
16073
16132
|
}
|
|
16074
16133
|
function installedAddonVersion(projectPath) {
|
|
16075
|
-
const marker =
|
|
16134
|
+
const marker = join22(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
16076
16135
|
if (!existsSync18(marker)) {
|
|
16077
16136
|
return null;
|
|
16078
16137
|
}
|
|
16079
|
-
const said =
|
|
16138
|
+
const said = readFileSync18(marker, "utf8").trim();
|
|
16080
16139
|
return said === "" ? null : said;
|
|
16081
16140
|
}
|
|
16082
16141
|
function removeAddons(projectPath) {
|
|
16083
16142
|
const removed = [];
|
|
16084
16143
|
for (const name of ADDONS) {
|
|
16085
|
-
const target =
|
|
16144
|
+
const target = join22(projectPath, "addons", name);
|
|
16086
16145
|
if (existsSync18(target)) {
|
|
16087
16146
|
rmSync9(target, { recursive: true, force: true });
|
|
16088
16147
|
removed.push(target);
|
|
16089
16148
|
}
|
|
16090
16149
|
}
|
|
16091
|
-
const addons =
|
|
16150
|
+
const addons = join22(projectPath, "addons");
|
|
16092
16151
|
if (removed.length > 0 && existsSync18(addons) && readdirSync11(addons).length === 0) {
|
|
16093
16152
|
rmdirSync(addons);
|
|
16094
16153
|
removed.push(addons);
|
|
@@ -16131,7 +16190,7 @@ function loaderAmong(projectPath, named) {
|
|
|
16131
16190
|
}
|
|
16132
16191
|
let source = null;
|
|
16133
16192
|
try {
|
|
16134
|
-
source =
|
|
16193
|
+
source = readFileSync18(join22(projectPath, path), "utf8");
|
|
16135
16194
|
} catch {}
|
|
16136
16195
|
if (source?.includes(RUNTIME_SCRIPT)) {
|
|
16137
16196
|
return { name: entry, path, exists: true };
|
|
@@ -16187,10 +16246,10 @@ function enabledPlugins(settings) {
|
|
|
16187
16246
|
function inspectProject(projectPath) {
|
|
16188
16247
|
const problems = [];
|
|
16189
16248
|
const addons = ADDONS.map((name) => {
|
|
16190
|
-
const directory =
|
|
16191
|
-
const marker =
|
|
16249
|
+
const directory = join22(projectPath, "addons", name);
|
|
16250
|
+
const marker = join22(directory, VERSION_MARKER);
|
|
16192
16251
|
const installed = existsSync18(directory);
|
|
16193
|
-
const version = existsSync18(marker) ?
|
|
16252
|
+
const version = existsSync18(marker) ? readFileSync18(marker, "utf8").trim() : null;
|
|
16194
16253
|
const current = version === SERVER_VERSION;
|
|
16195
16254
|
if (!installed) {
|
|
16196
16255
|
problems.push(`addons/${name} is not installed; run gdharness setup`);
|
|
@@ -16199,7 +16258,7 @@ function inspectProject(projectPath) {
|
|
|
16199
16258
|
}
|
|
16200
16259
|
return { name, installed, version, current };
|
|
16201
16260
|
});
|
|
16202
|
-
const settings = parseProjectGodot(
|
|
16261
|
+
const settings = parseProjectGodot(readFileSync18(join22(projectPath, "project.godot"), "utf8"));
|
|
16203
16262
|
const pluginsEnabled = enabledPlugins(settings);
|
|
16204
16263
|
for (const name of EDITOR_PLUGINS) {
|
|
16205
16264
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -16223,7 +16282,7 @@ function inspectProject(projectPath) {
|
|
|
16223
16282
|
const ignoredAutoloads = [];
|
|
16224
16283
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
16225
16284
|
for (const [name, path] of named) {
|
|
16226
|
-
if (!existsSync18(
|
|
16285
|
+
if (!existsSync18(join22(projectPath, path))) {
|
|
16227
16286
|
if (!addonMissing(path)) {
|
|
16228
16287
|
problems.push(`the ${name} autoload names res://${path}, which is not in this project, so it boots with a missing script; restore that file, or point the autoload at one that is here`);
|
|
16229
16288
|
}
|
|
@@ -16592,7 +16651,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16592
16651
|
include: {
|
|
16593
16652
|
type: "array",
|
|
16594
16653
|
items: { type: "string", enum: [...PROJECT_INFO_SECTIONS] },
|
|
16595
|
-
description: "Extra sections: registered autoloads, the classes the project's scripts declare with class_name, each with its script, read from the files, the input actions project.godot holds with their events (an engine ui_ action only when the project rebinds it, as engineAction), addons and whether each is enabled, export presets, the audio bus layout, a health report, or export validation. health and validation compile every script of the project in headless engines and name each that fails with its first error, each file a scene, resource or script refers to that does not exist, each input action a script reads by a name written in the call that neither the project nor the engine defines, with every line reading it, and each signal connection in a scene whose method or signal the node at its end does not have, judged from the node's type and scripts as their files are written."
|
|
16654
|
+
description: "Extra sections: registered autoloads, the classes the project's scripts declare with class_name, each with its script, read from the files, the input actions project.godot holds with their events (an engine ui_ action only when the project rebinds it, as engineAction), addons and whether each is enabled, export presets (runnable marks the one preset of its platform the editor's one-click deploy uses), the audio bus layout, a health report, or export validation. health and validation compile every script of the project in headless engines and name each that fails with its first error, each file a scene, resource or script refers to that does not exist, each input action a script reads by a name written in the call that neither the project nor the engine defines, with every line reading it, and each signal connection in a scene whose method or signal the node at its end does not have, judged from the node's type and scripts as their files are written."
|
|
16596
16655
|
},
|
|
16597
16656
|
preset: { type: "string", description: "For validation: the export preset to validate against." },
|
|
16598
16657
|
detail: {
|
|
@@ -16623,14 +16682,18 @@ var init_tool_definitions = __esm(() => {
|
|
|
16623
16682
|
prefix: {
|
|
16624
16683
|
type: "string",
|
|
16625
16684
|
ops: ["get"],
|
|
16626
|
-
description: 'get: answer with every setting whose name starts with this, and the type the engine registers for each, instead of one setting by name. "debug/gdscript/warnings/" answers the whole family. The type matters: a family of levels can hold a setting that is a bool, and writing a level over it looks like it worked.'
|
|
16685
|
+
description: 'get: answer with every setting whose name starts with this, and the type the engine registers for each, a typed array or dictionary with what it holds as project.godot writes it (Dictionary[String, bool]), instead of one setting by name. "debug/gdscript/warnings/" answers the whole family. The type matters: a family of levels can hold a setting that is a bool, and writing a level over it looks like it worked.'
|
|
16627
16686
|
},
|
|
16628
16687
|
value: {
|
|
16629
16688
|
blank: true,
|
|
16630
16689
|
ops: ["set"],
|
|
16631
16690
|
description: 'The value to write, fitted to the type the setting is registered as, the way scene_node set fits a property. Engine types may be written as their fields, {"x": 1, "y": 2}, or tagged, {"_type": "Vector2", "x": 1, "y": 2}, and infinity as {"_type": "float", "value": "inf"}, the form a read answers with; one carrying a field its type has not is refused naming it, and a colour may also be its html code, "#ff8000". "" writes an empty string. A setting the engine does not declare is written as given, a whole number as an int and a plain list as an Array, so a packed array is tagged with its type, {"_type": "PackedStringArray", "value": ["res://a/"]}; a tag that cannot be read as its type is refused rather than written as a dictionary. When a setting in its section is named like an undeclared one, note names the nearest, as it may be a misspelt one. Every line of project.godot for another setting stays as it was, one holding a default value included.'
|
|
16632
16691
|
},
|
|
16633
|
-
name: {
|
|
16692
|
+
name: {
|
|
16693
|
+
type: "string",
|
|
16694
|
+
ops: ["add_autoload", "remove_autoload"],
|
|
16695
|
+
description: "Autoload name. add_autoload on a name already registered replaces its entry, as the editor's Add does, and answers action updated with old_path, the path it held, which no longer loads."
|
|
16696
|
+
},
|
|
16634
16697
|
path: {
|
|
16635
16698
|
type: "string",
|
|
16636
16699
|
ops: ["add_autoload"],
|
|
@@ -16784,7 +16847,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16784
16847
|
maxResults: {
|
|
16785
16848
|
type: "integer",
|
|
16786
16849
|
minimum: 1,
|
|
16787
|
-
description: "How many matching lines to answer. Default 100."
|
|
16850
|
+
description: "How many matching lines to answer. Default 100. Past it truncated is true, and the summary still counts the whole search."
|
|
16788
16851
|
}
|
|
16789
16852
|
},
|
|
16790
16853
|
requires: ["projectPath", "query"]
|
|
@@ -16800,12 +16863,12 @@ var init_tool_definitions = __esm(() => {
|
|
|
16800
16863
|
direction: {
|
|
16801
16864
|
type: "string",
|
|
16802
16865
|
enum: ["forward", "reverse"],
|
|
16803
|
-
description: "forward: what this resource loads, with each reference closing a cycle marked circular and the cycle under circular_references, a script's dependencies including every script whose class_name it uses, marked by_class with the name, and the script of every autoload it names, marked by_autoload with the autoload's name. An ext_resource whose uid names another file than its path is followed to that file, as the engine loads it, with the path written under stale_path; one whose uid names no file the project has is followed by its path, as the engine does after warning, with the uid under unknown_uid; and a load or preload of a uid the project does not know is listed under that uid as missing. A shader's #include is followed as the preprocessor reads it, relative to the shader. A file the depth stops at carries beyond_depth, how many files it names that were not walked. summary.total_resources is the file asked about, so 1; total_dependencies counts each file reached, directly or through another, once however many places load it; total_references counts the entries in the tree, and missing names every file referenced that does not exist, with the files referring to it. reverse: every file that refers to it and how, for a file that is gone as well while anything still refers to it, answered with exists: false (a missing file nothing refers to is refused, naming the nearest files), a scene instancing it, a script extending, preloading or loading it, and for a script with a class_name every use of that name, and for an autoload's script every use of the autoload's name, with autoload naming it. Each reference carries a kind: ext_resource, extends, preload, load, include or path for a reference by path, with stale_path when an ext_resource reaches it by its uid under another path, and unknown_uid when one names it by path beside a uid naming no file; setting, plugin, export_preset and import for one in project.godot, a plugin.cfg, export_presets.cfg or an import sidecar; class_name for a use of the declared name, the declaration itself being the script asked about; autoload for code naming the autoload. Three kinds are not uses: doc is a name inside a ## documentation comment, which renaming would break in the generated docs; comment is one in ordinary prose, which it would only make stale; and stale_path is an ext_resource naming it by a path the engine does not load, since its uid leads to the file under loads. summary.in_code is the count with those left out, which is the number to read when the question is whether anything still uses this. Default forward."
|
|
16866
|
+
description: "forward: what this resource loads, with each reference closing a cycle marked circular and the cycle under circular_references, a script's dependencies including every script whose class_name it uses, marked by_class with the name, and the script of every autoload it names, marked by_autoload with the autoload's name. An ext_resource whose uid names another file than its path is followed to that file, as the engine loads it, with the path written under stale_path; one whose uid names no file the project has is followed by its path, as the engine does after warning, with the uid under unknown_uid; and a load or preload of a uid the project does not know is listed under that uid as missing. A shader's #include is followed as the preprocessor reads it, relative to the shader. A file the depth stops at carries beyond_depth, how many files it names that were not walked. A file's dependencies are listed at its first entry, and again further down only where the depth cut that list short and more is left; every other entry for a file naming any carries dependencies_listed_above. summary.total_resources is the file asked about, so 1; total_dependencies counts each file reached, directly or through another, once however many places load it; total_references counts the entries in the tree, and missing names every file referenced that does not exist, with the files referring to it. reverse: every file that refers to it and how, for a file that is gone as well while anything still refers to it, answered with exists: false (a missing file nothing refers to is refused, naming the nearest files), a scene instancing it, a script extending, preloading or loading it, and for a script with a class_name every use of that name, and for an autoload's script every use of the autoload's name, with autoload naming it. Each reference carries a kind: ext_resource, extends, preload, load, include or path for a reference by path, with stale_path when an ext_resource reaches it by its uid under another path, and unknown_uid when one names it by path beside a uid naming no file; setting, plugin, export_preset and import for one in project.godot, a plugin.cfg, export_presets.cfg or an import sidecar; class_name for a use of the declared name, the declaration itself being the script asked about; autoload for code naming the autoload. Three kinds are not uses: doc is a name inside a ## documentation comment, which renaming would break in the generated docs; comment is one in ordinary prose, which it would only make stale; and stale_path is an ext_resource naming it by a path the engine does not load, since its uid leads to the file under loads. summary.in_code is the count with those left out, which is the number to read when the question is whether anything still uses this. Default forward."
|
|
16804
16867
|
},
|
|
16805
16868
|
depth: {
|
|
16806
16869
|
type: "integer",
|
|
16807
16870
|
minimum: 1,
|
|
16808
|
-
description: "forward: how many levels to follow, 1 being what the file loads itself. Default unlimited."
|
|
16871
|
+
description: "forward: how many levels to follow, 1 being what the file loads itself. Default unlimited, which follows every class_name use too and in a project whose scripts name each other's classes reaches most of it: a game of a hundred scripts with large addons answered 488 files in about a megabyte from its main scene. A depth of 2 or 3 keeps the answer to what is near."
|
|
16809
16872
|
},
|
|
16810
16873
|
includeBuiltin: {
|
|
16811
16874
|
type: "boolean",
|
|
@@ -16823,7 +16886,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16823
16886
|
name: "project_import",
|
|
16824
16887
|
title: "Asset imports",
|
|
16825
16888
|
description: "The import pipeline: what needs importing, how a resource is imported, reimports, UIDs, and the global class list the editor and the engine read.",
|
|
16826
|
-
answer: "reimport is answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. importedByTheScan names what the editor's scan imported first, and alsoImported, on a reimport of one resourcePath, the others it imported. wroteInProject names, by source, what an import wrote into the project itself rather than under .godot, such as the .translation a CSV gives each language, which version control sees and internationalization/locale/translations names. The import gives every script without a .uid sidecar one, as the editor's scan does, and uidsWritten names those scripts, counted per addon under uidsWrittenInAddons for the ones inside an addon. When nothing needed reimporting, the two lists are empty, a note says so, and there is no via. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, even when nothing needed reimporting; one resourcePath leaves it out. stillImporting is an editor that had not finished within timeoutMs.",
|
|
16889
|
+
answer: "options answers remap, deps and params as the .import file holds them, so an enum option such as compress/mode is its number: the engine gives scripts no list of a built-in importer's options or of what the numbers mean, so Godot's documentation of that importer is where a number is looked up. reimport is answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. importedByTheScan names what the editor's scan imported first, and alsoImported, on a reimport of one resourcePath, the others it imported. wroteInProject names, by source, what an import wrote into the project itself rather than under .godot, such as the .translation a CSV gives each language, which version control sees and internationalization/locale/translations names. The import gives every script without a .uid sidecar one, as the editor's scan does, and uidsWritten names those scripts, counted per addon under uidsWrittenInAddons for the ones inside an addon. When nothing needed reimporting, the two lists are empty, a note says so, and there is no via. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, even when nothing needed reimporting; one resourcePath leaves it out. stillImporting is an editor that had not finished within timeoutMs.",
|
|
16827
16890
|
parameters: {
|
|
16828
16891
|
projectPath: PROJECT_PATH,
|
|
16829
16892
|
resourcePath: { ...RESOURCE_PATH, ops: ["status", "options", "set_options", "uid", "reimport"] },
|
|
@@ -16885,7 +16948,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16885
16948
|
},
|
|
16886
16949
|
refresh_classes: {
|
|
16887
16950
|
effect: "destructive",
|
|
16888
|
-
summary: 'rewrite .godot/global_script_class_cache.cfg from the class_name declarations on disk. That misses the list a running editor loaded, so "added: []" means the file was already right, not that nothing is wrong: unseenByEditor names a class the editor open on this project cannot resolve, and heldBehindInEditor one it holds compiled from an older text of its file. lostSinceLastRebuild names a class the cache held after the last rebuild and lost before this one: the editor wrote the file without it, on a save or a scan, and will again until restarted. staleInThisEngine names a class the rebuild added, unknown to the autoloads its own engine compiles afterwards, and autoloadsOnTheRebuiltList what a second engine on that list reports of them',
|
|
16951
|
+
summary: 'rewrite .godot/global_script_class_cache.cfg from the class_name declarations on disk. That misses the list a running editor loaded, so "added: []" means the file was already right, not that nothing is wrong: unseenByEditor names a class the editor open on this project cannot resolve, and heldBehindInEditor one it holds compiled from an older text of its file, with heldBehindUnchecked saying why when the editor did not say. lostSinceLastRebuild names a class the cache held after the last rebuild and lost before this one: the editor wrote the file without it, on a save or a scan, and will again until restarted. staleInThisEngine names a class the rebuild added, unknown to the autoloads its own engine compiles afterwards, and autoloadsOnTheRebuiltList what a second engine on that list reports of them',
|
|
16889
16952
|
requires: []
|
|
16890
16953
|
}
|
|
16891
16954
|
}
|
|
@@ -16893,7 +16956,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16893
16956
|
{
|
|
16894
16957
|
name: "project_export",
|
|
16895
16958
|
title: "Export",
|
|
16896
|
-
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; 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.",
|
|
16959
|
+
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.",
|
|
16897
16960
|
parameters: {
|
|
16898
16961
|
projectPath: PROJECT_PATH,
|
|
16899
16962
|
preset: {
|
|
@@ -16977,7 +17040,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16977
17040
|
title: "Run tests",
|
|
16978
17041
|
effect: "additive",
|
|
16979
17042
|
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.",
|
|
16980
|
-
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. 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 push_warning, so a case that printed one undeclared is in neither failed nor errors: it is named here, and the run is failed for it. 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,
|
|
17043
|
+
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 push_warning, so a case that printed one undeclared is in neither failed nor errors: it is named here, and the run is failed for it. 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,
|
|
16981
17044
|
parameters: {
|
|
16982
17045
|
projectPath: PROJECT_PATH,
|
|
16983
17046
|
path: {
|
|
@@ -17491,7 +17554,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17491
17554
|
operations: {
|
|
17492
17555
|
structure: {
|
|
17493
17556
|
effect: "read",
|
|
17494
|
-
summary: `functions (each parameter's default the source text after its =, so = null reads "null", and null for a parameter with none), variables (static ones marked is_static; one written with := has the type of a literal or built-in constructor it is given, marked inferred, as a constant always does), signals, constants, enums, inner_classes (each with its name, line and extends, and its own members in these same fields), class_name, extends and line_count, read from the file, and dependencies: the paths it loads or preloads as written, then the scripts declaring each project class_name its code uses, which classes_used names with the class; autoloads_used names each autoload the code uses by its global name, with the autoload's script, which the project loads rather than this script, so it is not under dependencies. A member or inner class with a ## documentation comment directly above it carries the comment's text as doc, and the script's own, the ## block above class_name or extends or one followed by a blank line before any member, comes back as class_doc`,
|
|
17557
|
+
summary: `functions (each parameter's default the source text after its =, so = null reads "null", and null for a parameter with none), variables (static ones marked is_static; one written with := has the type of a literal or built-in constructor it is given, marked inferred, as a constant always does), signals, constants, enums, inner_classes (each with its name, line and extends, and its own members in these same fields), class_name, extends and line_count, read from the file, and dependencies: the paths it loads or preloads as written, by a constant it declares too, and the paths it or an inner class extends by, then the scripts declaring each project class_name its code uses, which classes_used names with the class; autoloads_used names each autoload the code uses by its global name, with the autoload's script, which the project loads rather than this script, so it is not under dependencies. A member or inner class with a ## documentation comment directly above it carries the comment's text as doc, and the script's own, the ## block above class_name or extends or one followed by a blank line before any member, comes back as class_doc`,
|
|
17495
17558
|
requires: []
|
|
17496
17559
|
},
|
|
17497
17560
|
symbols: {
|
|
@@ -17512,7 +17575,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17512
17575
|
name: "script_diagnostics",
|
|
17513
17576
|
title: "Script diagnostics",
|
|
17514
17577
|
effect: "read",
|
|
17515
|
-
description: "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. With no editor of this project open, so no language server of it to ask, a short headless engine compiles the scripts instead and the answer says checkedBy: engine rather than language server: every error that stops a script compiling, up to its first syntax error, with its line and no column (a
|
|
17578
|
+
description: "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. With no editor of this project open, so no language server of it to ask, a short headless engine compiles the scripts instead and the answer says checkedBy: engine rather than language server: every error that stops a script compiling, up to its first syntax error, with its line and no column (a method or property another class lacks is not one, since GDScript checks a method only on self and a property only named bare: the object may be a subclass that has it, so it fails at run time instead), dependenciesWithErrors naming the other scripts that failed while it loaded, typesMissingFromTheClassCache naming a class the project declares that the engine could not resolve because the class cache lacks it, with staleAnalysis saying what rewrites the cache, and the warnings at the levels the project sets, which a second engine reports by raising each warning set to warn as an error for that run only, each at its line with no column, warnings being null when that engine could not answer; a script with a syntax error is checked for no warnings until it parses. Of several scripts, a language server that stops answering partway leaves the rest to the engine, and checkedBy then says language server, then engine, with checkedByLanguageServer counting the scripts it answered. Each diagnostic carries its severity (error or warning), its message, and where it is as the editor shows it: line and column counted from 1, and endLine and endColumn just past its end. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, or a project class it extends does, a method, property, constant, static function or enum member, comes back under contradictedByTheFile with the script declaring it, and so does one a diagnostic names alone as an external class member, read against the class it is looked up on in the line it is on; and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.",
|
|
17516
17579
|
parameters: {
|
|
17517
17580
|
projectPath: PROJECT_PATH,
|
|
17518
17581
|
scriptPath: {
|
|
@@ -17899,7 +17962,8 @@ var init_tool_definitions = __esm(() => {
|
|
|
17899
17962
|
name: "editor_rescan",
|
|
17900
17963
|
title: "Rescan the project",
|
|
17901
17964
|
effect: "additive",
|
|
17902
|
-
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. A scan or an import the editor is already running is waited out first rather than scanned over, which would start a second import over it.
|
|
17965
|
+
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. A scan or an import the editor is already running is waited out first rather than scanned over, which would start a second import over it. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script, measured against a real editor on three platforms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. The walk also passes over a file whose .uid alone changed, so the editor keeps the old UID and every engine reading its cache warns of a duplicate; before scanning, each file whose .uid names a UID the editor does not hold for it is read again and named under uidsReread, and files on disk naming one UID between them are named under uidsDuplicatedOnDisk, which reading again cannot settle. A class the scan brings in is named under broughtIn, and every script naming it is reloaded in the editor and named under dependentsReloaded: what the editor compiled from them while it could not see the class stays compiled that way after the scan, and a script extending a new base class went on reporting \"Could not find base class\" until reloaded. One that would not reload is named under dependentsNotReloaded and the call is not ok, as it is not when the script named in reloadScript was not reloaded. A script that fails to compile is tried again once the scripts it is compiled against that the editor holds behind their files are reloaded, which are named under reloadedFirst. An editor importing answers nothing until the import is over, and a hidden one imports on the thread that answers, so a large addon install can leave the rescan itself unanswered: that is waited for as any busy editor is, within timeoutMs, and when the wait runs out first the answer is stillWorking. The classes the editor could not see before that scan are then noted in the project, and the next editor_rescan of the same editor, from any server, names them under broughtIn and reloads what names them, which it could not otherwise know to do, since by then the editor sees them. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. The other direction is guarded the same way: a class the editor still holds for a script that was renamed or deleted comes back into the cache at a path that is not there, and the next engine to read it fails on \"Could not parse global class\" in whichever correct script shares the bare name, so the cache is rebuilt without it and the answer names it under cacheDropped; the editor goes on holding it until it is restarted, and every scan until then ends in that rebuild. A script or shader written outside the editor has no .uid sidecar until something imports it, and the scan writes one beside it, which a project that commits sidecars needs before the commit. Needs the editor connected.",
|
|
17966
|
+
answer: "waitedMs is the whole call, and waitedForEditorScanMs, given only when a scan or an import was already running, the part spent waiting it out. waitedForEditorAnswerMs, given when an idle editor took a second or more to say so, is how long that took: such an editor is busy on the thread that answers with an earlier request rather than scanning, so the time is added to timeoutMs rather than taken from it, and the note says so. stillWorking is true when a scan was still running at the end of the wait; the scan the call started is looked at once even when the wait has run out, so stillWorking is never said of a scan nothing looked at.",
|
|
17903
17967
|
parameters: {
|
|
17904
17968
|
projectPath: PROJECT_PATH,
|
|
17905
17969
|
timeoutMs: {
|
|
@@ -18557,7 +18621,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18557
18621
|
{
|
|
18558
18622
|
name: "debug_state",
|
|
18559
18623
|
title: "Debugger state",
|
|
18560
|
-
description: "Where the debugged game is stopped: the stack trace, what is in scope at a frame with the values, or the debugger's console output so far, its last thousand lines, with any earlier ones counted under notShown. For a game this server started the output is what the game printed; the errors and warnings it reported, with where each happened, are in editor_output. For a game this server started, its keeper's debugger answers: each frame names its script as the project spells it under script and on this machine under path, and held says which thread stopped and why. For a game the editor plays, the editor's debug adapter answers. A stop is reported by the editor's adapter only to the sessions connected when it happened, so a server that connected afterwards, which is every server after a reconnect, is shown a thread and no frames about a game still sitting at its breakpoint. That server asks the runtime instead: a held game accepts a connection and never answers, and the refusal then says held rather than running, and that the stack cannot be read from here. debug_control continue lets it go, and the next stop is one this session is told about, with its stack. A function that awaited is resumed with no caller below it, since the call that started it returned at the await, so its frame is the only one. The variables give an object as its class and instance id only, and evaluate reads into it: an expression worked out by the game at a frame, with the frame's locals, the globals and every global class in scope and the frame's instance as self, such as wave.spawned or wave.spawned.size(). The game runs what the expression calls, so a call that changes something changes the game. A frame with no instance, a static function's called on its class, is refused, since the engine lets the game go on rather than evaluate there; the frame below it has one. The compiler's own locals, named with @ such as a for loop's @counter_pos, are left out.",
|
|
18624
|
+
description: "Where the debugged game is stopped: the stack trace, what is in scope at a frame with the values, or the debugger's console output so far, its last thousand lines, with any earlier ones counted under notShown. For a game this server started the output is what the game printed; the errors and warnings it reported, with where each happened, are in editor_output, which also has what it printed once it has ended, as its debugger ends with it. For a game this server started, its keeper's debugger answers: each frame names its script as the project spells it under script and on this machine under path, and held says which thread stopped and why. For a game the editor plays, the editor's debug adapter answers. A stop is reported by the editor's adapter only to the sessions connected when it happened, so a server that connected afterwards, which is every server after a reconnect, is shown a thread and no frames about a game still sitting at its breakpoint. That server asks the runtime instead: a held game accepts a connection and never answers, and the refusal then says held rather than running, and that the stack cannot be read from here. debug_control continue lets it go, and the next stop is one this session is told about, with its stack. A function that awaited is resumed with no caller below it, since the call that started it returned at the await, so its frame is the only one. The variables give an object as its class and instance id only, and evaluate reads into it: an expression worked out by the game at a frame, with the frame's locals, the globals and every global class in scope and the frame's instance as self, such as wave.spawned or wave.spawned.size(). The game runs what the expression calls, so a call that changes something changes the game. A frame with no instance, a static function's called on its class, is refused, since the engine lets the game go on rather than evaluate there; the frame below it has one. The compiler's own locals, named with @ such as a for loop's @counter_pos, are left out.",
|
|
18561
18625
|
parameters: {
|
|
18562
18626
|
frameId: {
|
|
18563
18627
|
type: "number",
|
|
@@ -18698,9 +18762,9 @@ function whatItCanChange(tool) {
|
|
|
18698
18762
|
if (destroying.length === 0) {
|
|
18699
18763
|
return "Adds or starts things, and overwrites and removes nothing itself.";
|
|
18700
18764
|
}
|
|
18701
|
-
return `Can overwrite, remove or end what is there, through ${
|
|
18765
|
+
return `Can overwrite, remove or end what is there, through ${listed3(destroying)}.`;
|
|
18702
18766
|
}
|
|
18703
|
-
function
|
|
18767
|
+
function listed3(items) {
|
|
18704
18768
|
const last = items.at(-1) ?? "";
|
|
18705
18769
|
return items.length === 1 ? last : `${items.slice(0, -1).join(", ")} and ${last}`;
|
|
18706
18770
|
}
|
|
@@ -18758,8 +18822,8 @@ var init_tool_reference = __esm(() => {
|
|
|
18758
18822
|
});
|
|
18759
18823
|
|
|
18760
18824
|
// src/suite-plan.ts
|
|
18761
|
-
import { existsSync as existsSync22, mkdirSync as mkdirSync9, readFileSync as
|
|
18762
|
-
import { dirname as dirname11, join as
|
|
18825
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync9, readFileSync as readFileSync21 } from "node:fs";
|
|
18826
|
+
import { dirname as dirname11, join as join26, posix as posix3 } from "node:path";
|
|
18763
18827
|
function within(path, folder) {
|
|
18764
18828
|
return path === folder || path.startsWith(`${folder}/`);
|
|
18765
18829
|
}
|
|
@@ -18775,7 +18839,7 @@ class ProjectScripts {
|
|
|
18775
18839
|
this.projectPath = projectPath;
|
|
18776
18840
|
let settings = {};
|
|
18777
18841
|
try {
|
|
18778
|
-
settings = parseProjectGodot(
|
|
18842
|
+
settings = parseProjectGodot(readFileSync21(join26(projectPath, "project.godot"), "utf8"))["gdharness"] ?? {};
|
|
18779
18843
|
} catch {}
|
|
18780
18844
|
const [section, key] = [NOT_SEARCHED_SETTING, GUESS_WINDOW_SETTING].map((setting) => setting.slice("gdharness/".length));
|
|
18781
18845
|
const listed = settings[section ?? ""];
|
|
@@ -18890,7 +18954,7 @@ class ProjectScripts {
|
|
|
18890
18954
|
}
|
|
18891
18955
|
function readTimes(file) {
|
|
18892
18956
|
try {
|
|
18893
|
-
const parsed = JSON.parse(
|
|
18957
|
+
const parsed = JSON.parse(readFileSync21(file, "utf8"));
|
|
18894
18958
|
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
18895
18959
|
} catch {
|
|
18896
18960
|
return {};
|
|
@@ -19173,7 +19237,7 @@ function viewportOf(projectPath) {
|
|
|
19173
19237
|
if (!existsSync22(file)) {
|
|
19174
19238
|
return null;
|
|
19175
19239
|
}
|
|
19176
|
-
const text =
|
|
19240
|
+
const text = readFileSync21(file, "utf8");
|
|
19177
19241
|
const width = /^window\/size\/viewport_width\s*=\s*(\d+)/m.exec(text)?.[1];
|
|
19178
19242
|
const height = /^window\/size\/viewport_height\s*=\s*(\d+)/m.exec(text)?.[1];
|
|
19179
19243
|
return width === undefined || height === undefined ? null : { width: Number(width), height: Number(height) };
|
|
@@ -40792,13 +40856,13 @@ var init_stdio = __esm(() => {
|
|
|
40792
40856
|
});
|
|
40793
40857
|
|
|
40794
40858
|
// src/autoload-failure.ts
|
|
40795
|
-
import { existsSync as existsSync24, readFileSync as
|
|
40796
|
-
import { join as
|
|
40859
|
+
import { existsSync as existsSync24, readFileSync as readFileSync24 } from "node:fs";
|
|
40860
|
+
import { join as join29 } from "node:path";
|
|
40797
40861
|
function scriptsAutoloaded(projectPath) {
|
|
40798
40862
|
const scripts = new Map;
|
|
40799
40863
|
let settings;
|
|
40800
40864
|
try {
|
|
40801
|
-
settings = parseProjectGodot(
|
|
40865
|
+
settings = parseProjectGodot(readFileSync24(join29(projectPath, "project.godot"), "utf8"));
|
|
40802
40866
|
} catch {
|
|
40803
40867
|
return scripts;
|
|
40804
40868
|
}
|
|
@@ -40807,12 +40871,12 @@ function scriptsAutoloaded(projectPath) {
|
|
|
40807
40871
|
scripts.set(`res://${path}`, name);
|
|
40808
40872
|
continue;
|
|
40809
40873
|
}
|
|
40810
|
-
const scene =
|
|
40874
|
+
const scene = join29(projectPath, path);
|
|
40811
40875
|
if (!existsSync24(scene)) {
|
|
40812
40876
|
continue;
|
|
40813
40877
|
}
|
|
40814
40878
|
try {
|
|
40815
|
-
for (const named of
|
|
40879
|
+
for (const named of readFileSync24(scene, "utf8").matchAll(/^\[ext_resource\b[^\]]*\btype="Script"[^\]]*\bpath="(res:\/\/[^"]+)"/gm)) {
|
|
40816
40880
|
scripts.set(named[1] ?? "", name);
|
|
40817
40881
|
}
|
|
40818
40882
|
} catch {}
|
|
@@ -40831,10 +40895,10 @@ var init_autoload_failure = __esm(() => {
|
|
|
40831
40895
|
});
|
|
40832
40896
|
|
|
40833
40897
|
// src/runtime-client.ts
|
|
40834
|
-
import { existsSync as existsSync25, readdirSync as readdirSync15, readFileSync as
|
|
40898
|
+
import { existsSync as existsSync25, readdirSync as readdirSync15, readFileSync as readFileSync25, statSync as statSync15, unlinkSync as unlinkSync3 } from "node:fs";
|
|
40835
40899
|
import { createConnection as createConnection2 } from "node:net";
|
|
40836
40900
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
40837
|
-
import { dirname as dirname14, join as
|
|
40901
|
+
import { dirname as dirname14, join as join30, resolve as resolve11 } from "node:path";
|
|
40838
40902
|
import { setTimeout as delay5 } from "node:timers/promises";
|
|
40839
40903
|
function runtimeDirectory(variables = process.env) {
|
|
40840
40904
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -40842,19 +40906,19 @@ function runtimeDirectory(variables = process.env) {
|
|
|
40842
40906
|
return explicit;
|
|
40843
40907
|
}
|
|
40844
40908
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
40845
|
-
return
|
|
40909
|
+
return join30(perUser ?? tmpdir5(), "gdharness");
|
|
40846
40910
|
}
|
|
40847
40911
|
function runtimeDirectories(variables = process.env) {
|
|
40848
40912
|
const candidates = [runtimeDirectory(variables)];
|
|
40849
40913
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
40850
40914
|
for (const base of fallbacks) {
|
|
40851
|
-
candidates.push(
|
|
40915
|
+
candidates.push(join30(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
40852
40916
|
}
|
|
40853
40917
|
return [...new Set(candidates.map((path) => resolve11(path)))];
|
|
40854
40918
|
}
|
|
40855
40919
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
40856
40920
|
for (const directory of directories) {
|
|
40857
|
-
const path =
|
|
40921
|
+
const path = join30(directory, `runtime-${pid}.log`);
|
|
40858
40922
|
if (existsSync25(path)) {
|
|
40859
40923
|
return path;
|
|
40860
40924
|
}
|
|
@@ -40886,7 +40950,7 @@ function reportAlreadyInTranscript(transcript, report) {
|
|
|
40886
40950
|
function parseAnnouncement(file, pid) {
|
|
40887
40951
|
let fields;
|
|
40888
40952
|
try {
|
|
40889
|
-
fields = asParams(JSON.parse(
|
|
40953
|
+
fields = asParams(JSON.parse(readFileSync25(file, "utf8")));
|
|
40890
40954
|
} catch {
|
|
40891
40955
|
return { kind: "rubbish" };
|
|
40892
40956
|
}
|
|
@@ -40958,7 +41022,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
|
|
|
40958
41022
|
function wentAway(file, pid) {
|
|
40959
41023
|
let project = "";
|
|
40960
41024
|
try {
|
|
40961
|
-
const fields = asParams(JSON.parse(
|
|
41025
|
+
const fields = asParams(JSON.parse(readFileSync25(file, "utf8")));
|
|
40962
41026
|
project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
|
|
40963
41027
|
} catch {
|
|
40964
41028
|
return;
|
|
@@ -41010,14 +41074,14 @@ function announcedIn(directory) {
|
|
|
41010
41074
|
for (const entry of readdirSync15(directory)) {
|
|
41011
41075
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
41012
41076
|
if (report) {
|
|
41013
|
-
sweepErrorReport(
|
|
41077
|
+
sweepErrorReport(join30(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
41014
41078
|
continue;
|
|
41015
41079
|
}
|
|
41016
41080
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
41017
41081
|
if (!match) {
|
|
41018
41082
|
continue;
|
|
41019
41083
|
}
|
|
41020
|
-
const file =
|
|
41084
|
+
const file = join30(directory, entry);
|
|
41021
41085
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
41022
41086
|
const running = alive(pid);
|
|
41023
41087
|
entries.push({ file, pid, running });
|
|
@@ -41045,7 +41109,7 @@ function announcedIn(directory) {
|
|
|
41045
41109
|
found.push(announced);
|
|
41046
41110
|
}
|
|
41047
41111
|
const held = new Set(entries.map((one) => one.file));
|
|
41048
|
-
const here =
|
|
41112
|
+
const here = join30(directory, ".");
|
|
41049
41113
|
for (const file of verdicts.keys()) {
|
|
41050
41114
|
if (dirname14(file) === here && !held.has(file)) {
|
|
41051
41115
|
verdicts.delete(file);
|
|
@@ -41054,7 +41118,7 @@ function announcedIn(directory) {
|
|
|
41054
41118
|
return found;
|
|
41055
41119
|
}
|
|
41056
41120
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
41057
|
-
const files = directories.map((directory) =>
|
|
41121
|
+
const files = directories.map((directory) => join30(directory, `runtime-${pid}.json`)).filter((file) => existsSync25(file));
|
|
41058
41122
|
const first = files[0];
|
|
41059
41123
|
if (first === undefined) {
|
|
41060
41124
|
return;
|
|
@@ -41473,14 +41537,14 @@ var init_runtime_client = __esm(() => {
|
|
|
41473
41537
|
});
|
|
41474
41538
|
|
|
41475
41539
|
// src/boot-note.ts
|
|
41476
|
-
import { mkdirSync as mkdirSync11, readFileSync as
|
|
41477
|
-
import { dirname as dirname15, join as
|
|
41540
|
+
import { mkdirSync as mkdirSync11, readFileSync as readFileSync26, writeFileSync as writeFileSync13 } from "node:fs";
|
|
41541
|
+
import { dirname as dirname15, join as join31 } from "node:path";
|
|
41478
41542
|
function bootNotePath(projectPath) {
|
|
41479
|
-
return
|
|
41543
|
+
return join31(projectPath, ".godot", "gdharness-boot.json");
|
|
41480
41544
|
}
|
|
41481
41545
|
function readScenes(projectPath) {
|
|
41482
41546
|
try {
|
|
41483
|
-
return asParams(asParams(JSON.parse(
|
|
41547
|
+
return asParams(asParams(JSON.parse(readFileSync26(bootNotePath(projectPath), "utf8")))["scenes"]);
|
|
41484
41548
|
} catch {
|
|
41485
41549
|
return {};
|
|
41486
41550
|
}
|
|
@@ -41522,10 +41586,10 @@ var init_boot_note = __esm(() => {
|
|
|
41522
41586
|
});
|
|
41523
41587
|
|
|
41524
41588
|
// src/breakpoint-note.ts
|
|
41525
|
-
import { mkdirSync as mkdirSync12, readFileSync as
|
|
41526
|
-
import { dirname as dirname16, isAbsolute as isAbsolute7, join as
|
|
41589
|
+
import { mkdirSync as mkdirSync12, readFileSync as readFileSync27, rmSync as rmSync13, writeFileSync as writeFileSync14 } from "node:fs";
|
|
41590
|
+
import { dirname as dirname16, isAbsolute as isAbsolute7, join as join32, relative as relative6 } from "node:path";
|
|
41527
41591
|
function breakpointNotePath(projectPath) {
|
|
41528
|
-
return
|
|
41592
|
+
return join32(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
41529
41593
|
}
|
|
41530
41594
|
function writeBreakpointNote(projectPath, held) {
|
|
41531
41595
|
const path = breakpointNotePath(projectPath);
|
|
@@ -41551,7 +41615,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
41551
41615
|
}
|
|
41552
41616
|
function readBreakpointNote(projectPath) {
|
|
41553
41617
|
try {
|
|
41554
|
-
const parsed = JSON.parse(
|
|
41618
|
+
const parsed = JSON.parse(readFileSync27(breakpointNotePath(projectPath), "utf8"));
|
|
41555
41619
|
if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
|
|
41556
41620
|
return [];
|
|
41557
41621
|
}
|
|
@@ -41566,7 +41630,7 @@ function readBreakpointNote(projectPath) {
|
|
|
41566
41630
|
}
|
|
41567
41631
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
41568
41632
|
if (lines.length > 0) {
|
|
41569
|
-
held.push({ scriptPath: realAsFarAsItExists(
|
|
41633
|
+
held.push({ scriptPath: realAsFarAsItExists(join32(projectPath, script)), lines });
|
|
41570
41634
|
}
|
|
41571
41635
|
}
|
|
41572
41636
|
return held;
|
|
@@ -41584,14 +41648,14 @@ var init_breakpoint_note = __esm(() => {
|
|
|
41584
41648
|
});
|
|
41585
41649
|
|
|
41586
41650
|
// src/class-note.ts
|
|
41587
|
-
import { mkdirSync as mkdirSync13, readFileSync as
|
|
41588
|
-
import { dirname as dirname17, join as
|
|
41651
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync28 } from "node:fs";
|
|
41652
|
+
import { dirname as dirname17, join as join33 } from "node:path";
|
|
41589
41653
|
function classNotePath(projectPath) {
|
|
41590
|
-
return
|
|
41654
|
+
return join33(projectPath, ".godot", "gdharness-classes.json");
|
|
41591
41655
|
}
|
|
41592
41656
|
function readClassNote(projectPath) {
|
|
41593
41657
|
try {
|
|
41594
|
-
const fields = asParams(JSON.parse(
|
|
41658
|
+
const fields = asParams(JSON.parse(readFileSync28(classNotePath(projectPath), "utf8")));
|
|
41595
41659
|
const rebuiltWith = readArray(fields, "rebuiltWith");
|
|
41596
41660
|
if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
|
|
41597
41661
|
return null;
|
|
@@ -41633,16 +41697,16 @@ var init_class_note = __esm(() => {
|
|
|
41633
41697
|
});
|
|
41634
41698
|
|
|
41635
41699
|
// src/config-pin.ts
|
|
41636
|
-
import { existsSync as existsSync26, readFileSync as
|
|
41637
|
-
import { join as
|
|
41700
|
+
import { existsSync as existsSync26, readFileSync as readFileSync29 } from "node:fs";
|
|
41701
|
+
import { join as join34 } from "node:path";
|
|
41638
41702
|
function versionsNamedInConfig(projectPath) {
|
|
41639
|
-
const config =
|
|
41703
|
+
const config = join34(projectPath, ".mcp.json");
|
|
41640
41704
|
if (!existsSync26(config)) {
|
|
41641
41705
|
return [];
|
|
41642
41706
|
}
|
|
41643
41707
|
let text;
|
|
41644
41708
|
try {
|
|
41645
|
-
text =
|
|
41709
|
+
text = readFileSync29(config, "utf8");
|
|
41646
41710
|
JSON.parse(text);
|
|
41647
41711
|
} catch {
|
|
41648
41712
|
return [];
|
|
@@ -41663,10 +41727,10 @@ var init_config_pin = __esm(() => {
|
|
|
41663
41727
|
});
|
|
41664
41728
|
|
|
41665
41729
|
// src/cut-test-run.ts
|
|
41666
|
-
import { existsSync as existsSync27, mkdirSync as mkdirSync14, readFileSync as
|
|
41667
|
-
import { dirname as dirname18, join as
|
|
41730
|
+
import { existsSync as existsSync27, mkdirSync as mkdirSync14, readFileSync as readFileSync30, rmSync as rmSync14, writeFileSync as writeFileSync15 } from "node:fs";
|
|
41731
|
+
import { dirname as dirname18, join as join35 } from "node:path";
|
|
41668
41732
|
function keptAt(projectPath) {
|
|
41669
|
-
return
|
|
41733
|
+
return join35(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
41670
41734
|
}
|
|
41671
41735
|
function keepCutTestRun(projectPath, run) {
|
|
41672
41736
|
const path = keptAt(projectPath);
|
|
@@ -41682,7 +41746,7 @@ function takeCutTestRun(projectPath) {
|
|
|
41682
41746
|
return null;
|
|
41683
41747
|
}
|
|
41684
41748
|
try {
|
|
41685
|
-
const run = JSON.parse(
|
|
41749
|
+
const run = JSON.parse(readFileSync30(path, "utf8"));
|
|
41686
41750
|
rmSync14(path, { force: true });
|
|
41687
41751
|
return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
|
|
41688
41752
|
} catch {
|
|
@@ -42418,22 +42482,22 @@ import {
|
|
|
42418
42482
|
existsSync as existsSync28,
|
|
42419
42483
|
mkdirSync as mkdirSync15,
|
|
42420
42484
|
openSync as openSync4,
|
|
42421
|
-
readFileSync as
|
|
42485
|
+
readFileSync as readFileSync31,
|
|
42422
42486
|
readSync as readSync2,
|
|
42423
42487
|
rmSync as rmSync15,
|
|
42424
42488
|
statSync as statSync16,
|
|
42425
42489
|
writeFileSync as writeFileSync16
|
|
42426
42490
|
} from "node:fs";
|
|
42427
|
-
import { dirname as dirname19, join as
|
|
42491
|
+
import { dirname as dirname19, join as join36 } from "node:path";
|
|
42428
42492
|
function editorLogPath(projectPath) {
|
|
42429
|
-
return
|
|
42493
|
+
return join36(projectPath, ".godot", "gdharness-editor.log");
|
|
42430
42494
|
}
|
|
42431
42495
|
function editorLogNotePath(projectPath) {
|
|
42432
|
-
return
|
|
42496
|
+
return join36(projectPath, ".godot", "gdharness-editor.json");
|
|
42433
42497
|
}
|
|
42434
42498
|
function readEditorLogNote(projectPath) {
|
|
42435
42499
|
try {
|
|
42436
|
-
const fields = asParams(JSON.parse(
|
|
42500
|
+
const fields = asParams(JSON.parse(readFileSync31(editorLogNotePath(projectPath), "utf8")));
|
|
42437
42501
|
const pid = readNumber(fields, "pid");
|
|
42438
42502
|
const startedAt = readString(fields, "startedAt");
|
|
42439
42503
|
const processStartedAt = readNumber(fields, "processStartedAt");
|
|
@@ -42537,7 +42601,7 @@ function foldProgress(text) {
|
|
|
42537
42601
|
function theFirstOf(path) {
|
|
42538
42602
|
const whole = statSync16(path).size;
|
|
42539
42603
|
if (whole <= MOST_CONSOLE_BYTES) {
|
|
42540
|
-
return { text:
|
|
42604
|
+
return { text: readFileSync31(path, "utf8") };
|
|
42541
42605
|
}
|
|
42542
42606
|
const handle = openSync4(path, "r");
|
|
42543
42607
|
try {
|
|
@@ -42698,7 +42762,7 @@ var init_editor_log = __esm(() => {
|
|
|
42698
42762
|
});
|
|
42699
42763
|
|
|
42700
42764
|
// src/rename.ts
|
|
42701
|
-
import { existsSync as existsSync29, mkdirSync as mkdirSync16, readFileSync as
|
|
42765
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync16, readFileSync as readFileSync32, renameSync as renameSync3, unlinkSync as unlinkSync4, writeFileSync as writeFileSync17 } from "node:fs";
|
|
42702
42766
|
import { dirname as dirname20, posix as posix4 } from "node:path";
|
|
42703
42767
|
|
|
42704
42768
|
class Script {
|
|
@@ -43403,7 +43467,7 @@ function rewritesOf(plan, texts, move) {
|
|
|
43403
43467
|
}
|
|
43404
43468
|
function applyRewrites(rewrites) {
|
|
43405
43469
|
for (const rewrite of rewrites) {
|
|
43406
|
-
const now =
|
|
43470
|
+
const now = readFileSync32(rewrite.absolute, "utf8");
|
|
43407
43471
|
if (now !== rewrite.before) {
|
|
43408
43472
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
43409
43473
|
}
|
|
@@ -43468,7 +43532,7 @@ var init_rename = __esm(() => {
|
|
|
43468
43532
|
|
|
43469
43533
|
// src/rename-symbol.ts
|
|
43470
43534
|
import { existsSync as existsSync30, mkdirSync as mkdirSync17, readdirSync as readdirSync16, realpathSync as realpathSync4, rmSync as rmSync16, writeFileSync as writeFileSync18 } from "node:fs";
|
|
43471
|
-
import { basename as basename9, dirname as dirname21, join as
|
|
43535
|
+
import { basename as basename9, dirname as dirname21, join as join37, relative as relative7, resolve as resolve12 } from "node:path";
|
|
43472
43536
|
function resourcePathOf(projectPath, absolute) {
|
|
43473
43537
|
const inside = relative7(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
43474
43538
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -43481,7 +43545,7 @@ function onDiskSpelling(path) {
|
|
|
43481
43545
|
let at = resolve12(path);
|
|
43482
43546
|
for (;; ) {
|
|
43483
43547
|
try {
|
|
43484
|
-
return
|
|
43548
|
+
return join37(realpathSync4.native(at), ...missing.reverse());
|
|
43485
43549
|
} catch {
|
|
43486
43550
|
const parent = dirname21(at);
|
|
43487
43551
|
if (parent === at) {
|
|
@@ -43493,18 +43557,18 @@ function onDiskSpelling(path) {
|
|
|
43493
43557
|
}
|
|
43494
43558
|
}
|
|
43495
43559
|
function usedAsIdentifier(scripts, name, most) {
|
|
43496
|
-
const found =
|
|
43560
|
+
const found = new Set;
|
|
43497
43561
|
for (const script of scripts.values()) {
|
|
43498
43562
|
for (const occurrence of script.occurrences(name)) {
|
|
43499
43563
|
if (occurrence.kind === "code" && !occurrence.afterDot) {
|
|
43500
|
-
found.
|
|
43501
|
-
if (found.
|
|
43502
|
-
return found;
|
|
43564
|
+
found.add(`${script.path}:${script.lines.positionOf(occurrence.offset).line + 1}`);
|
|
43565
|
+
if (found.size >= most) {
|
|
43566
|
+
return [...found];
|
|
43503
43567
|
}
|
|
43504
43568
|
}
|
|
43505
43569
|
}
|
|
43506
43570
|
}
|
|
43507
|
-
return found;
|
|
43571
|
+
return [...found];
|
|
43508
43572
|
}
|
|
43509
43573
|
function autoloadNames(texts) {
|
|
43510
43574
|
const settings = texts.find((file) => file.path === "res://project.godot");
|
|
@@ -43568,7 +43632,7 @@ async function renameSymbol(request, services) {
|
|
|
43568
43632
|
conflicts.push(`${newName} is already an autoload in project.godot`);
|
|
43569
43633
|
}
|
|
43570
43634
|
for (const place of usedAsIdentifier(scripts, newName, 10)) {
|
|
43571
|
-
conflicts.push(`${newName} is already
|
|
43635
|
+
conflicts.push(`${newName} is already used in code at ${place}`);
|
|
43572
43636
|
}
|
|
43573
43637
|
const taken = (await services.namesTaken([newName], null))[newName];
|
|
43574
43638
|
if (taken !== undefined && taken.as.length > 0) {
|
|
@@ -43919,15 +43983,15 @@ ${mention.kind}`;
|
|
|
43919
43983
|
};
|
|
43920
43984
|
}
|
|
43921
43985
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
43922
|
-
const directory =
|
|
43986
|
+
const directory = join37(projectPath, ".godot", "gdharness-renames");
|
|
43923
43987
|
mkdirSync17(directory, { recursive: true });
|
|
43924
43988
|
detailWrites += 1;
|
|
43925
|
-
const path =
|
|
43989
|
+
const path = join37(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
43926
43990
|
writeFileSync18(path, `${JSON.stringify(detail, null, 2)}
|
|
43927
43991
|
`, "utf8");
|
|
43928
43992
|
const kept = readdirSync16(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
43929
43993
|
for (const old of kept.slice(10)) {
|
|
43930
|
-
rmSync16(
|
|
43994
|
+
rmSync16(join37(directory, old), { force: true });
|
|
43931
43995
|
}
|
|
43932
43996
|
return path;
|
|
43933
43997
|
}
|
|
@@ -43971,15 +44035,15 @@ import {
|
|
|
43971
44035
|
existsSync as existsSync31,
|
|
43972
44036
|
mkdirSync as mkdirSync18,
|
|
43973
44037
|
readdirSync as readdirSync17,
|
|
43974
|
-
readFileSync as
|
|
44038
|
+
readFileSync as readFileSync33,
|
|
43975
44039
|
renameSync as renameSync4,
|
|
43976
44040
|
statSync as statSync17,
|
|
43977
44041
|
writeFileSync as writeFileSync19
|
|
43978
44042
|
} from "node:fs";
|
|
43979
|
-
import { basename as basename10, dirname as dirname22, extname as extname3, join as
|
|
44043
|
+
import { basename as basename10, dirname as dirname22, extname as extname3, join as join38, relative as relative8 } from "node:path";
|
|
43980
44044
|
function filesUnder(directory) {
|
|
43981
44045
|
return readdirSync17(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
43982
|
-
const path =
|
|
44046
|
+
const path = join38(directory, entry.name);
|
|
43983
44047
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
43984
44048
|
});
|
|
43985
44049
|
}
|
|
@@ -44000,8 +44064,8 @@ function unseenByTheEngine(projectPath, path) {
|
|
|
44000
44064
|
return `${path} is under ${step}, and the engine passes over every directory whose name starts with a dot`;
|
|
44001
44065
|
}
|
|
44002
44066
|
if (at < steps.length - 1) {
|
|
44003
|
-
directory =
|
|
44004
|
-
if (existsSync31(
|
|
44067
|
+
directory = join38(directory, step);
|
|
44068
|
+
if (existsSync31(join38(directory, ".gdignore"))) {
|
|
44005
44069
|
return `${path} is under a directory holding a .gdignore, which the engine does not load`;
|
|
44006
44070
|
}
|
|
44007
44071
|
}
|
|
@@ -44013,12 +44077,12 @@ function installedAddonOf(projectPath, path) {
|
|
|
44013
44077
|
if (steps[0] !== "addons" || steps[1] === undefined) {
|
|
44014
44078
|
return null;
|
|
44015
44079
|
}
|
|
44016
|
-
return existsSync31(
|
|
44080
|
+
return existsSync31(join38(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
|
|
44017
44081
|
}
|
|
44018
44082
|
function binaryResources(projectPath) {
|
|
44019
44083
|
const found = [];
|
|
44020
44084
|
const visit = (directory, prefix) => {
|
|
44021
|
-
if (existsSync31(
|
|
44085
|
+
if (existsSync31(join38(directory, ".gdignore"))) {
|
|
44022
44086
|
return;
|
|
44023
44087
|
}
|
|
44024
44088
|
for (const entry of readdirSync17(directory, { withFileTypes: true })) {
|
|
@@ -44027,7 +44091,7 @@ function binaryResources(projectPath) {
|
|
|
44027
44091
|
}
|
|
44028
44092
|
const path = `${prefix}${entry.name}`;
|
|
44029
44093
|
if (entry.isDirectory()) {
|
|
44030
|
-
visit(
|
|
44094
|
+
visit(join38(directory, entry.name), `${path}/`);
|
|
44031
44095
|
} else if (/\.(scn|res)$/i.test(entry.name)) {
|
|
44032
44096
|
found.push(path);
|
|
44033
44097
|
}
|
|
@@ -44037,7 +44101,7 @@ function binaryResources(projectPath) {
|
|
|
44037
44101
|
return found;
|
|
44038
44102
|
}
|
|
44039
44103
|
function targetPathOf(projectPath, absolute) {
|
|
44040
|
-
const inside = relative8(onDiskSpelling(projectPath),
|
|
44104
|
+
const inside = relative8(onDiskSpelling(projectPath), join38(onDiskSpelling(dirname22(absolute)), basename10(absolute)));
|
|
44041
44105
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
44042
44106
|
return null;
|
|
44043
44107
|
}
|
|
@@ -44092,7 +44156,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
44092
44156
|
return { ok: false, reason: `${unseen}, so nothing there could load what was moved.` };
|
|
44093
44157
|
}
|
|
44094
44158
|
if (!isDirectory) {
|
|
44095
|
-
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync31(
|
|
44159
|
+
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync31(join38(projectPath, path.slice("res://".length))));
|
|
44096
44160
|
if (owner !== undefined && owner !== null) {
|
|
44097
44161
|
return { ok: false, reason: `${from} belongs to ${owner} and moves with it: move ${owner} instead.` };
|
|
44098
44162
|
}
|
|
@@ -44126,12 +44190,12 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
44126
44190
|
scripts,
|
|
44127
44191
|
moves,
|
|
44128
44192
|
named: [from],
|
|
44129
|
-
exists: (path) => existsSync31(
|
|
44193
|
+
exists: (path) => existsSync31(join38(projectPath, path.slice("res://".length)))
|
|
44130
44194
|
}, { edits: [], mentions: [] });
|
|
44131
44195
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
|
|
44132
44196
|
const binaries = [];
|
|
44133
44197
|
for (const path of binaryResources(projectPath)) {
|
|
44134
|
-
const bytes =
|
|
44198
|
+
const bytes = readFileSync33(join38(projectPath, path.slice("res://".length)));
|
|
44135
44199
|
const names = [...moves.keys()].find((moving) => bytes.includes(moving));
|
|
44136
44200
|
if (names !== undefined) {
|
|
44137
44201
|
binaries.push({
|
|
@@ -44150,7 +44214,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
44150
44214
|
file: afterwards(mention.file)
|
|
44151
44215
|
}));
|
|
44152
44216
|
const left = leftAloneReport(mentions);
|
|
44153
|
-
const absoluteOf = (path) =>
|
|
44217
|
+
const absoluteOf = (path) => join38(projectPath, path.slice("res://".length));
|
|
44154
44218
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
44155
44219
|
const moved = [...moves].map(([source, target]) => {
|
|
44156
44220
|
const rewrite = rewritten.get(source);
|
|
@@ -44192,7 +44256,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
44192
44256
|
}
|
|
44193
44257
|
function applyFileMove(outcome) {
|
|
44194
44258
|
for (const rewrite of outcome.inPlace) {
|
|
44195
|
-
if (
|
|
44259
|
+
if (readFileSync33(rewrite.absolute, "utf8") !== rewrite.before) {
|
|
44196
44260
|
throw new Error(`${rewrite.path} changed on disk while the move was being worked out; nothing was written`);
|
|
44197
44261
|
}
|
|
44198
44262
|
}
|
|
@@ -44200,7 +44264,7 @@ function applyFileMove(outcome) {
|
|
|
44200
44264
|
if (!existsSync31(file.absoluteFrom)) {
|
|
44201
44265
|
throw new Error(`${file.from} is gone from disk; nothing was written`);
|
|
44202
44266
|
}
|
|
44203
|
-
if (file.text !== null &&
|
|
44267
|
+
if (file.text !== null && readFileSync33(file.absoluteFrom, "utf8") !== file.text.before) {
|
|
44204
44268
|
throw new Error(`${file.from} changed on disk while the move was being worked out; nothing was written`);
|
|
44205
44269
|
}
|
|
44206
44270
|
if (existsSync31(file.absoluteTo) && !sameFile(file.absoluteFrom, file.absoluteTo)) {
|
|
@@ -47941,10 +48005,10 @@ var init_keeper_channel = () => {};
|
|
|
47941
48005
|
|
|
47942
48006
|
// src/lifecycle-log.ts
|
|
47943
48007
|
import { appendFileSync, mkdirSync as mkdirSync19, renameSync as renameSync5, statSync as statSync18 } from "node:fs";
|
|
47944
|
-
import { dirname as dirname23, join as
|
|
48008
|
+
import { dirname as dirname23, join as join39 } from "node:path";
|
|
47945
48009
|
import process16 from "node:process";
|
|
47946
48010
|
function lifecycleLogPath() {
|
|
47947
|
-
return
|
|
48011
|
+
return join39(runtimeDirectory(), "servers.log");
|
|
47948
48012
|
}
|
|
47949
48013
|
function writeLifecycle(event, fields = {}) {
|
|
47950
48014
|
const path = lifecycleLogPath();
|
|
@@ -48314,10 +48378,10 @@ var init_prompts = __esm(() => {
|
|
|
48314
48378
|
});
|
|
48315
48379
|
|
|
48316
48380
|
// src/restart-note.ts
|
|
48317
|
-
import { mkdirSync as mkdirSync20, readFileSync as
|
|
48318
|
-
import { dirname as dirname24, join as
|
|
48381
|
+
import { mkdirSync as mkdirSync20, readFileSync as readFileSync34, rmSync as rmSync17, writeFileSync as writeFileSync20 } from "node:fs";
|
|
48382
|
+
import { dirname as dirname24, join as join40 } from "node:path";
|
|
48319
48383
|
function restartNotePath(projectPath) {
|
|
48320
|
-
return
|
|
48384
|
+
return join40(projectPath, ".godot", "gdharness-restart.json");
|
|
48321
48385
|
}
|
|
48322
48386
|
function noteRestartBegun(note) {
|
|
48323
48387
|
const path = restartNotePath(note.projectPath);
|
|
@@ -48331,7 +48395,7 @@ function noteRestartBegun(note) {
|
|
|
48331
48395
|
}
|
|
48332
48396
|
function restartOwed(projectPath) {
|
|
48333
48397
|
try {
|
|
48334
|
-
const parsed = JSON.parse(
|
|
48398
|
+
const parsed = JSON.parse(readFileSync34(restartNotePath(projectPath), "utf8"));
|
|
48335
48399
|
if (typeof parsed !== "object" || parsed === null) {
|
|
48336
48400
|
return null;
|
|
48337
48401
|
}
|
|
@@ -48357,10 +48421,10 @@ var init_restart_note = __esm(() => {
|
|
|
48357
48421
|
|
|
48358
48422
|
// src/run-record.ts
|
|
48359
48423
|
import { createHash as createHash7 } from "node:crypto";
|
|
48360
|
-
import { mkdirSync as mkdirSync21, openSync as openSync5, readdirSync as readdirSync18, readFileSync as
|
|
48361
|
-
import { basename as basename11, join as
|
|
48424
|
+
import { mkdirSync as mkdirSync21, openSync as openSync5, readdirSync as readdirSync18, readFileSync as readFileSync35, rmSync as rmSync18, statSync as statSync19 } from "node:fs";
|
|
48425
|
+
import { basename as basename11, join as join41, resolve as resolve13 } from "node:path";
|
|
48362
48426
|
function runsDirectory() {
|
|
48363
|
-
return
|
|
48427
|
+
return join41(runtimeDirectory(), "runs");
|
|
48364
48428
|
}
|
|
48365
48429
|
function projectKey(projectPath) {
|
|
48366
48430
|
const real = realAsFarAsItExists(resolve13(projectPath)).replace(/[\\/]+$/, "");
|
|
@@ -48368,10 +48432,10 @@ function projectKey(projectPath) {
|
|
|
48368
48432
|
return createHash7("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
48369
48433
|
}
|
|
48370
48434
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
48371
|
-
return
|
|
48435
|
+
return join41(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
48372
48436
|
}
|
|
48373
48437
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
48374
|
-
return
|
|
48438
|
+
return join41(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
48375
48439
|
}
|
|
48376
48440
|
function writeLatestRun(servedBy, latest) {
|
|
48377
48441
|
try {
|
|
@@ -48381,7 +48445,7 @@ function writeLatestRun(servedBy, latest) {
|
|
|
48381
48445
|
}
|
|
48382
48446
|
function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
48383
48447
|
try {
|
|
48384
|
-
const fields = JSON.parse(
|
|
48448
|
+
const fields = JSON.parse(readFileSync35(latestRunPath(servedBy, directory), "utf8"));
|
|
48385
48449
|
if (typeof fields !== "object" || fields === null) {
|
|
48386
48450
|
return null;
|
|
48387
48451
|
}
|
|
@@ -48396,20 +48460,20 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
48396
48460
|
}
|
|
48397
48461
|
function openTranscript(startedAt) {
|
|
48398
48462
|
mkdirSync21(runsDirectory(), { recursive: true });
|
|
48399
|
-
const path =
|
|
48463
|
+
const path = join41(runsDirectory(), `run-${startedAt}.log`);
|
|
48400
48464
|
return { path, fd: openSync5(path, "a") };
|
|
48401
48465
|
}
|
|
48402
48466
|
function profilePath(startedAt) {
|
|
48403
|
-
return
|
|
48467
|
+
return join41(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
48404
48468
|
}
|
|
48405
48469
|
function profileMarkPath(startedAt) {
|
|
48406
|
-
return
|
|
48470
|
+
return join41(runsDirectory(), `run-${startedAt}.profile-mark.json`);
|
|
48407
48471
|
}
|
|
48408
48472
|
function noteIsAbout(note, playing) {
|
|
48409
48473
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
48410
48474
|
}
|
|
48411
48475
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
48412
|
-
return
|
|
48476
|
+
return join41(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
48413
48477
|
}
|
|
48414
48478
|
function writeEditorRunNote(note) {
|
|
48415
48479
|
mkdirSync21(runsDirectory(), { recursive: true });
|
|
@@ -48430,7 +48494,7 @@ function readEditorRunNote(projectPath) {
|
|
|
48430
48494
|
function editorNoteAt(path) {
|
|
48431
48495
|
let parsed;
|
|
48432
48496
|
try {
|
|
48433
|
-
parsed = JSON.parse(
|
|
48497
|
+
parsed = JSON.parse(readFileSync35(path, "utf8"));
|
|
48434
48498
|
} catch {
|
|
48435
48499
|
return null;
|
|
48436
48500
|
}
|
|
@@ -48462,13 +48526,13 @@ function everyRunRecord() {
|
|
|
48462
48526
|
for (const directory of runtimeDirectories()) {
|
|
48463
48527
|
let entries;
|
|
48464
48528
|
try {
|
|
48465
|
-
entries = readdirSync18(
|
|
48529
|
+
entries = readdirSync18(join41(directory, "runs"));
|
|
48466
48530
|
} catch {
|
|
48467
48531
|
continue;
|
|
48468
48532
|
}
|
|
48469
48533
|
for (const entry of entries) {
|
|
48470
48534
|
if (entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
48471
|
-
const record = recordAt(
|
|
48535
|
+
const record = recordAt(join41(directory, "runs", entry));
|
|
48472
48536
|
if (record !== null) {
|
|
48473
48537
|
found.push(record);
|
|
48474
48538
|
}
|
|
@@ -48480,7 +48544,7 @@ function everyRunRecord() {
|
|
|
48480
48544
|
function recordAt(path) {
|
|
48481
48545
|
let parsed;
|
|
48482
48546
|
try {
|
|
48483
|
-
parsed = JSON.parse(
|
|
48547
|
+
parsed = JSON.parse(readFileSync35(path, "utf8"));
|
|
48484
48548
|
} catch {
|
|
48485
48549
|
return null;
|
|
48486
48550
|
}
|
|
@@ -48561,7 +48625,7 @@ async function runningAs(pid) {
|
|
|
48561
48625
|
const began = await startedAt(pid, null);
|
|
48562
48626
|
const when = began === null ? {} : { startedAt: began };
|
|
48563
48627
|
if (process.platform === "linux") {
|
|
48564
|
-
const raw =
|
|
48628
|
+
const raw = readFileSync35(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
48565
48629
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
48566
48630
|
}
|
|
48567
48631
|
const args = (await runHidden("ps", ["-p", String(pid), "-o", "args="], { timeout: 15000 })).stdout.trim();
|
|
@@ -48736,7 +48800,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
48736
48800
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
48737
48801
|
continue;
|
|
48738
48802
|
}
|
|
48739
|
-
const path =
|
|
48803
|
+
const path = join41(directory, entry);
|
|
48740
48804
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
48741
48805
|
continue;
|
|
48742
48806
|
}
|
|
@@ -48757,10 +48821,10 @@ var init_run_record = __esm(() => {
|
|
|
48757
48821
|
});
|
|
48758
48822
|
|
|
48759
48823
|
// src/script-profile.ts
|
|
48760
|
-
import { readFileSync as
|
|
48824
|
+
import { readFileSync as readFileSync36 } from "node:fs";
|
|
48761
48825
|
function readProfileFile(path) {
|
|
48762
48826
|
try {
|
|
48763
|
-
const parsed = JSON.parse(
|
|
48827
|
+
const parsed = JSON.parse(readFileSync36(path, "utf8"));
|
|
48764
48828
|
return typeof parsed === "object" && typeof parsed.connected === "boolean" ? parsed : null;
|
|
48765
48829
|
} catch {
|
|
48766
48830
|
return null;
|
|
@@ -48818,7 +48882,7 @@ function writeProfileMark(path, mark) {
|
|
|
48818
48882
|
}
|
|
48819
48883
|
function readProfileMark(path) {
|
|
48820
48884
|
try {
|
|
48821
|
-
const parsed = JSON.parse(
|
|
48885
|
+
const parsed = JSON.parse(readFileSync36(path, "utf8"));
|
|
48822
48886
|
return typeof parsed === "object" && typeof parsed.totals === "object" ? parsed : null;
|
|
48823
48887
|
} catch {
|
|
48824
48888
|
return null;
|
|
@@ -48992,16 +49056,16 @@ var init_server_instructions = __esm(() => {
|
|
|
48992
49056
|
|
|
48993
49057
|
// src/suites-scene.ts
|
|
48994
49058
|
import { existsSync as existsSync32, mkdirSync as mkdirSync22, writeFileSync as writeFileSync21 } from "node:fs";
|
|
48995
|
-
import { join as
|
|
49059
|
+
import { join as join42, relative as relative9, sep as sep5 } from "node:path";
|
|
48996
49060
|
function hasSuitesScene(projectPath) {
|
|
48997
|
-
return existsSync32(
|
|
49061
|
+
return existsSync32(join42(projectPath, SUITES_SCENE));
|
|
48998
49062
|
}
|
|
48999
49063
|
function suitesSceneArgs(projectPath, suites) {
|
|
49000
|
-
const runs =
|
|
49064
|
+
const runs = join42(projectPath, ".godot", "gdharness", "suite-scenes");
|
|
49001
49065
|
sweepRunDirectories(runs);
|
|
49002
|
-
const directory =
|
|
49066
|
+
const directory = join42(runs, `${String(STARTED_AT)}-${String(process.pid)}`);
|
|
49003
49067
|
mkdirSync22(directory, { recursive: true });
|
|
49004
|
-
const suitesFile =
|
|
49068
|
+
const suitesFile = join42(directory, "suites.txt");
|
|
49005
49069
|
writeFileSync21(suitesFile, `${suites.join(`
|
|
49006
49070
|
`)}
|
|
49007
49071
|
`);
|
|
@@ -49016,7 +49080,7 @@ function suitesSceneArgs(projectPath, suites) {
|
|
|
49016
49080
|
"-c",
|
|
49017
49081
|
"--ignoreHeadlessMode",
|
|
49018
49082
|
"-rd",
|
|
49019
|
-
`res://${relative9(projectPath,
|
|
49083
|
+
`res://${relative9(projectPath, join42(directory, "reports")).split(sep5).join("/")}`,
|
|
49020
49084
|
"-rc",
|
|
49021
49085
|
"1"
|
|
49022
49086
|
];
|
|
@@ -49030,7 +49094,7 @@ var init_suites_scene = __esm(() => {
|
|
|
49030
49094
|
|
|
49031
49095
|
// src/test-selection.ts
|
|
49032
49096
|
import { existsSync as existsSync33 } from "node:fs";
|
|
49033
|
-
import { basename as basename12, join as
|
|
49097
|
+
import { basename as basename12, join as join43 } from "node:path";
|
|
49034
49098
|
function casesOutOfPath(projectPath, given, tests) {
|
|
49035
49099
|
const entries = Array.isArray(given) ? given : [given];
|
|
49036
49100
|
const cases = [];
|
|
@@ -49074,12 +49138,12 @@ function casesIn(scripts, suite) {
|
|
|
49074
49138
|
}
|
|
49075
49139
|
function scriptsWithoutCases(projectPath, asked) {
|
|
49076
49140
|
const scripts = new ProjectScripts(projectPath);
|
|
49077
|
-
return asked.filter((path) => path.endsWith(".gd") && existsSync33(
|
|
49141
|
+
return asked.filter((path) => path.endsWith(".gd") && existsSync33(join43(projectPath, path.slice("res://".length))) && casesIn(scripts, path).length === 0);
|
|
49078
49142
|
}
|
|
49079
49143
|
function suitesUnder(projectPath, asked) {
|
|
49080
49144
|
const scripts = projectFiles(projectPath, [".gd"]).map((path) => `res://${path}`);
|
|
49081
49145
|
return [
|
|
49082
|
-
...new Set(asked.flatMap((path) => path.endsWith(".gd") ? existsSync33(
|
|
49146
|
+
...new Set(asked.flatMap((path) => path.endsWith(".gd") ? existsSync33(join43(projectPath, path.slice("res://".length))) ? [path] : [] : scripts.filter((script) => script.startsWith(`${path}/`))))
|
|
49083
49147
|
];
|
|
49084
49148
|
}
|
|
49085
49149
|
function selectTests(projectPath, asked, given) {
|
|
@@ -49150,14 +49214,14 @@ import {
|
|
|
49150
49214
|
mkdirSync as mkdirSync23,
|
|
49151
49215
|
openSync as openSync6,
|
|
49152
49216
|
readdirSync as readdirSync19,
|
|
49153
|
-
readFileSync as
|
|
49217
|
+
readFileSync as readFileSync37,
|
|
49154
49218
|
readSync as readSync3,
|
|
49155
49219
|
realpathSync as realpathSync5,
|
|
49156
49220
|
rmdirSync as rmdirSync2,
|
|
49157
49221
|
statSync as statSync20,
|
|
49158
49222
|
writeFileSync as writeFileSync22
|
|
49159
49223
|
} from "node:fs";
|
|
49160
|
-
import { basename as basename13, dirname as dirname25, extname as extname4, isAbsolute as isAbsolute8, join as
|
|
49224
|
+
import { basename as basename13, dirname as dirname25, extname as extname4, isAbsolute as isAbsolute8, join as join44, normalize as normalize2, relative as relative10 } from "node:path";
|
|
49161
49225
|
import { setTimeout as delay7 } from "node:timers/promises";
|
|
49162
49226
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
49163
49227
|
function editorRestartTimeoutMs() {
|
|
@@ -49171,9 +49235,9 @@ function greetingWaitMs() {
|
|
|
49171
49235
|
return waitFromEnv("GDHARNESS_GREETING_WAIT_MS", 1e4);
|
|
49172
49236
|
}
|
|
49173
49237
|
function pictureToSend(pngPath, jpegPath) {
|
|
49174
|
-
const png =
|
|
49238
|
+
const png = readFileSync37(pngPath);
|
|
49175
49239
|
const beside = pngPath.replace(/\.png$/, ".jpg");
|
|
49176
|
-
const jpeg = jpegPath !== undefined && normalize2(jpegPath) === normalize2(beside) && existsSync34(beside) ?
|
|
49240
|
+
const jpeg = jpegPath !== undefined && normalize2(jpegPath) === normalize2(beside) && existsSync34(beside) ? readFileSync37(beside) : null;
|
|
49177
49241
|
const sent = jpeg !== null && jpeg.length < png.length ? jpeg : png;
|
|
49178
49242
|
return {
|
|
49179
49243
|
data: sent.toString("base64"),
|
|
@@ -49216,7 +49280,7 @@ function writtenFileRefusal(path, rule) {
|
|
|
49216
49280
|
if (!existsSync34(directory)) {
|
|
49217
49281
|
return `${named}'s directory ${dirname25(path)} does not exist; ${what} creates no directories.`;
|
|
49218
49282
|
}
|
|
49219
|
-
const where =
|
|
49283
|
+
const where = join44(directory, basename13(path));
|
|
49220
49284
|
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realAsFarAsItExists(project), where));
|
|
49221
49285
|
if (inside !== undefined) {
|
|
49222
49286
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
@@ -49652,7 +49716,7 @@ function endedToStartThis(ended) {
|
|
|
49652
49716
|
return ` ${which} was ended to start this one; its output is no longer what editor_output answers about.`;
|
|
49653
49717
|
}
|
|
49654
49718
|
function lineNothingStopsOn(absolutePath, relativePath, line) {
|
|
49655
|
-
const text =
|
|
49719
|
+
const text = readFileSync37(absolutePath, "utf8");
|
|
49656
49720
|
const lines = text === "" ? 0 : text.split(`
|
|
49657
49721
|
`).length - (text.endsWith(`
|
|
49658
49722
|
`) ? 1 : 0);
|
|
@@ -49863,7 +49927,7 @@ function notRunNote(notRun, failFast) {
|
|
|
49863
49927
|
function presetsIn(projectPath) {
|
|
49864
49928
|
let text;
|
|
49865
49929
|
try {
|
|
49866
|
-
text =
|
|
49930
|
+
text = readFileSync37(join44(projectPath, "export_presets.cfg"), "utf8");
|
|
49867
49931
|
} catch {
|
|
49868
49932
|
return null;
|
|
49869
49933
|
}
|
|
@@ -49944,7 +50008,7 @@ function filesIn(folder) {
|
|
|
49944
50008
|
try {
|
|
49945
50009
|
for (const entry of readdirSync19(folder, { withFileTypes: true })) {
|
|
49946
50010
|
if (entry.isFile()) {
|
|
49947
|
-
found.set(entry.name, statSync20(
|
|
50011
|
+
found.set(entry.name, statSync20(join44(folder, entry.name)).mtimeMs);
|
|
49948
50012
|
}
|
|
49949
50013
|
}
|
|
49950
50014
|
} catch {}
|
|
@@ -49984,7 +50048,7 @@ function unsavedInEditorNote(scripts) {
|
|
|
49984
50048
|
return `${named} ${one ? "is" : "are"} open in the editor's script ${one ? "tab" : "tabs"} with unsaved edits, and saving ${one ? "that tab" : "them"} there writes ${one ? "its" : "their"} text over what was written here: close ${one ? "it" : "them"} without saving, or save first and edit again.`;
|
|
49985
50049
|
}
|
|
49986
50050
|
function editorAddonMissing(projectPath) {
|
|
49987
|
-
if (!existsSync34(
|
|
50051
|
+
if (!existsSync34(join44(projectPath, "project.godot"))) {
|
|
49988
50052
|
return null;
|
|
49989
50053
|
}
|
|
49990
50054
|
const report = inspectProject(projectPath);
|
|
@@ -50012,8 +50076,8 @@ function rewriteBusNamed(projectPath, from, to) {
|
|
|
50012
50076
|
const giving = [`bus = &"${quoted(from)}"`, `bus = "${quoted(from)}"`];
|
|
50013
50077
|
const rewritten = [];
|
|
50014
50078
|
for (const file of projectFiles(projectPath, [".tscn", ".tres"])) {
|
|
50015
|
-
const absolute =
|
|
50016
|
-
const lines =
|
|
50079
|
+
const absolute = join44(projectPath, file);
|
|
50080
|
+
const lines = readFileSync37(absolute, "utf8").split(`
|
|
50017
50081
|
`);
|
|
50018
50082
|
const at = [];
|
|
50019
50083
|
const after = lines.map((line, index) => {
|
|
@@ -50032,7 +50096,7 @@ function rewriteBusNamed(projectPath, from, to) {
|
|
|
50032
50096
|
}
|
|
50033
50097
|
const namedInScripts = [];
|
|
50034
50098
|
for (const file of projectFiles(projectPath, [".gd"])) {
|
|
50035
|
-
|
|
50099
|
+
readFileSync37(join44(projectPath, file), "utf8").split(`
|
|
50036
50100
|
`).forEach((line, index) => {
|
|
50037
50101
|
if (line.includes(`"${from}"`)) {
|
|
50038
50102
|
namedInScripts.push({ file: `res://${file}`, line: index + 1, text: line.trim() });
|
|
@@ -50317,7 +50381,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
50317
50381
|
let projectGodot = null;
|
|
50318
50382
|
if (projectPath !== null) {
|
|
50319
50383
|
try {
|
|
50320
|
-
projectGodot =
|
|
50384
|
+
projectGodot = readFileSync37(join44(projectPath, "project.godot"), "utf8");
|
|
50321
50385
|
} catch {}
|
|
50322
50386
|
}
|
|
50323
50387
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -50341,7 +50405,7 @@ function projectSourceOf(projectPath) {
|
|
|
50341
50405
|
let source = null;
|
|
50342
50406
|
if (inside.ok) {
|
|
50343
50407
|
try {
|
|
50344
|
-
source =
|
|
50408
|
+
source = readFileSync37(inside.absolutePath, "utf8");
|
|
50345
50409
|
} catch {}
|
|
50346
50410
|
}
|
|
50347
50411
|
read.set(script, source);
|
|
@@ -50513,7 +50577,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
50513
50577
|
}
|
|
50514
50578
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
50515
50579
|
try {
|
|
50516
|
-
return settingKeys(
|
|
50580
|
+
return settingKeys(readFileSync37(join44(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
50517
50581
|
} catch {
|
|
50518
50582
|
return true;
|
|
50519
50583
|
}
|
|
@@ -50528,7 +50592,7 @@ function uncaughtNote(uncaught) {
|
|
|
50528
50592
|
}
|
|
50529
50593
|
function elsewhereIn(projectPath, asked) {
|
|
50530
50594
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
50531
|
-
if (wanted === "" || existsSync34(
|
|
50595
|
+
if (wanted === "" || existsSync34(join44(projectPath, wanted))) {
|
|
50532
50596
|
return "";
|
|
50533
50597
|
}
|
|
50534
50598
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -50551,7 +50615,7 @@ function windowNoteFor(needing, sharing) {
|
|
|
50551
50615
|
return `${named(needing)} ${one ? "needs" : "need"} a window, as gdharness test reads ${one ? "it" : "them"}, so the run had one: the engine ran without --headless, at the size project.godot gives its viewport, with no sound${process.platform === "win32" ? ", on a desktop of its own, where its window neither shows nor takes the keyboard" : ""}. ${why}${shared}`;
|
|
50552
50616
|
}
|
|
50553
50617
|
function testPathsIn(projectPath, given, named = "path") {
|
|
50554
|
-
const defaults = TEST_DIRECTORIES.filter((directory) => existsSync34(
|
|
50618
|
+
const defaults = TEST_DIRECTORIES.filter((directory) => existsSync34(join44(projectPath, directory)));
|
|
50555
50619
|
const entries = given === undefined || given === null || given === "" ? defaults.length > 0 ? defaults : TEST_DIRECTORIES : given;
|
|
50556
50620
|
const listed = Array.isArray(entries) ? entries : [entries];
|
|
50557
50621
|
if (listed.length === 0) {
|
|
@@ -50645,7 +50709,7 @@ function engineChecked(why) {
|
|
|
50645
50709
|
return {
|
|
50646
50710
|
checkedBy: "engine",
|
|
50647
50711
|
languageServer: why,
|
|
50648
|
-
note: "No editor of this project is open to ask, so a headless engine compiled the scripts the way loading them does. It reports every error that stops a script compiling, at its line and without a column, but stops reading a script at its first syntax error, so an error after one shows once it is fixed. A second engine reports the warnings, at the levels the project sets, by raising each warning set to warn as an error for that run only, since the engine tells a warning as one only to an editor; each comes at its line without a column, and warnings is null when that engine could not answer. editor_launch with hidden: true opens this project's editor without a window, and its language server reports both with their columns."
|
|
50712
|
+
note: "No editor of this project is open to ask, so a headless engine compiled the scripts the way loading them does. It reports every error that stops a script compiling, at its line and without a column, but stops reading a script at its first syntax error, so an error after one shows once it is fixed, and so do its warnings, since a script that does not parse is not checked for any. A second engine reports the warnings, at the levels the project sets, by raising each warning set to warn as an error for that run only, since the engine tells a warning as one only to an editor; each comes at its line without a column, and warnings is null when that engine could not answer. editor_launch with hidden: true opens this project's editor without a window, and its language server reports both with their columns."
|
|
50649
50713
|
};
|
|
50650
50714
|
}
|
|
50651
50715
|
function asWarning(raised) {
|
|
@@ -50660,11 +50724,11 @@ function isDirectory(path) {
|
|
|
50660
50724
|
}
|
|
50661
50725
|
}
|
|
50662
50726
|
function mainSceneOf(projectFile) {
|
|
50663
|
-
const scene = parseProjectGodot(
|
|
50727
|
+
const scene = parseProjectGodot(readFileSync37(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
50664
50728
|
return typeof scene === "string" && scene !== "" ? scene : null;
|
|
50665
50729
|
}
|
|
50666
50730
|
function editorPlaysHeadless(projectFile) {
|
|
50667
|
-
const runArgs = parseProjectGodot(
|
|
50731
|
+
const runArgs = parseProjectGodot(readFileSync37(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
50668
50732
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
50669
50733
|
}
|
|
50670
50734
|
function ownArguments(params) {
|
|
@@ -50682,7 +50746,7 @@ function needsItsOwnProcess(asked) {
|
|
|
50682
50746
|
class GodotServer {
|
|
50683
50747
|
stdio = null;
|
|
50684
50748
|
locator = new GodotLocator;
|
|
50685
|
-
operationsScript =
|
|
50749
|
+
operationsScript = join44(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
50686
50750
|
exportOptionsRead = new Map;
|
|
50687
50751
|
heldWhilePending = new Set;
|
|
50688
50752
|
evaluatedWhilePending = new Set;
|
|
@@ -50954,7 +51018,7 @@ class GodotServer {
|
|
|
50954
51018
|
}
|
|
50955
51019
|
noteTheStart() {
|
|
50956
51020
|
const cwd = process.cwd();
|
|
50957
|
-
const mcpJson =
|
|
51021
|
+
const mcpJson = join44(cwd, ".mcp.json");
|
|
50958
51022
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
50959
51023
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
50960
51024
|
writeLifecycle("started", {
|
|
@@ -51102,7 +51166,7 @@ class GodotServer {
|
|
|
51102
51166
|
return checked.response;
|
|
51103
51167
|
}
|
|
51104
51168
|
const named = args["projectPath"];
|
|
51105
|
-
if (typeof named === "string" && existsSync34(
|
|
51169
|
+
if (typeof named === "string" && existsSync34(join44(named, "project.godot"))) {
|
|
51106
51170
|
this.lastProjectPath = named;
|
|
51107
51171
|
}
|
|
51108
51172
|
this.updates.refresh();
|
|
@@ -51117,12 +51181,16 @@ class GodotServer {
|
|
|
51117
51181
|
};
|
|
51118
51182
|
const call = this.callsTaken++;
|
|
51119
51183
|
this.inFlight.set(call, { tool: spec.name, op: checked.op ?? "", startedAt: Date.now() });
|
|
51184
|
+
const slotWaits = { ms: 0, said: undefined };
|
|
51120
51185
|
try {
|
|
51121
51186
|
const answer = await withCallSignal(ctx.mcpReq.signal, () => this.answered(spec.name, checked.op ?? "", args), progress, {
|
|
51122
51187
|
everyMs: CALL_HEARTBEAT_MS,
|
|
51123
51188
|
said: (elapsedMs) => stillGoingSaid(spec.name, checked.op, elapsedMs)
|
|
51189
|
+
}, slotWaits);
|
|
51190
|
+
const waited = slotWaits.said === undefined || slotWaits.ms < 1000 ? answer : this.saying(answer, {
|
|
51191
|
+
engine_slot_wait: { waitedMs: slotWaits.ms, note: slotWaits.said(slotWaits.ms) }
|
|
51124
51192
|
});
|
|
51125
|
-
return this.withinTheClientsLimit(spec.name, this.withFeedbackNotice(this.withUpdateNotice(
|
|
51193
|
+
return this.withinTheClientsLimit(spec.name, this.withFeedbackNotice(this.withUpdateNotice(waited)));
|
|
51126
51194
|
} finally {
|
|
51127
51195
|
this.inFlight.delete(call);
|
|
51128
51196
|
}
|
|
@@ -51141,7 +51209,7 @@ class GodotServer {
|
|
|
51141
51209
|
let notSaved;
|
|
51142
51210
|
try {
|
|
51143
51211
|
savedAt = answer.content.map((item, index) => {
|
|
51144
|
-
const file =
|
|
51212
|
+
const file = join44(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
|
|
51145
51213
|
writeFileSync22(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
|
|
51146
51214
|
return file;
|
|
51147
51215
|
});
|
|
@@ -51801,6 +51869,10 @@ class GodotServer {
|
|
|
51801
51869
|
if (kept !== null) {
|
|
51802
51870
|
return await this.askTheKeptGame(kept.keeper, keptGameRequest(tool, op, args), kept.projectPath ?? "");
|
|
51803
51871
|
}
|
|
51872
|
+
const last = this.activeProcess;
|
|
51873
|
+
if (tool === "debug_state" && op === "output" && last !== null && !last.throughEditor && last.keeper !== undefined) {
|
|
51874
|
+
return this.createErrorResponse("The game this server started has ended, and its debugger, which held its console output, ended with it.", ["editor_output reads what the game printed, with the errors and warnings it reported"]);
|
|
51875
|
+
}
|
|
51804
51876
|
if (tool === "debug_state" && op === "output") {
|
|
51805
51877
|
return await this.handleDAP("dap_get_output", args);
|
|
51806
51878
|
}
|
|
@@ -51834,7 +51906,7 @@ class GodotServer {
|
|
|
51834
51906
|
if (path === undefined) {
|
|
51835
51907
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
51836
51908
|
}
|
|
51837
|
-
const file =
|
|
51909
|
+
const file = join44(path, "project.godot");
|
|
51838
51910
|
if (!existsSync34(file)) {
|
|
51839
51911
|
return {
|
|
51840
51912
|
ok: false,
|
|
@@ -52579,7 +52651,7 @@ class GodotServer {
|
|
|
52579
52651
|
if (!engine.ok) {
|
|
52580
52652
|
return engine.response;
|
|
52581
52653
|
}
|
|
52582
|
-
const application = parseProjectGodot(
|
|
52654
|
+
const application = parseProjectGodot(readFileSync37(project.value.file, "utf8"))["application"] ?? {};
|
|
52583
52655
|
const name = application["config/name"];
|
|
52584
52656
|
const mainScene = application["run/main_scene"];
|
|
52585
52657
|
const info = {
|
|
@@ -52716,7 +52788,7 @@ class GodotServer {
|
|
|
52716
52788
|
const platform = presetPlatform(project.value.path, preset);
|
|
52717
52789
|
if (platform !== null) {
|
|
52718
52790
|
const known = await this.exportOptions(platform, {
|
|
52719
|
-
presetsFile:
|
|
52791
|
+
presetsFile: join44(project.value.path, "export_presets.cfg"),
|
|
52720
52792
|
preset,
|
|
52721
52793
|
asked: options
|
|
52722
52794
|
});
|
|
@@ -52767,7 +52839,7 @@ ${platform}`;
|
|
|
52767
52839
|
}
|
|
52768
52840
|
const empty = scratchDirectory("export");
|
|
52769
52841
|
try {
|
|
52770
|
-
writeFileSync22(
|
|
52842
|
+
writeFileSync22(join44(empty, "project.godot"), `config_version=5
|
|
52771
52843
|
`);
|
|
52772
52844
|
const outcome = await runOperation({ godotPath: engine.value, script: this.operationsScript, debug: DEBUG_MODE, editor: true }, "export_options", { platform, ...standing }, empty);
|
|
52773
52845
|
const options = outcome.ok ? outcome.payload["options"] : undefined;
|
|
@@ -52823,7 +52895,7 @@ ${platform}`;
|
|
|
52823
52895
|
const exportArgs = [
|
|
52824
52896
|
"--headless",
|
|
52825
52897
|
"--log-file",
|
|
52826
|
-
|
|
52898
|
+
join44(exportLogs, "engine.log"),
|
|
52827
52899
|
"--path",
|
|
52828
52900
|
project.value.path,
|
|
52829
52901
|
pack ? "--export-pack" : debug ? "--export-debug" : "--export-release",
|
|
@@ -52922,7 +52994,7 @@ ${platform}`;
|
|
|
52922
52994
|
if (movie !== null) {
|
|
52923
52995
|
return this.createErrorResponse(movie);
|
|
52924
52996
|
}
|
|
52925
|
-
const missing = paths.value.filter((path) => !existsSync34(
|
|
52997
|
+
const missing = paths.value.filter((path) => !existsSync34(join44(project.value.path, path.slice("res://".length))));
|
|
52926
52998
|
const projectScripts = new ProjectScripts(project.value.path);
|
|
52927
52999
|
const present = paths.value.filter((path) => !missing.includes(path));
|
|
52928
53000
|
const asked = projectScripts.searchedAsTold(present);
|
|
@@ -53014,10 +53086,10 @@ ${platform}`;
|
|
|
53014
53086
|
const ignored = readStringArray(args, "ignore") ?? [];
|
|
53015
53087
|
const declaredUnder = ignored.length === 0 ? [] : asked.flatMap((path) => path.endsWith(".gd") ? projectScripts.suite(path) ?? [] : projectScripts.suitesUnder([path]));
|
|
53016
53088
|
const userData = scratchDirectory("tests");
|
|
53017
|
-
const suitesFile =
|
|
53018
|
-
const skippingFile =
|
|
53019
|
-
const engineLog =
|
|
53020
|
-
const expectedFile =
|
|
53089
|
+
const suitesFile = join44(userData, "suites.txt");
|
|
53090
|
+
const skippingFile = join44(userData, "skipping.txt");
|
|
53091
|
+
const engineLog = join44(userData, "engine.log");
|
|
53092
|
+
const expectedFile = join44(userData, "expected.json");
|
|
53021
53093
|
writeFileSync22(suitesFile, `${(selection.value?.suites ?? asked).join(`
|
|
53022
53094
|
`)}
|
|
53023
53095
|
`);
|
|
@@ -53058,11 +53130,11 @@ ${platform}`;
|
|
|
53058
53130
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? TEST_TIMEOUT_MS;
|
|
53059
53131
|
this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
53060
53132
|
const helperFiles = {
|
|
53061
|
-
spec:
|
|
53062
|
-
pid:
|
|
53063
|
-
error:
|
|
53064
|
-
stop:
|
|
53065
|
-
step:
|
|
53133
|
+
spec: join44(userData, "spec"),
|
|
53134
|
+
pid: join44(userData, "pid"),
|
|
53135
|
+
error: join44(userData, "error"),
|
|
53136
|
+
stop: join44(userData, "stop"),
|
|
53137
|
+
step: join44(userData, "step")
|
|
53066
53138
|
};
|
|
53067
53139
|
const onDesktop = windowed && process.platform === "win32";
|
|
53068
53140
|
const run = await this.spawnGame(engine.value, cmdArgs, userDataIn(userData), onDesktop ? { directory: project.value.path, files: helperFiles } : undefined);
|
|
@@ -53116,7 +53188,7 @@ ${platform}`;
|
|
|
53116
53188
|
await run.settled;
|
|
53117
53189
|
const helperFailed = onDesktop ? helperFailure(helperFiles.error) ?? (existsSync34(helperFiles.pid) ? null : `it never started, and ${helperStep(helperFiles.step)}`) : null;
|
|
53118
53190
|
if (helperFailed !== null) {
|
|
53119
|
-
discard(
|
|
53191
|
+
discard(join44(project.value.path, ".godot", ...ours.split("/")), userData);
|
|
53120
53192
|
return this.createErrorResponse(`No tests ran: ${named(needing)} ${needing.length === 1 ? "needs" : "need"} a window, and the engine could not be started on a desktop of its own: ${helperFailed}`);
|
|
53121
53193
|
}
|
|
53122
53194
|
const endedAt = Date.now();
|
|
@@ -53127,16 +53199,16 @@ ${platform}`;
|
|
|
53127
53199
|
engine: endedAt - importedAt
|
|
53128
53200
|
};
|
|
53129
53201
|
const notCompiled = stopped.for ?? run.log.all.map((entry) => autoloadNotCompiled(entry, autoloaded)).find((found) => found !== null) ?? null;
|
|
53130
|
-
const reportsDir =
|
|
53202
|
+
const reportsDir = join44(project.value.path, ".godot", ...ours.split("/"));
|
|
53131
53203
|
let report = null;
|
|
53132
53204
|
let reportProblem = null;
|
|
53133
|
-
const casePrinted = withNestedErrors(readRun(existsSync34(engineLog) ?
|
|
53205
|
+
const casePrinted = withNestedErrors(readRun(existsSync34(engineLog) ? readFileSync37(engineLog, "utf8") : ""), run.log.all);
|
|
53134
53206
|
const declaredErrors = readExpected(expectedFile);
|
|
53135
53207
|
try {
|
|
53136
53208
|
const written = existsSync34(reportsDir) && notCompiled === null ? readdirSync19(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
53137
53209
|
const newest = written.at(-1);
|
|
53138
53210
|
if (newest !== undefined) {
|
|
53139
|
-
report = parseJUnit(
|
|
53211
|
+
report = parseJUnit(readFileSync37(join44(reportsDir, newest, "results.xml"), "utf8"));
|
|
53140
53212
|
}
|
|
53141
53213
|
} catch (error) {
|
|
53142
53214
|
reportProblem = errorMessage(error);
|
|
@@ -53541,7 +53613,7 @@ ${error.text}`;
|
|
|
53541
53613
|
const scripts = projectFiles(projectPath, [".gd"]).map((script) => `res://${script}`);
|
|
53542
53614
|
const wanted = `res://${relativePath}`;
|
|
53543
53615
|
const folder = dirname25(relativePath.replaceAll("\\", "/"));
|
|
53544
|
-
const inFolder =
|
|
53616
|
+
const inFolder = join44(projectPath, folder);
|
|
53545
53617
|
const beside = existsSync34(inFolder) ? readdirSync19(inFolder, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => `res://${folder === "." ? "" : `${folder}/`}${entry.name}`) : [];
|
|
53546
53618
|
return this.createErrorResponse(withNearestFiles(`Script file does not exist: ${wanted}`, wanted, scripts, beside));
|
|
53547
53619
|
}
|
|
@@ -53674,7 +53746,7 @@ ${error.text}`;
|
|
|
53674
53746
|
return null;
|
|
53675
53747
|
}
|
|
53676
53748
|
try {
|
|
53677
|
-
return
|
|
53749
|
+
return readFileSync37(contained.absolutePath, "utf8");
|
|
53678
53750
|
} catch {
|
|
53679
53751
|
return null;
|
|
53680
53752
|
}
|
|
@@ -53768,7 +53840,7 @@ ${error.text}`;
|
|
|
53768
53840
|
return null;
|
|
53769
53841
|
}
|
|
53770
53842
|
try {
|
|
53771
|
-
return
|
|
53843
|
+
return readFileSync37(contained.absolutePath, "utf8");
|
|
53772
53844
|
} catch {
|
|
53773
53845
|
return null;
|
|
53774
53846
|
}
|
|
@@ -53942,7 +54014,7 @@ ${error.text}`;
|
|
|
53942
54014
|
...Array.isArray(stack) ? {
|
|
53943
54015
|
stack: stack.map((frame) => ({
|
|
53944
54016
|
...frame,
|
|
53945
|
-
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path:
|
|
54017
|
+
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join44(projectPath, frame["script"].slice("res://".length)) } : {}
|
|
53946
54018
|
}))
|
|
53947
54019
|
} : {}
|
|
53948
54020
|
});
|
|
@@ -54287,7 +54359,7 @@ ${error.text}`;
|
|
|
54287
54359
|
return null;
|
|
54288
54360
|
}
|
|
54289
54361
|
try {
|
|
54290
|
-
return settingKeys(
|
|
54362
|
+
return settingKeys(readFileSync37(join44(projectPath, "project.godot"), "utf8"));
|
|
54291
54363
|
} catch {
|
|
54292
54364
|
return null;
|
|
54293
54365
|
}
|
|
@@ -54632,7 +54704,7 @@ ${error.text}`;
|
|
|
54632
54704
|
if (!paths.ok) {
|
|
54633
54705
|
return refused(paths.reason, PATH_SOLUTIONS);
|
|
54634
54706
|
}
|
|
54635
|
-
const missing = paths.value.filter((path) => !existsSync34(
|
|
54707
|
+
const missing = paths.value.filter((path) => !existsSync34(join44(projectPath, path.slice("res://".length))));
|
|
54636
54708
|
if (missing.length > 0) {
|
|
54637
54709
|
return refused(`Nothing at ${missing.join(" and ")} in the project at ${projectPath}.`, PATH_SOLUTIONS);
|
|
54638
54710
|
}
|
|
@@ -54803,7 +54875,7 @@ ${error.text}`;
|
|
|
54803
54875
|
}
|
|
54804
54876
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
54805
54877
|
const budgetMs = budget.ms;
|
|
54806
|
-
if (!existsSync34(
|
|
54878
|
+
if (!existsSync34(join44(projectPath, RUNTIME_SCRIPT))) {
|
|
54807
54879
|
return runtimeVerdict(null, {
|
|
54808
54880
|
addon: false,
|
|
54809
54881
|
budgetMs,
|
|
@@ -55112,7 +55184,7 @@ ${error.text}`;
|
|
|
55112
55184
|
return 0;
|
|
55113
55185
|
}
|
|
55114
55186
|
try {
|
|
55115
|
-
return reportAlreadyInTranscript(
|
|
55187
|
+
return reportAlreadyInTranscript(readFileSync37(game.transcript), readFileSync37(report));
|
|
55116
55188
|
} catch (error) {
|
|
55117
55189
|
this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
|
|
55118
55190
|
return 0;
|
|
@@ -55845,7 +55917,7 @@ ${error.text}`;
|
|
|
55845
55917
|
};
|
|
55846
55918
|
const going = await this.runStillGoing(run);
|
|
55847
55919
|
const projectPath = run.projectPath;
|
|
55848
|
-
const projectGone = projectPath !== null && !existsSync34(
|
|
55920
|
+
const projectGone = projectPath !== null && !existsSync34(join44(projectPath, "project.godot"));
|
|
55849
55921
|
if (projectGone && callersName !== undefined) {
|
|
55850
55922
|
return this.createErrorResponse(`callersOf reads the project's scripts, and the run's project at ${projectPath} has no project.godot any more.`, ["editor_run profile without callersOf answers with the profile itself"]);
|
|
55851
55923
|
}
|
|
@@ -56230,7 +56302,7 @@ ${error.text}`;
|
|
|
56230
56302
|
notes.push(endedWithoutACode({
|
|
56231
56303
|
throughEditor: run.throughEditor,
|
|
56232
56304
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
56233
|
-
couldAnnounce: run.projectPath !== null && existsSync34(
|
|
56305
|
+
couldAnnounce: run.projectPath !== null && existsSync34(join44(run.projectPath, RUNTIME_SCRIPT)),
|
|
56234
56306
|
endedHere: typeof run.endedHere === "string",
|
|
56235
56307
|
pickedUp: run.pickedUp === true
|
|
56236
56308
|
}));
|
|
@@ -56496,6 +56568,8 @@ ${error.text}`;
|
|
|
56496
56568
|
let writtenBefore = null;
|
|
56497
56569
|
let blind = { unseen: [] };
|
|
56498
56570
|
let waitedForEditorMs = 0;
|
|
56571
|
+
let answerStalledMs = 0;
|
|
56572
|
+
const deadline = () => started + timeoutMs + answerStalledMs;
|
|
56499
56573
|
let startUnanswered = false;
|
|
56500
56574
|
let completedBefore;
|
|
56501
56575
|
let uidsReread = [];
|
|
@@ -56503,7 +56577,15 @@ ${error.text}`;
|
|
|
56503
56577
|
for (;; ) {
|
|
56504
56578
|
const busySince = Date.now();
|
|
56505
56579
|
let wasBusy = false;
|
|
56506
|
-
while (Date.now()
|
|
56580
|
+
while (Date.now() < deadline()) {
|
|
56581
|
+
const asked = Date.now();
|
|
56582
|
+
if (!await this.editorIsScanningOrImporting()) {
|
|
56583
|
+
const took = Date.now() - asked;
|
|
56584
|
+
if (took >= SLOW_ANSWER_MS) {
|
|
56585
|
+
answerStalledMs += took;
|
|
56586
|
+
}
|
|
56587
|
+
break;
|
|
56588
|
+
}
|
|
56507
56589
|
wasBusy = true;
|
|
56508
56590
|
await new Promise((settle) => setTimeout(settle, 100));
|
|
56509
56591
|
}
|
|
@@ -56528,7 +56610,7 @@ ${error.text}`;
|
|
|
56528
56610
|
if (duplicated !== undefined) {
|
|
56529
56611
|
uidsDuplicatedOnDisk = duplicated.map((group) => Array.isArray(group) ? group.map(String) : []);
|
|
56530
56612
|
}
|
|
56531
|
-
if (firstAnswer["started"] !== false || Date.now()
|
|
56613
|
+
if (firstAnswer["started"] !== false || Date.now() >= deadline()) {
|
|
56532
56614
|
break;
|
|
56533
56615
|
}
|
|
56534
56616
|
await new Promise((settle) => setTimeout(settle, 100));
|
|
@@ -56538,7 +56620,9 @@ ${error.text}`;
|
|
|
56538
56620
|
const ghosts = blind.stillHeld ?? [];
|
|
56539
56621
|
let busy = true;
|
|
56540
56622
|
let idleSince = null;
|
|
56541
|
-
|
|
56623
|
+
let looked = false;
|
|
56624
|
+
while (!looked || Date.now() < deadline()) {
|
|
56625
|
+
looked = true;
|
|
56542
56626
|
await new Promise((settle) => setTimeout(settle, 100));
|
|
56543
56627
|
let status;
|
|
56544
56628
|
try {
|
|
@@ -56584,6 +56668,7 @@ ${error.text}`;
|
|
|
56584
56668
|
let reloaded = {};
|
|
56585
56669
|
const reloadedFirst = [];
|
|
56586
56670
|
let builtOn;
|
|
56671
|
+
let builtOnUnchecked = false;
|
|
56587
56672
|
const reloadingSource = reloading === undefined || projectPath === "" ? null : this.sourceOfClass(projectPath, reloading);
|
|
56588
56673
|
if (reloading !== undefined && reloadingSource !== null && isToolScript(reloadingSource)) {
|
|
56589
56674
|
reloaded = {
|
|
@@ -56601,6 +56686,7 @@ ${error.text}`;
|
|
|
56601
56686
|
answer = await reloadAsked();
|
|
56602
56687
|
}
|
|
56603
56688
|
builtOn = first.said;
|
|
56689
|
+
builtOnUnchecked = first.unchecked === true;
|
|
56604
56690
|
}
|
|
56605
56691
|
const methods = readArray(answer, "methods");
|
|
56606
56692
|
const listed = (field) => {
|
|
@@ -56627,7 +56713,8 @@ ${error.text}`;
|
|
|
56627
56713
|
names: projectPath === "" ? [] : this.classesAScriptNames(projectPath, script),
|
|
56628
56714
|
restored: answer["restored"] === true,
|
|
56629
56715
|
restoredAs: readString(answer, "restoredAs") ?? "",
|
|
56630
|
-
...builtOn === undefined ? {} : { builtOn }
|
|
56716
|
+
...builtOn === undefined ? {} : { builtOn },
|
|
56717
|
+
...builtOnUnchecked ? { builtOnUnchecked } : {}
|
|
56631
56718
|
})
|
|
56632
56719
|
};
|
|
56633
56720
|
} else {
|
|
@@ -56655,6 +56742,9 @@ ${error.text}`;
|
|
|
56655
56742
|
const dependents = busy || projectPath === "" || broughtIn.length === 0 ? null : await this.reloadDependents(projectPath, broughtIn);
|
|
56656
56743
|
const stillGone = lost.filter((name) => !restored.includes(name));
|
|
56657
56744
|
const notes = [];
|
|
56745
|
+
if (answerStalledMs > 0) {
|
|
56746
|
+
notes.push(`The editor took ${(answerStalledMs / 1000).toFixed(1)} seconds to say it was idle, busy on the thread that answers with an earlier request, and that time was added to the wait rather than taken from it.`);
|
|
56747
|
+
}
|
|
56658
56748
|
if (busy) {
|
|
56659
56749
|
notes.push(startUnanswered ? "The editor took the rescan in and did not answer it before the wait ran out, which is an editor importing: a hidden editor imports on the thread that answers, and answers nothing until it is done. It carries the scan out once it is free, so new files may not be visible yet." : "The editor was still scanning or importing when the wait ran out, so new files may not be visible yet.");
|
|
56660
56750
|
if (unseenBefore.length > 0) {
|
|
@@ -56683,6 +56773,7 @@ ${error.text}`;
|
|
|
56683
56773
|
stillWorking: busy,
|
|
56684
56774
|
waitedMs: Date.now() - started,
|
|
56685
56775
|
waitedForEditorScanMs: waitedForEditorMs > 0 ? waitedForEditorMs : undefined,
|
|
56776
|
+
waitedForEditorAnswerMs: answerStalledMs > 0 ? answerStalledMs : undefined,
|
|
56686
56777
|
reloadedAs: reloaded.as,
|
|
56687
56778
|
...reloadDifference(reloaded),
|
|
56688
56779
|
...args["listMembers"] === true ? {
|
|
@@ -56800,17 +56891,20 @@ ${error.text}`;
|
|
|
56800
56891
|
try {
|
|
56801
56892
|
const answer = asParams(await this.godotBridge.invokeTool("scripts_behind", { scriptPaths }));
|
|
56802
56893
|
const behind = readArray(answer, "behind");
|
|
56803
|
-
return behind === undefined ?
|
|
56894
|
+
return behind === undefined ? { unchecked: "the editor answered without a list of them" } : { behind: behind.map(String), unsaved: (readArray(answer, "unsavedInEditor") ?? []).map(String) };
|
|
56804
56895
|
} catch (error) {
|
|
56805
|
-
|
|
56806
|
-
return null;
|
|
56896
|
+
return { unchecked: `the editor would not say: ${errorMessage(error)}` };
|
|
56807
56897
|
}
|
|
56808
56898
|
}
|
|
56809
56899
|
async reloadWhatItIsBuiltOn(projectPath, scriptPath) {
|
|
56810
56900
|
const against = this.scriptsCompiledAgainst(projectPath, scriptPath);
|
|
56811
56901
|
const held = await this.scriptsHeldBehind(against);
|
|
56812
|
-
if (
|
|
56813
|
-
return {
|
|
56902
|
+
if ("unchecked" in held) {
|
|
56903
|
+
return {
|
|
56904
|
+
reloaded: [],
|
|
56905
|
+
said: `Whether the editor holds the scripts it is compiled against behind their files could not be checked: ${held.unchecked}.`,
|
|
56906
|
+
unchecked: true
|
|
56907
|
+
};
|
|
56814
56908
|
}
|
|
56815
56909
|
const reloadedFor = scriptPath.startsWith("res://") ? scriptPath : `res://${scriptPath.replace(/^\/+/, "")}`;
|
|
56816
56910
|
const reloaded = [];
|
|
@@ -57460,10 +57554,11 @@ ${error.text}`;
|
|
|
57460
57554
|
const checked = await this.classesTheEditorCannotSee(args);
|
|
57461
57555
|
const stillHeld = checked.stillHeld ?? [];
|
|
57462
57556
|
const projectPath = typeof args["projectPath"] === "string" ? args["projectPath"] : "";
|
|
57463
|
-
const listed = projectPath === "" ? null : cachedClasses(projectPath);
|
|
57557
|
+
const listed = projectPath === "" || checked.unchecked !== undefined ? null : cachedClasses(projectPath);
|
|
57464
57558
|
const held = listed === null ? null : await this.scriptsHeldBehind([...new Set(listed.values())]);
|
|
57465
|
-
const
|
|
57466
|
-
|
|
57559
|
+
const behindUnchecked = held !== null && "unchecked" in held ? held.unchecked : undefined;
|
|
57560
|
+
const behind = listed === null || held === null || "unchecked" in held ? [] : [...listed].filter(([, path]) => held.behind.includes(path)).map(([className, scriptPath]) => ({ className, scriptPath }));
|
|
57561
|
+
if (checked.unseen.length === 0 && stillHeld.length === 0 && checked.unchecked === undefined && behindUnchecked === undefined && behind.length === 0) {
|
|
57467
57562
|
return answered;
|
|
57468
57563
|
}
|
|
57469
57564
|
const payload = asParams(JSON.parse(first.text));
|
|
@@ -57486,6 +57581,7 @@ ${error.text}`;
|
|
|
57486
57581
|
stillHeldByEditor: stillHeld.length > 0 ? stillHeld : undefined,
|
|
57487
57582
|
heldBehindInEditor: behind.length > 0 ? behind : undefined,
|
|
57488
57583
|
classesUnchecked: checked.unchecked,
|
|
57584
|
+
heldBehindUnchecked: behindUnchecked,
|
|
57489
57585
|
note: notes.length > 0 ? notes.join(" ") : undefined
|
|
57490
57586
|
});
|
|
57491
57587
|
}
|
|
@@ -57495,7 +57591,7 @@ ${error.text}`;
|
|
|
57495
57591
|
}
|
|
57496
57592
|
const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
57497
57593
|
const declared = new RegExp(`^\\s*(?:@\\w+(?:\\([^)]*\\))?\\s+)*class_name\\s+${escaped}\\b`, "m");
|
|
57498
|
-
if (declared.test(
|
|
57594
|
+
if (declared.test(readFileSync37(scriptPath, "utf8"))) {
|
|
57499
57595
|
return null;
|
|
57500
57596
|
}
|
|
57501
57597
|
const checked = await this.classesTheEditorCannotSee(args);
|
|
@@ -57692,7 +57788,7 @@ ${error.text}`;
|
|
|
57692
57788
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
57693
57789
|
const run = await this.currentRun();
|
|
57694
57790
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
57695
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync34(
|
|
57791
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync34(join44(project, RUNTIME_SCRIPT)) || !await this.runStillGoing(run)) {
|
|
57696
57792
|
return null;
|
|
57697
57793
|
}
|
|
57698
57794
|
const already = runtimesAnnounced().running;
|
|
@@ -57802,7 +57898,7 @@ ${error.text}`;
|
|
|
57802
57898
|
return this.createErrorResponse(staleNote);
|
|
57803
57899
|
}
|
|
57804
57900
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
57805
|
-
const screenshotPath = screenshotDir ?
|
|
57901
|
+
const screenshotPath = screenshotDir ? join44(screenshotDir, "capture.png") : null;
|
|
57806
57902
|
const refused = (message) => this.createErrorResponse(message);
|
|
57807
57903
|
const onGame = {
|
|
57808
57904
|
pid: choice.endpoint.pid,
|
|
@@ -58006,7 +58102,7 @@ async function runGodotServer() {
|
|
|
58006
58102
|
const server = new GodotServer;
|
|
58007
58103
|
await server.run();
|
|
58008
58104
|
}
|
|
58009
|
-
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, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_SETTLED_MS = 500, 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;
|
|
58105
|
+
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, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_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;
|
|
58010
58106
|
var init_server = __esm(() => {
|
|
58011
58107
|
init_dist2();
|
|
58012
58108
|
init_stdio();
|
|
@@ -58141,7 +58237,7 @@ init_bridge_announce();
|
|
|
58141
58237
|
init_engine_launch();
|
|
58142
58238
|
init_engine_slots();
|
|
58143
58239
|
import { existsSync as existsSync35 } from "node:fs";
|
|
58144
|
-
import { dirname as dirname26, join as
|
|
58240
|
+
import { dirname as dirname26, join as join45, resolve as resolve14 } from "node:path";
|
|
58145
58241
|
|
|
58146
58242
|
// src/error-mode.ts
|
|
58147
58243
|
import process8 from "node:process";
|
|
@@ -58179,15 +58275,15 @@ init_alive();
|
|
|
58179
58275
|
init_engine_launch();
|
|
58180
58276
|
init_lsp_client();
|
|
58181
58277
|
init_process_children();
|
|
58182
|
-
import { existsSync as existsSync17, readdirSync as readdirSync10, readFileSync as
|
|
58278
|
+
import { existsSync as existsSync17, readdirSync as readdirSync10, readFileSync as readFileSync16, unlinkSync as unlinkSync2 } from "node:fs";
|
|
58183
58279
|
import { connect as connect2 } from "node:net";
|
|
58184
|
-
import { join as
|
|
58280
|
+
import { join as join21, resolve as resolve7 } from "node:path";
|
|
58185
58281
|
var EDITOR_RECORD_PROTOCOL = 1;
|
|
58186
58282
|
var LSP_WAIT_SECONDS = 20;
|
|
58187
58283
|
var HOST = "127.0.0.1";
|
|
58188
58284
|
var LOOK_AGAIN_MS = 500;
|
|
58189
58285
|
function editorRecordsIn(projectPath) {
|
|
58190
|
-
const directory =
|
|
58286
|
+
const directory = join21(projectPath, ".godot", "gdharness-editors");
|
|
58191
58287
|
let names;
|
|
58192
58288
|
try {
|
|
58193
58289
|
names = readdirSync10(directory).filter((name) => /^\d+\.json$/.test(name));
|
|
@@ -58196,9 +58292,9 @@ function editorRecordsIn(projectPath) {
|
|
|
58196
58292
|
}
|
|
58197
58293
|
const records = [];
|
|
58198
58294
|
for (const name of names) {
|
|
58199
|
-
const file =
|
|
58295
|
+
const file = join21(directory, name);
|
|
58200
58296
|
try {
|
|
58201
|
-
const said = JSON.parse(
|
|
58297
|
+
const said = JSON.parse(readFileSync16(file, "utf8"));
|
|
58202
58298
|
const { protocol, pid, lsp_port: lspPort, written_at_ms: writtenAtMs } = said;
|
|
58203
58299
|
if (protocol === EDITOR_RECORD_PROTOCOL && typeof pid === "number" && typeof lspPort === "number" && lspPort > 0 && typeof writtenAtMs === "number") {
|
|
58204
58300
|
records.push({ file, pid, lspPort, writtenAtMs });
|
|
@@ -58291,7 +58387,7 @@ function lspOptionsOf(args, cwd) {
|
|
|
58291
58387
|
}
|
|
58292
58388
|
}
|
|
58293
58389
|
const projectPath = resolve7(cwd, named ?? ".");
|
|
58294
|
-
if (!existsSync17(
|
|
58390
|
+
if (!existsSync17(join21(projectPath, "project.godot"))) {
|
|
58295
58391
|
throw new EngineUsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
58296
58392
|
}
|
|
58297
58393
|
return { projectPath, port, waitSeconds };
|
|
@@ -58398,9 +58494,9 @@ init_setup();
|
|
|
58398
58494
|
init_tool_definitions();
|
|
58399
58495
|
init_tool_reference();
|
|
58400
58496
|
import { existsSync as existsSync19, mkdirSync as mkdirSync7, readdirSync as readdirSync12, rmSync as rmSync10, writeFileSync as writeFileSync11 } from "node:fs";
|
|
58401
|
-
import { basename as basename8, dirname as dirname10, join as
|
|
58497
|
+
import { basename as basename8, dirname as dirname10, join as join23 } from "node:path";
|
|
58402
58498
|
var SKILL_NAME = "gdharness";
|
|
58403
|
-
var SHARED_SKILLS =
|
|
58499
|
+
var SHARED_SKILLS = join23(".agents", "skills");
|
|
58404
58500
|
function skillMarkdown(version) {
|
|
58405
58501
|
return `---
|
|
58406
58502
|
name: ${SKILL_NAME}
|
|
@@ -58641,27 +58737,27 @@ what they report while starting comes back under \`engine_messages_before_operat
|
|
|
58641
58737
|
function skillFiles(version) {
|
|
58642
58738
|
return new Map([
|
|
58643
58739
|
["SKILL.md", skillMarkdown(version)],
|
|
58644
|
-
[
|
|
58740
|
+
[join23("references", "tools.md"), renderToolsMarkdown()]
|
|
58645
58741
|
]);
|
|
58646
58742
|
}
|
|
58647
58743
|
function skillDirectories(harnesses, projectPath, exists) {
|
|
58648
|
-
const directories = [
|
|
58744
|
+
const directories = [join23(projectPath, SHARED_SKILLS, SKILL_NAME)];
|
|
58649
58745
|
for (const harness of harnesses) {
|
|
58650
58746
|
if (harness.skills === undefined) {
|
|
58651
58747
|
continue;
|
|
58652
58748
|
}
|
|
58653
|
-
const own =
|
|
58654
|
-
if ((!harness.skills.shared || exists(own)) && !directories.includes(
|
|
58655
|
-
directories.push(
|
|
58749
|
+
const own = join23(projectPath, harness.skills.dir);
|
|
58750
|
+
if ((!harness.skills.shared || exists(own)) && !directories.includes(join23(own, SKILL_NAME))) {
|
|
58751
|
+
directories.push(join23(own, SKILL_NAME));
|
|
58656
58752
|
}
|
|
58657
58753
|
}
|
|
58658
58754
|
return directories;
|
|
58659
58755
|
}
|
|
58660
58756
|
function everySkillDirectory(projectPath, harnesses) {
|
|
58661
|
-
const directories = new Set([
|
|
58757
|
+
const directories = new Set([join23(projectPath, SHARED_SKILLS, SKILL_NAME)]);
|
|
58662
58758
|
for (const harness of harnesses) {
|
|
58663
58759
|
if (harness.skills !== undefined) {
|
|
58664
|
-
directories.add(
|
|
58760
|
+
directories.add(join23(projectPath, harness.skills.dir, SKILL_NAME));
|
|
58665
58761
|
}
|
|
58666
58762
|
}
|
|
58667
58763
|
return [...directories];
|
|
@@ -58678,7 +58774,7 @@ function removeSkill(directories, projectPath) {
|
|
|
58678
58774
|
return removed;
|
|
58679
58775
|
}
|
|
58680
58776
|
function pruneEmpty(directory, projectPath) {
|
|
58681
|
-
const shared =
|
|
58777
|
+
const shared = join23(projectPath, ".agents");
|
|
58682
58778
|
let path = directory;
|
|
58683
58779
|
while (path !== projectPath && (basename8(path) === "skills" || path === shared)) {
|
|
58684
58780
|
if (readdirSync12(path).length > 0) {
|
|
@@ -58694,7 +58790,7 @@ function writeSkill(directories, version) {
|
|
|
58694
58790
|
const replaced = existsSync19(directory);
|
|
58695
58791
|
rmSync10(directory, { recursive: true, force: true });
|
|
58696
58792
|
for (const [name, contents] of files) {
|
|
58697
|
-
const path =
|
|
58793
|
+
const path = join23(directory, name);
|
|
58698
58794
|
mkdirSync7(dirname10(path), { recursive: true });
|
|
58699
58795
|
writeFileSync11(path, contents, "utf8");
|
|
58700
58796
|
}
|
|
@@ -58707,9 +58803,9 @@ init_engine_launch();
|
|
|
58707
58803
|
init_engine_slots();
|
|
58708
58804
|
init_setup();
|
|
58709
58805
|
init_suite_judge();
|
|
58710
|
-
import { existsSync as existsSync20, mkdirSync as mkdirSync8, readFileSync as
|
|
58806
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync8, readFileSync as readFileSync19, rmSync as rmSync11 } from "node:fs";
|
|
58711
58807
|
import { availableParallelism as availableParallelism3 } from "node:os";
|
|
58712
|
-
import { join as
|
|
58808
|
+
import { join as join24, resolve as resolve9 } from "node:path";
|
|
58713
58809
|
import process13 from "node:process";
|
|
58714
58810
|
var SHARE = "--gdh-share=";
|
|
58715
58811
|
var GATHER = "--gdh-share-gather=";
|
|
@@ -58749,9 +58845,9 @@ function spreadOptionsOf(args, cwd) {
|
|
|
58749
58845
|
}
|
|
58750
58846
|
const first = positional[0];
|
|
58751
58847
|
const named = first === undefined ? null : resolve9(cwd, first);
|
|
58752
|
-
const projectNamed = named !== null && existsSync20(
|
|
58848
|
+
const projectNamed = named !== null && existsSync20(join24(named, "project.godot"));
|
|
58753
58849
|
const projectPath = projectNamed ? named : resolve9(cwd);
|
|
58754
|
-
if (!existsSync20(
|
|
58850
|
+
if (!existsSync20(join24(projectPath, "project.godot"))) {
|
|
58755
58851
|
throw new EngineUsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
58756
58852
|
}
|
|
58757
58853
|
const scenes = positional.slice(projectNamed ? 1 : 0);
|
|
@@ -58759,7 +58855,7 @@ function spreadOptionsOf(args, cwd) {
|
|
|
58759
58855
|
throw new EngineUsageError(scenes.length === 0 ? "gdharness spread needs the scene of the bench to play." : `gdharness spread plays one scene, and was given ${String(scenes.length)}: ${scenes.join(", ")}.`);
|
|
58760
58856
|
}
|
|
58761
58857
|
const scene = resPathOf(scenes[0] ?? "", projectPath, cwd);
|
|
58762
|
-
if (!existsSync20(
|
|
58858
|
+
if (!existsSync20(join24(projectPath, scene.slice("res://".length)))) {
|
|
58763
58859
|
throw new EngineUsageError(`Nothing at ${scene} in the project at ${projectPath}.`);
|
|
58764
58860
|
}
|
|
58765
58861
|
const benchArguments = dashes === -1 ? [] : args.slice(dashes + 1);
|
|
@@ -58830,14 +58926,14 @@ function shareFaults(played) {
|
|
|
58830
58926
|
return faults;
|
|
58831
58927
|
}
|
|
58832
58928
|
async function runSpreadCommand(options, godotPath) {
|
|
58833
|
-
if (!existsSync20(
|
|
58929
|
+
if (!existsSync20(join24(options.projectPath, SHARE_SCRIPT))) {
|
|
58834
58930
|
throw new EngineUsageError(`res://${SHARE_SCRIPT} is not in the project, so its bench cannot ask GdhShare for outcomes. Run gdharness setup.`);
|
|
58835
58931
|
}
|
|
58836
58932
|
const startedAt = Date.now();
|
|
58837
|
-
const spreadRuns =
|
|
58933
|
+
const spreadRuns = join24(options.projectPath, ".godot", "gdharness", "spread-runs");
|
|
58838
58934
|
sweepRunDirectories(spreadRuns);
|
|
58839
|
-
const runDirectory =
|
|
58840
|
-
const shareDirectory =
|
|
58935
|
+
const runDirectory = join24(spreadRuns, `${String(startedAt)}-${String(process13.pid)}`);
|
|
58936
|
+
const shareDirectory = join24(runDirectory, "share");
|
|
58841
58937
|
mkdirSync8(shareDirectory, { recursive: true });
|
|
58842
58938
|
const chosen = machineSlots({ directory: options.slotsDirectory, count: options.slotCount });
|
|
58843
58939
|
const slotCount = chosen.count;
|
|
@@ -58876,7 +58972,7 @@ async function runSpreadCommand(options, godotPath) {
|
|
|
58876
58972
|
share,
|
|
58877
58973
|
`${INTO}${shareDirectory}`
|
|
58878
58974
|
]);
|
|
58879
|
-
const logs = Array.from({ length: workers }, (_, index) =>
|
|
58975
|
+
const logs = Array.from({ length: workers }, (_, index) => join24(runDirectory, `worker-${String(index)}`, "engine.log"));
|
|
58880
58976
|
let done = 0;
|
|
58881
58977
|
const heartbeat = setInterval(() => {
|
|
58882
58978
|
console.log(`[spread] ${progressOf(logs)}`);
|
|
@@ -58894,18 +58990,18 @@ async function runSpreadCommand(options, godotPath) {
|
|
|
58894
58990
|
}
|
|
58895
58991
|
const faults = left.flatMap((worker) => engineFaults(worker, options.godotVersion));
|
|
58896
58992
|
const arms = left.map((_, index) => {
|
|
58897
|
-
const file =
|
|
58898
|
-
return existsSync20(file) ? armsPlayed(
|
|
58993
|
+
const file = join24(shareDirectory, `worker-${String(index)}.arms`);
|
|
58994
|
+
return existsSync20(file) ? armsPlayed(readFileSync19(file, "utf8")) : [];
|
|
58899
58995
|
});
|
|
58900
58996
|
faults.push(...shareFaults(arms));
|
|
58901
58997
|
if (faults.length > 0) {
|
|
58902
58998
|
return failed(faults, left, runDirectory, "so nothing was gathered");
|
|
58903
58999
|
}
|
|
58904
59000
|
const gather = await engine("gather", `${GATHER}${String(workers)}`);
|
|
58905
|
-
console.log(
|
|
59001
|
+
console.log(readFileSync19(gather.log, "utf8").replace(CONTROL_SEQUENCE, "").trimEnd());
|
|
58906
59002
|
const gatherFaults = engineFaults(gather, options.godotVersion);
|
|
58907
|
-
const gatheredFile =
|
|
58908
|
-
const gathered = new Set((existsSync20(gatheredFile) ? armsPlayed(
|
|
59003
|
+
const gatheredFile = join24(shareDirectory, "gathered.arms");
|
|
59004
|
+
const gathered = new Set((existsSync20(gatheredFile) ? armsPlayed(readFileSync19(gatheredFile, "utf8")) ?? [] : []).map((arm) => arm.arm));
|
|
58909
59005
|
const ungathered = (arms[0] ?? []).filter((arm) => !gathered.has(arm.arm)).map((arm) => arm.arm);
|
|
58910
59006
|
if (ungathered.length > 0) {
|
|
58911
59007
|
gatherFaults.push(`the gathering run never asked for ${ungathered.join(", ")}, which the workers played: it went another way through the arms than they did`);
|
|
@@ -58925,9 +59021,9 @@ async function runSpreadCommand(options, godotPath) {
|
|
|
58925
59021
|
}
|
|
58926
59022
|
}
|
|
58927
59023
|
async function played(launch, slots, options, runDirectory, name, benchArguments) {
|
|
58928
|
-
const directory =
|
|
58929
|
-
mkdirSync8(
|
|
58930
|
-
const log =
|
|
59024
|
+
const directory = join24(runDirectory, name);
|
|
59025
|
+
mkdirSync8(join24(directory, "user"), { recursive: true });
|
|
59026
|
+
const log = join24(directory, "engine.log");
|
|
58931
59027
|
const args = [
|
|
58932
59028
|
"--headless",
|
|
58933
59029
|
"--audio-driver",
|
|
@@ -58959,7 +59055,7 @@ function engineFaults(engine, godotVersion) {
|
|
|
58959
59055
|
if (!existsSync20(engine.log)) {
|
|
58960
59056
|
return faults;
|
|
58961
59057
|
}
|
|
58962
|
-
const printed = readRun(
|
|
59058
|
+
const printed = readRun(readFileSync19(engine.log, "utf8"));
|
|
58963
59059
|
const wrongEngine = godotVersion === null ? null : versionFault(printed.engineVersion, godotVersion);
|
|
58964
59060
|
if (wrongEngine !== null) {
|
|
58965
59061
|
faults.push(`${engine.name}: ${wrongEngine}`);
|
|
@@ -58972,7 +59068,7 @@ function engineFaults(engine, godotVersion) {
|
|
|
58972
59068
|
}
|
|
58973
59069
|
function progressOf(logs) {
|
|
58974
59070
|
const each = logs.map((log, index) => {
|
|
58975
|
-
const text = existsSync20(log) ?
|
|
59071
|
+
const text = existsSync20(log) ? readFileSync19(log, "utf8") : "";
|
|
58976
59072
|
const last = [...text.matchAll(PROGRESS)].at(-1);
|
|
58977
59073
|
return last === undefined ? `worker ${String(index)} has played no arm yet` : `worker ${String(index)} at ${last[1] ?? ""} ${last[2] ?? ""} of ${last[3] ?? ""}`;
|
|
58978
59074
|
});
|
|
@@ -59004,20 +59100,20 @@ import {
|
|
|
59004
59100
|
existsSync as existsSync23,
|
|
59005
59101
|
mkdirSync as mkdirSync10,
|
|
59006
59102
|
readdirSync as readdirSync14,
|
|
59007
|
-
readFileSync as
|
|
59103
|
+
readFileSync as readFileSync23,
|
|
59008
59104
|
rmSync as rmSync12,
|
|
59009
59105
|
statSync as statSync14,
|
|
59010
59106
|
writeFileSync as writeFileSync12
|
|
59011
59107
|
} from "node:fs";
|
|
59012
59108
|
import { availableParallelism as availableParallelism4 } from "node:os";
|
|
59013
|
-
import { dirname as dirname13, join as
|
|
59109
|
+
import { dirname as dirname13, join as join28, relative as relative5, resolve as resolve10, sep as sep4 } from "node:path";
|
|
59014
59110
|
import process14 from "node:process";
|
|
59015
59111
|
|
|
59016
59112
|
// src/suite-fingerprint.ts
|
|
59017
59113
|
init_class_cache();
|
|
59018
59114
|
import { createHash as createHash6 } from "node:crypto";
|
|
59019
|
-
import { existsSync as existsSync21, readdirSync as readdirSync13, readFileSync as
|
|
59020
|
-
import { join as
|
|
59115
|
+
import { existsSync as existsSync21, readdirSync as readdirSync13, readFileSync as readFileSync20, statSync as statSync13 } from "node:fs";
|
|
59116
|
+
import { join as join25, posix as posix2 } from "node:path";
|
|
59021
59117
|
var RES_LITERAL = /res:\/\/[^"'\s)\]<>]*/g;
|
|
59022
59118
|
var RELATIVE_LOAD = /\b(?:pre)?load\(\s*["']((?!res:\/\/|uid:\/\/)[^"']+)["']/g;
|
|
59023
59119
|
var REACHES_AT_RUN_TIME = [
|
|
@@ -59076,16 +59172,16 @@ ${this.read(path).digest}
|
|
|
59076
59172
|
const hash = createHash6("sha256");
|
|
59077
59173
|
const walk = (directory, under) => {
|
|
59078
59174
|
for (const entry of readdirSync13(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
59079
|
-
const file =
|
|
59175
|
+
const file = join25(directory, entry.name);
|
|
59080
59176
|
if (entry.isDirectory()) {
|
|
59081
59177
|
walk(file, `${under}${entry.name}/`);
|
|
59082
59178
|
} else if (entry.isFile()) {
|
|
59083
59179
|
hash.update(`${under}${entry.name}
|
|
59084
|
-
`).update(
|
|
59180
|
+
`).update(readFileSync20(file));
|
|
59085
59181
|
}
|
|
59086
59182
|
}
|
|
59087
59183
|
};
|
|
59088
|
-
const directory =
|
|
59184
|
+
const directory = join25(this.scripts.projectPath, root.slice("res://".length));
|
|
59089
59185
|
if (existsSync21(directory)) {
|
|
59090
59186
|
walk(directory, "");
|
|
59091
59187
|
}
|
|
@@ -59095,7 +59191,7 @@ ${this.read(path).digest}
|
|
|
59095
59191
|
if (PLACEHOLDER.test(path)) {
|
|
59096
59192
|
return { digest: "", reaches: [], unreadable: `${path} is filled in at run time` };
|
|
59097
59193
|
}
|
|
59098
|
-
const file =
|
|
59194
|
+
const file = join25(this.scripts.projectPath, path.slice("res://".length));
|
|
59099
59195
|
if (!existsSync21(file)) {
|
|
59100
59196
|
return { digest: "missing", reaches: [], unreadable: null };
|
|
59101
59197
|
}
|
|
@@ -59107,7 +59203,7 @@ ${this.read(path).digest}
|
|
|
59107
59203
|
if (BINARY_RESOURCE.test(path)) {
|
|
59108
59204
|
return { digest: "", reaches: [], unreadable: `${path} is a binary resource` };
|
|
59109
59205
|
}
|
|
59110
|
-
const bytes =
|
|
59206
|
+
const bytes = readFileSync20(file);
|
|
59111
59207
|
const digest = createHash6("sha256").update(bytes).digest("hex");
|
|
59112
59208
|
const text = bytes.toString("latin1");
|
|
59113
59209
|
const reaches = new Set;
|
|
@@ -59144,8 +59240,8 @@ ${this.read(path).digest}
|
|
|
59144
59240
|
}
|
|
59145
59241
|
}
|
|
59146
59242
|
function extensionsOf(projectPath) {
|
|
59147
|
-
const list =
|
|
59148
|
-
return existsSync21(list) ?
|
|
59243
|
+
const list = join25(projectPath, ".godot", "extension_list.cfg");
|
|
59244
|
+
return existsSync21(list) ? readFileSync20(list, "utf8").split(`
|
|
59149
59245
|
`).map((line) => line.trim()).filter((line) => line.startsWith("res://")) : [];
|
|
59150
59246
|
}
|
|
59151
59247
|
|
|
@@ -59154,18 +59250,18 @@ init_suite_judge();
|
|
|
59154
59250
|
init_suite_plan();
|
|
59155
59251
|
|
|
59156
59252
|
// src/version.ts
|
|
59157
|
-
import { readFileSync as
|
|
59158
|
-
import { dirname as dirname12, join as
|
|
59253
|
+
import { readFileSync as readFileSync22 } from "node:fs";
|
|
59254
|
+
import { dirname as dirname12, join as join27 } from "node:path";
|
|
59159
59255
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
59160
59256
|
function getLocalVersion() {
|
|
59161
59257
|
const currentDirectory = dirname12(fileURLToPath3(import.meta.url));
|
|
59162
59258
|
const packageCandidates = [
|
|
59163
|
-
|
|
59164
|
-
|
|
59259
|
+
join27(currentDirectory, "..", "package.json"),
|
|
59260
|
+
join27(currentDirectory, "..", "..", "package.json")
|
|
59165
59261
|
];
|
|
59166
59262
|
for (const packagePath of packageCandidates) {
|
|
59167
59263
|
try {
|
|
59168
|
-
const value = JSON.parse(
|
|
59264
|
+
const value = JSON.parse(readFileSync22(packagePath, "utf-8"));
|
|
59169
59265
|
if (typeof value === "object" && value !== null && "name" in value && value.name === "gdharness" && "version" in value && typeof value.version === "string") {
|
|
59170
59266
|
return value.version;
|
|
59171
59267
|
}
|
|
@@ -59232,9 +59328,9 @@ function testOptionsOf(args, cwd) {
|
|
|
59232
59328
|
}
|
|
59233
59329
|
const first = positional[0];
|
|
59234
59330
|
const named = first === undefined ? null : resolve10(cwd, first);
|
|
59235
|
-
const projectNamed = named !== null && existsSync23(
|
|
59331
|
+
const projectNamed = named !== null && existsSync23(join28(named, "project.godot"));
|
|
59236
59332
|
const projectPath = projectNamed ? named : resolve10(cwd);
|
|
59237
|
-
if (!existsSync23(
|
|
59333
|
+
if (!existsSync23(join28(projectPath, "project.godot"))) {
|
|
59238
59334
|
throw new EngineUsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
59239
59335
|
}
|
|
59240
59336
|
const last = (flag) => valued.get(flag)?.at(-1);
|
|
@@ -59268,11 +59364,11 @@ function seedOf(value) {
|
|
|
59268
59364
|
return seed;
|
|
59269
59365
|
}
|
|
59270
59366
|
function rootsOf(options) {
|
|
59271
|
-
return options.roots.length > 0 ? options.roots : TEST_DIRECTORIES.filter((root) => existsSync23(
|
|
59367
|
+
return options.roots.length > 0 ? options.roots : TEST_DIRECTORIES.filter((root) => existsSync23(join28(options.projectPath, root))).map((root) => `res://${root}`);
|
|
59272
59368
|
}
|
|
59273
59369
|
function searchedRoots(options) {
|
|
59274
59370
|
return options.selected.length === 0 ? rootsOf(options) : options.selected.filter((entry) => {
|
|
59275
|
-
const directory =
|
|
59371
|
+
const directory = join28(options.projectPath, entry.slice("res://".length));
|
|
59276
59372
|
return existsSync23(directory) && statSync14(directory).isDirectory();
|
|
59277
59373
|
});
|
|
59278
59374
|
}
|
|
@@ -59289,7 +59385,7 @@ function selectedSuites(scripts, options) {
|
|
|
59289
59385
|
for (const entry of options.selected) {
|
|
59290
59386
|
const caseAt = /^(res:\/\/.*\.gd):(\w+)$/.exec(entry);
|
|
59291
59387
|
const path = caseAt?.[1] ?? entry;
|
|
59292
|
-
const directory =
|
|
59388
|
+
const directory = join28(options.projectPath, path.slice(6));
|
|
59293
59389
|
const suites = existsSync23(directory) && statSync14(directory).isDirectory() ? scripts.suitesUnder([path.replace(/\/$/, "")]) : [scripts.suite(path)].filter((suite) => suite !== null);
|
|
59294
59390
|
if (suites.length === 0) {
|
|
59295
59391
|
throw new EngineUsageError(existsSync23(directory) ? `${path} holds no gdUnit4 suite: nothing there extends GdUnitTestSuite and declares a test_ function.` : `Nothing at ${path} in the project at ${options.projectPath}.`);
|
|
@@ -59346,7 +59442,7 @@ class RunLogs {
|
|
|
59346
59442
|
return this.kept ?? this.directory;
|
|
59347
59443
|
}
|
|
59348
59444
|
named(path) {
|
|
59349
|
-
return this.kept === null ? path :
|
|
59445
|
+
return this.kept === null ? path : join28(this.kept, relative5(this.directory, path));
|
|
59350
59446
|
}
|
|
59351
59447
|
keep() {
|
|
59352
59448
|
if (this.kept === null) {
|
|
@@ -59354,8 +59450,8 @@ class RunLogs {
|
|
|
59354
59450
|
}
|
|
59355
59451
|
for (const file of readdirSync14(this.directory, { recursive: true, encoding: "utf8" })) {
|
|
59356
59452
|
if (file.endsWith(".log") && file.split(sep4).length === 2) {
|
|
59357
|
-
mkdirSync10(dirname13(
|
|
59358
|
-
copyFileSync(
|
|
59453
|
+
mkdirSync10(dirname13(join28(this.kept, file)), { recursive: true });
|
|
59454
|
+
copyFileSync(join28(this.directory, file), join28(this.kept, file));
|
|
59359
59455
|
}
|
|
59360
59456
|
}
|
|
59361
59457
|
}
|
|
@@ -59368,24 +59464,24 @@ async function runTestCommand(options, godotPath) {
|
|
|
59368
59464
|
const startedAt = Date.now();
|
|
59369
59465
|
const scripts = new ProjectScripts(options.projectPath);
|
|
59370
59466
|
const home = homeOf(options.projectPath);
|
|
59371
|
-
const state =
|
|
59372
|
-
const timesFile =
|
|
59373
|
-
const passedFile =
|
|
59374
|
-
const lastRunFile =
|
|
59467
|
+
const state = join28(home, ".godot", "gdharness");
|
|
59468
|
+
const timesFile = join28(state, "suite-times.json");
|
|
59469
|
+
const passedFile = join28(state, "suite-passed.json");
|
|
59470
|
+
const lastRunFile = join28(state, LAST_RUN_FILE);
|
|
59375
59471
|
let suites = selectedSuites(scripts, options);
|
|
59376
59472
|
const uncollected = uncollectedSaid(scripts.uncollectedUnder(searchedRoots(options)));
|
|
59377
59473
|
const wholeSuites = suites.map((suite) => scripts.suite(suite.path) ?? suite);
|
|
59378
59474
|
const declared = new Map(wholeSuites.map((suite) => [suite.path, suite.cases]));
|
|
59379
|
-
const testRuns =
|
|
59380
|
-
const keptRuns =
|
|
59475
|
+
const testRuns = join28(options.projectPath, ".godot", "gdharness", "test-runs");
|
|
59476
|
+
const keptRuns = join28(state, "test-runs");
|
|
59381
59477
|
sweepRunDirectories(testRuns);
|
|
59382
59478
|
if (keptRuns !== testRuns) {
|
|
59383
59479
|
sweepRunDirectories(keptRuns);
|
|
59384
59480
|
}
|
|
59385
59481
|
const runName = `${String(startedAt)}-${String(process14.pid)}`;
|
|
59386
|
-
const runDirectory =
|
|
59482
|
+
const runDirectory = join28(testRuns, runName);
|
|
59387
59483
|
mkdirSync10(runDirectory, { recursive: true });
|
|
59388
|
-
const logs = new RunLogs(runDirectory, keptRuns === testRuns ? null :
|
|
59484
|
+
const logs = new RunLogs(runDirectory, keptRuns === testRuns ? null : join28(keptRuns, runName));
|
|
59389
59485
|
const chosen = machineSlots({ directory: options.slotsDirectory, count: options.slotCount });
|
|
59390
59486
|
const slots = new EngineSlots(chosen.directory, chosen.count, { pid: process14.pid, started: await ownStart(), by: `gdharness test of ${options.projectPath}` }, (line) => {
|
|
59391
59487
|
console.log(line);
|
|
@@ -59426,9 +59522,9 @@ async function runTestCommand(options, godotPath) {
|
|
|
59426
59522
|
}
|
|
59427
59523
|
const fingerprints = new SuiteFingerprints(scripts, [
|
|
59428
59524
|
getLocalVersion(),
|
|
59429
|
-
|
|
59525
|
+
readFileSync23(shippedListedRunner(), "utf8"),
|
|
59430
59526
|
...options.reachAlso.map((path) => `${path}
|
|
59431
|
-
${existsSync23(path) ?
|
|
59527
|
+
${existsSync23(path) ? readFileSync23(path, "latin1") : "missing"}`)
|
|
59432
59528
|
]);
|
|
59433
59529
|
const printsOf = new Map(suites.map((suite) => [suite.path, fingerprints.of(suite)]));
|
|
59434
59530
|
if (options.changedOnly) {
|
|
@@ -59544,7 +59640,7 @@ function roomSaid(engines, lanes, unheld, held, asked) {
|
|
|
59544
59640
|
}
|
|
59545
59641
|
function readRecord(file) {
|
|
59546
59642
|
try {
|
|
59547
|
-
const parsed = JSON.parse(
|
|
59643
|
+
const parsed = JSON.parse(readFileSync23(file, "utf8"));
|
|
59548
59644
|
return typeof parsed === "object" && parsed !== null ? parsed : {};
|
|
59549
59645
|
} catch {
|
|
59550
59646
|
return {};
|
|
@@ -59593,9 +59689,9 @@ async function importProject(launch) {
|
|
|
59593
59689
|
console.log("The import was skipped: every imported file and the class cache are current.");
|
|
59594
59690
|
return null;
|
|
59595
59691
|
}
|
|
59596
|
-
const directory =
|
|
59597
|
-
mkdirSync10(
|
|
59598
|
-
const log =
|
|
59692
|
+
const directory = join28(launch.runDirectory, "import");
|
|
59693
|
+
mkdirSync10(join28(directory, "user"), { recursive: true });
|
|
59694
|
+
const log = join28(directory, "import.log");
|
|
59599
59695
|
const lock = await launch.slots.take();
|
|
59600
59696
|
try {
|
|
59601
59697
|
freeLibraryCopies(launch.projectPath);
|
|
@@ -59603,7 +59699,7 @@ async function importProject(launch) {
|
|
|
59603
59699
|
if (ended.ended !== null || ended.code !== 0) {
|
|
59604
59700
|
return `${ended.ended ?? `the engine exited ${exitNamed(ended.code ?? 0)}`}; everything it printed is in ${launch.logs.named(log)}`;
|
|
59605
59701
|
}
|
|
59606
|
-
const printed = readRun(
|
|
59702
|
+
const printed = readRun(readFileSync23(log, "utf8"));
|
|
59607
59703
|
const wrongEngine = launch.godotVersion === null ? null : versionFault(printed.engineVersion, launch.godotVersion);
|
|
59608
59704
|
if (wrongEngine !== null) {
|
|
59609
59705
|
return `${wrongEngine}; everything it printed is in ${launch.logs.named(log)}`;
|
|
@@ -59660,12 +59756,12 @@ async function runShares(launch, units, declared, lanes, taken, times) {
|
|
|
59660
59756
|
return (await Promise.all(Array.from({ length: lanes }, (_, at) => lane(at)))).flat().sort((a, b) => a.index - b.index);
|
|
59661
59757
|
}
|
|
59662
59758
|
async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
59663
|
-
const directory =
|
|
59664
|
-
mkdirSync10(
|
|
59665
|
-
const suitesFile =
|
|
59666
|
-
const skippingFile =
|
|
59667
|
-
const rowsFile =
|
|
59668
|
-
const expectedFile =
|
|
59759
|
+
const directory = join28(launch.runDirectory, `engine-${String(index)}${again === "" ? "" : `-${again}`}`);
|
|
59760
|
+
mkdirSync10(join28(directory, "user"), { recursive: true });
|
|
59761
|
+
const suitesFile = join28(directory, "suites.txt");
|
|
59762
|
+
const skippingFile = join28(directory, "skipping.txt");
|
|
59763
|
+
const rowsFile = join28(directory, "rows.txt");
|
|
59764
|
+
const expectedFile = join28(directory, "expected.json");
|
|
59669
59765
|
writeFileSync12(suitesFile, `${plan.suites.map((suite) => suite.path).join(`
|
|
59670
59766
|
`)}
|
|
59671
59767
|
`);
|
|
@@ -59692,7 +59788,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59692
59788
|
...plan.windowed ? [] : ["--ignoreHeadlessMode"],
|
|
59693
59789
|
"-c",
|
|
59694
59790
|
"-rd",
|
|
59695
|
-
`res://${relative5(launch.projectPath,
|
|
59791
|
+
`res://${relative5(launch.projectPath, join28(directory, "reports")).split(sep4).join("/")}`,
|
|
59696
59792
|
"-rc",
|
|
59697
59793
|
"1",
|
|
59698
59794
|
"--",
|
|
@@ -59708,7 +59804,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59708
59804
|
"--expected-out",
|
|
59709
59805
|
expectedFile
|
|
59710
59806
|
];
|
|
59711
|
-
const log =
|
|
59807
|
+
const log = join28(directory, "engine.log");
|
|
59712
59808
|
const lock = slot ?? await launch.slots.take();
|
|
59713
59809
|
const began = Date.now();
|
|
59714
59810
|
let end;
|
|
@@ -59719,7 +59815,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59719
59815
|
launch.slots.release(lock);
|
|
59720
59816
|
}
|
|
59721
59817
|
}
|
|
59722
|
-
const printed = readRun(
|
|
59818
|
+
const printed = readRun(readFileSync23(log, "utf8"));
|
|
59723
59819
|
const assignment = {
|
|
59724
59820
|
suites: plan.suites,
|
|
59725
59821
|
expected: readExpected(expectedFile),
|
|
@@ -59727,7 +59823,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59727
59823
|
godotVersion: launch.godotVersion
|
|
59728
59824
|
};
|
|
59729
59825
|
const judged = judgeEngine(printed, assignment, end);
|
|
59730
|
-
const written = reportsFault(
|
|
59826
|
+
const written = reportsFault(join28(directory, "reports"));
|
|
59731
59827
|
return {
|
|
59732
59828
|
index,
|
|
59733
59829
|
plan,
|
|
@@ -59741,7 +59837,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59741
59837
|
}
|
|
59742
59838
|
var REPORT_FILES_NAMED = 3;
|
|
59743
59839
|
function reportsFault(reports) {
|
|
59744
|
-
const files = existsSync23(reports) ? readdirSync14(reports, { recursive: true, encoding: "utf8" }).filter((file) => statSync14(
|
|
59840
|
+
const files = existsSync23(reports) ? readdirSync14(reports, { recursive: true, encoding: "utf8" }).filter((file) => statSync14(join28(reports, file)).isFile()).sort() : [];
|
|
59745
59841
|
if (files.length === 0) {
|
|
59746
59842
|
return null;
|
|
59747
59843
|
}
|
|
@@ -59754,7 +59850,7 @@ function report(ran, across, uncollected, startedAt, logs, printOutput, targets)
|
|
|
59754
59850
|
for (const engine of ran) {
|
|
59755
59851
|
console.log("");
|
|
59756
59852
|
console.log(`--- what engine ${String(engine.index)} printed, running ${named(engine.plan.suites.map((suite) => suite.path))}:`);
|
|
59757
|
-
console.log(existsSync23(engine.log) ?
|
|
59853
|
+
console.log(existsSync23(engine.log) ? readFileSync23(engine.log, "utf8").replace(CONTROL_SEQUENCE, "").trimEnd() : "(no log was written)");
|
|
59758
59854
|
}
|
|
59759
59855
|
}
|
|
59760
59856
|
const failed = ran.filter((engine) => !engine.verdict.passed);
|
|
@@ -59773,7 +59869,7 @@ function report(ran, across, uncollected, startedAt, logs, printOutput, targets)
|
|
|
59773
59869
|
writeFileSync12(engine.log, `${verdict.join(`
|
|
59774
59870
|
`)}
|
|
59775
59871
|
---
|
|
59776
|
-
${
|
|
59872
|
+
${readFileSync23(engine.log, "utf8")}`);
|
|
59777
59873
|
}
|
|
59778
59874
|
if (engine.verdict.shown.length > 0) {
|
|
59779
59875
|
console.log("");
|
|
@@ -59899,7 +59995,7 @@ class UsageError extends Refusal {
|
|
|
59899
59995
|
function projectArgument(at) {
|
|
59900
59996
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
59901
59997
|
const projectPath = resolve14(named ?? ".");
|
|
59902
|
-
if (!existsSync35(
|
|
59998
|
+
if (!existsSync35(join45(projectPath, "project.godot"))) {
|
|
59903
59999
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
59904
60000
|
}
|
|
59905
60001
|
return projectPath;
|
|
@@ -59963,7 +60059,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
59963
60059
|
}
|
|
59964
60060
|
const path = (named ?? "").replace(/^res:\/\//, "");
|
|
59965
60061
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
59966
|
-
if (!existsSync35(
|
|
60062
|
+
if (!existsSync35(join45(projectPath, path))) {
|
|
59967
60063
|
console.log(" that file is not in this project, so it boots with a missing script");
|
|
59968
60064
|
}
|
|
59969
60065
|
console.log(` ${useTheShippedLoader(RUNTIME_AUTOLOAD.name, path)}`);
|