gdharness 1.3.44 → 1.3.46
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 +587 -478
- package/build/godot/addons/gdharness_editor/animation_reads.gd +8 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +190 -0
- package/build/godot/addons/gdharness_editor/setting_reads.gd +42 -1
- package/build/godot/addons/gdharness_editor/tool_executor.gd +4 -0
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +89 -3
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +61 -0
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +21 -2
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +18 -5
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +22 -3
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +7 -0
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +9 -0
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +29 -2
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +13 -0
- package/build/godot/operations/animation_reads.gd +8 -0
- package/build/godot/operations/dependencies.gd +103 -69
- package/build/godot/operations/editor_tools.gd +2 -0
- package/build/godot/operations/gdscript_analysis.gd +45 -8
- package/build/godot/operations/patterns.gd +33 -2
- package/build/godot/operations/project_diagnostics.gd +2 -1
- package/build/godot/operations/project_validation.gd +7 -6
- package/build/godot/operations/setting_reads.gd +42 -1
- package/build/keeper.js +3 -2
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -68,6 +68,124 @@ var init_errors = __esm(() => {
|
|
|
68
68
|
};
|
|
69
69
|
});
|
|
70
70
|
|
|
71
|
+
// src/paths.ts
|
|
72
|
+
import { realpathSync, statSync } from "node:fs";
|
|
73
|
+
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
74
|
+
function realAsFarAsItExists(path) {
|
|
75
|
+
const missing = [];
|
|
76
|
+
let at = path;
|
|
77
|
+
for (;; ) {
|
|
78
|
+
try {
|
|
79
|
+
return join(realpathSync.native(at), ...missing);
|
|
80
|
+
} catch {
|
|
81
|
+
const parent = dirname(at);
|
|
82
|
+
if (parent === at) {
|
|
83
|
+
return path;
|
|
84
|
+
}
|
|
85
|
+
missing.unshift(basename(at));
|
|
86
|
+
at = parent;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function isSameDirectory(onePath, otherPath) {
|
|
91
|
+
return relative(realAsFarAsItExists(resolve(onePath)), realAsFarAsItExists(resolve(otherPath))) === "";
|
|
92
|
+
}
|
|
93
|
+
function isWithinRoot(rootPath, candidatePath) {
|
|
94
|
+
const step = relative(resolve(rootPath), resolve(candidatePath));
|
|
95
|
+
return step !== ".." && !step.startsWith(`..${sep}`) && !isAbsolute(step);
|
|
96
|
+
}
|
|
97
|
+
function keptInside(given) {
|
|
98
|
+
const at = given.indexOf("::");
|
|
99
|
+
const inner = at === -1 ? "" : given.slice(at + 2);
|
|
100
|
+
return KEPT_ID.test(inner) ? { file: given.slice(0, at), inner } : { file: given, inner: undefined };
|
|
101
|
+
}
|
|
102
|
+
function resolveWithinProject(projectPath, candidatePath, options = {}) {
|
|
103
|
+
const trimmed = candidatePath.trim();
|
|
104
|
+
if (trimmed === "") {
|
|
105
|
+
return { ok: false, reason: "Path is empty." };
|
|
106
|
+
}
|
|
107
|
+
if (trimmed.includes("\x00")) {
|
|
108
|
+
return { ok: false, reason: "Path contains a null byte." };
|
|
109
|
+
}
|
|
110
|
+
const root = resolve(projectPath);
|
|
111
|
+
const withoutScheme = trimmed.startsWith(RESOURCE_SCHEME) ? trimmed.slice(RESOURCE_SCHEME.length) : isAbsolute(trimmed) && isWithinRoot(root, trimmed) ? relative(root, resolve(trimmed)).split(sep).join("/") : trimmed;
|
|
112
|
+
if (withoutScheme.startsWith("uid://")) {
|
|
113
|
+
return {
|
|
114
|
+
ok: false,
|
|
115
|
+
reason: `Path '${trimmed}' is a UID, and a file is judged here by where it is on disk, which a UID does not say. Give the file's res:// path.`
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
const scheme = OTHER_SCHEME.exec(withoutScheme);
|
|
119
|
+
if (scheme) {
|
|
120
|
+
const elsewhere = scheme[0].toLowerCase() === "user://" ? " A game's user:// is a directory of its own outside the project: a running game's is under userDir in editor_status runtimes and in editor_run start's runtime, where a file reader of your own reads it." : "";
|
|
121
|
+
return {
|
|
122
|
+
ok: false,
|
|
123
|
+
reason: `Path '${trimmed}' uses the '${scheme[0]}' scheme, which names somewhere other than this project.${elsewhere}`
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
if (withoutScheme.startsWith("/") || withoutScheme.startsWith("\\") || isAbsolute(withoutScheme)) {
|
|
127
|
+
const tail = withoutScheme.split(/[\\/]+/).filter((step) => step !== "" && !step.endsWith(":"));
|
|
128
|
+
const besideIt = isAbsolute(trimmed) && isWithinRoot(realAsFarAsItExists(dirname(resolve(trimmed))), realAsFarAsItExists(root));
|
|
129
|
+
const example = tail.slice(besideIt ? -1 : -2).join("/") || "scenes/main.tscn";
|
|
130
|
+
return {
|
|
131
|
+
ok: false,
|
|
132
|
+
reason: `Path '${trimmed}' is absolute and outside the project; give a path inside it, such as '${example}'.`
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
const kept = keptInside(trimmed);
|
|
136
|
+
if (kept.inner !== undefined) {
|
|
137
|
+
return {
|
|
138
|
+
ok: false,
|
|
139
|
+
reason: `Path '${trimmed}' names the resource ${kept.inner} kept inside ${kept.file}, not a file. resource_edit read reads one by that path; this argument takes a file.`
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
if (withoutScheme.includes(":")) {
|
|
143
|
+
return {
|
|
144
|
+
ok: false,
|
|
145
|
+
reason: `Path '${trimmed}' contains ':', which no file inside a project is named with.`
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
const absolutePath = resolve(root, withoutScheme);
|
|
149
|
+
if (!isWithinRoot(root, absolutePath)) {
|
|
150
|
+
return { ok: false, reason: `Path '${trimmed}' resolves outside the project directory.` };
|
|
151
|
+
}
|
|
152
|
+
const relativePath = relative(root, absolutePath).split(sep).join("/");
|
|
153
|
+
if (relativePath === "" && options.root !== true) {
|
|
154
|
+
return { ok: false, reason: `Path '${trimmed}' is the project directory itself, not a file in it.` };
|
|
155
|
+
}
|
|
156
|
+
return { ok: true, absolutePath, relativePath };
|
|
157
|
+
}
|
|
158
|
+
function exportedBuild(projectPath, candidatePath) {
|
|
159
|
+
const found = resolveWithinProject(projectPath, candidatePath);
|
|
160
|
+
if (!found.ok) {
|
|
161
|
+
return found;
|
|
162
|
+
}
|
|
163
|
+
let real;
|
|
164
|
+
try {
|
|
165
|
+
real = realpathSync.native(found.absolutePath);
|
|
166
|
+
} catch {
|
|
167
|
+
return {
|
|
168
|
+
ok: false,
|
|
169
|
+
reason: `There is no ${found.relativePath} in the project to start: export the build first, with project_export run.`
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (!isWithinRoot(realAsFarAsItExists(projectPath), real)) {
|
|
173
|
+
return {
|
|
174
|
+
ok: false,
|
|
175
|
+
reason: `${found.relativePath} leads outside the project, to ${real}, so it is not started.`
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
if (!statSync(real).isFile()) {
|
|
179
|
+
return { ok: false, reason: `${found.relativePath} is not a file, so there is nothing to start.` };
|
|
180
|
+
}
|
|
181
|
+
return { ok: true, absolutePath: real, relativePath: found.relativePath };
|
|
182
|
+
}
|
|
183
|
+
var RESOURCE_SCHEME = "res://", OTHER_SCHEME, KEPT_ID;
|
|
184
|
+
var init_paths = __esm(() => {
|
|
185
|
+
OTHER_SCHEME = /^[a-zA-Z][a-zA-Z\d+.-]+:\/\//;
|
|
186
|
+
KEPT_ID = /^[A-Za-z0-9_]+$/;
|
|
187
|
+
});
|
|
188
|
+
|
|
71
189
|
// src/spawn.ts
|
|
72
190
|
import {
|
|
73
191
|
execFile,
|
|
@@ -243,7 +361,7 @@ var init_windows_ask = __esm(() => {
|
|
|
243
361
|
|
|
244
362
|
// src/process-children.ts
|
|
245
363
|
import { readFileSync } from "node:fs";
|
|
246
|
-
import { basename } from "node:path";
|
|
364
|
+
import { basename as basename2 } from "node:path";
|
|
247
365
|
async function startTimesOf(pids) {
|
|
248
366
|
const began = new Map;
|
|
249
367
|
if (pids.length === 0) {
|
|
@@ -345,7 +463,7 @@ function listingFailure(error) {
|
|
|
345
463
|
const failed = error;
|
|
346
464
|
const how = failed.killed === true ? `was killed after the ${ASK_TIMEOUT_MS}ms budget${failed.signal ? ` (${failed.signal})` : ""}` : failed.signal ? `was ended by ${failed.signal}` : failed.code !== undefined && failed.code !== null ? `exited with ${failed.code}` : "failed";
|
|
347
465
|
const said = typeof failed.stderr === "string" ? failed.stderr.trim() : "";
|
|
348
|
-
const asked =
|
|
466
|
+
const asked = basename2((failed.cmd ?? "").split(" ")[0] ?? "") || "the listing";
|
|
349
467
|
return `${asked} ${how}${said === "" ? ", with nothing on stderr" : `; stderr: ${said.slice(0, 400)}`}`;
|
|
350
468
|
}
|
|
351
469
|
function whyTheProcessTreeFailed() {
|
|
@@ -440,11 +558,21 @@ function ancestorsIn(tree, pid, root) {
|
|
|
440
558
|
}
|
|
441
559
|
return chain;
|
|
442
560
|
}
|
|
561
|
+
function editorsOfProjectIn(tree, projectPath) {
|
|
562
|
+
const found = [];
|
|
563
|
+
for (const [pid, listed] of tree) {
|
|
564
|
+
const read = readCommandLine(listed.command);
|
|
565
|
+
if (read.editor && read.oneShot === null && read.projectPath !== null && /godot/i.test(read.executable) && !CONSOLE_WRAPPER.test(read.executable) && isSameDirectory(read.projectPath, projectPath)) {
|
|
566
|
+
found.push(pid);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return found.sort((a, b) => a - b);
|
|
570
|
+
}
|
|
443
571
|
function readCommandLine(command) {
|
|
444
572
|
const all = commandWords(command);
|
|
445
573
|
const separator = all.findIndex((word) => word === "--" || word === "++");
|
|
446
574
|
const words = separator === -1 ? all : all.slice(0, separator);
|
|
447
|
-
const executable = words[0] === undefined ? "" :
|
|
575
|
+
const executable = words[0] === undefined ? "" : basename2(words[0].replaceAll("\\", "/"));
|
|
448
576
|
const at = words.indexOf("--path");
|
|
449
577
|
let projectPath = null;
|
|
450
578
|
if (at !== -1 && at + 1 < words.length) {
|
|
@@ -507,8 +635,9 @@ async function askPosix() {
|
|
|
507
635
|
});
|
|
508
636
|
return stdout;
|
|
509
637
|
}
|
|
510
|
-
var ticksAnswered, ASK_TIMEOUT_MS = 15000, lastListingFailure, ONE_SHOT_OPTIONS;
|
|
638
|
+
var ticksAnswered, ASK_TIMEOUT_MS = 15000, lastListingFailure, ONE_SHOT_OPTIONS, CONSOLE_WRAPPER;
|
|
511
639
|
var init_process_children = __esm(() => {
|
|
640
|
+
init_paths();
|
|
512
641
|
init_spawn();
|
|
513
642
|
init_windows_ask();
|
|
514
643
|
ONE_SHOT_OPTIONS = [
|
|
@@ -532,13 +661,14 @@ var init_process_children = __esm(() => {
|
|
|
532
661
|
"--convert-3to4",
|
|
533
662
|
"--validate-conversion-3to4"
|
|
534
663
|
];
|
|
664
|
+
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
535
665
|
});
|
|
536
666
|
|
|
537
667
|
// src/bridge-announce.ts
|
|
538
668
|
import { mkdirSync, readFileSync as readFileSync2, rmSync, writeFileSync } from "node:fs";
|
|
539
|
-
import { dirname, join } from "node:path";
|
|
669
|
+
import { dirname as dirname2, join as join2 } from "node:path";
|
|
540
670
|
function announcementPath(projectPath) {
|
|
541
|
-
return
|
|
671
|
+
return join2(projectPath, ".godot", "gdharness-bridge.json");
|
|
542
672
|
}
|
|
543
673
|
function readAnnouncement(path) {
|
|
544
674
|
try {
|
|
@@ -585,7 +715,7 @@ function announcementIsOurs(path) {
|
|
|
585
715
|
function announceBridge(projectPath, bridge) {
|
|
586
716
|
const path = announcementPath(projectPath);
|
|
587
717
|
try {
|
|
588
|
-
mkdirSync(
|
|
718
|
+
mkdirSync(dirname2(path), { recursive: true });
|
|
589
719
|
const announcement = {
|
|
590
720
|
protocol: BRIDGE_ANNOUNCE_PROTOCOL,
|
|
591
721
|
host: bridge.host,
|
|
@@ -652,15 +782,15 @@ import {
|
|
|
652
782
|
openSync,
|
|
653
783
|
readFileSync as readFileSync3,
|
|
654
784
|
rmSync as rmSync2,
|
|
655
|
-
statSync,
|
|
785
|
+
statSync as statSync2,
|
|
656
786
|
writeFileSync as writeFileSync2,
|
|
657
787
|
writeSync
|
|
658
788
|
} from "node:fs";
|
|
659
789
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
660
|
-
import { join as
|
|
790
|
+
import { join as join3 } from "node:path";
|
|
661
791
|
import { setTimeout as delay } from "node:timers/promises";
|
|
662
792
|
function helperAssembly(directory = tmpdir2()) {
|
|
663
|
-
return
|
|
793
|
+
return join3(directory, `gdharness-desktop-${DIGEST}.dll`);
|
|
664
794
|
}
|
|
665
795
|
async function ensureHelperCompiled(directory = tmpdir2(), budgetMs = HELPER_COMPILE_MS, wait = true) {
|
|
666
796
|
const assembly = helperAssembly(directory);
|
|
@@ -686,7 +816,7 @@ async function ensureHelperCompiled(directory = tmpdir2(), budgetMs = HELPER_COM
|
|
|
686
816
|
}
|
|
687
817
|
try {
|
|
688
818
|
const holder = Number(readFileSync3(lock, "utf8").trim());
|
|
689
|
-
const old = Date.now() -
|
|
819
|
+
const old = Date.now() - statSync2(lock).mtimeMs > budgetMs;
|
|
690
820
|
if (old || Number.isInteger(holder) && holder > 0 && !alive(holder)) {
|
|
691
821
|
rmSync2(lock, { force: true });
|
|
692
822
|
continue;
|
|
@@ -700,8 +830,8 @@ async function ensureHelperCompiled(directory = tmpdir2(), budgetMs = HELPER_COM
|
|
|
700
830
|
return `the desktop helper was not compiled within ${String(budgetMs)} ms`;
|
|
701
831
|
}
|
|
702
832
|
async function compileHelper(directory, assembly, budgetMs) {
|
|
703
|
-
const scratch = mkdtempSync(
|
|
704
|
-
const source =
|
|
833
|
+
const scratch = mkdtempSync(join3(directory, "gdharness-desktop-source-"));
|
|
834
|
+
const source = join3(scratch, "helper.cs");
|
|
705
835
|
writeFileSync2(source, SOURCE, "utf8");
|
|
706
836
|
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
707
837
|
const script = [
|
|
@@ -8135,124 +8265,6 @@ var init_config_formats = __esm(() => {
|
|
|
8135
8265
|
init_dist();
|
|
8136
8266
|
});
|
|
8137
8267
|
|
|
8138
|
-
// src/paths.ts
|
|
8139
|
-
import { realpathSync, statSync as statSync2 } from "node:fs";
|
|
8140
|
-
import { basename as basename2, dirname as dirname2, isAbsolute, join as join3, relative, resolve, sep } from "node:path";
|
|
8141
|
-
function realAsFarAsItExists(path) {
|
|
8142
|
-
const missing = [];
|
|
8143
|
-
let at = path;
|
|
8144
|
-
for (;; ) {
|
|
8145
|
-
try {
|
|
8146
|
-
return join3(realpathSync.native(at), ...missing);
|
|
8147
|
-
} catch {
|
|
8148
|
-
const parent = dirname2(at);
|
|
8149
|
-
if (parent === at) {
|
|
8150
|
-
return path;
|
|
8151
|
-
}
|
|
8152
|
-
missing.unshift(basename2(at));
|
|
8153
|
-
at = parent;
|
|
8154
|
-
}
|
|
8155
|
-
}
|
|
8156
|
-
}
|
|
8157
|
-
function isSameDirectory(onePath, otherPath) {
|
|
8158
|
-
return relative(realAsFarAsItExists(resolve(onePath)), realAsFarAsItExists(resolve(otherPath))) === "";
|
|
8159
|
-
}
|
|
8160
|
-
function isWithinRoot(rootPath, candidatePath) {
|
|
8161
|
-
const step = relative(resolve(rootPath), resolve(candidatePath));
|
|
8162
|
-
return step !== ".." && !step.startsWith(`..${sep}`) && !isAbsolute(step);
|
|
8163
|
-
}
|
|
8164
|
-
function keptInside(given) {
|
|
8165
|
-
const at = given.indexOf("::");
|
|
8166
|
-
const inner = at === -1 ? "" : given.slice(at + 2);
|
|
8167
|
-
return KEPT_ID.test(inner) ? { file: given.slice(0, at), inner } : { file: given, inner: undefined };
|
|
8168
|
-
}
|
|
8169
|
-
function resolveWithinProject(projectPath, candidatePath, options = {}) {
|
|
8170
|
-
const trimmed = candidatePath.trim();
|
|
8171
|
-
if (trimmed === "") {
|
|
8172
|
-
return { ok: false, reason: "Path is empty." };
|
|
8173
|
-
}
|
|
8174
|
-
if (trimmed.includes("\x00")) {
|
|
8175
|
-
return { ok: false, reason: "Path contains a null byte." };
|
|
8176
|
-
}
|
|
8177
|
-
const root = resolve(projectPath);
|
|
8178
|
-
const withoutScheme = trimmed.startsWith(RESOURCE_SCHEME) ? trimmed.slice(RESOURCE_SCHEME.length) : isAbsolute(trimmed) && isWithinRoot(root, trimmed) ? relative(root, resolve(trimmed)).split(sep).join("/") : trimmed;
|
|
8179
|
-
if (withoutScheme.startsWith("uid://")) {
|
|
8180
|
-
return {
|
|
8181
|
-
ok: false,
|
|
8182
|
-
reason: `Path '${trimmed}' is a UID, and a file is judged here by where it is on disk, which a UID does not say. Give the file's res:// path.`
|
|
8183
|
-
};
|
|
8184
|
-
}
|
|
8185
|
-
const scheme = OTHER_SCHEME.exec(withoutScheme);
|
|
8186
|
-
if (scheme) {
|
|
8187
|
-
const elsewhere = scheme[0].toLowerCase() === "user://" ? " A game's user:// is a directory of its own outside the project: a running game's is under userDir in editor_status runtimes and in editor_run start's runtime, where a file reader of your own reads it." : "";
|
|
8188
|
-
return {
|
|
8189
|
-
ok: false,
|
|
8190
|
-
reason: `Path '${trimmed}' uses the '${scheme[0]}' scheme, which names somewhere other than this project.${elsewhere}`
|
|
8191
|
-
};
|
|
8192
|
-
}
|
|
8193
|
-
if (withoutScheme.startsWith("/") || withoutScheme.startsWith("\\") || isAbsolute(withoutScheme)) {
|
|
8194
|
-
const tail = withoutScheme.split(/[\\/]+/).filter((step) => step !== "" && !step.endsWith(":"));
|
|
8195
|
-
const besideIt = isAbsolute(trimmed) && isWithinRoot(realAsFarAsItExists(dirname2(resolve(trimmed))), realAsFarAsItExists(root));
|
|
8196
|
-
const example = tail.slice(besideIt ? -1 : -2).join("/") || "scenes/main.tscn";
|
|
8197
|
-
return {
|
|
8198
|
-
ok: false,
|
|
8199
|
-
reason: `Path '${trimmed}' is absolute and outside the project; give a path inside it, such as '${example}'.`
|
|
8200
|
-
};
|
|
8201
|
-
}
|
|
8202
|
-
const kept = keptInside(trimmed);
|
|
8203
|
-
if (kept.inner !== undefined) {
|
|
8204
|
-
return {
|
|
8205
|
-
ok: false,
|
|
8206
|
-
reason: `Path '${trimmed}' names the resource ${kept.inner} kept inside ${kept.file}, not a file. resource_edit read reads one by that path; this argument takes a file.`
|
|
8207
|
-
};
|
|
8208
|
-
}
|
|
8209
|
-
if (withoutScheme.includes(":")) {
|
|
8210
|
-
return {
|
|
8211
|
-
ok: false,
|
|
8212
|
-
reason: `Path '${trimmed}' contains ':', which no file inside a project is named with.`
|
|
8213
|
-
};
|
|
8214
|
-
}
|
|
8215
|
-
const absolutePath = resolve(root, withoutScheme);
|
|
8216
|
-
if (!isWithinRoot(root, absolutePath)) {
|
|
8217
|
-
return { ok: false, reason: `Path '${trimmed}' resolves outside the project directory.` };
|
|
8218
|
-
}
|
|
8219
|
-
const relativePath = relative(root, absolutePath).split(sep).join("/");
|
|
8220
|
-
if (relativePath === "" && options.root !== true) {
|
|
8221
|
-
return { ok: false, reason: `Path '${trimmed}' is the project directory itself, not a file in it.` };
|
|
8222
|
-
}
|
|
8223
|
-
return { ok: true, absolutePath, relativePath };
|
|
8224
|
-
}
|
|
8225
|
-
function exportedBuild(projectPath, candidatePath) {
|
|
8226
|
-
const found = resolveWithinProject(projectPath, candidatePath);
|
|
8227
|
-
if (!found.ok) {
|
|
8228
|
-
return found;
|
|
8229
|
-
}
|
|
8230
|
-
let real;
|
|
8231
|
-
try {
|
|
8232
|
-
real = realpathSync.native(found.absolutePath);
|
|
8233
|
-
} catch {
|
|
8234
|
-
return {
|
|
8235
|
-
ok: false,
|
|
8236
|
-
reason: `There is no ${found.relativePath} in the project to start: export the build first, with project_export run.`
|
|
8237
|
-
};
|
|
8238
|
-
}
|
|
8239
|
-
if (!isWithinRoot(realAsFarAsItExists(projectPath), real)) {
|
|
8240
|
-
return {
|
|
8241
|
-
ok: false,
|
|
8242
|
-
reason: `${found.relativePath} leads outside the project, to ${real}, so it is not started.`
|
|
8243
|
-
};
|
|
8244
|
-
}
|
|
8245
|
-
if (!statSync2(real).isFile()) {
|
|
8246
|
-
return { ok: false, reason: `${found.relativePath} is not a file, so there is nothing to start.` };
|
|
8247
|
-
}
|
|
8248
|
-
return { ok: true, absolutePath: real, relativePath: found.relativePath };
|
|
8249
|
-
}
|
|
8250
|
-
var RESOURCE_SCHEME = "res://", OTHER_SCHEME, KEPT_ID;
|
|
8251
|
-
var init_paths = __esm(() => {
|
|
8252
|
-
OTHER_SCHEME = /^[a-zA-Z][a-zA-Z\d+.-]+:\/\//;
|
|
8253
|
-
KEPT_ID = /^[A-Za-z0-9_]+$/;
|
|
8254
|
-
});
|
|
8255
|
-
|
|
8256
8268
|
// src/runner.ts
|
|
8257
8269
|
import { existsSync as existsSync2 } from "node:fs";
|
|
8258
8270
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
@@ -9905,6 +9917,29 @@ function cachedClasses(projectPath) {
|
|
|
9905
9917
|
}
|
|
9906
9918
|
return listed;
|
|
9907
9919
|
}
|
|
9920
|
+
function cachedEntries(projectPath) {
|
|
9921
|
+
const text = readWhole(join6(projectPath, ".godot", "global_script_class_cache.cfg"));
|
|
9922
|
+
if (text === null) {
|
|
9923
|
+
return null;
|
|
9924
|
+
}
|
|
9925
|
+
const listed = new Map;
|
|
9926
|
+
for (const entry of text.matchAll(/\{[^{}]*\}/g)) {
|
|
9927
|
+
const named = /"class":\s*&"([^"]+)"/.exec(entry[0])?.[1];
|
|
9928
|
+
if (named !== undefined) {
|
|
9929
|
+
listed.set(named, entry[0]);
|
|
9930
|
+
}
|
|
9931
|
+
}
|
|
9932
|
+
return listed;
|
|
9933
|
+
}
|
|
9934
|
+
function cacheChangesBetween(before, after) {
|
|
9935
|
+
if (before === null || after === null) {
|
|
9936
|
+
return {};
|
|
9937
|
+
}
|
|
9938
|
+
const added = [...after.keys()].filter((name) => !before.has(name)).sort();
|
|
9939
|
+
const removed = [...before.keys()].filter((name) => !after.has(name)).sort();
|
|
9940
|
+
const changed = [...after.keys()].filter((name) => before.has(name) && before.get(name) !== after.get(name)).sort();
|
|
9941
|
+
return { added, removed, changed };
|
|
9942
|
+
}
|
|
9908
9943
|
function addonOf(path) {
|
|
9909
9944
|
return /^res:\/\/addons\/([^/]+)\//.exec(path)?.[1];
|
|
9910
9945
|
}
|
|
@@ -9921,6 +9956,20 @@ function withAddonPathsCounted(paths) {
|
|
|
9921
9956
|
}
|
|
9922
9957
|
return { own, inAddons };
|
|
9923
9958
|
}
|
|
9959
|
+
function classesWithAddonsCounted(classes, listed) {
|
|
9960
|
+
const kept = [];
|
|
9961
|
+
const inAddons = {};
|
|
9962
|
+
for (const entry of classes) {
|
|
9963
|
+
const path = entry !== null && typeof entry === "object" ? entry["path"] : "";
|
|
9964
|
+
const addon = addonOf(typeof path === "string" ? path : "");
|
|
9965
|
+
if (addon === undefined || listed.has(addon)) {
|
|
9966
|
+
kept.push(entry);
|
|
9967
|
+
} else {
|
|
9968
|
+
inAddons[addon] = (inAddons[addon] ?? 0) + 1;
|
|
9969
|
+
}
|
|
9970
|
+
}
|
|
9971
|
+
return { classes: kept, inAddons };
|
|
9972
|
+
}
|
|
9924
9973
|
function withAddonClassesCounted(rebuilt, listed) {
|
|
9925
9974
|
const counted = { ...rebuilt };
|
|
9926
9975
|
for (const key of ["added", "changed"]) {
|
|
@@ -11818,6 +11867,16 @@ var init_engine_launch = __esm(() => {
|
|
|
11818
11867
|
MEMORY_PER_ENGINE = 1.5 * 1024 ** 3;
|
|
11819
11868
|
});
|
|
11820
11869
|
|
|
11870
|
+
// src/cache-home.ts
|
|
11871
|
+
import { homedir as homedir2 } from "node:os";
|
|
11872
|
+
import { join as join8 } from "node:path";
|
|
11873
|
+
function cacheHome(env = process.env, platform = process.platform, home = homedir2()) {
|
|
11874
|
+
const set = (name) => env[name] === "" ? undefined : env[name];
|
|
11875
|
+
const local = platform === "win32" ? set("LOCALAPPDATA") : undefined;
|
|
11876
|
+
return join8(set("XDG_CACHE_HOME") ?? local ?? join8(home, ".cache"), "gdharness");
|
|
11877
|
+
}
|
|
11878
|
+
var init_cache_home = () => {};
|
|
11879
|
+
|
|
11821
11880
|
// src/call-signal.ts
|
|
11822
11881
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
11823
11882
|
function stillGoingSaid(tool, op, elapsedMs) {
|
|
@@ -11889,8 +11948,8 @@ import {
|
|
|
11889
11948
|
watch,
|
|
11890
11949
|
writeSync as writeSync3
|
|
11891
11950
|
} from "node:fs";
|
|
11892
|
-
import { availableParallelism as availableParallelism2, homedir as
|
|
11893
|
-
import { join as
|
|
11951
|
+
import { availableParallelism as availableParallelism2, homedir as homedir3 } from "node:os";
|
|
11952
|
+
import { join as join9, resolve as resolve4 } from "node:path";
|
|
11894
11953
|
import process7 from "node:process";
|
|
11895
11954
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
11896
11955
|
function machineSlots(asked = {
|
|
@@ -11900,15 +11959,13 @@ function machineSlots(asked = {
|
|
|
11900
11959
|
env: process7.env,
|
|
11901
11960
|
platform: process7.platform,
|
|
11902
11961
|
processors: availableParallelism2(),
|
|
11903
|
-
home:
|
|
11962
|
+
home: homedir3(),
|
|
11904
11963
|
cwd: process7.cwd()
|
|
11905
11964
|
}) {
|
|
11906
11965
|
const { env } = machine;
|
|
11907
11966
|
const named = nonEmpty(env[SLOTS_VARIABLE]);
|
|
11908
|
-
const
|
|
11909
|
-
const
|
|
11910
|
-
const cache = join8(xdg ?? local ?? join8(machine.home, ".cache"), "gdharness");
|
|
11911
|
-
const directory = asked.directory ?? (named === undefined ? join8(cache, "slots") : resolve4(machine.cwd, named));
|
|
11967
|
+
const cache = cacheHome(env, machine.platform, machine.home);
|
|
11968
|
+
const directory = asked.directory ?? (named === undefined ? join9(cache, "slots") : resolve4(machine.cwd, named));
|
|
11912
11969
|
const counted = nonEmpty(env[SLOT_COUNT_VARIABLE]);
|
|
11913
11970
|
let count = asked.count;
|
|
11914
11971
|
if (count === null && counted !== undefined) {
|
|
@@ -12056,7 +12113,7 @@ class EngineSlots {
|
|
|
12056
12113
|
async takeFree() {
|
|
12057
12114
|
mkdirSync3(this.directory, { recursive: true });
|
|
12058
12115
|
for (let slot = 0;slot < this.count; slot += 1) {
|
|
12059
|
-
const lock =
|
|
12116
|
+
const lock = join9(this.directory, `${String(slot)}.lock`);
|
|
12060
12117
|
if (this.made(lock) || await this.takenOver(lock)) {
|
|
12061
12118
|
this.held.add(lock);
|
|
12062
12119
|
return lock;
|
|
@@ -12065,9 +12122,9 @@ class EngineSlots {
|
|
|
12065
12122
|
return null;
|
|
12066
12123
|
}
|
|
12067
12124
|
waitMarked() {
|
|
12068
|
-
const marks =
|
|
12125
|
+
const marks = join9(this.directory, WAITING_FOLDER);
|
|
12069
12126
|
waitsMarked += 1;
|
|
12070
|
-
const mark =
|
|
12127
|
+
const mark = join9(marks, `${String(this.holder.pid)}-${String(waitsMarked)}.json`);
|
|
12071
12128
|
try {
|
|
12072
12129
|
mkdirSync3(marks, { recursive: true });
|
|
12073
12130
|
} catch {
|
|
@@ -12083,7 +12140,7 @@ class EngineSlots {
|
|
|
12083
12140
|
const found = [];
|
|
12084
12141
|
for (let slot = 0;slot < this.count; slot += 1) {
|
|
12085
12142
|
try {
|
|
12086
|
-
const holder = holderOf(readFileSync8(
|
|
12143
|
+
const holder = holderOf(readFileSync8(join9(this.directory, `${String(slot)}.lock`), "utf8"));
|
|
12087
12144
|
if (holder !== null) {
|
|
12088
12145
|
found.push(holder);
|
|
12089
12146
|
}
|
|
@@ -12257,6 +12314,7 @@ async function inASlot(body) {
|
|
|
12257
12314
|
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;
|
|
12258
12315
|
var init_engine_slots = __esm(() => {
|
|
12259
12316
|
init_alive();
|
|
12317
|
+
init_cache_home();
|
|
12260
12318
|
init_call_signal();
|
|
12261
12319
|
init_engine_launch();
|
|
12262
12320
|
init_process_children();
|
|
@@ -12265,11 +12323,11 @@ var init_engine_slots = __esm(() => {
|
|
|
12265
12323
|
|
|
12266
12324
|
// src/detection.ts
|
|
12267
12325
|
import { existsSync as existsSync6, readdirSync as readdirSync4, statSync as statSync7 } from "node:fs";
|
|
12268
|
-
import { homedir as
|
|
12269
|
-
import { join as
|
|
12326
|
+
import { homedir as homedir4 } from "node:os";
|
|
12327
|
+
import { join as join10 } from "node:path";
|
|
12270
12328
|
function resolveHomeDirectory() {
|
|
12271
12329
|
try {
|
|
12272
|
-
return
|
|
12330
|
+
return homedir4();
|
|
12273
12331
|
} catch {
|
|
12274
12332
|
return "";
|
|
12275
12333
|
}
|
|
@@ -12290,7 +12348,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
12290
12348
|
if (!pattern.test(name)) {
|
|
12291
12349
|
continue;
|
|
12292
12350
|
}
|
|
12293
|
-
const fullPath =
|
|
12351
|
+
const fullPath = join10(directory, name);
|
|
12294
12352
|
try {
|
|
12295
12353
|
const stat = statSync7(fullPath);
|
|
12296
12354
|
if (stat.isFile()) {
|
|
@@ -12299,7 +12357,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
12299
12357
|
} catch {}
|
|
12300
12358
|
}
|
|
12301
12359
|
matches.sort((a, b) => b.mtime - a.mtime);
|
|
12302
|
-
return matches.map((m) =>
|
|
12360
|
+
return matches.map((m) => join10(directory, m.name));
|
|
12303
12361
|
}
|
|
12304
12362
|
function conventionalPaths(platform, home) {
|
|
12305
12363
|
const paths = ["godot"];
|
|
@@ -12954,26 +13012,26 @@ var init_engine_run = __esm(() => {
|
|
|
12954
13012
|
|
|
12955
13013
|
// src/doc-cache.ts
|
|
12956
13014
|
import { existsSync as existsSync8, readdirSync as readdirSync5, statSync as statSync8 } from "node:fs";
|
|
12957
|
-
import { homedir as
|
|
12958
|
-
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute5, join as
|
|
13015
|
+
import { homedir as homedir5 } from "node:os";
|
|
13016
|
+
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute5, join as join11 } from "node:path";
|
|
12959
13017
|
import process9 from "node:process";
|
|
12960
|
-
function editorCacheDirectory(godotPath, platform = process9.platform, env = process9.env, home =
|
|
13018
|
+
function editorCacheDirectory(godotPath, platform = process9.platform, env = process9.env, home = homedir5()) {
|
|
12961
13019
|
const beside = dirname5(godotPath);
|
|
12962
13020
|
const bundle = platform === "darwin" && basename4(beside) === "MacOS" && basename4(dirname5(beside)) === "Contents" ? dirname5(dirname5(dirname5(beside))) : null;
|
|
12963
13021
|
for (const folder of bundle === null ? [beside] : [beside, bundle]) {
|
|
12964
|
-
if (existsSync8(
|
|
12965
|
-
return
|
|
13022
|
+
if (existsSync8(join11(folder, "._sc_")) || existsSync8(join11(folder, "_sc_"))) {
|
|
13023
|
+
return join11(folder, "editor_data", "cache");
|
|
12966
13024
|
}
|
|
12967
13025
|
}
|
|
12968
13026
|
if (platform === "win32") {
|
|
12969
13027
|
const set = (name) => env[name] === "" ? undefined : env[name];
|
|
12970
|
-
return
|
|
13028
|
+
return join11(set("LOCALAPPDATA") ?? set("TEMP") ?? join11(home, "AppData", "Local"), "Godot");
|
|
12971
13029
|
}
|
|
12972
13030
|
if (platform === "darwin") {
|
|
12973
|
-
return
|
|
13031
|
+
return join11(home, "Library", "Caches", "Godot");
|
|
12974
13032
|
}
|
|
12975
13033
|
const xdg = env["XDG_CACHE_HOME"];
|
|
12976
|
-
return
|
|
13034
|
+
return join11(xdg !== undefined && isAbsolute5(xdg) ? xdg : join11(home, ".cache"), "godot");
|
|
12977
13035
|
}
|
|
12978
13036
|
function docCachesWritten(directory) {
|
|
12979
13037
|
const written = new Map;
|
|
@@ -12985,7 +13043,7 @@ function docCachesWritten(directory) {
|
|
|
12985
13043
|
}
|
|
12986
13044
|
for (const name of names.filter((one) => CACHE_FILE.test(one))) {
|
|
12987
13045
|
try {
|
|
12988
|
-
written.set(
|
|
13046
|
+
written.set(join11(directory, name), statSync8(join11(directory, name)).mtimeMs);
|
|
12989
13047
|
} catch {}
|
|
12990
13048
|
}
|
|
12991
13049
|
return written;
|
|
@@ -13010,7 +13068,7 @@ var init_doc_cache = __esm(() => {
|
|
|
13010
13068
|
|
|
13011
13069
|
// src/project-scan.ts
|
|
13012
13070
|
import { existsSync as existsSync9, readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync9 } from "node:fs";
|
|
13013
|
-
import { basename as basename5, extname, join as
|
|
13071
|
+
import { basename as basename5, extname, join as join12, relative as relative4, sep as sep3 } from "node:path";
|
|
13014
13072
|
function skipped(name) {
|
|
13015
13073
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
13016
13074
|
}
|
|
@@ -13018,10 +13076,10 @@ function steppedOver(directory) {
|
|
|
13018
13076
|
return gdignored(directory) || installedByUs(directory);
|
|
13019
13077
|
}
|
|
13020
13078
|
function gdignored(directory) {
|
|
13021
|
-
return existsSync9(
|
|
13079
|
+
return existsSync9(join12(directory, ".gdignore"));
|
|
13022
13080
|
}
|
|
13023
13081
|
function installedByUs(directory) {
|
|
13024
|
-
return existsSync9(
|
|
13082
|
+
return existsSync9(join12(directory, VERSION_MARKER));
|
|
13025
13083
|
}
|
|
13026
13084
|
function projectStructure(projectPath) {
|
|
13027
13085
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -13034,7 +13092,7 @@ function projectStructure(projectPath) {
|
|
|
13034
13092
|
continue;
|
|
13035
13093
|
}
|
|
13036
13094
|
if (entry.isDirectory()) {
|
|
13037
|
-
visit(
|
|
13095
|
+
visit(join12(directory, entry.name));
|
|
13038
13096
|
} else if (entry.isFile()) {
|
|
13039
13097
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
13040
13098
|
if (SIDECAR_EXTENSIONS.has(extension)) {
|
|
@@ -13064,7 +13122,7 @@ function folderFiles(directory, extensions) {
|
|
|
13064
13122
|
function underGdignore(projectPath, relative) {
|
|
13065
13123
|
const folders = relative.split("/").slice(0, -1);
|
|
13066
13124
|
for (let depth = 1;depth <= folders.length; depth += 1) {
|
|
13067
|
-
if (gdignored(
|
|
13125
|
+
if (gdignored(join12(projectPath, ...folders.slice(0, depth)))) {
|
|
13068
13126
|
return true;
|
|
13069
13127
|
}
|
|
13070
13128
|
}
|
|
@@ -13081,7 +13139,7 @@ function steppedOverIn(directory) {
|
|
|
13081
13139
|
continue;
|
|
13082
13140
|
}
|
|
13083
13141
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
13084
|
-
const path =
|
|
13142
|
+
const path = join12(at, entry.name);
|
|
13085
13143
|
if (gdignored(path)) {
|
|
13086
13144
|
found.push({ folder: spelled, why: "gdignore" });
|
|
13087
13145
|
} else if (installedByUs(path)) {
|
|
@@ -13102,7 +13160,7 @@ function filesBelow(root, extensions) {
|
|
|
13102
13160
|
continue;
|
|
13103
13161
|
}
|
|
13104
13162
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
13105
|
-
const path =
|
|
13163
|
+
const path = join12(directory, entry.name);
|
|
13106
13164
|
if (entry.isDirectory()) {
|
|
13107
13165
|
if (!steppedOver(path)) {
|
|
13108
13166
|
visit(path, spelled);
|
|
@@ -13127,14 +13185,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
13127
13185
|
}
|
|
13128
13186
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
13129
13187
|
if (entry.isDirectory()) {
|
|
13130
|
-
visit(
|
|
13188
|
+
visit(join12(directory, entry.name), spelled);
|
|
13131
13189
|
continue;
|
|
13132
13190
|
}
|
|
13133
13191
|
if (!entry.isFile()) {
|
|
13134
13192
|
continue;
|
|
13135
13193
|
}
|
|
13136
13194
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
13137
|
-
if (CARRIES_A_UID.has(extension) && !existsSync9(
|
|
13195
|
+
if (CARRIES_A_UID.has(extension) && !existsSync9(join12(directory, `${entry.name}.uid`))) {
|
|
13138
13196
|
found.push(spelled);
|
|
13139
13197
|
}
|
|
13140
13198
|
}
|
|
@@ -13145,7 +13203,7 @@ function scriptsWithoutUid(projectPath) {
|
|
|
13145
13203
|
function pathsInUidCache(projectPath) {
|
|
13146
13204
|
let data;
|
|
13147
13205
|
try {
|
|
13148
|
-
data = readFileSync9(
|
|
13206
|
+
data = readFileSync9(join12(projectPath, ".godot", "uid_cache.bin"));
|
|
13149
13207
|
} catch {
|
|
13150
13208
|
return null;
|
|
13151
13209
|
}
|
|
@@ -13173,7 +13231,7 @@ function pathsInUidCache(projectPath) {
|
|
|
13173
13231
|
function steppedOverAround(projectPath, place) {
|
|
13174
13232
|
let at = projectPath;
|
|
13175
13233
|
for (const step of relative4(projectPath, place).split(sep3).filter((part) => part !== "")) {
|
|
13176
|
-
at =
|
|
13234
|
+
at = join12(at, step);
|
|
13177
13235
|
if (!statSync9(at).isDirectory()) {
|
|
13178
13236
|
break;
|
|
13179
13237
|
}
|
|
@@ -13197,7 +13255,7 @@ function projectTexts(projectPath) {
|
|
|
13197
13255
|
if (skipped(entry.name)) {
|
|
13198
13256
|
continue;
|
|
13199
13257
|
}
|
|
13200
|
-
const absolute =
|
|
13258
|
+
const absolute = join12(directory, entry.name);
|
|
13201
13259
|
const spelled = `${prefix}${entry.name}`;
|
|
13202
13260
|
if (entry.isDirectory()) {
|
|
13203
13261
|
visit(absolute, `${spelled}/`);
|
|
@@ -13227,7 +13285,7 @@ function ignoredTexts(projectPath) {
|
|
|
13227
13285
|
if (skipped(entry.name)) {
|
|
13228
13286
|
continue;
|
|
13229
13287
|
}
|
|
13230
|
-
const absolute =
|
|
13288
|
+
const absolute = join12(directory, entry.name);
|
|
13231
13289
|
const spelled = `${prefix}${entry.name}`;
|
|
13232
13290
|
if (entry.isDirectory()) {
|
|
13233
13291
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -13325,7 +13383,7 @@ function searchProject(projectPath, options) {
|
|
|
13325
13383
|
if (skipped(entry.name)) {
|
|
13326
13384
|
continue;
|
|
13327
13385
|
}
|
|
13328
|
-
const entryPath =
|
|
13386
|
+
const entryPath = join12(directory, entry.name);
|
|
13329
13387
|
if (entry.isDirectory()) {
|
|
13330
13388
|
visit(entryPath);
|
|
13331
13389
|
} else if (entry.isFile()) {
|
|
@@ -13402,7 +13460,7 @@ var init_project_scan = __esm(() => {
|
|
|
13402
13460
|
// src/import-freshness.ts
|
|
13403
13461
|
import { createHash as createHash2 } from "node:crypto";
|
|
13404
13462
|
import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync10 } from "node:fs";
|
|
13405
|
-
import { join as
|
|
13463
|
+
import { join as join13 } from "node:path";
|
|
13406
13464
|
function md5Of(file) {
|
|
13407
13465
|
let stats;
|
|
13408
13466
|
try {
|
|
@@ -13462,7 +13520,7 @@ function importedIntoTheProject(projectPath, resPaths) {
|
|
|
13462
13520
|
return wrote;
|
|
13463
13521
|
}
|
|
13464
13522
|
function onDisk(projectPath, resPath) {
|
|
13465
|
-
return
|
|
13523
|
+
return join13(projectPath, resPath.slice("res://".length));
|
|
13466
13524
|
}
|
|
13467
13525
|
function behind(projectPath, resPath, sidecar) {
|
|
13468
13526
|
const remap = sectionOf(sidecar, "remap");
|
|
@@ -13475,7 +13533,7 @@ function behind(projectPath, resPath, sidecar) {
|
|
|
13475
13533
|
return "its imported output is not on disk";
|
|
13476
13534
|
}
|
|
13477
13535
|
const name = resPath.slice(resPath.lastIndexOf("/") + 1);
|
|
13478
|
-
const record =
|
|
13536
|
+
const record = join13(projectPath, ".godot", "imported", `${name}-${createHash2("md5").update(resPath, "utf8").digest("hex")}.md5`);
|
|
13479
13537
|
let recorded = "";
|
|
13480
13538
|
try {
|
|
13481
13539
|
recorded = /^source_md5="([0-9a-f]*)"/m.exec(readFileSync10(record, "utf8"))?.[1] ?? "";
|
|
@@ -13491,7 +13549,7 @@ function behind(projectPath, resPath, sidecar) {
|
|
|
13491
13549
|
function staleImports(projectPath) {
|
|
13492
13550
|
const stale = [];
|
|
13493
13551
|
const visit = (directory, resDirectory) => {
|
|
13494
|
-
if (existsSync10(
|
|
13552
|
+
if (existsSync10(join13(directory, ".gdignore")) || existsSync10(join13(directory, VERSION_MARKER))) {
|
|
13495
13553
|
return;
|
|
13496
13554
|
}
|
|
13497
13555
|
let entries;
|
|
@@ -13507,7 +13565,7 @@ function staleImports(projectPath) {
|
|
|
13507
13565
|
}
|
|
13508
13566
|
const resPath = `${resDirectory}${entry.name}`;
|
|
13509
13567
|
if (entry.isDirectory()) {
|
|
13510
|
-
visit(
|
|
13568
|
+
visit(join13(directory, entry.name), `${resPath}/`);
|
|
13511
13569
|
continue;
|
|
13512
13570
|
}
|
|
13513
13571
|
if (!entry.isFile()) {
|
|
@@ -13520,7 +13578,7 @@ function staleImports(projectPath) {
|
|
|
13520
13578
|
}
|
|
13521
13579
|
let sidecar;
|
|
13522
13580
|
try {
|
|
13523
|
-
sidecar = readFileSync10(
|
|
13581
|
+
sidecar = readFileSync10(join13(directory, entry.name), "utf8");
|
|
13524
13582
|
} catch {
|
|
13525
13583
|
continue;
|
|
13526
13584
|
}
|
|
@@ -13589,7 +13647,7 @@ import {
|
|
|
13589
13647
|
unlinkSync,
|
|
13590
13648
|
writeFileSync as writeFileSync6
|
|
13591
13649
|
} from "node:fs";
|
|
13592
|
-
import { basename as basename6, dirname as dirname6, isAbsolute as isAbsolute6, join as
|
|
13650
|
+
import { basename as basename6, dirname as dirname6, isAbsolute as isAbsolute6, join as join14 } from "node:path";
|
|
13593
13651
|
function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
13594
13652
|
const setAside = [];
|
|
13595
13653
|
const swept = [];
|
|
@@ -13599,10 +13657,10 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
13599
13657
|
for (const library of windowsLibraries(projectPath)) {
|
|
13600
13658
|
const folder = dirname6(library);
|
|
13601
13659
|
const copy = `~${basename6(library)}`;
|
|
13602
|
-
if (existsSync11(
|
|
13603
|
-
const aside =
|
|
13660
|
+
if (existsSync11(join14(folder, copy))) {
|
|
13661
|
+
const aside = join14(folder, `${copy}.aside-${process.pid}-${Date.now()}`);
|
|
13604
13662
|
try {
|
|
13605
|
-
renameSync2(
|
|
13663
|
+
renameSync2(join14(folder, copy), aside);
|
|
13606
13664
|
setAside.push(aside);
|
|
13607
13665
|
} catch {}
|
|
13608
13666
|
}
|
|
@@ -13615,8 +13673,8 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
13615
13673
|
for (const name of names) {
|
|
13616
13674
|
if (name.length > copy.length && name.toLowerCase().startsWith(copy.toLowerCase())) {
|
|
13617
13675
|
try {
|
|
13618
|
-
unlinkSync(
|
|
13619
|
-
swept.push(
|
|
13676
|
+
unlinkSync(join14(folder, name));
|
|
13677
|
+
swept.push(join14(folder, name));
|
|
13620
13678
|
} catch {}
|
|
13621
13679
|
}
|
|
13622
13680
|
}
|
|
@@ -13624,13 +13682,13 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
13624
13682
|
return { setAside, swept };
|
|
13625
13683
|
}
|
|
13626
13684
|
function seedExtensionList(projectPath) {
|
|
13627
|
-
const list =
|
|
13685
|
+
const list = join14(projectPath, ".godot", "extension_list.cfg");
|
|
13628
13686
|
if (existsSync11(list)) {
|
|
13629
13687
|
return null;
|
|
13630
13688
|
}
|
|
13631
13689
|
const found = [];
|
|
13632
13690
|
const visit = (directory, resDirectory) => {
|
|
13633
|
-
if (existsSync11(
|
|
13691
|
+
if (existsSync11(join14(directory, ".gdignore"))) {
|
|
13634
13692
|
return;
|
|
13635
13693
|
}
|
|
13636
13694
|
let entries;
|
|
@@ -13644,7 +13702,7 @@ function seedExtensionList(projectPath) {
|
|
|
13644
13702
|
continue;
|
|
13645
13703
|
}
|
|
13646
13704
|
if (entry.isDirectory()) {
|
|
13647
|
-
visit(
|
|
13705
|
+
visit(join14(directory, entry.name), `${resDirectory}${entry.name}/`);
|
|
13648
13706
|
} else if (entry.name.endsWith(".gdextension")) {
|
|
13649
13707
|
found.push(`${resDirectory}${entry.name}`);
|
|
13650
13708
|
}
|
|
@@ -13662,7 +13720,7 @@ function seedExtensionList(projectPath) {
|
|
|
13662
13720
|
return found;
|
|
13663
13721
|
}
|
|
13664
13722
|
function windowsLibraries(projectPath) {
|
|
13665
|
-
const listed = readOrNull(
|
|
13723
|
+
const listed = readOrNull(join14(projectPath, ".godot", "extension_list.cfg"));
|
|
13666
13724
|
if (listed === null) {
|
|
13667
13725
|
return [];
|
|
13668
13726
|
}
|
|
@@ -13694,9 +13752,9 @@ function resolveResource(projectPath, relativeTo, path) {
|
|
|
13694
13752
|
return null;
|
|
13695
13753
|
}
|
|
13696
13754
|
if (path.startsWith("res://")) {
|
|
13697
|
-
return
|
|
13755
|
+
return join14(projectPath, path.slice("res://".length));
|
|
13698
13756
|
}
|
|
13699
|
-
return isAbsolute6(path) ? path :
|
|
13757
|
+
return isAbsolute6(path) ? path : join14(relativeTo, path);
|
|
13700
13758
|
}
|
|
13701
13759
|
function readOrNull(path) {
|
|
13702
13760
|
try {
|
|
@@ -13710,10 +13768,10 @@ var init_library_copies = () => {};
|
|
|
13710
13768
|
// src/reimport.ts
|
|
13711
13769
|
import { createHash as createHash3 } from "node:crypto";
|
|
13712
13770
|
import { existsSync as existsSync12, readFileSync as readFileSync12, rmSync as rmSync7, statSync as statSync11, utimesSync, writeFileSync as writeFileSync7 } from "node:fs";
|
|
13713
|
-
import { basename as basename7, join as
|
|
13771
|
+
import { basename as basename7, join as join15 } from "node:path";
|
|
13714
13772
|
function forceNextImport(projectPath, resourcePath) {
|
|
13715
13773
|
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
13716
|
-
const sidecar =
|
|
13774
|
+
const sidecar = join15(projectPath, `${relative}.import`);
|
|
13717
13775
|
if (!existsSync12(sidecar)) {
|
|
13718
13776
|
return;
|
|
13719
13777
|
}
|
|
@@ -13724,17 +13782,17 @@ function forceNextImport(projectPath, resourcePath) {
|
|
|
13724
13782
|
}
|
|
13725
13783
|
function importRecord(projectPath, resourcePath) {
|
|
13726
13784
|
const digest = createHash3("md5").update(resourcePath).digest("hex");
|
|
13727
|
-
return
|
|
13785
|
+
return join15(projectPath, ".godot", "imported", `${basename7(resourcePath)}-${digest}.md5`);
|
|
13728
13786
|
}
|
|
13729
13787
|
var init_reimport = () => {};
|
|
13730
13788
|
|
|
13731
13789
|
// src/scratch.ts
|
|
13732
13790
|
import { existsSync as existsSync13, mkdtempSync as mkdtempSync2, readdirSync as readdirSync9, rmSync as rmSync8 } from "node:fs";
|
|
13733
13791
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
13734
|
-
import { join as
|
|
13792
|
+
import { join as join16 } from "node:path";
|
|
13735
13793
|
import process10 from "node:process";
|
|
13736
13794
|
function scratchDirectory(kind) {
|
|
13737
|
-
return mkdtempSync2(
|
|
13795
|
+
return mkdtempSync2(join16(tmpdir3(), `gdharness-${kind}-${process10.pid}-`));
|
|
13738
13796
|
}
|
|
13739
13797
|
function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
13740
13798
|
let entries;
|
|
@@ -13750,7 +13808,7 @@ function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
|
13750
13808
|
continue;
|
|
13751
13809
|
}
|
|
13752
13810
|
try {
|
|
13753
|
-
remove(
|
|
13811
|
+
remove(join16(directory, entry));
|
|
13754
13812
|
} catch {}
|
|
13755
13813
|
}
|
|
13756
13814
|
}
|
|
@@ -13836,7 +13894,7 @@ var init_scratch = __esm(() => {
|
|
|
13836
13894
|
});
|
|
13837
13895
|
|
|
13838
13896
|
// src/script-origins.ts
|
|
13839
|
-
import { join as
|
|
13897
|
+
import { join as join17 } from "node:path";
|
|
13840
13898
|
function unresolvedClassIn(message) {
|
|
13841
13899
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
13842
13900
|
}
|
|
@@ -13898,7 +13956,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
13898
13956
|
const ran = await runEngine(godotPath, [
|
|
13899
13957
|
"--headless",
|
|
13900
13958
|
"--log-file",
|
|
13901
|
-
|
|
13959
|
+
join17(logDir, "engine.log"),
|
|
13902
13960
|
"--path",
|
|
13903
13961
|
projectPath,
|
|
13904
13962
|
"--check-only",
|
|
@@ -13945,7 +14003,7 @@ var init_script_origins = __esm(() => {
|
|
|
13945
14003
|
|
|
13946
14004
|
// src/headless.ts
|
|
13947
14005
|
import { existsSync as existsSync14, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "node:fs";
|
|
13948
|
-
import { dirname as dirname7, join as
|
|
14006
|
+
import { dirname as dirname7, join as join18 } from "node:path";
|
|
13949
14007
|
function snakeCased(params) {
|
|
13950
14008
|
const result = emptyRecord();
|
|
13951
14009
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -14101,7 +14159,7 @@ async function importOnce(godotPath, projectPath) {
|
|
|
14101
14159
|
ran = await runEngine(godotPath, [
|
|
14102
14160
|
"--headless",
|
|
14103
14161
|
"--log-file",
|
|
14104
|
-
|
|
14162
|
+
join18(logDir, "engine.log"),
|
|
14105
14163
|
"--path",
|
|
14106
14164
|
projectPath,
|
|
14107
14165
|
"--import"
|
|
@@ -14137,14 +14195,14 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
14137
14195
|
}
|
|
14138
14196
|
async function operationOutcome(engine, operation, params, projectPath) {
|
|
14139
14197
|
const paramsDir = scratchDirectory("params");
|
|
14140
|
-
const paramsFile =
|
|
14141
|
-
const answerFile =
|
|
14198
|
+
const paramsFile = join18(paramsDir, `${operation}.json`);
|
|
14199
|
+
const answerFile = join18(paramsDir, "answer.json");
|
|
14142
14200
|
writeFileSync8(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
14143
14201
|
const args = [
|
|
14144
14202
|
"--headless",
|
|
14145
14203
|
...engine.editor === true ? ["--editor"] : [],
|
|
14146
14204
|
"--log-file",
|
|
14147
|
-
|
|
14205
|
+
join18(paramsDir, "engine.log"),
|
|
14148
14206
|
"--path",
|
|
14149
14207
|
projectPath,
|
|
14150
14208
|
"--script",
|
|
@@ -14222,7 +14280,8 @@ async function operationOutcome(engine, operation, params, projectPath) {
|
|
|
14222
14280
|
...afterAnswered
|
|
14223
14281
|
};
|
|
14224
14282
|
}
|
|
14225
|
-
const
|
|
14283
|
+
const onTheWayOut = answered === -1 ? problems : after;
|
|
14284
|
+
const afterAnswer = ran.exitCode === 0 && ran.failure === null ? undefined : `The operation finished and wrote this answer, and then the engine did not exit cleanly (${howItEnded(ran)}). ${onTheWayOut.length === 0 ? "It printed no error or warning on the way out." : `What it reported on the way out is under ${answered === -1 ? "engine_messages" : "engine_messages_after_answer"}.`}`;
|
|
14226
14285
|
return {
|
|
14227
14286
|
ok: true,
|
|
14228
14287
|
payload: answer.payload,
|
|
@@ -14261,8 +14320,8 @@ var init_headless = __esm(() => {
|
|
|
14261
14320
|
|
|
14262
14321
|
// src/update-check.ts
|
|
14263
14322
|
import { existsSync as existsSync15, mkdirSync as mkdirSync5, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "node:fs";
|
|
14264
|
-
import { homedir as
|
|
14265
|
-
import { join as
|
|
14323
|
+
import { homedir as homedir6, tmpdir as tmpdir4 } from "node:os";
|
|
14324
|
+
import { join as join19 } from "node:path";
|
|
14266
14325
|
function packageUrl(root) {
|
|
14267
14326
|
return `${root.replace(/\/+$/, "")}/gdharness/latest`;
|
|
14268
14327
|
}
|
|
@@ -14282,22 +14341,22 @@ function cacheDirectory(environment) {
|
|
|
14282
14341
|
const value = environment[name];
|
|
14283
14342
|
return value !== undefined && value !== "" ? value : null;
|
|
14284
14343
|
};
|
|
14285
|
-
const home = set("HOME") ??
|
|
14344
|
+
const home = set("HOME") ?? homedir6();
|
|
14286
14345
|
if (process.platform === "win32") {
|
|
14287
|
-
return
|
|
14346
|
+
return join19(set("LOCALAPPDATA") ?? home, "gdharness");
|
|
14288
14347
|
}
|
|
14289
14348
|
if (process.platform === "darwin") {
|
|
14290
|
-
return
|
|
14349
|
+
return join19(home, "Library", "Caches", "gdharness");
|
|
14291
14350
|
}
|
|
14292
|
-
return
|
|
14351
|
+
return join19(set("XDG_CACHE_HOME") ?? join19(home, ".cache"), "gdharness");
|
|
14293
14352
|
}
|
|
14294
14353
|
function cacheFile(environment = process.env) {
|
|
14295
14354
|
try {
|
|
14296
14355
|
const directory = cacheDirectory(environment);
|
|
14297
14356
|
mkdirSync5(directory, { recursive: true, mode: 448 });
|
|
14298
|
-
return
|
|
14357
|
+
return join19(directory, "update-check.json");
|
|
14299
14358
|
} catch {
|
|
14300
|
-
return
|
|
14359
|
+
return join19(tmpdir4(), "gdharness-update-check.json");
|
|
14301
14360
|
}
|
|
14302
14361
|
}
|
|
14303
14362
|
function readCache(path) {
|
|
@@ -14785,7 +14844,7 @@ var init_ports = __esm(() => {
|
|
|
14785
14844
|
import { existsSync as existsSync16, realpathSync as realpathSync2 } from "node:fs";
|
|
14786
14845
|
import { readFile, realpath } from "node:fs/promises";
|
|
14787
14846
|
import { createConnection } from "node:net";
|
|
14788
|
-
import { dirname as dirname8, join as
|
|
14847
|
+
import { dirname as dirname8, extname as extname2, join as join20, resolve as resolve6 } from "node:path";
|
|
14789
14848
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
14790
14849
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
14791
14850
|
function initializeTimeoutMs() {
|
|
@@ -15463,7 +15522,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
15463
15522
|
} catch {
|
|
15464
15523
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
15465
15524
|
}
|
|
15466
|
-
if (!existsSync16(
|
|
15525
|
+
if (!existsSync16(join20(projectPath, "project.godot"))) {
|
|
15467
15526
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
15468
15527
|
}
|
|
15469
15528
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -15476,6 +15535,11 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
15476
15535
|
} catch {
|
|
15477
15536
|
throw new ArgumentRefusal(`Script file does not exist: res://${contained.relativePath}`);
|
|
15478
15537
|
}
|
|
15538
|
+
if (!scriptPath.toLowerCase().endsWith(".gd")) {
|
|
15539
|
+
const said = `res://${contained.relativePath} is not a GDScript file, and the language server reads .gd files only`;
|
|
15540
|
+
const kind = extname2(scriptPath).toLowerCase();
|
|
15541
|
+
throw new ArgumentRefusal(kind === ".tscn" || kind === ".scn" ? `${said}: scene_tree reads a scene's nodes, and with properties the script each carries.` : kind === ".tres" || kind === ".res" ? `${said}: resource_edit read reads a resource.` : `${said}.`);
|
|
15542
|
+
}
|
|
15479
15543
|
if (!isWithinRoot(projectPath, scriptPath)) {
|
|
15480
15544
|
throw new ArgumentRefusal("scriptPath resolves outside the project root boundary.");
|
|
15481
15545
|
}
|
|
@@ -15624,7 +15688,7 @@ var init_lsp_client = __esm(() => {
|
|
|
15624
15688
|
|
|
15625
15689
|
// src/resources.ts
|
|
15626
15690
|
import { readFileSync as readFileSync17, realpathSync as realpathSync3 } from "node:fs";
|
|
15627
|
-
import { extname as
|
|
15691
|
+
import { extname as extname3, resolve as resolve8 } from "node:path";
|
|
15628
15692
|
function ensureProjectPath(getProjectPath) {
|
|
15629
15693
|
const projectPath = getProjectPath();
|
|
15630
15694
|
if (!projectPath) {
|
|
@@ -15683,7 +15747,7 @@ function parseGodotUri(uri) {
|
|
|
15683
15747
|
}
|
|
15684
15748
|
function ensureAllowedExtension(kind, filePath) {
|
|
15685
15749
|
const allowed = EXTENSIONS[kind];
|
|
15686
|
-
if (!allowed.includes(
|
|
15750
|
+
if (!allowed.includes(extname3(filePath).toLowerCase())) {
|
|
15687
15751
|
throw new Refusal(`godot://${kind}/ reads only ${allowed.join(", ")} files.`);
|
|
15688
15752
|
}
|
|
15689
15753
|
}
|
|
@@ -15981,7 +16045,7 @@ import {
|
|
|
15981
16045
|
statSync as statSync12,
|
|
15982
16046
|
writeFileSync as writeFileSync10
|
|
15983
16047
|
} from "node:fs";
|
|
15984
|
-
import { dirname as dirname9, join as
|
|
16048
|
+
import { dirname as dirname9, join as join22 } from "node:path";
|
|
15985
16049
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
15986
16050
|
function addonMarking(name) {
|
|
15987
16051
|
if (name === RUNTIME_ADDON) {
|
|
@@ -16002,7 +16066,7 @@ function addonsDigest(names, from) {
|
|
|
16002
16066
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
16003
16067
|
continue;
|
|
16004
16068
|
}
|
|
16005
|
-
const path =
|
|
16069
|
+
const path = join22(directory, name);
|
|
16006
16070
|
const inside = `${relative}/${name}`;
|
|
16007
16071
|
if (statSync12(path).isDirectory()) {
|
|
16008
16072
|
walk(path, inside);
|
|
@@ -16016,7 +16080,7 @@ function addonsDigest(names, from) {
|
|
|
16016
16080
|
}
|
|
16017
16081
|
};
|
|
16018
16082
|
for (const name of names) {
|
|
16019
|
-
walk(
|
|
16083
|
+
walk(join22(from, name), name);
|
|
16020
16084
|
}
|
|
16021
16085
|
return hash.digest("hex");
|
|
16022
16086
|
}
|
|
@@ -16031,11 +16095,30 @@ function shippedRuntimeDigest() {
|
|
|
16031
16095
|
function installedEditorDigest(projectPath) {
|
|
16032
16096
|
return digestMarker(projectPath, ADDONS[0]);
|
|
16033
16097
|
}
|
|
16098
|
+
function editorUpgradeNote(sameCode, editors) {
|
|
16099
|
+
if (sameCode) {
|
|
16100
|
+
return ` 1. Nothing for the editor: this upgrade left the editor addons the same code as before,
|
|
16101
|
+
` + ` so it gives an open editor nothing to restart for. editor_status says addonIsStale
|
|
16102
|
+
` + " if the editor is running other code.";
|
|
16103
|
+
}
|
|
16104
|
+
if (editors === null) {
|
|
16105
|
+
return ` 1. An editor of this project that is open is still running the addons it loaded at
|
|
16106
|
+
` + ` startup. Restart it with the editor_launch restart tool, which closes and reopens
|
|
16107
|
+
` + " the window.";
|
|
16108
|
+
}
|
|
16109
|
+
if (editors.length === 0) {
|
|
16110
|
+
return ` 1. Nothing for the editor: none of this project is running, and the next one opened loads
|
|
16111
|
+
these addons.`;
|
|
16112
|
+
}
|
|
16113
|
+
const which = editors.length === 1 ? `The open editor (pid ${editors[0]}) is` : `The open editors (pids ${editors.join(", ")}) are`;
|
|
16114
|
+
return ` 1. ${which} still running the addons loaded at startup. Restart with the
|
|
16115
|
+
` + " editor_launch restart tool, which closes and reopens the window.";
|
|
16116
|
+
}
|
|
16034
16117
|
function installedRuntimeDigest(projectPath) {
|
|
16035
16118
|
return digestMarker(projectPath, RUNTIME_ADDON);
|
|
16036
16119
|
}
|
|
16037
16120
|
function digestMarker(projectPath, addon) {
|
|
16038
|
-
const marker =
|
|
16121
|
+
const marker = join22(projectPath, "addons", addon, DIGEST_MARKER);
|
|
16039
16122
|
if (!existsSync18(marker)) {
|
|
16040
16123
|
return null;
|
|
16041
16124
|
}
|
|
@@ -16043,16 +16126,16 @@ function digestMarker(projectPath, addon) {
|
|
|
16043
16126
|
return said === "" ? null : said;
|
|
16044
16127
|
}
|
|
16045
16128
|
function shippedAddonsDirectory() {
|
|
16046
|
-
return
|
|
16129
|
+
return join22(dirname9(fileURLToPath2(import.meta.url)), "godot", "addons");
|
|
16047
16130
|
}
|
|
16048
16131
|
function shippedOperationsScript() {
|
|
16049
|
-
return
|
|
16132
|
+
return join22(dirname9(fileURLToPath2(import.meta.url)), "godot", "operations", "godot_operations.gd");
|
|
16050
16133
|
}
|
|
16051
16134
|
function shippedLoader() {
|
|
16052
|
-
return
|
|
16135
|
+
return join22(dirname9(fileURLToPath2(import.meta.url)), "godot", "loader", "loader.gd");
|
|
16053
16136
|
}
|
|
16054
16137
|
function loaderState(projectPath) {
|
|
16055
|
-
const copy =
|
|
16138
|
+
const copy = join22(projectPath, ...RUNTIME_AUTOLOAD.path.split("/"));
|
|
16056
16139
|
if (!existsSync18(copy)) {
|
|
16057
16140
|
return "missing";
|
|
16058
16141
|
}
|
|
@@ -16065,13 +16148,13 @@ function writeLoader(projectPath) {
|
|
|
16065
16148
|
if (loaderState(projectPath) === "current") {
|
|
16066
16149
|
return false;
|
|
16067
16150
|
}
|
|
16068
|
-
const copy =
|
|
16151
|
+
const copy = join22(projectPath, ...RUNTIME_AUTOLOAD.path.split("/"));
|
|
16069
16152
|
mkdirSync6(dirname9(copy), { recursive: true });
|
|
16070
16153
|
writeFileSync10(copy, readFileSync18(shippedLoader()));
|
|
16071
16154
|
return true;
|
|
16072
16155
|
}
|
|
16073
16156
|
function removeLoader(projectPath) {
|
|
16074
|
-
const copy =
|
|
16157
|
+
const copy = join22(projectPath, ...RUNTIME_AUTOLOAD.path.split("/"));
|
|
16075
16158
|
if (!existsSync18(copy)) {
|
|
16076
16159
|
return false;
|
|
16077
16160
|
}
|
|
@@ -16084,14 +16167,14 @@ function removeLoader(projectPath) {
|
|
|
16084
16167
|
return true;
|
|
16085
16168
|
}
|
|
16086
16169
|
function shippedListedRunner() {
|
|
16087
|
-
return
|
|
16170
|
+
return join22(dirname9(fileURLToPath2(import.meta.url)), "godot", "tests", "listed.gd");
|
|
16088
16171
|
}
|
|
16089
16172
|
function gdUnit4Unusable(projectPath) {
|
|
16090
16173
|
const marker = "addons/gdUnit4/plugin.cfg";
|
|
16091
|
-
if (!existsSync18(
|
|
16174
|
+
if (!existsSync18(join22(projectPath, ...marker.split("/")))) {
|
|
16092
16175
|
return { missing: true, reason: `gdUnit4 is not installed in this project: no ${marker}` };
|
|
16093
16176
|
}
|
|
16094
|
-
if (!existsSync18(
|
|
16177
|
+
if (!existsSync18(join22(projectPath, ...GDUNIT4_CI_RUNNER.split("/")))) {
|
|
16095
16178
|
return {
|
|
16096
16179
|
missing: false,
|
|
16097
16180
|
reason: `gdUnit4 in this project is older than 6.0, which the shipped runner needs: no ${GDUNIT4_CI_RUNNER}`
|
|
@@ -16102,27 +16185,27 @@ function gdUnit4Unusable(projectPath) {
|
|
|
16102
16185
|
function installAddons(projectPath, from = shippedAddonsDirectory()) {
|
|
16103
16186
|
const installed = [];
|
|
16104
16187
|
for (const name of ADDONS) {
|
|
16105
|
-
const source =
|
|
16106
|
-
if (!existsSync18(
|
|
16188
|
+
const source = join22(from, name);
|
|
16189
|
+
if (!existsSync18(join22(source, addonMarking(name)))) {
|
|
16107
16190
|
throw new Error(`The package holds no ${name} addon at ${source}.`);
|
|
16108
16191
|
}
|
|
16109
|
-
const target =
|
|
16192
|
+
const target = join22(projectPath, "addons", name);
|
|
16110
16193
|
const replaced = existsSync18(target);
|
|
16111
16194
|
rmSync9(target, { recursive: true, force: true });
|
|
16112
16195
|
mkdirSync6(dirname9(target), { recursive: true });
|
|
16113
16196
|
cpSync(source, target, { recursive: true });
|
|
16114
|
-
writeFileSync10(
|
|
16197
|
+
writeFileSync10(join22(target, VERSION_MARKER), `${SERVER_VERSION}
|
|
16115
16198
|
`);
|
|
16116
16199
|
installed.push({ name, path: target, replaced });
|
|
16117
16200
|
}
|
|
16118
|
-
writeFileSync10(
|
|
16201
|
+
writeFileSync10(join22(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
|
|
16119
16202
|
`);
|
|
16120
|
-
writeFileSync10(
|
|
16203
|
+
writeFileSync10(join22(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
|
|
16121
16204
|
`);
|
|
16122
16205
|
return installed;
|
|
16123
16206
|
}
|
|
16124
16207
|
function installedAddonVersion(projectPath) {
|
|
16125
|
-
const marker =
|
|
16208
|
+
const marker = join22(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
16126
16209
|
if (!existsSync18(marker)) {
|
|
16127
16210
|
return null;
|
|
16128
16211
|
}
|
|
@@ -16132,13 +16215,13 @@ function installedAddonVersion(projectPath) {
|
|
|
16132
16215
|
function removeAddons(projectPath) {
|
|
16133
16216
|
const removed = [];
|
|
16134
16217
|
for (const name of ADDONS) {
|
|
16135
|
-
const target =
|
|
16218
|
+
const target = join22(projectPath, "addons", name);
|
|
16136
16219
|
if (existsSync18(target)) {
|
|
16137
16220
|
rmSync9(target, { recursive: true, force: true });
|
|
16138
16221
|
removed.push(target);
|
|
16139
16222
|
}
|
|
16140
16223
|
}
|
|
16141
|
-
const addons =
|
|
16224
|
+
const addons = join22(projectPath, "addons");
|
|
16142
16225
|
if (removed.length > 0 && existsSync18(addons) && readdirSync11(addons).length === 0) {
|
|
16143
16226
|
rmdirSync(addons);
|
|
16144
16227
|
removed.push(addons);
|
|
@@ -16181,7 +16264,7 @@ function loaderAmong(projectPath, named) {
|
|
|
16181
16264
|
}
|
|
16182
16265
|
let source = null;
|
|
16183
16266
|
try {
|
|
16184
|
-
source = readFileSync18(
|
|
16267
|
+
source = readFileSync18(join22(projectPath, path), "utf8");
|
|
16185
16268
|
} catch {}
|
|
16186
16269
|
if (source?.includes(RUNTIME_SCRIPT)) {
|
|
16187
16270
|
return { name: entry, path, exists: true };
|
|
@@ -16237,8 +16320,8 @@ function enabledPlugins(settings) {
|
|
|
16237
16320
|
function inspectProject(projectPath) {
|
|
16238
16321
|
const problems = [];
|
|
16239
16322
|
const addons = ADDONS.map((name) => {
|
|
16240
|
-
const directory =
|
|
16241
|
-
const marker =
|
|
16323
|
+
const directory = join22(projectPath, "addons", name);
|
|
16324
|
+
const marker = join22(directory, VERSION_MARKER);
|
|
16242
16325
|
const installed = existsSync18(directory);
|
|
16243
16326
|
const version = existsSync18(marker) ? readFileSync18(marker, "utf8").trim() : null;
|
|
16244
16327
|
const current = version === SERVER_VERSION;
|
|
@@ -16249,7 +16332,7 @@ function inspectProject(projectPath) {
|
|
|
16249
16332
|
}
|
|
16250
16333
|
return { name, installed, version, current };
|
|
16251
16334
|
});
|
|
16252
|
-
const settings = parseProjectGodot(readFileSync18(
|
|
16335
|
+
const settings = parseProjectGodot(readFileSync18(join22(projectPath, "project.godot"), "utf8"));
|
|
16253
16336
|
const pluginsEnabled = enabledPlugins(settings);
|
|
16254
16337
|
for (const name of EDITOR_PLUGINS) {
|
|
16255
16338
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -16273,7 +16356,7 @@ function inspectProject(projectPath) {
|
|
|
16273
16356
|
const ignoredAutoloads = [];
|
|
16274
16357
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
16275
16358
|
for (const [name, path] of named) {
|
|
16276
|
-
if (!existsSync18(
|
|
16359
|
+
if (!existsSync18(join22(projectPath, path))) {
|
|
16277
16360
|
if (!addonMissing(path)) {
|
|
16278
16361
|
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`);
|
|
16279
16362
|
}
|
|
@@ -16502,6 +16585,11 @@ var init_tool_definitions = __esm(() => {
|
|
|
16502
16585
|
},
|
|
16503
16586
|
nodePath: { type: "string", description: "Target node, relative to the AnimationPlayer root." },
|
|
16504
16587
|
property: { type: "string", description: "For property tracks: the property to animate." },
|
|
16588
|
+
update: {
|
|
16589
|
+
type: "string",
|
|
16590
|
+
enum: ["continuous", "discrete", "capture"],
|
|
16591
|
+
description: "For property tracks: how the value moves between keys. continuous blends toward the next key, so a bool or an int changes halfway to it; discrete holds each key's value until the next; capture blends from the value the property holds when playing starts. Without it a new track is made as the editor makes one: as the RESET animation's track for the same property, otherwise continuous for a float, vector, colour or transform and discrete for anything else. The answer gives the track's update."
|
|
16592
|
+
},
|
|
16505
16593
|
method: {
|
|
16506
16594
|
type: "string",
|
|
16507
16595
|
description: "For method tracks: the method to call. A method track holds every method key for its node, so a key calling another method joins the node's track, with existingTrack true. The answer's methodFound says whether the target node has it; a method it lacks is still added, while a nodePath that reaches no node is refused, since it may be written next, and until it exists each call the track makes fails. A key whose args the method cannot take (too few or too many, or an object where it takes a value or the reverse) is written too, and answers argumentsFit false with misfit saying why. remove_track: only the keys calling this method."
|
|
@@ -16642,7 +16730,12 @@ var init_tool_definitions = __esm(() => {
|
|
|
16642
16730
|
include: {
|
|
16643
16731
|
type: "array",
|
|
16644
16732
|
items: { type: "string", enum: [...PROJECT_INFO_SECTIONS] },
|
|
16645
|
-
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."
|
|
16733
|
+
description: "Extra sections: registered autoloads, the classes the project's scripts declare with class_name, each with its script, read from the files (those an addon declares counted per addon under inAddons, unless addonClasses names the addon), 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."
|
|
16734
|
+
},
|
|
16735
|
+
addonClasses: {
|
|
16736
|
+
type: "array",
|
|
16737
|
+
items: { type: "string" },
|
|
16738
|
+
description: "For classes: the addons, by their folder under res://addons, whose classes are listed by name and script like the project's own, rather than counted under inAddons. Default none: a game with two large addons answered 686 classes, 19 of them its own."
|
|
16646
16739
|
},
|
|
16647
16740
|
preset: { type: "string", description: "For validation: the export preset to validate against." },
|
|
16648
16741
|
detail: {
|
|
@@ -16673,7 +16766,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16673
16766
|
prefix: {
|
|
16674
16767
|
type: "string",
|
|
16675
16768
|
ops: ["get"],
|
|
16676
|
-
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.'
|
|
16769
|
+
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.'
|
|
16677
16770
|
},
|
|
16678
16771
|
value: {
|
|
16679
16772
|
blank: true,
|
|
@@ -16854,12 +16947,12 @@ var init_tool_definitions = __esm(() => {
|
|
|
16854
16947
|
direction: {
|
|
16855
16948
|
type: "string",
|
|
16856
16949
|
enum: ["forward", "reverse"],
|
|
16857
|
-
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."
|
|
16950
|
+
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."
|
|
16858
16951
|
},
|
|
16859
16952
|
depth: {
|
|
16860
16953
|
type: "integer",
|
|
16861
16954
|
minimum: 1,
|
|
16862
|
-
description: "forward: how many levels to follow, 1 being what the file loads itself. Default unlimited."
|
|
16955
|
+
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."
|
|
16863
16956
|
},
|
|
16864
16957
|
includeBuiltin: {
|
|
16865
16958
|
type: "boolean",
|
|
@@ -17135,7 +17228,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17135
17228
|
name: "scene_node",
|
|
17136
17229
|
title: "Scene nodes",
|
|
17137
17230
|
description: `One node in a scene file: add, read, set, duplicate, reparent or delete it, paint TileMap cells, or bake a navigation region. Any ClassDB node type or script class can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {"name": "Boss"} renames a node, and answers nodePath as its new path. A rename or a reparent carries the node paths naming the node, as the editor's rename does: NodePath values on nodes (in arrays, dictionaries, built-in resources, %unique paths) and the tracks its players' animations hold, saving a library kept in a .tres of its own, and answers each rewrite under retargeted and each path it left under notRetargeted. The move is followed into every other .tscn that instances or inherits this scene at any depth: the editable children, values, added nodes and connections named by the old path are rewritten, since Godot finds a moved node's values by unique_id but editable children only by path and drops the values kept below them on the next save, and that scene's node paths and tracks are carried as above. A delete rewrites no path, and answers under stillNaming what named the deleted node and now names nothing. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene. A node this scene inherits takes values of this scene's own, and so does a node inside an instance of another scene once that instance's editableChildren is true; neither can be moved, renamed, copied or deleted, since the other scene makes it again on load. ${WITHOUT_AN_EDITOR}`,
|
|
17138
|
-
answer: "get answers nodeType, the node's engine class, with scriptClass when its script declares a class_name; nodePath, its path from the root however it was named; setHere, the properties this scene's own file sets on it; and valuesFrom, the scene every other value comes from, when another scene makes the node. A set that gives a property the value the node has without it (what the scene it comes from gives, or its default) leaves it out of the file, as the editor does, and answers it under notStored, with notStoredNote saying what it follows now. retargeted lists each path a rename or a reparent rewrote, as {scenePath, nodePath, property or animation and track, from, to}, and notRetargeted each it left, with a reason: a track kept in another scene or an imported file, or a value another scene gives that the move left naming nothing. followed names the other scenes a move rewrote, and notFollowed any left as they were, with why. scriptsNaming lists each line of a script that reads a node a rename, a reparent or a delete took by the path it had, as {scenePath, nodePath, script, line, from, to}: the script is on the node at nodePath, or is one that node's script extends, in this scene or one instancing or inheriting it, and to is the path that reaches the node from there now, absent after a delete. It reads $Path, %Unique, and a string handed to get_node, get_node_or_null or has_node on the node itself; a path built at run time is not seen. Scripts are left as they were, as the editor's own rename leaves them, so each line listed fails until it is changed. A 2D bake_navigation that read no obstacle says in its note that all its outlines enclose is walkable, and names the nodes giving obstacles that sit under no region. stillNaming lists each node path and track, here and in the scenes instancing or inheriting this one, that named a deleted node, and deletedWithIt each node of this scene's own that went with it, the nodes inside an instance going with the instance. groups lists, sorted, every group a node is in, on a get, an add that gave addGroups and every set that changes them; get adds groupsHere, the groups this file puts it in, when another scene puts it in others, and layers, each layer mask it answers (collision_layer, collision_mask, visibility_layer) as the layers it holds, {layer, name} with the name project.godot gives the layer. Each of these lists is answered only when it names anything. add and set answer each property given under properties as the node holds it once written; alsoChanged gives the other properties the engine changed in step with a set, as a Control's offsets with its size, and those a reparent changed to keep the node where it is drawn, as {was, now}; namesNoNode, each node path written naming no node; sharedWith, for a property written through a resource built into the scene, each other {node, property} holding it, which changed too; setByScript, what a script that is not a tool set as a headless write ran, which the file does not hold; loadsAs, a value given that the file holds and the node's own script changes as the scene loads, as it loads. get adds placement, a Node3D's position, rotation_degrees and scale from its transform. " + ALSO_SAVED,
|
|
17231
|
+
answer: "get answers nodeType, the node's engine class, with scriptClass when its script declares a class_name; nodePath, its path from the root however it was named; setHere, the properties this scene's own file sets on it; and valuesFrom, the scene every other value comes from, when another scene makes the node. A set that gives a property the value the node has without it (what the scene it comes from gives, or its default) leaves it out of the file, as the editor does, and answers it under notStored, with notStoredNote saying what it follows now. retargeted lists each path a rename or a reparent rewrote, as {scenePath, nodePath, property or animation and track, from, to}, and notRetargeted each it left, with a reason: a track kept in another scene or an imported file, or a value another scene gives that the move left naming nothing. followed names the other scenes a move rewrote, and notFollowed any left as they were, with why. scriptsNaming lists each line of a script that reads a node a rename, a reparent or a delete took by the path it had, as {scenePath, nodePath, script, line, from, to}: the script is on the node at nodePath, or is one that node's script extends, in this scene or one instancing or inheriting it, and to is the path that reaches the node from there now, absent after a delete. It reads $Path, %Unique, and a string handed to get_node, get_node_or_null or has_node on the node itself; a path built at run time is not seen. Scripts are left as they were, as the editor's own rename leaves them, so each line listed fails until it is changed. A 2D bake_navigation that read no obstacle says in its note that all its outlines enclose is walkable, and names the nodes giving obstacles that sit under no region. stillNaming lists each node path and track, here and in the scenes instancing or inheriting this one, that named a deleted node; a property typed as a node, or an array or dictionary of nodes, holds the node itself, so the one that held a deleted node holds null there, as the editor saves it, and its entry adds cleared: true. deletedWithIt lists each node of this scene's own that went with it, the nodes inside an instance going with the instance. groups lists, sorted, every group a node is in, on a get, an add that gave addGroups and every set that changes them; get adds groupsHere, the groups this file puts it in, when another scene puts it in others, and layers, each layer mask it answers (collision_layer, collision_mask, visibility_layer) as the layers it holds, {layer, name} with the name project.godot gives the layer. Each of these lists is answered only when it names anything. add and set answer each property given under properties as the node holds it once written; alsoChanged gives the other properties the engine changed in step with a set, as a Control's offsets with its size, and those a reparent changed to keep the node where it is drawn or that the new parent sets, as a container gives a Control its layout_mode, as {was, now}; namesNoNode, each node path written naming no node; sharedWith, for a property written through a resource built into the scene, each other {node, property} holding it, which changed too; setByScript, what a script that is not a tool set as a headless write ran, which the file does not hold; loadsAs, a value given that the file holds and the node's own script changes as the scene loads, as it loads. get adds placement, a Node3D's position, rotation_degrees and scale from its transform. " + ALSO_SAVED,
|
|
17139
17232
|
parameters: {
|
|
17140
17233
|
projectPath: PROJECT_PATH,
|
|
17141
17234
|
scenePath: SCENE_PATH,
|
|
@@ -17177,7 +17270,11 @@ var init_tool_definitions = __esm(() => {
|
|
|
17177
17270
|
ops: ["set"],
|
|
17178
17271
|
description: "set: groups to take the node out of. One it is not in is refused, and so is one another scene puts it in (the scene that makes it, instances it or is inherited here), which only that scene can take it out of."
|
|
17179
17272
|
},
|
|
17180
|
-
newName: {
|
|
17273
|
+
newName: {
|
|
17274
|
+
type: "string",
|
|
17275
|
+
ops: ["duplicate"],
|
|
17276
|
+
description: "duplicate: the copy's name. The copy has the nodes under the original and their connections, as the editor's Duplicate makes it: each connection keeps its flags, binds and unbinds, and one between two copied nodes joins the copies."
|
|
17277
|
+
},
|
|
17181
17278
|
newParentPath: {
|
|
17182
17279
|
type: "string",
|
|
17183
17280
|
ops: ["reparent"],
|
|
@@ -17249,7 +17346,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17249
17346
|
{
|
|
17250
17347
|
name: "scene_signal",
|
|
17251
17348
|
title: "Scene signals",
|
|
17252
|
-
description: `Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connection carries binds, the values passed after the signal's own, and unbinds, how many of the signal's own are dropped first, as the editor's connect dialog offers them; each is answered only when there is one. A connect of a pair already connected says alreadyConnected, with flagsBefore, bindsBefore and unbindsBefore when it changed them, and keeps the binds and unbinds it had unless the call gives new ones. A connect to a method the target does not have is still written, as the editor's own dialog allows, since the method may be written next; methodFound false says so, and until the method exists the signal fails each time it is emitted. One whose method cannot take what the signal passes, with its binds and unbinds (too few or too many arguments, an object where the method takes a value or the reverse, or an engine class unrelated to the one it takes), is written too, and answers argumentsFit false with misfit saying why; list gives each connection its methodFound, and misfit when it has one. list marks editable false on a connection a scene this one instances or inherits makes, on its own root as well as inside it, and names that scene under madeIn; only that scene can change it, so a disconnect of one is refused. ${WITHOUT_AN_EDITOR} ${ALSO_SAVED}`,
|
|
17349
|
+
description: `Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connection carries binds, the values passed after the signal's own, and unbinds, how many of the signal's own are dropped first, as the editor's connect dialog offers them; each is answered only when there is one. A connect of a pair already connected says alreadyConnected, with flagsBefore, bindsBefore and unbindsBefore when it changed them, and keeps the binds and unbinds it had unless the call gives new ones. A connect to a method the target does not have is still written, as the editor's own dialog allows, since the method may be written next; methodFound false says so, and until the method exists the signal fails each time it is emitted. That holds only without binds and unbinds: the engine makes a connection that binds or unbinds arguments only to a method its target has, and a scene loads without one aimed elsewhere, so such a connect is refused. A file can hold one already, written by hand or before its method was renamed: the engine loads the scene without it and a save drops it, so every tool that writes a .tscn puts it back as the file held it and says so under unmadeConnections, a write to a binary .scn holding one is refused, and a disconnect takes it out. One whose method cannot take what the signal passes, with its binds and unbinds (too few or too many arguments, an object where the method takes a value or the reverse, or an engine class unrelated to the one it takes), is written too, and answers argumentsFit false with misfit saying why; list gives each connection its methodFound, and misfit when it has one. list marks editable false on a connection a scene this one instances or inherits makes, on its own root as well as inside it, and names that scene under madeIn; only that scene can change it, so a disconnect of one is refused. ${WITHOUT_AN_EDITOR} ${ALSO_SAVED}`,
|
|
17253
17350
|
parameters: {
|
|
17254
17351
|
projectPath: PROJECT_PATH,
|
|
17255
17352
|
scenePath: SCENE_PATH,
|
|
@@ -17545,7 +17642,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17545
17642
|
operations: {
|
|
17546
17643
|
structure: {
|
|
17547
17644
|
effect: "read",
|
|
17548
|
-
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`,
|
|
17645
|
+
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`,
|
|
17549
17646
|
requires: []
|
|
17550
17647
|
},
|
|
17551
17648
|
symbols: {
|
|
@@ -18056,7 +18153,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18056
18153
|
property: {
|
|
18057
18154
|
type: "string",
|
|
18058
18155
|
ops: ["property", "find"],
|
|
18059
|
-
description: `property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, "_game:clock:speed", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, "_game:run:roster:0:traits", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A key holding a colon, a bracket or a quote is written in double quotes, 'stock:"base:dew"', as a refusal listing the keys writes it. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A vector, a colour, a rectangle or a transform is stepped into by its components as GDScript names them, so one axis is one number: "size:x", "global_position:y", "modulate:a", "global_transform:origin:z"; a name the value does not have is refused, naming the ones it has. It also answers the calls that read it with no arguments, by its type, such as "linear_velocity:length()" or "global_transform:get_origin()"; one it does not answer is refused, naming those it does. A string answers such calls too, "text:length()" or "text:strip_edges()", and "text:to_utf32_buffer():to_int32_array()" gives its code points, which tell a non-breaking space (160) from the space an answer shows in its place. A step written as a call, "get_viewport():gui_get_focus_owner()", calls a method that only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. An engine method that reads takes literal arguments in its brackets, words in double quotes, numbers, true, false or null, so a control's theme is read where it is drawn: 'get_theme_stylebox("panel"):content_margin_top' or 'get_theme_constant("separation")'. A game method is given none in a path, since nothing marks one as only reading; runtime_invoke call takes those. A list or a map answers the calls that read it and take no arguments, "_game:run:board:size()" or "_game:inbox:keys()", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead.`
|
|
18156
|
+
description: `property: which one to read, or a constant the node's script or a script it extends declares. find: read this one off every node matched, so a panel of labels is one call rather than one per label. Colons read through what a node holds, "_game:clock:speed", which is where a game keeps what is worth asking about. A number or a key steps into a list or a map, "_game:run:roster:0:traits", which is how the lists a game keeps its state in are walked: a roster, a board, an in-tray. A key holding a colon, a bracket or a quote is written in double quotes, 'stock:"base:dew"', as a refusal listing the keys writes it. A packed list, such as a PackedStringArray, steps the same way. A negative number counts from the end. A vector, a colour, a rectangle or a transform is stepped into by its components as GDScript names them, so one axis is one number: "size:x", "global_position:y", "modulate:a", "global_transform:origin:z"; a name the value does not have is refused, naming the ones it has. It also answers the calls that read it with no arguments, by its type, such as "linear_velocity:length()" or "global_transform:get_origin()"; one it does not answer is refused, naming those it does. A string answers such calls too, "text:length()" or "text:strip_edges()", and "text:to_utf32_buffer():to_int32_array()" gives its code points, which tell a non-breaking space (160) from the space an answer shows in its place. A step written as a call, "get_viewport():gui_get_focus_owner()", calls a method that only reads (an engine method the engine marks as reading, or a game method not declared -> void, so queue_free() is refused) and walks into what it returned, which is how a question only a method answers is read in one call: which control holds the focus in the viewport this one is in. An engine method that reads takes literal arguments in its brackets, words in double quotes, numbers, true, false or null, so a control's theme is read where it is drawn: 'get_theme_stylebox("panel"):content_margin_top' or 'get_theme_constant("separation")'. A game method is given none in a path, since nothing marks one as only reading; runtime_invoke call takes those. A list or a map answers the calls that read it and take no arguments, "_game:run:board:size()" or "_game:inbox:keys()", and not those that change it, such as clear() or sort(). A step that is not there is named, and says what was there instead.`
|
|
18060
18157
|
},
|
|
18061
18158
|
depth: {
|
|
18062
18159
|
type: "integer",
|
|
@@ -18814,7 +18911,7 @@ var init_tool_reference = __esm(() => {
|
|
|
18814
18911
|
|
|
18815
18912
|
// src/suite-plan.ts
|
|
18816
18913
|
import { existsSync as existsSync22, mkdirSync as mkdirSync9, readFileSync as readFileSync21 } from "node:fs";
|
|
18817
|
-
import { dirname as dirname11, join as
|
|
18914
|
+
import { dirname as dirname11, join as join26, posix as posix3 } from "node:path";
|
|
18818
18915
|
function within(path, folder) {
|
|
18819
18916
|
return path === folder || path.startsWith(`${folder}/`);
|
|
18820
18917
|
}
|
|
@@ -18830,7 +18927,7 @@ class ProjectScripts {
|
|
|
18830
18927
|
this.projectPath = projectPath;
|
|
18831
18928
|
let settings = {};
|
|
18832
18929
|
try {
|
|
18833
|
-
settings = parseProjectGodot(readFileSync21(
|
|
18930
|
+
settings = parseProjectGodot(readFileSync21(join26(projectPath, "project.godot"), "utf8"))["gdharness"] ?? {};
|
|
18834
18931
|
} catch {}
|
|
18835
18932
|
const [section, key] = [NOT_SEARCHED_SETTING, GUESS_WINDOW_SETTING].map((setting) => setting.slice("gdharness/".length));
|
|
18836
18933
|
const listed = settings[section ?? ""];
|
|
@@ -40848,12 +40945,12 @@ var init_stdio = __esm(() => {
|
|
|
40848
40945
|
|
|
40849
40946
|
// src/autoload-failure.ts
|
|
40850
40947
|
import { existsSync as existsSync24, readFileSync as readFileSync24 } from "node:fs";
|
|
40851
|
-
import { join as
|
|
40948
|
+
import { join as join29 } from "node:path";
|
|
40852
40949
|
function scriptsAutoloaded(projectPath) {
|
|
40853
40950
|
const scripts = new Map;
|
|
40854
40951
|
let settings;
|
|
40855
40952
|
try {
|
|
40856
|
-
settings = parseProjectGodot(readFileSync24(
|
|
40953
|
+
settings = parseProjectGodot(readFileSync24(join29(projectPath, "project.godot"), "utf8"));
|
|
40857
40954
|
} catch {
|
|
40858
40955
|
return scripts;
|
|
40859
40956
|
}
|
|
@@ -40862,7 +40959,7 @@ function scriptsAutoloaded(projectPath) {
|
|
|
40862
40959
|
scripts.set(`res://${path}`, name);
|
|
40863
40960
|
continue;
|
|
40864
40961
|
}
|
|
40865
|
-
const scene =
|
|
40962
|
+
const scene = join29(projectPath, path);
|
|
40866
40963
|
if (!existsSync24(scene)) {
|
|
40867
40964
|
continue;
|
|
40868
40965
|
}
|
|
@@ -40889,7 +40986,7 @@ var init_autoload_failure = __esm(() => {
|
|
|
40889
40986
|
import { existsSync as existsSync25, readdirSync as readdirSync15, readFileSync as readFileSync25, statSync as statSync15, unlinkSync as unlinkSync3 } from "node:fs";
|
|
40890
40987
|
import { createConnection as createConnection2 } from "node:net";
|
|
40891
40988
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
40892
|
-
import { dirname as dirname14, join as
|
|
40989
|
+
import { dirname as dirname14, join as join30, resolve as resolve11 } from "node:path";
|
|
40893
40990
|
import { setTimeout as delay5 } from "node:timers/promises";
|
|
40894
40991
|
function runtimeDirectory(variables = process.env) {
|
|
40895
40992
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -40897,19 +40994,19 @@ function runtimeDirectory(variables = process.env) {
|
|
|
40897
40994
|
return explicit;
|
|
40898
40995
|
}
|
|
40899
40996
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
40900
|
-
return
|
|
40997
|
+
return join30(perUser ?? tmpdir5(), "gdharness");
|
|
40901
40998
|
}
|
|
40902
40999
|
function runtimeDirectories(variables = process.env) {
|
|
40903
41000
|
const candidates = [runtimeDirectory(variables)];
|
|
40904
41001
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
40905
41002
|
for (const base of fallbacks) {
|
|
40906
|
-
candidates.push(
|
|
41003
|
+
candidates.push(join30(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
40907
41004
|
}
|
|
40908
41005
|
return [...new Set(candidates.map((path) => resolve11(path)))];
|
|
40909
41006
|
}
|
|
40910
41007
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
40911
41008
|
for (const directory of directories) {
|
|
40912
|
-
const path =
|
|
41009
|
+
const path = join30(directory, `runtime-${pid}.log`);
|
|
40913
41010
|
if (existsSync25(path)) {
|
|
40914
41011
|
return path;
|
|
40915
41012
|
}
|
|
@@ -41065,14 +41162,14 @@ function announcedIn(directory) {
|
|
|
41065
41162
|
for (const entry of readdirSync15(directory)) {
|
|
41066
41163
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
41067
41164
|
if (report) {
|
|
41068
|
-
sweepErrorReport(
|
|
41165
|
+
sweepErrorReport(join30(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
41069
41166
|
continue;
|
|
41070
41167
|
}
|
|
41071
41168
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
41072
41169
|
if (!match) {
|
|
41073
41170
|
continue;
|
|
41074
41171
|
}
|
|
41075
|
-
const file =
|
|
41172
|
+
const file = join30(directory, entry);
|
|
41076
41173
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
41077
41174
|
const running = alive(pid);
|
|
41078
41175
|
entries.push({ file, pid, running });
|
|
@@ -41100,7 +41197,7 @@ function announcedIn(directory) {
|
|
|
41100
41197
|
found.push(announced);
|
|
41101
41198
|
}
|
|
41102
41199
|
const held = new Set(entries.map((one) => one.file));
|
|
41103
|
-
const here =
|
|
41200
|
+
const here = join30(directory, ".");
|
|
41104
41201
|
for (const file of verdicts.keys()) {
|
|
41105
41202
|
if (dirname14(file) === here && !held.has(file)) {
|
|
41106
41203
|
verdicts.delete(file);
|
|
@@ -41109,7 +41206,7 @@ function announcedIn(directory) {
|
|
|
41109
41206
|
return found;
|
|
41110
41207
|
}
|
|
41111
41208
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
41112
|
-
const files = directories.map((directory) =>
|
|
41209
|
+
const files = directories.map((directory) => join30(directory, `runtime-${pid}.json`)).filter((file) => existsSync25(file));
|
|
41113
41210
|
const first = files[0];
|
|
41114
41211
|
if (first === undefined) {
|
|
41115
41212
|
return;
|
|
@@ -41529,9 +41626,9 @@ var init_runtime_client = __esm(() => {
|
|
|
41529
41626
|
|
|
41530
41627
|
// src/boot-note.ts
|
|
41531
41628
|
import { mkdirSync as mkdirSync11, readFileSync as readFileSync26, writeFileSync as writeFileSync13 } from "node:fs";
|
|
41532
|
-
import { dirname as dirname15, join as
|
|
41629
|
+
import { dirname as dirname15, join as join31 } from "node:path";
|
|
41533
41630
|
function bootNotePath(projectPath) {
|
|
41534
|
-
return
|
|
41631
|
+
return join31(projectPath, ".godot", "gdharness-boot.json");
|
|
41535
41632
|
}
|
|
41536
41633
|
function readScenes(projectPath) {
|
|
41537
41634
|
try {
|
|
@@ -41578,9 +41675,9 @@ var init_boot_note = __esm(() => {
|
|
|
41578
41675
|
|
|
41579
41676
|
// src/breakpoint-note.ts
|
|
41580
41677
|
import { mkdirSync as mkdirSync12, readFileSync as readFileSync27, rmSync as rmSync13, writeFileSync as writeFileSync14 } from "node:fs";
|
|
41581
|
-
import { dirname as dirname16, isAbsolute as isAbsolute7, join as
|
|
41678
|
+
import { dirname as dirname16, isAbsolute as isAbsolute7, join as join32, relative as relative6 } from "node:path";
|
|
41582
41679
|
function breakpointNotePath(projectPath) {
|
|
41583
|
-
return
|
|
41680
|
+
return join32(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
41584
41681
|
}
|
|
41585
41682
|
function writeBreakpointNote(projectPath, held) {
|
|
41586
41683
|
const path = breakpointNotePath(projectPath);
|
|
@@ -41621,7 +41718,7 @@ function readBreakpointNote(projectPath) {
|
|
|
41621
41718
|
}
|
|
41622
41719
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
41623
41720
|
if (lines.length > 0) {
|
|
41624
|
-
held.push({ scriptPath: realAsFarAsItExists(
|
|
41721
|
+
held.push({ scriptPath: realAsFarAsItExists(join32(projectPath, script)), lines });
|
|
41625
41722
|
}
|
|
41626
41723
|
}
|
|
41627
41724
|
return held;
|
|
@@ -41640,9 +41737,9 @@ var init_breakpoint_note = __esm(() => {
|
|
|
41640
41737
|
|
|
41641
41738
|
// src/class-note.ts
|
|
41642
41739
|
import { mkdirSync as mkdirSync13, readFileSync as readFileSync28 } from "node:fs";
|
|
41643
|
-
import { dirname as dirname17, join as
|
|
41740
|
+
import { dirname as dirname17, join as join33 } from "node:path";
|
|
41644
41741
|
function classNotePath(projectPath) {
|
|
41645
|
-
return
|
|
41742
|
+
return join33(projectPath, ".godot", "gdharness-classes.json");
|
|
41646
41743
|
}
|
|
41647
41744
|
function readClassNote(projectPath) {
|
|
41648
41745
|
try {
|
|
@@ -41689,9 +41786,9 @@ var init_class_note = __esm(() => {
|
|
|
41689
41786
|
|
|
41690
41787
|
// src/config-pin.ts
|
|
41691
41788
|
import { existsSync as existsSync26, readFileSync as readFileSync29 } from "node:fs";
|
|
41692
|
-
import { join as
|
|
41789
|
+
import { join as join34 } from "node:path";
|
|
41693
41790
|
function versionsNamedInConfig(projectPath) {
|
|
41694
|
-
const config =
|
|
41791
|
+
const config = join34(projectPath, ".mcp.json");
|
|
41695
41792
|
if (!existsSync26(config)) {
|
|
41696
41793
|
return [];
|
|
41697
41794
|
}
|
|
@@ -41719,9 +41816,9 @@ var init_config_pin = __esm(() => {
|
|
|
41719
41816
|
|
|
41720
41817
|
// src/cut-test-run.ts
|
|
41721
41818
|
import { existsSync as existsSync27, mkdirSync as mkdirSync14, readFileSync as readFileSync30, rmSync as rmSync14, writeFileSync as writeFileSync15 } from "node:fs";
|
|
41722
|
-
import { dirname as dirname18, join as
|
|
41819
|
+
import { dirname as dirname18, join as join35 } from "node:path";
|
|
41723
41820
|
function keptAt(projectPath) {
|
|
41724
|
-
return
|
|
41821
|
+
return join35(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
41725
41822
|
}
|
|
41726
41823
|
function keepCutTestRun(projectPath, run) {
|
|
41727
41824
|
const path = keptAt(projectPath);
|
|
@@ -41753,6 +41850,30 @@ function cutTestRunNote(run) {
|
|
|
41753
41850
|
}
|
|
41754
41851
|
var init_cut_test_run = () => {};
|
|
41755
41852
|
|
|
41853
|
+
// src/godot-variant.ts
|
|
41854
|
+
var FLAG_64, MOST_MESSAGE_BYTES;
|
|
41855
|
+
var init_godot_variant = __esm(() => {
|
|
41856
|
+
FLAG_64 = 1 << 16;
|
|
41857
|
+
MOST_MESSAGE_BYTES = (8 << 20) + 4;
|
|
41858
|
+
});
|
|
41859
|
+
|
|
41860
|
+
// src/game-debugger.ts
|
|
41861
|
+
var PAUSE_MS = 30000, STEP_MS = 1e4, RUNTIME_ADDON2 = "res://addons/gdharness_runtime/", RUNTIME_AUTOLOAD2, READ_MS = 5000, EVALUATE_MS = 60000, REPLIES;
|
|
41862
|
+
var init_game_debugger = __esm(() => {
|
|
41863
|
+
init_godot_variant();
|
|
41864
|
+
init_setup();
|
|
41865
|
+
RUNTIME_AUTOLOAD2 = `${RUNTIME_ADDON2}runtime_autoload.gd`;
|
|
41866
|
+
REPLIES = new Set([
|
|
41867
|
+
"debug_enter",
|
|
41868
|
+
"debug_exit",
|
|
41869
|
+
"stack_dump",
|
|
41870
|
+
"stack_frame_vars",
|
|
41871
|
+
"stack_frame_var",
|
|
41872
|
+
"evaluation_return",
|
|
41873
|
+
"scene:inspect_object"
|
|
41874
|
+
]);
|
|
41875
|
+
});
|
|
41876
|
+
|
|
41756
41877
|
// src/dap_client.ts
|
|
41757
41878
|
import { createConnection as createConnection3 } from "node:net";
|
|
41758
41879
|
import { setTimeout as delay6 } from "node:timers/promises";
|
|
@@ -41795,11 +41916,13 @@ class GodotDAPClient {
|
|
|
41795
41916
|
onBreakpointsChanged = null;
|
|
41796
41917
|
scopesPatienceMs;
|
|
41797
41918
|
stepPatienceMs;
|
|
41919
|
+
evaluatePatienceMs;
|
|
41798
41920
|
constructor(port = portFromEnv("GDHARNESS_DAP_PORT", DEFAULT_DAP_PORT), host = "127.0.0.1", patience = {}) {
|
|
41799
41921
|
this.port = port;
|
|
41800
41922
|
this.host = host;
|
|
41801
41923
|
this.scopesPatienceMs = patience.scopesMs ?? SCOPES_TIMEOUT_MS;
|
|
41802
41924
|
this.stepPatienceMs = patience.stepMs ?? STEP_LANDS_WITHIN_MS;
|
|
41925
|
+
this.evaluatePatienceMs = patience.evaluateMs ?? EVALUATE_MS;
|
|
41803
41926
|
this.pendingRequests = new Map;
|
|
41804
41927
|
}
|
|
41805
41928
|
async connect() {
|
|
@@ -42303,7 +42426,7 @@ class GodotDAPClient {
|
|
|
42303
42426
|
if (members?.variables?.some((variable) => variable["name"] === "self") !== true) {
|
|
42304
42427
|
throw new ReadRefusal(`Frame ${frame} has no instance of its script to evaluate on, as a static function called on its class has none, and the engine lets the game go on rather than answer one there, so it was not asked. Evaluate in a frame below it, or read this one's locals with debug_state variables`);
|
|
42305
42428
|
}
|
|
42306
|
-
const deadline = Date.now() + this.
|
|
42429
|
+
const deadline = Date.now() + this.evaluatePatienceMs;
|
|
42307
42430
|
while (Date.now() < deadline) {
|
|
42308
42431
|
try {
|
|
42309
42432
|
const answered = await this.sendRequest("evaluate", { expression, frameId: frame, context: "watch" }, VARIABLES_POLL_TIMEOUT_MS);
|
|
@@ -42315,7 +42438,7 @@ class GodotDAPClient {
|
|
|
42315
42438
|
} catch {}
|
|
42316
42439
|
await delay6(200);
|
|
42317
42440
|
}
|
|
42318
|
-
throw new ReadRefusal(`What ${expression} comes to did not arrive from the game within ${this.
|
|
42441
|
+
throw new ReadRefusal(`What ${expression} comes to did not arrive from the game within ${this.evaluatePatienceMs / 1000}s. It comes from the game through the editor, and the first expression a game evaluates loads the script of every global class in the project first, static initialisers included, so one of those may still be running: ask again`);
|
|
42319
42442
|
}
|
|
42320
42443
|
isConnected() {
|
|
42321
42444
|
return this.connected;
|
|
@@ -42462,6 +42585,7 @@ var init_dap_client = __esm(() => {
|
|
|
42462
42585
|
init_answer_json();
|
|
42463
42586
|
init_errors();
|
|
42464
42587
|
init_framing();
|
|
42588
|
+
init_game_debugger();
|
|
42465
42589
|
init_ports();
|
|
42466
42590
|
ReadRefusal = class ReadRefusal extends Refusal {
|
|
42467
42591
|
};
|
|
@@ -42479,12 +42603,12 @@ import {
|
|
|
42479
42603
|
statSync as statSync16,
|
|
42480
42604
|
writeFileSync as writeFileSync16
|
|
42481
42605
|
} from "node:fs";
|
|
42482
|
-
import { dirname as dirname19, join as
|
|
42606
|
+
import { dirname as dirname19, join as join36 } from "node:path";
|
|
42483
42607
|
function editorLogPath(projectPath) {
|
|
42484
|
-
return
|
|
42608
|
+
return join36(projectPath, ".godot", "gdharness-editor.log");
|
|
42485
42609
|
}
|
|
42486
42610
|
function editorLogNotePath(projectPath) {
|
|
42487
|
-
return
|
|
42611
|
+
return join36(projectPath, ".godot", "gdharness-editor.json");
|
|
42488
42612
|
}
|
|
42489
42613
|
function readEditorLogNote(projectPath) {
|
|
42490
42614
|
try {
|
|
@@ -43523,7 +43647,7 @@ var init_rename = __esm(() => {
|
|
|
43523
43647
|
|
|
43524
43648
|
// src/rename-symbol.ts
|
|
43525
43649
|
import { existsSync as existsSync30, mkdirSync as mkdirSync17, readdirSync as readdirSync16, realpathSync as realpathSync4, rmSync as rmSync16, writeFileSync as writeFileSync18 } from "node:fs";
|
|
43526
|
-
import { basename as basename9, dirname as dirname21, join as
|
|
43650
|
+
import { basename as basename9, dirname as dirname21, join as join37, relative as relative7, resolve as resolve12 } from "node:path";
|
|
43527
43651
|
function resourcePathOf(projectPath, absolute) {
|
|
43528
43652
|
const inside = relative7(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
43529
43653
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -43536,7 +43660,7 @@ function onDiskSpelling(path) {
|
|
|
43536
43660
|
let at = resolve12(path);
|
|
43537
43661
|
for (;; ) {
|
|
43538
43662
|
try {
|
|
43539
|
-
return
|
|
43663
|
+
return join37(realpathSync4.native(at), ...missing.reverse());
|
|
43540
43664
|
} catch {
|
|
43541
43665
|
const parent = dirname21(at);
|
|
43542
43666
|
if (parent === at) {
|
|
@@ -43974,15 +44098,15 @@ ${mention.kind}`;
|
|
|
43974
44098
|
};
|
|
43975
44099
|
}
|
|
43976
44100
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
43977
|
-
const directory =
|
|
44101
|
+
const directory = join37(projectPath, ".godot", "gdharness-renames");
|
|
43978
44102
|
mkdirSync17(directory, { recursive: true });
|
|
43979
44103
|
detailWrites += 1;
|
|
43980
|
-
const path =
|
|
44104
|
+
const path = join37(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
43981
44105
|
writeFileSync18(path, `${JSON.stringify(detail, null, 2)}
|
|
43982
44106
|
`, "utf8");
|
|
43983
44107
|
const kept = readdirSync16(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
43984
44108
|
for (const old of kept.slice(10)) {
|
|
43985
|
-
rmSync16(
|
|
44109
|
+
rmSync16(join37(directory, old), { force: true });
|
|
43986
44110
|
}
|
|
43987
44111
|
return path;
|
|
43988
44112
|
}
|
|
@@ -44031,10 +44155,10 @@ import {
|
|
|
44031
44155
|
statSync as statSync17,
|
|
44032
44156
|
writeFileSync as writeFileSync19
|
|
44033
44157
|
} from "node:fs";
|
|
44034
|
-
import { basename as basename10, dirname as dirname22, extname as
|
|
44158
|
+
import { basename as basename10, dirname as dirname22, extname as extname4, join as join38, relative as relative8 } from "node:path";
|
|
44035
44159
|
function filesUnder(directory) {
|
|
44036
44160
|
return readdirSync17(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
44037
|
-
const path =
|
|
44161
|
+
const path = join38(directory, entry.name);
|
|
44038
44162
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
44039
44163
|
});
|
|
44040
44164
|
}
|
|
@@ -44055,8 +44179,8 @@ function unseenByTheEngine(projectPath, path) {
|
|
|
44055
44179
|
return `${path} is under ${step}, and the engine passes over every directory whose name starts with a dot`;
|
|
44056
44180
|
}
|
|
44057
44181
|
if (at < steps.length - 1) {
|
|
44058
|
-
directory =
|
|
44059
|
-
if (existsSync31(
|
|
44182
|
+
directory = join38(directory, step);
|
|
44183
|
+
if (existsSync31(join38(directory, ".gdignore"))) {
|
|
44060
44184
|
return `${path} is under a directory holding a .gdignore, which the engine does not load`;
|
|
44061
44185
|
}
|
|
44062
44186
|
}
|
|
@@ -44068,12 +44192,12 @@ function installedAddonOf(projectPath, path) {
|
|
|
44068
44192
|
if (steps[0] !== "addons" || steps[1] === undefined) {
|
|
44069
44193
|
return null;
|
|
44070
44194
|
}
|
|
44071
|
-
return existsSync31(
|
|
44195
|
+
return existsSync31(join38(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
|
|
44072
44196
|
}
|
|
44073
44197
|
function binaryResources(projectPath) {
|
|
44074
44198
|
const found = [];
|
|
44075
44199
|
const visit = (directory, prefix) => {
|
|
44076
|
-
if (existsSync31(
|
|
44200
|
+
if (existsSync31(join38(directory, ".gdignore"))) {
|
|
44077
44201
|
return;
|
|
44078
44202
|
}
|
|
44079
44203
|
for (const entry of readdirSync17(directory, { withFileTypes: true })) {
|
|
@@ -44082,7 +44206,7 @@ function binaryResources(projectPath) {
|
|
|
44082
44206
|
}
|
|
44083
44207
|
const path = `${prefix}${entry.name}`;
|
|
44084
44208
|
if (entry.isDirectory()) {
|
|
44085
|
-
visit(
|
|
44209
|
+
visit(join38(directory, entry.name), `${path}/`);
|
|
44086
44210
|
} else if (/\.(scn|res)$/i.test(entry.name)) {
|
|
44087
44211
|
found.push(path);
|
|
44088
44212
|
}
|
|
@@ -44092,7 +44216,7 @@ function binaryResources(projectPath) {
|
|
|
44092
44216
|
return found;
|
|
44093
44217
|
}
|
|
44094
44218
|
function targetPathOf(projectPath, absolute) {
|
|
44095
|
-
const inside = relative8(onDiskSpelling(projectPath),
|
|
44219
|
+
const inside = relative8(onDiskSpelling(projectPath), join38(onDiskSpelling(dirname22(absolute)), basename10(absolute)));
|
|
44096
44220
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
44097
44221
|
return null;
|
|
44098
44222
|
}
|
|
@@ -44147,14 +44271,14 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
44147
44271
|
return { ok: false, reason: `${unseen}, so nothing there could load what was moved.` };
|
|
44148
44272
|
}
|
|
44149
44273
|
if (!isDirectory) {
|
|
44150
|
-
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync31(
|
|
44274
|
+
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))));
|
|
44151
44275
|
if (owner !== undefined && owner !== null) {
|
|
44152
44276
|
return { ok: false, reason: `${from} belongs to ${owner} and moves with it: move ${owner} instead.` };
|
|
44153
44277
|
}
|
|
44154
|
-
if (
|
|
44278
|
+
if (extname4(from).toLowerCase() !== extname4(to).toLowerCase()) {
|
|
44155
44279
|
return {
|
|
44156
44280
|
ok: false,
|
|
44157
|
-
reason: `${from} would become ${to}, and the engine picks how to read a file by its extension: give newPath the extension it has, ${
|
|
44281
|
+
reason: `${from} would become ${to}, and the engine picks how to read a file by its extension: give newPath the extension it has, ${extname4(from) || "none"}.`
|
|
44158
44282
|
};
|
|
44159
44283
|
}
|
|
44160
44284
|
}
|
|
@@ -44181,12 +44305,12 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
44181
44305
|
scripts,
|
|
44182
44306
|
moves,
|
|
44183
44307
|
named: [from],
|
|
44184
|
-
exists: (path) => existsSync31(
|
|
44308
|
+
exists: (path) => existsSync31(join38(projectPath, path.slice("res://".length)))
|
|
44185
44309
|
}, { edits: [], mentions: [] });
|
|
44186
44310
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
|
|
44187
44311
|
const binaries = [];
|
|
44188
44312
|
for (const path of binaryResources(projectPath)) {
|
|
44189
|
-
const bytes = readFileSync33(
|
|
44313
|
+
const bytes = readFileSync33(join38(projectPath, path.slice("res://".length)));
|
|
44190
44314
|
const names = [...moves.keys()].find((moving) => bytes.includes(moving));
|
|
44191
44315
|
if (names !== undefined) {
|
|
44192
44316
|
binaries.push({
|
|
@@ -44205,7 +44329,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
44205
44329
|
file: afterwards(mention.file)
|
|
44206
44330
|
}));
|
|
44207
44331
|
const left = leftAloneReport(mentions);
|
|
44208
|
-
const absoluteOf = (path) =>
|
|
44332
|
+
const absoluteOf = (path) => join38(projectPath, path.slice("res://".length));
|
|
44209
44333
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
44210
44334
|
const moved = [...moves].map(([source, target]) => {
|
|
44211
44335
|
const rewrite = rewritten.get(source);
|
|
@@ -44311,30 +44435,6 @@ var init_file_move = __esm(() => {
|
|
|
44311
44435
|
SIDECARS = [".uid", ".import"];
|
|
44312
44436
|
});
|
|
44313
44437
|
|
|
44314
|
-
// src/godot-variant.ts
|
|
44315
|
-
var FLAG_64, MOST_MESSAGE_BYTES;
|
|
44316
|
-
var init_godot_variant = __esm(() => {
|
|
44317
|
-
FLAG_64 = 1 << 16;
|
|
44318
|
-
MOST_MESSAGE_BYTES = (8 << 20) + 4;
|
|
44319
|
-
});
|
|
44320
|
-
|
|
44321
|
-
// src/game-debugger.ts
|
|
44322
|
-
var PAUSE_MS = 30000, STEP_MS = 1e4, RUNTIME_ADDON2 = "res://addons/gdharness_runtime/", RUNTIME_AUTOLOAD2, READ_MS = 5000, REPLIES;
|
|
44323
|
-
var init_game_debugger = __esm(() => {
|
|
44324
|
-
init_godot_variant();
|
|
44325
|
-
init_setup();
|
|
44326
|
-
RUNTIME_AUTOLOAD2 = `${RUNTIME_ADDON2}runtime_autoload.gd`;
|
|
44327
|
-
REPLIES = new Set([
|
|
44328
|
-
"debug_enter",
|
|
44329
|
-
"debug_exit",
|
|
44330
|
-
"stack_dump",
|
|
44331
|
-
"stack_frame_vars",
|
|
44332
|
-
"stack_frame_var",
|
|
44333
|
-
"evaluation_return",
|
|
44334
|
-
"scene:inspect_object"
|
|
44335
|
-
]);
|
|
44336
|
-
});
|
|
44337
|
-
|
|
44338
44438
|
// node_modules/ws/lib/constants.js
|
|
44339
44439
|
var require_constants = __commonJS(function(exports, module) {
|
|
44340
44440
|
var BINARY_TYPES = ["nodebuffer", "arraybuffer", "fragments"];
|
|
@@ -47996,10 +48096,10 @@ var init_keeper_channel = () => {};
|
|
|
47996
48096
|
|
|
47997
48097
|
// src/lifecycle-log.ts
|
|
47998
48098
|
import { appendFileSync, mkdirSync as mkdirSync19, renameSync as renameSync5, statSync as statSync18 } from "node:fs";
|
|
47999
|
-
import { dirname as dirname23, join as
|
|
48099
|
+
import { dirname as dirname23, join as join39 } from "node:path";
|
|
48000
48100
|
import process16 from "node:process";
|
|
48001
48101
|
function lifecycleLogPath() {
|
|
48002
|
-
return
|
|
48102
|
+
return join39(runtimeDirectory(), "servers.log");
|
|
48003
48103
|
}
|
|
48004
48104
|
function writeLifecycle(event, fields = {}) {
|
|
48005
48105
|
const path = lifecycleLogPath();
|
|
@@ -48370,9 +48470,9 @@ var init_prompts = __esm(() => {
|
|
|
48370
48470
|
|
|
48371
48471
|
// src/restart-note.ts
|
|
48372
48472
|
import { mkdirSync as mkdirSync20, readFileSync as readFileSync34, rmSync as rmSync17, writeFileSync as writeFileSync20 } from "node:fs";
|
|
48373
|
-
import { dirname as dirname24, join as
|
|
48473
|
+
import { dirname as dirname24, join as join40 } from "node:path";
|
|
48374
48474
|
function restartNotePath(projectPath) {
|
|
48375
|
-
return
|
|
48475
|
+
return join40(projectPath, ".godot", "gdharness-restart.json");
|
|
48376
48476
|
}
|
|
48377
48477
|
function noteRestartBegun(note) {
|
|
48378
48478
|
const path = restartNotePath(note.projectPath);
|
|
@@ -48413,9 +48513,9 @@ var init_restart_note = __esm(() => {
|
|
|
48413
48513
|
// src/run-record.ts
|
|
48414
48514
|
import { createHash as createHash7 } from "node:crypto";
|
|
48415
48515
|
import { mkdirSync as mkdirSync21, openSync as openSync5, readdirSync as readdirSync18, readFileSync as readFileSync35, rmSync as rmSync18, statSync as statSync19 } from "node:fs";
|
|
48416
|
-
import { basename as basename11, join as
|
|
48516
|
+
import { basename as basename11, join as join41, resolve as resolve13 } from "node:path";
|
|
48417
48517
|
function runsDirectory() {
|
|
48418
|
-
return
|
|
48518
|
+
return join41(runtimeDirectory(), "runs");
|
|
48419
48519
|
}
|
|
48420
48520
|
function projectKey(projectPath) {
|
|
48421
48521
|
const real = realAsFarAsItExists(resolve13(projectPath)).replace(/[\\/]+$/, "");
|
|
@@ -48423,10 +48523,10 @@ function projectKey(projectPath) {
|
|
|
48423
48523
|
return createHash7("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
48424
48524
|
}
|
|
48425
48525
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
48426
|
-
return
|
|
48526
|
+
return join41(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
48427
48527
|
}
|
|
48428
48528
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
48429
|
-
return
|
|
48529
|
+
return join41(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
48430
48530
|
}
|
|
48431
48531
|
function writeLatestRun(servedBy, latest) {
|
|
48432
48532
|
try {
|
|
@@ -48451,20 +48551,20 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
48451
48551
|
}
|
|
48452
48552
|
function openTranscript(startedAt) {
|
|
48453
48553
|
mkdirSync21(runsDirectory(), { recursive: true });
|
|
48454
|
-
const path =
|
|
48554
|
+
const path = join41(runsDirectory(), `run-${startedAt}.log`);
|
|
48455
48555
|
return { path, fd: openSync5(path, "a") };
|
|
48456
48556
|
}
|
|
48457
48557
|
function profilePath(startedAt) {
|
|
48458
|
-
return
|
|
48558
|
+
return join41(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
48459
48559
|
}
|
|
48460
48560
|
function profileMarkPath(startedAt) {
|
|
48461
|
-
return
|
|
48561
|
+
return join41(runsDirectory(), `run-${startedAt}.profile-mark.json`);
|
|
48462
48562
|
}
|
|
48463
48563
|
function noteIsAbout(note, playing) {
|
|
48464
48564
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
48465
48565
|
}
|
|
48466
48566
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
48467
|
-
return
|
|
48567
|
+
return join41(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
48468
48568
|
}
|
|
48469
48569
|
function writeEditorRunNote(note) {
|
|
48470
48570
|
mkdirSync21(runsDirectory(), { recursive: true });
|
|
@@ -48517,13 +48617,13 @@ function everyRunRecord() {
|
|
|
48517
48617
|
for (const directory of runtimeDirectories()) {
|
|
48518
48618
|
let entries;
|
|
48519
48619
|
try {
|
|
48520
|
-
entries = readdirSync18(
|
|
48620
|
+
entries = readdirSync18(join41(directory, "runs"));
|
|
48521
48621
|
} catch {
|
|
48522
48622
|
continue;
|
|
48523
48623
|
}
|
|
48524
48624
|
for (const entry of entries) {
|
|
48525
48625
|
if (entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
48526
|
-
const record = recordAt(
|
|
48626
|
+
const record = recordAt(join41(directory, "runs", entry));
|
|
48527
48627
|
if (record !== null) {
|
|
48528
48628
|
found.push(record);
|
|
48529
48629
|
}
|
|
@@ -48791,7 +48891,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
48791
48891
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
48792
48892
|
continue;
|
|
48793
48893
|
}
|
|
48794
|
-
const path =
|
|
48894
|
+
const path = join41(directory, entry);
|
|
48795
48895
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
48796
48896
|
continue;
|
|
48797
48897
|
}
|
|
@@ -49047,16 +49147,16 @@ var init_server_instructions = __esm(() => {
|
|
|
49047
49147
|
|
|
49048
49148
|
// src/suites-scene.ts
|
|
49049
49149
|
import { existsSync as existsSync32, mkdirSync as mkdirSync22, writeFileSync as writeFileSync21 } from "node:fs";
|
|
49050
|
-
import { join as
|
|
49150
|
+
import { join as join42, relative as relative9, sep as sep5 } from "node:path";
|
|
49051
49151
|
function hasSuitesScene(projectPath) {
|
|
49052
|
-
return existsSync32(
|
|
49152
|
+
return existsSync32(join42(projectPath, SUITES_SCENE));
|
|
49053
49153
|
}
|
|
49054
49154
|
function suitesSceneArgs(projectPath, suites) {
|
|
49055
|
-
const runs =
|
|
49155
|
+
const runs = join42(projectPath, ".godot", "gdharness", "suite-scenes");
|
|
49056
49156
|
sweepRunDirectories(runs);
|
|
49057
|
-
const directory =
|
|
49157
|
+
const directory = join42(runs, `${String(STARTED_AT)}-${String(process.pid)}`);
|
|
49058
49158
|
mkdirSync22(directory, { recursive: true });
|
|
49059
|
-
const suitesFile =
|
|
49159
|
+
const suitesFile = join42(directory, "suites.txt");
|
|
49060
49160
|
writeFileSync21(suitesFile, `${suites.join(`
|
|
49061
49161
|
`)}
|
|
49062
49162
|
`);
|
|
@@ -49071,7 +49171,7 @@ function suitesSceneArgs(projectPath, suites) {
|
|
|
49071
49171
|
"-c",
|
|
49072
49172
|
"--ignoreHeadlessMode",
|
|
49073
49173
|
"-rd",
|
|
49074
|
-
`res://${relative9(projectPath,
|
|
49174
|
+
`res://${relative9(projectPath, join42(directory, "reports")).split(sep5).join("/")}`,
|
|
49075
49175
|
"-rc",
|
|
49076
49176
|
"1"
|
|
49077
49177
|
];
|
|
@@ -49085,7 +49185,7 @@ var init_suites_scene = __esm(() => {
|
|
|
49085
49185
|
|
|
49086
49186
|
// src/test-selection.ts
|
|
49087
49187
|
import { existsSync as existsSync33 } from "node:fs";
|
|
49088
|
-
import { basename as basename12, join as
|
|
49188
|
+
import { basename as basename12, join as join43 } from "node:path";
|
|
49089
49189
|
function casesOutOfPath(projectPath, given, tests) {
|
|
49090
49190
|
const entries = Array.isArray(given) ? given : [given];
|
|
49091
49191
|
const cases = [];
|
|
@@ -49129,12 +49229,12 @@ function casesIn(scripts, suite) {
|
|
|
49129
49229
|
}
|
|
49130
49230
|
function scriptsWithoutCases(projectPath, asked) {
|
|
49131
49231
|
const scripts = new ProjectScripts(projectPath);
|
|
49132
|
-
return asked.filter((path) => path.endsWith(".gd") && existsSync33(
|
|
49232
|
+
return asked.filter((path) => path.endsWith(".gd") && existsSync33(join43(projectPath, path.slice("res://".length))) && casesIn(scripts, path).length === 0);
|
|
49133
49233
|
}
|
|
49134
49234
|
function suitesUnder(projectPath, asked) {
|
|
49135
49235
|
const scripts = projectFiles(projectPath, [".gd"]).map((path) => `res://${path}`);
|
|
49136
49236
|
return [
|
|
49137
|
-
...new Set(asked.flatMap((path) => path.endsWith(".gd") ? existsSync33(
|
|
49237
|
+
...new Set(asked.flatMap((path) => path.endsWith(".gd") ? existsSync33(join43(projectPath, path.slice("res://".length))) ? [path] : [] : scripts.filter((script) => script.startsWith(`${path}/`))))
|
|
49138
49238
|
];
|
|
49139
49239
|
}
|
|
49140
49240
|
function selectTests(projectPath, asked, given) {
|
|
@@ -49212,7 +49312,7 @@ import {
|
|
|
49212
49312
|
statSync as statSync20,
|
|
49213
49313
|
writeFileSync as writeFileSync22
|
|
49214
49314
|
} from "node:fs";
|
|
49215
|
-
import { basename as basename13, dirname as dirname25, extname as
|
|
49315
|
+
import { basename as basename13, dirname as dirname25, extname as extname5, isAbsolute as isAbsolute8, join as join44, normalize as normalize2, relative as relative10 } from "node:path";
|
|
49216
49316
|
import { setTimeout as delay7 } from "node:timers/promises";
|
|
49217
49317
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
49218
49318
|
function editorRestartTimeoutMs() {
|
|
@@ -49264,14 +49364,14 @@ function writtenFileRefusal(path, rule) {
|
|
|
49264
49364
|
if (!isAbsolute8(path)) {
|
|
49265
49365
|
return `${named} ${path} is not absolute; name the whole path, so where it is written does not depend on this server's directory.`;
|
|
49266
49366
|
}
|
|
49267
|
-
if (!rule.extensions.includes(
|
|
49367
|
+
if (!rule.extensions.includes(extname5(path).toLowerCase())) {
|
|
49268
49368
|
return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
|
|
49269
49369
|
}
|
|
49270
49370
|
const directory = realAsFarAsItExists(dirname25(path));
|
|
49271
49371
|
if (!existsSync34(directory)) {
|
|
49272
49372
|
return `${named}'s directory ${dirname25(path)} does not exist; ${what} creates no directories.`;
|
|
49273
49373
|
}
|
|
49274
|
-
const where =
|
|
49374
|
+
const where = join44(directory, basename13(path));
|
|
49275
49375
|
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realAsFarAsItExists(project), where));
|
|
49276
49376
|
if (inside !== undefined) {
|
|
49277
49377
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
@@ -49296,7 +49396,7 @@ function movieRefusal(engineArgs, projects, headless) {
|
|
|
49296
49396
|
extensions: [".avi", ".png"],
|
|
49297
49397
|
projects,
|
|
49298
49398
|
writes: (where) => {
|
|
49299
|
-
if (
|
|
49399
|
+
if (extname5(where).toLowerCase() !== ".png") {
|
|
49300
49400
|
return [where];
|
|
49301
49401
|
}
|
|
49302
49402
|
const base = where.slice(0, -".png".length);
|
|
@@ -49611,16 +49711,6 @@ function endedPreviousRun(ended) {
|
|
|
49611
49711
|
function previousRunLeft(ended) {
|
|
49612
49712
|
return (ended?.ending === "refused" || ended?.ending === "lingering") && ended.pid !== null ? ended.pid : undefined;
|
|
49613
49713
|
}
|
|
49614
|
-
function editorsOfProjectIn(tree, projectPath) {
|
|
49615
|
-
const found = [];
|
|
49616
|
-
for (const [pid, listed] of tree) {
|
|
49617
|
-
const read = readCommandLine(listed.command);
|
|
49618
|
-
if (read.editor && read.oneShot === null && read.projectPath !== null && /godot/i.test(read.executable) && !CONSOLE_WRAPPER.test(read.executable) && isSameDirectory(read.projectPath, projectPath)) {
|
|
49619
|
-
found.push(pid);
|
|
49620
|
-
}
|
|
49621
|
-
}
|
|
49622
|
-
return found.sort((a, b) => a - b);
|
|
49623
|
-
}
|
|
49624
49714
|
function engineNamesOf(executable) {
|
|
49625
49715
|
return CONSOLE_WRAPPER.test(executable) ? [executable, executable.replace(CONSOLE_WRAPPER, "$1")] : [executable];
|
|
49626
49716
|
}
|
|
@@ -49918,7 +50008,7 @@ function notRunNote(notRun, failFast) {
|
|
|
49918
50008
|
function presetsIn(projectPath) {
|
|
49919
50009
|
let text;
|
|
49920
50010
|
try {
|
|
49921
|
-
text = readFileSync37(
|
|
50011
|
+
text = readFileSync37(join44(projectPath, "export_presets.cfg"), "utf8");
|
|
49922
50012
|
} catch {
|
|
49923
50013
|
return null;
|
|
49924
50014
|
}
|
|
@@ -49999,7 +50089,7 @@ function filesIn(folder) {
|
|
|
49999
50089
|
try {
|
|
50000
50090
|
for (const entry of readdirSync19(folder, { withFileTypes: true })) {
|
|
50001
50091
|
if (entry.isFile()) {
|
|
50002
|
-
found.set(entry.name, statSync20(
|
|
50092
|
+
found.set(entry.name, statSync20(join44(folder, entry.name)).mtimeMs);
|
|
50003
50093
|
}
|
|
50004
50094
|
}
|
|
50005
50095
|
} catch {}
|
|
@@ -50039,7 +50129,7 @@ function unsavedInEditorNote(scripts) {
|
|
|
50039
50129
|
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.`;
|
|
50040
50130
|
}
|
|
50041
50131
|
function editorAddonMissing(projectPath) {
|
|
50042
|
-
if (!existsSync34(
|
|
50132
|
+
if (!existsSync34(join44(projectPath, "project.godot"))) {
|
|
50043
50133
|
return null;
|
|
50044
50134
|
}
|
|
50045
50135
|
const report = inspectProject(projectPath);
|
|
@@ -50067,7 +50157,7 @@ function rewriteBusNamed(projectPath, from, to) {
|
|
|
50067
50157
|
const giving = [`bus = &"${quoted(from)}"`, `bus = "${quoted(from)}"`];
|
|
50068
50158
|
const rewritten = [];
|
|
50069
50159
|
for (const file of projectFiles(projectPath, [".tscn", ".tres"])) {
|
|
50070
|
-
const absolute =
|
|
50160
|
+
const absolute = join44(projectPath, file);
|
|
50071
50161
|
const lines = readFileSync37(absolute, "utf8").split(`
|
|
50072
50162
|
`);
|
|
50073
50163
|
const at = [];
|
|
@@ -50087,7 +50177,7 @@ function rewriteBusNamed(projectPath, from, to) {
|
|
|
50087
50177
|
}
|
|
50088
50178
|
const namedInScripts = [];
|
|
50089
50179
|
for (const file of projectFiles(projectPath, [".gd"])) {
|
|
50090
|
-
readFileSync37(
|
|
50180
|
+
readFileSync37(join44(projectPath, file), "utf8").split(`
|
|
50091
50181
|
`).forEach((line, index) => {
|
|
50092
50182
|
if (line.includes(`"${from}"`)) {
|
|
50093
50183
|
namedInScripts.push({ file: `res://${file}`, line: index + 1, text: line.trim() });
|
|
@@ -50372,7 +50462,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
50372
50462
|
let projectGodot = null;
|
|
50373
50463
|
if (projectPath !== null) {
|
|
50374
50464
|
try {
|
|
50375
|
-
projectGodot = readFileSync37(
|
|
50465
|
+
projectGodot = readFileSync37(join44(projectPath, "project.godot"), "utf8");
|
|
50376
50466
|
} catch {}
|
|
50377
50467
|
}
|
|
50378
50468
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -50568,7 +50658,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
50568
50658
|
}
|
|
50569
50659
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
50570
50660
|
try {
|
|
50571
|
-
return settingKeys(readFileSync37(
|
|
50661
|
+
return settingKeys(readFileSync37(join44(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
50572
50662
|
} catch {
|
|
50573
50663
|
return true;
|
|
50574
50664
|
}
|
|
@@ -50583,7 +50673,7 @@ function uncaughtNote(uncaught) {
|
|
|
50583
50673
|
}
|
|
50584
50674
|
function elsewhereIn(projectPath, asked) {
|
|
50585
50675
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
50586
|
-
if (wanted === "" || existsSync34(
|
|
50676
|
+
if (wanted === "" || existsSync34(join44(projectPath, wanted))) {
|
|
50587
50677
|
return "";
|
|
50588
50678
|
}
|
|
50589
50679
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -50606,7 +50696,7 @@ function windowNoteFor(needing, sharing) {
|
|
|
50606
50696
|
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}`;
|
|
50607
50697
|
}
|
|
50608
50698
|
function testPathsIn(projectPath, given, named = "path") {
|
|
50609
|
-
const defaults = TEST_DIRECTORIES.filter((directory) => existsSync34(
|
|
50699
|
+
const defaults = TEST_DIRECTORIES.filter((directory) => existsSync34(join44(projectPath, directory)));
|
|
50610
50700
|
const entries = given === undefined || given === null || given === "" ? defaults.length > 0 ? defaults : TEST_DIRECTORIES : given;
|
|
50611
50701
|
const listed = Array.isArray(entries) ? entries : [entries];
|
|
50612
50702
|
if (listed.length === 0) {
|
|
@@ -50737,7 +50827,7 @@ function needsItsOwnProcess(asked) {
|
|
|
50737
50827
|
class GodotServer {
|
|
50738
50828
|
stdio = null;
|
|
50739
50829
|
locator = new GodotLocator;
|
|
50740
|
-
operationsScript =
|
|
50830
|
+
operationsScript = join44(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
50741
50831
|
exportOptionsRead = new Map;
|
|
50742
50832
|
heldWhilePending = new Set;
|
|
50743
50833
|
evaluatedWhilePending = new Set;
|
|
@@ -51009,7 +51099,7 @@ class GodotServer {
|
|
|
51009
51099
|
}
|
|
51010
51100
|
noteTheStart() {
|
|
51011
51101
|
const cwd = process.cwd();
|
|
51012
|
-
const mcpJson =
|
|
51102
|
+
const mcpJson = join44(cwd, ".mcp.json");
|
|
51013
51103
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
51014
51104
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
51015
51105
|
writeLifecycle("started", {
|
|
@@ -51157,7 +51247,7 @@ class GodotServer {
|
|
|
51157
51247
|
return checked.response;
|
|
51158
51248
|
}
|
|
51159
51249
|
const named = args["projectPath"];
|
|
51160
|
-
if (typeof named === "string" && existsSync34(
|
|
51250
|
+
if (typeof named === "string" && existsSync34(join44(named, "project.godot"))) {
|
|
51161
51251
|
this.lastProjectPath = named;
|
|
51162
51252
|
}
|
|
51163
51253
|
this.updates.refresh();
|
|
@@ -51200,7 +51290,7 @@ class GodotServer {
|
|
|
51200
51290
|
let notSaved;
|
|
51201
51291
|
try {
|
|
51202
51292
|
savedAt = answer.content.map((item, index) => {
|
|
51203
|
-
const file =
|
|
51293
|
+
const file = join44(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
|
|
51204
51294
|
writeFileSync22(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
|
|
51205
51295
|
return file;
|
|
51206
51296
|
});
|
|
@@ -51897,7 +51987,7 @@ class GodotServer {
|
|
|
51897
51987
|
if (path === undefined) {
|
|
51898
51988
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
51899
51989
|
}
|
|
51900
|
-
const file =
|
|
51990
|
+
const file = join44(path, "project.godot");
|
|
51901
51991
|
if (!existsSync34(file)) {
|
|
51902
51992
|
return {
|
|
51903
51993
|
ok: false,
|
|
@@ -52634,6 +52724,9 @@ class GodotServer {
|
|
|
52634
52724
|
if (forValidation.length > 0 && !include.includes("validation")) {
|
|
52635
52725
|
return this.createErrorResponse(`${forValidation.join(" and ")} ${forValidation.length === 1 ? "is" : "are"} read only by the validation section, which this call does not include: add "validation" to include, or leave ${forValidation.length === 1 ? "it" : "them"} out.`);
|
|
52636
52726
|
}
|
|
52727
|
+
if (args["addonClasses"] !== undefined && !include.includes("classes")) {
|
|
52728
|
+
return this.createErrorResponse('addonClasses is read only by the classes section, which this call does not include: add "classes" to include, or leave it out.');
|
|
52729
|
+
}
|
|
52637
52730
|
const project = this.project(args);
|
|
52638
52731
|
if (!project.ok) {
|
|
52639
52732
|
return project.response;
|
|
@@ -52664,6 +52757,23 @@ class GodotServer {
|
|
|
52664
52757
|
...compiledScripts !== undefined && (section === "health" || section === "validation") ? { compiled: compiledScripts } : {}
|
|
52665
52758
|
};
|
|
52666
52759
|
const outcome = await this.operation(spec.operation, params, project.value.path);
|
|
52760
|
+
if (section === "classes" && outcome.ok && Array.isArray(outcome.payload["classes"])) {
|
|
52761
|
+
const declared = outcome.payload["classes"];
|
|
52762
|
+
const asked = readStringArray(args, "addonClasses") ?? [];
|
|
52763
|
+
const addons = Object.keys(classesWithAddonsCounted(declared, new Set).inAddons).sort();
|
|
52764
|
+
const unknownAddons = asked.filter((addon) => !addons.includes(addon));
|
|
52765
|
+
if (unknownAddons.length > 0) {
|
|
52766
|
+
return this.createErrorResponse(`addonClasses names ${unknownAddons.join(", ")}, and no folder of that name under res://addons declares a class. ${addons.length === 0 ? "No addon declares one." : `The addons declaring classes are ${addons.join(", ")}.`}`);
|
|
52767
|
+
}
|
|
52768
|
+
const counted = classesWithAddonsCounted(declared, new Set(asked));
|
|
52769
|
+
info[section] = {
|
|
52770
|
+
...outcome.payload,
|
|
52771
|
+
classes: counted.classes,
|
|
52772
|
+
...Object.keys(counted.inAddons).length > 0 ? { inAddons: counted.inAddons } : {},
|
|
52773
|
+
...engineExtras(outcome)
|
|
52774
|
+
};
|
|
52775
|
+
continue;
|
|
52776
|
+
}
|
|
52667
52777
|
info[section] = outcome.ok ? { ...outcome.payload, ...engineExtras(outcome) } : { error: outcome.message, ...engineExtras(outcome) };
|
|
52668
52778
|
}
|
|
52669
52779
|
return this.jsonTextResponse(info);
|
|
@@ -52779,7 +52889,7 @@ class GodotServer {
|
|
|
52779
52889
|
const platform = presetPlatform(project.value.path, preset);
|
|
52780
52890
|
if (platform !== null) {
|
|
52781
52891
|
const known = await this.exportOptions(platform, {
|
|
52782
|
-
presetsFile:
|
|
52892
|
+
presetsFile: join44(project.value.path, "export_presets.cfg"),
|
|
52783
52893
|
preset,
|
|
52784
52894
|
asked: options
|
|
52785
52895
|
});
|
|
@@ -52830,7 +52940,7 @@ ${platform}`;
|
|
|
52830
52940
|
}
|
|
52831
52941
|
const empty = scratchDirectory("export");
|
|
52832
52942
|
try {
|
|
52833
|
-
writeFileSync22(
|
|
52943
|
+
writeFileSync22(join44(empty, "project.godot"), `config_version=5
|
|
52834
52944
|
`);
|
|
52835
52945
|
const outcome = await runOperation({ godotPath: engine.value, script: this.operationsScript, debug: DEBUG_MODE, editor: true }, "export_options", { platform, ...standing }, empty);
|
|
52836
52946
|
const options = outcome.ok ? outcome.payload["options"] : undefined;
|
|
@@ -52878,7 +52988,7 @@ ${platform}`;
|
|
|
52878
52988
|
if (pack && debug) {
|
|
52879
52989
|
return this.createErrorResponse("A pack is exported as a release: the command-line exporter has no debug pack, so debug and pack cannot both be true.", ["Leave debug out for a pack", "Or leave pack out for a debug build"]);
|
|
52880
52990
|
}
|
|
52881
|
-
const extension =
|
|
52991
|
+
const extension = extname5(output.relativePath).toLowerCase();
|
|
52882
52992
|
if (pack && extension !== ".pck" && extension !== ".zip") {
|
|
52883
52993
|
return this.createErrorResponse(`A pack is written as a .pck or a .zip, and ${output.relativePath} is neither: the exporter picks the format by the extension.`, ["Give outputPath ending in .pck, or .zip for a zip of the resources"]);
|
|
52884
52994
|
}
|
|
@@ -52886,7 +52996,7 @@ ${platform}`;
|
|
|
52886
52996
|
const exportArgs = [
|
|
52887
52997
|
"--headless",
|
|
52888
52998
|
"--log-file",
|
|
52889
|
-
|
|
52999
|
+
join44(exportLogs, "engine.log"),
|
|
52890
53000
|
"--path",
|
|
52891
53001
|
project.value.path,
|
|
52892
53002
|
pack ? "--export-pack" : debug ? "--export-debug" : "--export-release",
|
|
@@ -52985,7 +53095,7 @@ ${platform}`;
|
|
|
52985
53095
|
if (movie !== null) {
|
|
52986
53096
|
return this.createErrorResponse(movie);
|
|
52987
53097
|
}
|
|
52988
|
-
const missing = paths.value.filter((path) => !existsSync34(
|
|
53098
|
+
const missing = paths.value.filter((path) => !existsSync34(join44(project.value.path, path.slice("res://".length))));
|
|
52989
53099
|
const projectScripts = new ProjectScripts(project.value.path);
|
|
52990
53100
|
const present = paths.value.filter((path) => !missing.includes(path));
|
|
52991
53101
|
const asked = projectScripts.searchedAsTold(present);
|
|
@@ -53077,10 +53187,10 @@ ${platform}`;
|
|
|
53077
53187
|
const ignored = readStringArray(args, "ignore") ?? [];
|
|
53078
53188
|
const declaredUnder = ignored.length === 0 ? [] : asked.flatMap((path) => path.endsWith(".gd") ? projectScripts.suite(path) ?? [] : projectScripts.suitesUnder([path]));
|
|
53079
53189
|
const userData = scratchDirectory("tests");
|
|
53080
|
-
const suitesFile =
|
|
53081
|
-
const skippingFile =
|
|
53082
|
-
const engineLog =
|
|
53083
|
-
const expectedFile =
|
|
53190
|
+
const suitesFile = join44(userData, "suites.txt");
|
|
53191
|
+
const skippingFile = join44(userData, "skipping.txt");
|
|
53192
|
+
const engineLog = join44(userData, "engine.log");
|
|
53193
|
+
const expectedFile = join44(userData, "expected.json");
|
|
53084
53194
|
writeFileSync22(suitesFile, `${(selection.value?.suites ?? asked).join(`
|
|
53085
53195
|
`)}
|
|
53086
53196
|
`);
|
|
@@ -53121,11 +53231,11 @@ ${platform}`;
|
|
|
53121
53231
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? TEST_TIMEOUT_MS;
|
|
53122
53232
|
this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
53123
53233
|
const helperFiles = {
|
|
53124
|
-
spec:
|
|
53125
|
-
pid:
|
|
53126
|
-
error:
|
|
53127
|
-
stop:
|
|
53128
|
-
step:
|
|
53234
|
+
spec: join44(userData, "spec"),
|
|
53235
|
+
pid: join44(userData, "pid"),
|
|
53236
|
+
error: join44(userData, "error"),
|
|
53237
|
+
stop: join44(userData, "stop"),
|
|
53238
|
+
step: join44(userData, "step")
|
|
53129
53239
|
};
|
|
53130
53240
|
const onDesktop = windowed && process.platform === "win32";
|
|
53131
53241
|
const run = await this.spawnGame(engine.value, cmdArgs, userDataIn(userData), onDesktop ? { directory: project.value.path, files: helperFiles } : undefined);
|
|
@@ -53179,7 +53289,7 @@ ${platform}`;
|
|
|
53179
53289
|
await run.settled;
|
|
53180
53290
|
const helperFailed = onDesktop ? helperFailure(helperFiles.error) ?? (existsSync34(helperFiles.pid) ? null : `it never started, and ${helperStep(helperFiles.step)}`) : null;
|
|
53181
53291
|
if (helperFailed !== null) {
|
|
53182
|
-
discard(
|
|
53292
|
+
discard(join44(project.value.path, ".godot", ...ours.split("/")), userData);
|
|
53183
53293
|
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}`);
|
|
53184
53294
|
}
|
|
53185
53295
|
const endedAt = Date.now();
|
|
@@ -53190,7 +53300,7 @@ ${platform}`;
|
|
|
53190
53300
|
engine: endedAt - importedAt
|
|
53191
53301
|
};
|
|
53192
53302
|
const notCompiled = stopped.for ?? run.log.all.map((entry) => autoloadNotCompiled(entry, autoloaded)).find((found) => found !== null) ?? null;
|
|
53193
|
-
const reportsDir =
|
|
53303
|
+
const reportsDir = join44(project.value.path, ".godot", ...ours.split("/"));
|
|
53194
53304
|
let report = null;
|
|
53195
53305
|
let reportProblem = null;
|
|
53196
53306
|
const casePrinted = withNestedErrors(readRun(existsSync34(engineLog) ? readFileSync37(engineLog, "utf8") : ""), run.log.all);
|
|
@@ -53199,7 +53309,7 @@ ${platform}`;
|
|
|
53199
53309
|
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))) : [];
|
|
53200
53310
|
const newest = written.at(-1);
|
|
53201
53311
|
if (newest !== undefined) {
|
|
53202
|
-
report = parseJUnit(readFileSync37(
|
|
53312
|
+
report = parseJUnit(readFileSync37(join44(reportsDir, newest, "results.xml"), "utf8"));
|
|
53203
53313
|
}
|
|
53204
53314
|
} catch (error) {
|
|
53205
53315
|
reportProblem = errorMessage(error);
|
|
@@ -53604,7 +53714,7 @@ ${error.text}`;
|
|
|
53604
53714
|
const scripts = projectFiles(projectPath, [".gd"]).map((script) => `res://${script}`);
|
|
53605
53715
|
const wanted = `res://${relativePath}`;
|
|
53606
53716
|
const folder = dirname25(relativePath.replaceAll("\\", "/"));
|
|
53607
|
-
const inFolder =
|
|
53717
|
+
const inFolder = join44(projectPath, folder);
|
|
53608
53718
|
const beside = existsSync34(inFolder) ? readdirSync19(inFolder, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => `res://${folder === "." ? "" : `${folder}/`}${entry.name}`) : [];
|
|
53609
53719
|
return this.createErrorResponse(withNearestFiles(`Script file does not exist: ${wanted}`, wanted, scripts, beside));
|
|
53610
53720
|
}
|
|
@@ -53989,7 +54099,7 @@ ${error.text}`;
|
|
|
53989
54099
|
return { ...game, keeper: game.keeper };
|
|
53990
54100
|
}
|
|
53991
54101
|
async askTheKeptGame(keeper, request, projectPath) {
|
|
53992
|
-
const answered = await askTheKeepersDebugger(keeper, request, KEEPER_DEBUG_MS);
|
|
54102
|
+
const answered = await askTheKeepersDebugger(keeper, request, request.op === "evaluate" ? EVALUATE_MS + 2 * READ_MS : KEEPER_DEBUG_MS);
|
|
53993
54103
|
if ("unreached" in answered) {
|
|
53994
54104
|
return this.createErrorResponse(`The debugger of the running game did not answer: ${String(answered.unreached)}.`, [
|
|
53995
54105
|
"editor_run start starts the game again, with a debugger",
|
|
@@ -54005,7 +54115,7 @@ ${error.text}`;
|
|
|
54005
54115
|
...Array.isArray(stack) ? {
|
|
54006
54116
|
stack: stack.map((frame) => ({
|
|
54007
54117
|
...frame,
|
|
54008
|
-
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path:
|
|
54118
|
+
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join44(projectPath, frame["script"].slice("res://".length)) } : {}
|
|
54009
54119
|
}))
|
|
54010
54120
|
} : {}
|
|
54011
54121
|
});
|
|
@@ -54350,7 +54460,7 @@ ${error.text}`;
|
|
|
54350
54460
|
return null;
|
|
54351
54461
|
}
|
|
54352
54462
|
try {
|
|
54353
|
-
return settingKeys(readFileSync37(
|
|
54463
|
+
return settingKeys(readFileSync37(join44(projectPath, "project.godot"), "utf8"));
|
|
54354
54464
|
} catch {
|
|
54355
54465
|
return null;
|
|
54356
54466
|
}
|
|
@@ -54695,7 +54805,7 @@ ${error.text}`;
|
|
|
54695
54805
|
if (!paths.ok) {
|
|
54696
54806
|
return refused(paths.reason, PATH_SOLUTIONS);
|
|
54697
54807
|
}
|
|
54698
|
-
const missing = paths.value.filter((path) => !existsSync34(
|
|
54808
|
+
const missing = paths.value.filter((path) => !existsSync34(join44(projectPath, path.slice("res://".length))));
|
|
54699
54809
|
if (missing.length > 0) {
|
|
54700
54810
|
return refused(`Nothing at ${missing.join(" and ")} in the project at ${projectPath}.`, PATH_SOLUTIONS);
|
|
54701
54811
|
}
|
|
@@ -54866,7 +54976,7 @@ ${error.text}`;
|
|
|
54866
54976
|
}
|
|
54867
54977
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
54868
54978
|
const budgetMs = budget.ms;
|
|
54869
|
-
if (!existsSync34(
|
|
54979
|
+
if (!existsSync34(join44(projectPath, RUNTIME_SCRIPT))) {
|
|
54870
54980
|
return runtimeVerdict(null, {
|
|
54871
54981
|
addon: false,
|
|
54872
54982
|
budgetMs,
|
|
@@ -55908,7 +56018,7 @@ ${error.text}`;
|
|
|
55908
56018
|
};
|
|
55909
56019
|
const going = await this.runStillGoing(run);
|
|
55910
56020
|
const projectPath = run.projectPath;
|
|
55911
|
-
const projectGone = projectPath !== null && !existsSync34(
|
|
56021
|
+
const projectGone = projectPath !== null && !existsSync34(join44(projectPath, "project.godot"));
|
|
55912
56022
|
if (projectGone && callersName !== undefined) {
|
|
55913
56023
|
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"]);
|
|
55914
56024
|
}
|
|
@@ -56293,7 +56403,7 @@ ${error.text}`;
|
|
|
56293
56403
|
notes.push(endedWithoutACode({
|
|
56294
56404
|
throughEditor: run.throughEditor,
|
|
56295
56405
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
56296
|
-
couldAnnounce: run.projectPath !== null && existsSync34(
|
|
56406
|
+
couldAnnounce: run.projectPath !== null && existsSync34(join44(run.projectPath, RUNTIME_SCRIPT)),
|
|
56297
56407
|
endedHere: typeof run.endedHere === "string",
|
|
56298
56408
|
pickedUp: run.pickedUp === true
|
|
56299
56409
|
}));
|
|
@@ -57513,6 +57623,7 @@ ${error.text}`;
|
|
|
57513
57623
|
async settleRenameOnDisk(projectPath, changed, asked) {
|
|
57514
57624
|
const report = {};
|
|
57515
57625
|
let ok = true;
|
|
57626
|
+
const cacheBefore = asked.rebuildCache ? cachedEntries(projectPath) : null;
|
|
57516
57627
|
if (asked.moved) {
|
|
57517
57628
|
const engine = await this.engine();
|
|
57518
57629
|
const sidecarsBefore = new Set(projectFiles(projectPath, [".uid", ".import"]));
|
|
@@ -57527,7 +57638,7 @@ ${error.text}`;
|
|
|
57527
57638
|
}
|
|
57528
57639
|
if (asked.rebuildCache) {
|
|
57529
57640
|
const rebuilt = await this.rebuildClassCache(projectPath);
|
|
57530
|
-
report["classCache"] = rebuilt.ok ? rebuilt.payload : { rebuilt: false, problem: rebuilt.message };
|
|
57641
|
+
report["classCache"] = rebuilt.ok ? { ...rebuilt.payload, ...cacheChangesBetween(cacheBefore, cachedEntries(projectPath)) } : { rebuilt: false, problem: rebuilt.message };
|
|
57531
57642
|
ok &&= rebuilt.ok;
|
|
57532
57643
|
}
|
|
57533
57644
|
if (changed.length > 0) {
|
|
@@ -57779,7 +57890,7 @@ ${error.text}`;
|
|
|
57779
57890
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
57780
57891
|
const run = await this.currentRun();
|
|
57781
57892
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
57782
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync34(
|
|
57893
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync34(join44(project, RUNTIME_SCRIPT)) || !await this.runStillGoing(run)) {
|
|
57783
57894
|
return null;
|
|
57784
57895
|
}
|
|
57785
57896
|
const already = runtimesAnnounced().running;
|
|
@@ -57889,7 +58000,7 @@ ${error.text}`;
|
|
|
57889
58000
|
return this.createErrorResponse(staleNote);
|
|
57890
58001
|
}
|
|
57891
58002
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
57892
|
-
const screenshotPath = screenshotDir ?
|
|
58003
|
+
const screenshotPath = screenshotDir ? join44(screenshotDir, "capture.png") : null;
|
|
57893
58004
|
const refused = (message) => this.createErrorResponse(message);
|
|
57894
58005
|
const onGame = {
|
|
57895
58006
|
pid: choice.endpoint.pid,
|
|
@@ -58093,7 +58204,7 @@ async function runGodotServer() {
|
|
|
58093
58204
|
const server = new GodotServer;
|
|
58094
58205
|
await server.run();
|
|
58095
58206
|
}
|
|
58096
|
-
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,
|
|
58207
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, SUCCESSOR_SOUGHT_WITHIN_MS = 20000, CLOSE_ASK_MS = 3000, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_SETTLED_MS = 500, SLOW_ANSWER_MS = 1000, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_TIMEOUT_MS = 600000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", LOOPING_AFTER = 100, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, DESTINATION_ARGUMENTS, OUTPUT_PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, MOST_PRINTED_LINES = 50, MOST_UNDECLARED_ERRORS = 20, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", RAISED_WARNING = " (Warning treated as error.)", PARSE_ERROR2 = "Parse Error: ", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
58097
58208
|
var init_server = __esm(() => {
|
|
58098
58209
|
init_dist2();
|
|
58099
58210
|
init_stdio();
|
|
@@ -58162,7 +58273,6 @@ var init_server = __esm(() => {
|
|
|
58162
58273
|
init_update_check();
|
|
58163
58274
|
__dirname2 = dirname25(fileURLToPath5(import.meta.url));
|
|
58164
58275
|
KEEPER_DEBUG_MS = PAUSE_MS + STEP_MS + 2 * READ_MS;
|
|
58165
|
-
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
58166
58276
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
58167
58277
|
VALUE_NAMES = {
|
|
58168
58278
|
string: "a string",
|
|
@@ -58228,7 +58338,7 @@ init_bridge_announce();
|
|
|
58228
58338
|
init_engine_launch();
|
|
58229
58339
|
init_engine_slots();
|
|
58230
58340
|
import { existsSync as existsSync35 } from "node:fs";
|
|
58231
|
-
import { dirname as dirname26, join as
|
|
58341
|
+
import { dirname as dirname26, join as join45, resolve as resolve14 } from "node:path";
|
|
58232
58342
|
|
|
58233
58343
|
// src/error-mode.ts
|
|
58234
58344
|
import process8 from "node:process";
|
|
@@ -58268,13 +58378,13 @@ init_lsp_client();
|
|
|
58268
58378
|
init_process_children();
|
|
58269
58379
|
import { existsSync as existsSync17, readdirSync as readdirSync10, readFileSync as readFileSync16, unlinkSync as unlinkSync2 } from "node:fs";
|
|
58270
58380
|
import { connect as connect2 } from "node:net";
|
|
58271
|
-
import { join as
|
|
58381
|
+
import { join as join21, resolve as resolve7 } from "node:path";
|
|
58272
58382
|
var EDITOR_RECORD_PROTOCOL = 1;
|
|
58273
58383
|
var LSP_WAIT_SECONDS = 20;
|
|
58274
58384
|
var HOST = "127.0.0.1";
|
|
58275
58385
|
var LOOK_AGAIN_MS = 500;
|
|
58276
58386
|
function editorRecordsIn(projectPath) {
|
|
58277
|
-
const directory =
|
|
58387
|
+
const directory = join21(projectPath, ".godot", "gdharness-editors");
|
|
58278
58388
|
let names;
|
|
58279
58389
|
try {
|
|
58280
58390
|
names = readdirSync10(directory).filter((name) => /^\d+\.json$/.test(name));
|
|
@@ -58283,7 +58393,7 @@ function editorRecordsIn(projectPath) {
|
|
|
58283
58393
|
}
|
|
58284
58394
|
const records = [];
|
|
58285
58395
|
for (const name of names) {
|
|
58286
|
-
const file =
|
|
58396
|
+
const file = join21(directory, name);
|
|
58287
58397
|
try {
|
|
58288
58398
|
const said = JSON.parse(readFileSync16(file, "utf8"));
|
|
58289
58399
|
const { protocol, pid, lsp_port: lspPort, written_at_ms: writtenAtMs } = said;
|
|
@@ -58378,7 +58488,7 @@ function lspOptionsOf(args, cwd) {
|
|
|
58378
58488
|
}
|
|
58379
58489
|
}
|
|
58380
58490
|
const projectPath = resolve7(cwd, named ?? ".");
|
|
58381
|
-
if (!existsSync17(
|
|
58491
|
+
if (!existsSync17(join21(projectPath, "project.godot"))) {
|
|
58382
58492
|
throw new EngineUsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
58383
58493
|
}
|
|
58384
58494
|
return { projectPath, port, waitSeconds };
|
|
@@ -58452,6 +58562,7 @@ async function pipe(port) {
|
|
|
58452
58562
|
|
|
58453
58563
|
// src/cli.ts
|
|
58454
58564
|
init_paths();
|
|
58565
|
+
init_process_children();
|
|
58455
58566
|
|
|
58456
58567
|
// src/prompt.ts
|
|
58457
58568
|
import process12 from "node:process";
|
|
@@ -58485,9 +58596,9 @@ init_setup();
|
|
|
58485
58596
|
init_tool_definitions();
|
|
58486
58597
|
init_tool_reference();
|
|
58487
58598
|
import { existsSync as existsSync19, mkdirSync as mkdirSync7, readdirSync as readdirSync12, rmSync as rmSync10, writeFileSync as writeFileSync11 } from "node:fs";
|
|
58488
|
-
import { basename as basename8, dirname as dirname10, join as
|
|
58599
|
+
import { basename as basename8, dirname as dirname10, join as join23 } from "node:path";
|
|
58489
58600
|
var SKILL_NAME = "gdharness";
|
|
58490
|
-
var SHARED_SKILLS =
|
|
58601
|
+
var SHARED_SKILLS = join23(".agents", "skills");
|
|
58491
58602
|
function skillMarkdown(version) {
|
|
58492
58603
|
return `---
|
|
58493
58604
|
name: ${SKILL_NAME}
|
|
@@ -58728,27 +58839,27 @@ what they report while starting comes back under \`engine_messages_before_operat
|
|
|
58728
58839
|
function skillFiles(version) {
|
|
58729
58840
|
return new Map([
|
|
58730
58841
|
["SKILL.md", skillMarkdown(version)],
|
|
58731
|
-
[
|
|
58842
|
+
[join23("references", "tools.md"), renderToolsMarkdown()]
|
|
58732
58843
|
]);
|
|
58733
58844
|
}
|
|
58734
58845
|
function skillDirectories(harnesses, projectPath, exists) {
|
|
58735
|
-
const directories = [
|
|
58846
|
+
const directories = [join23(projectPath, SHARED_SKILLS, SKILL_NAME)];
|
|
58736
58847
|
for (const harness of harnesses) {
|
|
58737
58848
|
if (harness.skills === undefined) {
|
|
58738
58849
|
continue;
|
|
58739
58850
|
}
|
|
58740
|
-
const own =
|
|
58741
|
-
if ((!harness.skills.shared || exists(own)) && !directories.includes(
|
|
58742
|
-
directories.push(
|
|
58851
|
+
const own = join23(projectPath, harness.skills.dir);
|
|
58852
|
+
if ((!harness.skills.shared || exists(own)) && !directories.includes(join23(own, SKILL_NAME))) {
|
|
58853
|
+
directories.push(join23(own, SKILL_NAME));
|
|
58743
58854
|
}
|
|
58744
58855
|
}
|
|
58745
58856
|
return directories;
|
|
58746
58857
|
}
|
|
58747
58858
|
function everySkillDirectory(projectPath, harnesses) {
|
|
58748
|
-
const directories = new Set([
|
|
58859
|
+
const directories = new Set([join23(projectPath, SHARED_SKILLS, SKILL_NAME)]);
|
|
58749
58860
|
for (const harness of harnesses) {
|
|
58750
58861
|
if (harness.skills !== undefined) {
|
|
58751
|
-
directories.add(
|
|
58862
|
+
directories.add(join23(projectPath, harness.skills.dir, SKILL_NAME));
|
|
58752
58863
|
}
|
|
58753
58864
|
}
|
|
58754
58865
|
return [...directories];
|
|
@@ -58765,7 +58876,7 @@ function removeSkill(directories, projectPath) {
|
|
|
58765
58876
|
return removed;
|
|
58766
58877
|
}
|
|
58767
58878
|
function pruneEmpty(directory, projectPath) {
|
|
58768
|
-
const shared =
|
|
58879
|
+
const shared = join23(projectPath, ".agents");
|
|
58769
58880
|
let path = directory;
|
|
58770
58881
|
while (path !== projectPath && (basename8(path) === "skills" || path === shared)) {
|
|
58771
58882
|
if (readdirSync12(path).length > 0) {
|
|
@@ -58781,7 +58892,7 @@ function writeSkill(directories, version) {
|
|
|
58781
58892
|
const replaced = existsSync19(directory);
|
|
58782
58893
|
rmSync10(directory, { recursive: true, force: true });
|
|
58783
58894
|
for (const [name, contents] of files) {
|
|
58784
|
-
const path =
|
|
58895
|
+
const path = join23(directory, name);
|
|
58785
58896
|
mkdirSync7(dirname10(path), { recursive: true });
|
|
58786
58897
|
writeFileSync11(path, contents, "utf8");
|
|
58787
58898
|
}
|
|
@@ -58796,7 +58907,7 @@ init_setup();
|
|
|
58796
58907
|
init_suite_judge();
|
|
58797
58908
|
import { existsSync as existsSync20, mkdirSync as mkdirSync8, readFileSync as readFileSync19, rmSync as rmSync11 } from "node:fs";
|
|
58798
58909
|
import { availableParallelism as availableParallelism3 } from "node:os";
|
|
58799
|
-
import { join as
|
|
58910
|
+
import { join as join24, resolve as resolve9 } from "node:path";
|
|
58800
58911
|
import process13 from "node:process";
|
|
58801
58912
|
var SHARE = "--gdh-share=";
|
|
58802
58913
|
var GATHER = "--gdh-share-gather=";
|
|
@@ -58836,9 +58947,9 @@ function spreadOptionsOf(args, cwd) {
|
|
|
58836
58947
|
}
|
|
58837
58948
|
const first = positional[0];
|
|
58838
58949
|
const named = first === undefined ? null : resolve9(cwd, first);
|
|
58839
|
-
const projectNamed = named !== null && existsSync20(
|
|
58950
|
+
const projectNamed = named !== null && existsSync20(join24(named, "project.godot"));
|
|
58840
58951
|
const projectPath = projectNamed ? named : resolve9(cwd);
|
|
58841
|
-
if (!existsSync20(
|
|
58952
|
+
if (!existsSync20(join24(projectPath, "project.godot"))) {
|
|
58842
58953
|
throw new EngineUsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
58843
58954
|
}
|
|
58844
58955
|
const scenes = positional.slice(projectNamed ? 1 : 0);
|
|
@@ -58846,7 +58957,7 @@ function spreadOptionsOf(args, cwd) {
|
|
|
58846
58957
|
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(", ")}.`);
|
|
58847
58958
|
}
|
|
58848
58959
|
const scene = resPathOf(scenes[0] ?? "", projectPath, cwd);
|
|
58849
|
-
if (!existsSync20(
|
|
58960
|
+
if (!existsSync20(join24(projectPath, scene.slice("res://".length)))) {
|
|
58850
58961
|
throw new EngineUsageError(`Nothing at ${scene} in the project at ${projectPath}.`);
|
|
58851
58962
|
}
|
|
58852
58963
|
const benchArguments = dashes === -1 ? [] : args.slice(dashes + 1);
|
|
@@ -58917,14 +59028,14 @@ function shareFaults(played) {
|
|
|
58917
59028
|
return faults;
|
|
58918
59029
|
}
|
|
58919
59030
|
async function runSpreadCommand(options, godotPath) {
|
|
58920
|
-
if (!existsSync20(
|
|
59031
|
+
if (!existsSync20(join24(options.projectPath, SHARE_SCRIPT))) {
|
|
58921
59032
|
throw new EngineUsageError(`res://${SHARE_SCRIPT} is not in the project, so its bench cannot ask GdhShare for outcomes. Run gdharness setup.`);
|
|
58922
59033
|
}
|
|
58923
59034
|
const startedAt = Date.now();
|
|
58924
|
-
const spreadRuns =
|
|
59035
|
+
const spreadRuns = join24(options.projectPath, ".godot", "gdharness", "spread-runs");
|
|
58925
59036
|
sweepRunDirectories(spreadRuns);
|
|
58926
|
-
const runDirectory =
|
|
58927
|
-
const shareDirectory =
|
|
59037
|
+
const runDirectory = join24(spreadRuns, `${String(startedAt)}-${String(process13.pid)}`);
|
|
59038
|
+
const shareDirectory = join24(runDirectory, "share");
|
|
58928
59039
|
mkdirSync8(shareDirectory, { recursive: true });
|
|
58929
59040
|
const chosen = machineSlots({ directory: options.slotsDirectory, count: options.slotCount });
|
|
58930
59041
|
const slotCount = chosen.count;
|
|
@@ -58963,7 +59074,7 @@ async function runSpreadCommand(options, godotPath) {
|
|
|
58963
59074
|
share,
|
|
58964
59075
|
`${INTO}${shareDirectory}`
|
|
58965
59076
|
]);
|
|
58966
|
-
const logs = Array.from({ length: workers }, (_, index) =>
|
|
59077
|
+
const logs = Array.from({ length: workers }, (_, index) => join24(runDirectory, `worker-${String(index)}`, "engine.log"));
|
|
58967
59078
|
let done = 0;
|
|
58968
59079
|
const heartbeat = setInterval(() => {
|
|
58969
59080
|
console.log(`[spread] ${progressOf(logs)}`);
|
|
@@ -58981,7 +59092,7 @@ async function runSpreadCommand(options, godotPath) {
|
|
|
58981
59092
|
}
|
|
58982
59093
|
const faults = left.flatMap((worker) => engineFaults(worker, options.godotVersion));
|
|
58983
59094
|
const arms = left.map((_, index) => {
|
|
58984
|
-
const file =
|
|
59095
|
+
const file = join24(shareDirectory, `worker-${String(index)}.arms`);
|
|
58985
59096
|
return existsSync20(file) ? armsPlayed(readFileSync19(file, "utf8")) : [];
|
|
58986
59097
|
});
|
|
58987
59098
|
faults.push(...shareFaults(arms));
|
|
@@ -58991,7 +59102,7 @@ async function runSpreadCommand(options, godotPath) {
|
|
|
58991
59102
|
const gather = await engine("gather", `${GATHER}${String(workers)}`);
|
|
58992
59103
|
console.log(readFileSync19(gather.log, "utf8").replace(CONTROL_SEQUENCE, "").trimEnd());
|
|
58993
59104
|
const gatherFaults = engineFaults(gather, options.godotVersion);
|
|
58994
|
-
const gatheredFile =
|
|
59105
|
+
const gatheredFile = join24(shareDirectory, "gathered.arms");
|
|
58995
59106
|
const gathered = new Set((existsSync20(gatheredFile) ? armsPlayed(readFileSync19(gatheredFile, "utf8")) ?? [] : []).map((arm) => arm.arm));
|
|
58996
59107
|
const ungathered = (arms[0] ?? []).filter((arm) => !gathered.has(arm.arm)).map((arm) => arm.arm);
|
|
58997
59108
|
if (ungathered.length > 0) {
|
|
@@ -59012,9 +59123,9 @@ async function runSpreadCommand(options, godotPath) {
|
|
|
59012
59123
|
}
|
|
59013
59124
|
}
|
|
59014
59125
|
async function played(launch, slots, options, runDirectory, name, benchArguments) {
|
|
59015
|
-
const directory =
|
|
59016
|
-
mkdirSync8(
|
|
59017
|
-
const log =
|
|
59126
|
+
const directory = join24(runDirectory, name);
|
|
59127
|
+
mkdirSync8(join24(directory, "user"), { recursive: true });
|
|
59128
|
+
const log = join24(directory, "engine.log");
|
|
59018
59129
|
const args = [
|
|
59019
59130
|
"--headless",
|
|
59020
59131
|
"--audio-driver",
|
|
@@ -59097,14 +59208,14 @@ import {
|
|
|
59097
59208
|
writeFileSync as writeFileSync12
|
|
59098
59209
|
} from "node:fs";
|
|
59099
59210
|
import { availableParallelism as availableParallelism4 } from "node:os";
|
|
59100
|
-
import { dirname as dirname13, join as
|
|
59211
|
+
import { dirname as dirname13, join as join28, relative as relative5, resolve as resolve10, sep as sep4 } from "node:path";
|
|
59101
59212
|
import process14 from "node:process";
|
|
59102
59213
|
|
|
59103
59214
|
// src/suite-fingerprint.ts
|
|
59104
59215
|
init_class_cache();
|
|
59105
59216
|
import { createHash as createHash6 } from "node:crypto";
|
|
59106
59217
|
import { existsSync as existsSync21, readdirSync as readdirSync13, readFileSync as readFileSync20, statSync as statSync13 } from "node:fs";
|
|
59107
|
-
import { join as
|
|
59218
|
+
import { join as join25, posix as posix2 } from "node:path";
|
|
59108
59219
|
var RES_LITERAL = /res:\/\/[^"'\s)\]<>]*/g;
|
|
59109
59220
|
var RELATIVE_LOAD = /\b(?:pre)?load\(\s*["']((?!res:\/\/|uid:\/\/)[^"']+)["']/g;
|
|
59110
59221
|
var REACHES_AT_RUN_TIME = [
|
|
@@ -59163,7 +59274,7 @@ ${this.read(path).digest}
|
|
|
59163
59274
|
const hash = createHash6("sha256");
|
|
59164
59275
|
const walk = (directory, under) => {
|
|
59165
59276
|
for (const entry of readdirSync13(directory, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
|
|
59166
|
-
const file =
|
|
59277
|
+
const file = join25(directory, entry.name);
|
|
59167
59278
|
if (entry.isDirectory()) {
|
|
59168
59279
|
walk(file, `${under}${entry.name}/`);
|
|
59169
59280
|
} else if (entry.isFile()) {
|
|
@@ -59172,7 +59283,7 @@ ${this.read(path).digest}
|
|
|
59172
59283
|
}
|
|
59173
59284
|
}
|
|
59174
59285
|
};
|
|
59175
|
-
const directory =
|
|
59286
|
+
const directory = join25(this.scripts.projectPath, root.slice("res://".length));
|
|
59176
59287
|
if (existsSync21(directory)) {
|
|
59177
59288
|
walk(directory, "");
|
|
59178
59289
|
}
|
|
@@ -59182,7 +59293,7 @@ ${this.read(path).digest}
|
|
|
59182
59293
|
if (PLACEHOLDER.test(path)) {
|
|
59183
59294
|
return { digest: "", reaches: [], unreadable: `${path} is filled in at run time` };
|
|
59184
59295
|
}
|
|
59185
|
-
const file =
|
|
59296
|
+
const file = join25(this.scripts.projectPath, path.slice("res://".length));
|
|
59186
59297
|
if (!existsSync21(file)) {
|
|
59187
59298
|
return { digest: "missing", reaches: [], unreadable: null };
|
|
59188
59299
|
}
|
|
@@ -59231,7 +59342,7 @@ ${this.read(path).digest}
|
|
|
59231
59342
|
}
|
|
59232
59343
|
}
|
|
59233
59344
|
function extensionsOf(projectPath) {
|
|
59234
|
-
const list =
|
|
59345
|
+
const list = join25(projectPath, ".godot", "extension_list.cfg");
|
|
59235
59346
|
return existsSync21(list) ? readFileSync20(list, "utf8").split(`
|
|
59236
59347
|
`).map((line) => line.trim()).filter((line) => line.startsWith("res://")) : [];
|
|
59237
59348
|
}
|
|
@@ -59242,13 +59353,13 @@ init_suite_plan();
|
|
|
59242
59353
|
|
|
59243
59354
|
// src/version.ts
|
|
59244
59355
|
import { readFileSync as readFileSync22 } from "node:fs";
|
|
59245
|
-
import { dirname as dirname12, join as
|
|
59356
|
+
import { dirname as dirname12, join as join27 } from "node:path";
|
|
59246
59357
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
59247
59358
|
function getLocalVersion() {
|
|
59248
59359
|
const currentDirectory = dirname12(fileURLToPath3(import.meta.url));
|
|
59249
59360
|
const packageCandidates = [
|
|
59250
|
-
|
|
59251
|
-
|
|
59361
|
+
join27(currentDirectory, "..", "package.json"),
|
|
59362
|
+
join27(currentDirectory, "..", "..", "package.json")
|
|
59252
59363
|
];
|
|
59253
59364
|
for (const packagePath of packageCandidates) {
|
|
59254
59365
|
try {
|
|
@@ -59319,9 +59430,9 @@ function testOptionsOf(args, cwd) {
|
|
|
59319
59430
|
}
|
|
59320
59431
|
const first = positional[0];
|
|
59321
59432
|
const named = first === undefined ? null : resolve10(cwd, first);
|
|
59322
|
-
const projectNamed = named !== null && existsSync23(
|
|
59433
|
+
const projectNamed = named !== null && existsSync23(join28(named, "project.godot"));
|
|
59323
59434
|
const projectPath = projectNamed ? named : resolve10(cwd);
|
|
59324
|
-
if (!existsSync23(
|
|
59435
|
+
if (!existsSync23(join28(projectPath, "project.godot"))) {
|
|
59325
59436
|
throw new EngineUsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
59326
59437
|
}
|
|
59327
59438
|
const last = (flag) => valued.get(flag)?.at(-1);
|
|
@@ -59355,11 +59466,11 @@ function seedOf(value) {
|
|
|
59355
59466
|
return seed;
|
|
59356
59467
|
}
|
|
59357
59468
|
function rootsOf(options) {
|
|
59358
|
-
return options.roots.length > 0 ? options.roots : TEST_DIRECTORIES.filter((root) => existsSync23(
|
|
59469
|
+
return options.roots.length > 0 ? options.roots : TEST_DIRECTORIES.filter((root) => existsSync23(join28(options.projectPath, root))).map((root) => `res://${root}`);
|
|
59359
59470
|
}
|
|
59360
59471
|
function searchedRoots(options) {
|
|
59361
59472
|
return options.selected.length === 0 ? rootsOf(options) : options.selected.filter((entry) => {
|
|
59362
|
-
const directory =
|
|
59473
|
+
const directory = join28(options.projectPath, entry.slice("res://".length));
|
|
59363
59474
|
return existsSync23(directory) && statSync14(directory).isDirectory();
|
|
59364
59475
|
});
|
|
59365
59476
|
}
|
|
@@ -59376,7 +59487,7 @@ function selectedSuites(scripts, options) {
|
|
|
59376
59487
|
for (const entry of options.selected) {
|
|
59377
59488
|
const caseAt = /^(res:\/\/.*\.gd):(\w+)$/.exec(entry);
|
|
59378
59489
|
const path = caseAt?.[1] ?? entry;
|
|
59379
|
-
const directory =
|
|
59490
|
+
const directory = join28(options.projectPath, path.slice(6));
|
|
59380
59491
|
const suites = existsSync23(directory) && statSync14(directory).isDirectory() ? scripts.suitesUnder([path.replace(/\/$/, "")]) : [scripts.suite(path)].filter((suite) => suite !== null);
|
|
59381
59492
|
if (suites.length === 0) {
|
|
59382
59493
|
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}.`);
|
|
@@ -59433,7 +59544,7 @@ class RunLogs {
|
|
|
59433
59544
|
return this.kept ?? this.directory;
|
|
59434
59545
|
}
|
|
59435
59546
|
named(path) {
|
|
59436
|
-
return this.kept === null ? path :
|
|
59547
|
+
return this.kept === null ? path : join28(this.kept, relative5(this.directory, path));
|
|
59437
59548
|
}
|
|
59438
59549
|
keep() {
|
|
59439
59550
|
if (this.kept === null) {
|
|
@@ -59441,8 +59552,8 @@ class RunLogs {
|
|
|
59441
59552
|
}
|
|
59442
59553
|
for (const file of readdirSync14(this.directory, { recursive: true, encoding: "utf8" })) {
|
|
59443
59554
|
if (file.endsWith(".log") && file.split(sep4).length === 2) {
|
|
59444
|
-
mkdirSync10(dirname13(
|
|
59445
|
-
copyFileSync(
|
|
59555
|
+
mkdirSync10(dirname13(join28(this.kept, file)), { recursive: true });
|
|
59556
|
+
copyFileSync(join28(this.directory, file), join28(this.kept, file));
|
|
59446
59557
|
}
|
|
59447
59558
|
}
|
|
59448
59559
|
}
|
|
@@ -59455,24 +59566,24 @@ async function runTestCommand(options, godotPath) {
|
|
|
59455
59566
|
const startedAt = Date.now();
|
|
59456
59567
|
const scripts = new ProjectScripts(options.projectPath);
|
|
59457
59568
|
const home = homeOf(options.projectPath);
|
|
59458
|
-
const state =
|
|
59459
|
-
const timesFile =
|
|
59460
|
-
const passedFile =
|
|
59461
|
-
const lastRunFile =
|
|
59569
|
+
const state = join28(home, ".godot", "gdharness");
|
|
59570
|
+
const timesFile = join28(state, "suite-times.json");
|
|
59571
|
+
const passedFile = join28(state, "suite-passed.json");
|
|
59572
|
+
const lastRunFile = join28(state, LAST_RUN_FILE);
|
|
59462
59573
|
let suites = selectedSuites(scripts, options);
|
|
59463
59574
|
const uncollected = uncollectedSaid(scripts.uncollectedUnder(searchedRoots(options)));
|
|
59464
59575
|
const wholeSuites = suites.map((suite) => scripts.suite(suite.path) ?? suite);
|
|
59465
59576
|
const declared = new Map(wholeSuites.map((suite) => [suite.path, suite.cases]));
|
|
59466
|
-
const testRuns =
|
|
59467
|
-
const keptRuns =
|
|
59577
|
+
const testRuns = join28(options.projectPath, ".godot", "gdharness", "test-runs");
|
|
59578
|
+
const keptRuns = join28(state, "test-runs");
|
|
59468
59579
|
sweepRunDirectories(testRuns);
|
|
59469
59580
|
if (keptRuns !== testRuns) {
|
|
59470
59581
|
sweepRunDirectories(keptRuns);
|
|
59471
59582
|
}
|
|
59472
59583
|
const runName = `${String(startedAt)}-${String(process14.pid)}`;
|
|
59473
|
-
const runDirectory =
|
|
59584
|
+
const runDirectory = join28(testRuns, runName);
|
|
59474
59585
|
mkdirSync10(runDirectory, { recursive: true });
|
|
59475
|
-
const logs = new RunLogs(runDirectory, keptRuns === testRuns ? null :
|
|
59586
|
+
const logs = new RunLogs(runDirectory, keptRuns === testRuns ? null : join28(keptRuns, runName));
|
|
59476
59587
|
const chosen = machineSlots({ directory: options.slotsDirectory, count: options.slotCount });
|
|
59477
59588
|
const slots = new EngineSlots(chosen.directory, chosen.count, { pid: process14.pid, started: await ownStart(), by: `gdharness test of ${options.projectPath}` }, (line) => {
|
|
59478
59589
|
console.log(line);
|
|
@@ -59680,9 +59791,9 @@ async function importProject(launch) {
|
|
|
59680
59791
|
console.log("The import was skipped: every imported file and the class cache are current.");
|
|
59681
59792
|
return null;
|
|
59682
59793
|
}
|
|
59683
|
-
const directory =
|
|
59684
|
-
mkdirSync10(
|
|
59685
|
-
const log =
|
|
59794
|
+
const directory = join28(launch.runDirectory, "import");
|
|
59795
|
+
mkdirSync10(join28(directory, "user"), { recursive: true });
|
|
59796
|
+
const log = join28(directory, "import.log");
|
|
59686
59797
|
const lock = await launch.slots.take();
|
|
59687
59798
|
try {
|
|
59688
59799
|
freeLibraryCopies(launch.projectPath);
|
|
@@ -59747,12 +59858,12 @@ async function runShares(launch, units, declared, lanes, taken, times) {
|
|
|
59747
59858
|
return (await Promise.all(Array.from({ length: lanes }, (_, at) => lane(at)))).flat().sort((a, b) => a.index - b.index);
|
|
59748
59859
|
}
|
|
59749
59860
|
async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
59750
|
-
const directory =
|
|
59751
|
-
mkdirSync10(
|
|
59752
|
-
const suitesFile =
|
|
59753
|
-
const skippingFile =
|
|
59754
|
-
const rowsFile =
|
|
59755
|
-
const expectedFile =
|
|
59861
|
+
const directory = join28(launch.runDirectory, `engine-${String(index)}${again === "" ? "" : `-${again}`}`);
|
|
59862
|
+
mkdirSync10(join28(directory, "user"), { recursive: true });
|
|
59863
|
+
const suitesFile = join28(directory, "suites.txt");
|
|
59864
|
+
const skippingFile = join28(directory, "skipping.txt");
|
|
59865
|
+
const rowsFile = join28(directory, "rows.txt");
|
|
59866
|
+
const expectedFile = join28(directory, "expected.json");
|
|
59756
59867
|
writeFileSync12(suitesFile, `${plan.suites.map((suite) => suite.path).join(`
|
|
59757
59868
|
`)}
|
|
59758
59869
|
`);
|
|
@@ -59779,7 +59890,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59779
59890
|
...plan.windowed ? [] : ["--ignoreHeadlessMode"],
|
|
59780
59891
|
"-c",
|
|
59781
59892
|
"-rd",
|
|
59782
|
-
`res://${relative5(launch.projectPath,
|
|
59893
|
+
`res://${relative5(launch.projectPath, join28(directory, "reports")).split(sep4).join("/")}`,
|
|
59783
59894
|
"-rc",
|
|
59784
59895
|
"1",
|
|
59785
59896
|
"--",
|
|
@@ -59795,7 +59906,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59795
59906
|
"--expected-out",
|
|
59796
59907
|
expectedFile
|
|
59797
59908
|
];
|
|
59798
|
-
const log =
|
|
59909
|
+
const log = join28(directory, "engine.log");
|
|
59799
59910
|
const lock = slot ?? await launch.slots.take();
|
|
59800
59911
|
const began = Date.now();
|
|
59801
59912
|
let end;
|
|
@@ -59814,7 +59925,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59814
59925
|
godotVersion: launch.godotVersion
|
|
59815
59926
|
};
|
|
59816
59927
|
const judged = judgeEngine(printed, assignment, end);
|
|
59817
|
-
const written = reportsFault(
|
|
59928
|
+
const written = reportsFault(join28(directory, "reports"));
|
|
59818
59929
|
return {
|
|
59819
59930
|
index,
|
|
59820
59931
|
plan,
|
|
@@ -59828,7 +59939,7 @@ async function runEngine2(launch, plan, index, times, again = "", slot = null) {
|
|
|
59828
59939
|
}
|
|
59829
59940
|
var REPORT_FILES_NAMED = 3;
|
|
59830
59941
|
function reportsFault(reports) {
|
|
59831
|
-
const files = existsSync23(reports) ? readdirSync14(reports, { recursive: true, encoding: "utf8" }).filter((file) => statSync14(
|
|
59942
|
+
const files = existsSync23(reports) ? readdirSync14(reports, { recursive: true, encoding: "utf8" }).filter((file) => statSync14(join28(reports, file)).isFile()).sort() : [];
|
|
59832
59943
|
if (files.length === 0) {
|
|
59833
59944
|
return null;
|
|
59834
59945
|
}
|
|
@@ -59986,7 +60097,7 @@ class UsageError extends Refusal {
|
|
|
59986
60097
|
function projectArgument(at) {
|
|
59987
60098
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
59988
60099
|
const projectPath = resolve14(named ?? ".");
|
|
59989
|
-
if (!existsSync35(
|
|
60100
|
+
if (!existsSync35(join45(projectPath, "project.godot"))) {
|
|
59990
60101
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
59991
60102
|
}
|
|
59992
60103
|
return projectPath;
|
|
@@ -60050,7 +60161,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
60050
60161
|
}
|
|
60051
60162
|
const path = (named ?? "").replace(/^res:\/\//, "");
|
|
60052
60163
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
60053
|
-
if (!existsSync35(
|
|
60164
|
+
if (!existsSync35(join45(projectPath, path))) {
|
|
60054
60165
|
console.log(" that file is not in this project, so it boots with a missing script");
|
|
60055
60166
|
}
|
|
60056
60167
|
console.log(` ${useTheShippedLoader(RUNTIME_AUTOLOAD.name, path)}`);
|
|
@@ -60308,10 +60419,8 @@ async function upgrade() {
|
|
|
60308
60419
|
}
|
|
60309
60420
|
console.log(`
|
|
60310
60421
|
On ${version}. What is left for you:`);
|
|
60311
|
-
|
|
60312
|
-
|
|
60313
|
-
` + " if the editor is running other code." : ` 1. The open editor is still running the addons it loaded at startup. Restart it with the
|
|
60314
|
-
` + " editor_launch restart tool, which closes and reopens the window.");
|
|
60422
|
+
const tree = await processTree();
|
|
60423
|
+
console.log(editorUpgradeNote(digestBefore !== null && digestBefore === installedEditorDigest(projectPath), tree === undefined ? null : editorsOfProjectIn(tree, projectPath)));
|
|
60315
60424
|
const serving = await serverServing(projectPath);
|
|
60316
60425
|
console.log(harnessNote({ moved, written, byHand }, [launch.command, ...launch.args].join(" "), serving === null ? null : { server: serving, isThisVersion: serving.version === version }));
|
|
60317
60426
|
const trailing = worktreeCheckoutsOf(projectPath).flatMap((path) => {
|