gdharness 1.2.26 → 1.2.28
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 +625 -350
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +152 -6
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +22 -0
- package/build/index.js +494 -224
- package/build/keeper.js +1207 -753
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -8198,24 +8198,106 @@ var init_engine_run = __esm(() => {
|
|
|
8198
8198
|
init_game_log();
|
|
8199
8199
|
});
|
|
8200
8200
|
|
|
8201
|
+
// src/library-copies.ts
|
|
8202
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync3, renameSync, unlinkSync } from "node:fs";
|
|
8203
|
+
import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute2, join as join5 } from "node:path";
|
|
8204
|
+
function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
8205
|
+
const setAside = [];
|
|
8206
|
+
const swept = [];
|
|
8207
|
+
if (platform !== "win32") {
|
|
8208
|
+
return { setAside, swept };
|
|
8209
|
+
}
|
|
8210
|
+
for (const library of windowsLibraries(projectPath)) {
|
|
8211
|
+
const folder = dirname4(library);
|
|
8212
|
+
const copy = `~${basename3(library)}`;
|
|
8213
|
+
if (existsSync5(join5(folder, copy))) {
|
|
8214
|
+
const aside = join5(folder, `${copy}.aside-${process.pid}-${Date.now()}`);
|
|
8215
|
+
try {
|
|
8216
|
+
renameSync(join5(folder, copy), aside);
|
|
8217
|
+
setAside.push(aside);
|
|
8218
|
+
} catch {}
|
|
8219
|
+
}
|
|
8220
|
+
let names;
|
|
8221
|
+
try {
|
|
8222
|
+
names = readdirSync3(folder);
|
|
8223
|
+
} catch {
|
|
8224
|
+
continue;
|
|
8225
|
+
}
|
|
8226
|
+
for (const name of names) {
|
|
8227
|
+
if (name.length > copy.length && name.toLowerCase().startsWith(copy.toLowerCase())) {
|
|
8228
|
+
try {
|
|
8229
|
+
unlinkSync(join5(folder, name));
|
|
8230
|
+
swept.push(join5(folder, name));
|
|
8231
|
+
} catch {}
|
|
8232
|
+
}
|
|
8233
|
+
}
|
|
8234
|
+
}
|
|
8235
|
+
return { setAside, swept };
|
|
8236
|
+
}
|
|
8237
|
+
function windowsLibraries(projectPath) {
|
|
8238
|
+
const listed = readOrNull(join5(projectPath, ".godot", "extension_list.cfg"));
|
|
8239
|
+
if (listed === null) {
|
|
8240
|
+
return [];
|
|
8241
|
+
}
|
|
8242
|
+
const libraries = new Set;
|
|
8243
|
+
for (const line of listed.split(/\r?\n/)) {
|
|
8244
|
+
const manifest = resolveResource(projectPath, projectPath, line.trim());
|
|
8245
|
+
const text = manifest === null ? null : readOrNull(manifest);
|
|
8246
|
+
if (manifest === null || text === null) {
|
|
8247
|
+
continue;
|
|
8248
|
+
}
|
|
8249
|
+
let inLibraries = false;
|
|
8250
|
+
for (const row of text.split(/\r?\n/)) {
|
|
8251
|
+
const section = /^\s*\[([^\]]+)\]/.exec(row)?.[1];
|
|
8252
|
+
if (section !== undefined) {
|
|
8253
|
+
inLibraries = section.trim() === "libraries";
|
|
8254
|
+
continue;
|
|
8255
|
+
}
|
|
8256
|
+
const named = inLibraries ? /^\s*windows[\w.]*\s*=\s*"([^"]+)"/.exec(row)?.[1] : undefined;
|
|
8257
|
+
const library = named === undefined ? null : resolveResource(projectPath, dirname4(manifest), named);
|
|
8258
|
+
if (library !== null) {
|
|
8259
|
+
libraries.add(library);
|
|
8260
|
+
}
|
|
8261
|
+
}
|
|
8262
|
+
}
|
|
8263
|
+
return [...libraries];
|
|
8264
|
+
}
|
|
8265
|
+
function resolveResource(projectPath, relativeTo, path) {
|
|
8266
|
+
if (path === "") {
|
|
8267
|
+
return null;
|
|
8268
|
+
}
|
|
8269
|
+
if (path.startsWith("res://")) {
|
|
8270
|
+
return join5(projectPath, path.slice("res://".length));
|
|
8271
|
+
}
|
|
8272
|
+
return isAbsolute2(path) ? path : join5(relativeTo, path);
|
|
8273
|
+
}
|
|
8274
|
+
function readOrNull(path) {
|
|
8275
|
+
try {
|
|
8276
|
+
return readFileSync3(path, "utf8");
|
|
8277
|
+
} catch {
|
|
8278
|
+
return null;
|
|
8279
|
+
}
|
|
8280
|
+
}
|
|
8281
|
+
var init_library_copies = () => {};
|
|
8282
|
+
|
|
8201
8283
|
// src/reimport.ts
|
|
8202
8284
|
import { createHash } from "node:crypto";
|
|
8203
|
-
import { existsSync as
|
|
8204
|
-
import { basename as
|
|
8285
|
+
import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3, statSync as statSync3, utimesSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8286
|
+
import { basename as basename4, join as join6 } from "node:path";
|
|
8205
8287
|
function forceNextImport(projectPath, resourcePath) {
|
|
8206
8288
|
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
8207
|
-
const sidecar =
|
|
8208
|
-
if (!
|
|
8289
|
+
const sidecar = join6(projectPath, `${relative}.import`);
|
|
8290
|
+
if (!existsSync6(sidecar)) {
|
|
8209
8291
|
return;
|
|
8210
8292
|
}
|
|
8211
8293
|
const { atime, mtimeMs } = statSync3(sidecar);
|
|
8212
|
-
writeFileSync3(sidecar,
|
|
8294
|
+
writeFileSync3(sidecar, readFileSync4(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
8213
8295
|
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
8214
8296
|
rmSync3(importRecord(projectPath, resourcePath), { force: true });
|
|
8215
8297
|
}
|
|
8216
8298
|
function importRecord(projectPath, resourcePath) {
|
|
8217
8299
|
const digest = createHash("md5").update(resourcePath).digest("hex");
|
|
8218
|
-
return
|
|
8300
|
+
return join6(projectPath, ".godot", "imported", `${basename4(resourcePath)}-${digest}.md5`);
|
|
8219
8301
|
}
|
|
8220
8302
|
var init_reimport = () => {};
|
|
8221
8303
|
|
|
@@ -8235,17 +8317,17 @@ function alive(pid) {
|
|
|
8235
8317
|
var init_alive = () => {};
|
|
8236
8318
|
|
|
8237
8319
|
// src/scratch.ts
|
|
8238
|
-
import { existsSync as
|
|
8320
|
+
import { existsSync as existsSync7, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync4 } from "node:fs";
|
|
8239
8321
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
8240
|
-
import { join as
|
|
8322
|
+
import { join as join7 } from "node:path";
|
|
8241
8323
|
import process6 from "node:process";
|
|
8242
8324
|
function scratchDirectory(kind) {
|
|
8243
|
-
return mkdtempSync(
|
|
8325
|
+
return mkdtempSync(join7(tmpdir2(), `gdharness-${kind}-${process6.pid}-`));
|
|
8244
8326
|
}
|
|
8245
8327
|
function sweepAbandonedScratch(directory = tmpdir2()) {
|
|
8246
8328
|
let entries;
|
|
8247
8329
|
try {
|
|
8248
|
-
entries =
|
|
8330
|
+
entries = readdirSync4(directory);
|
|
8249
8331
|
} catch {
|
|
8250
8332
|
return;
|
|
8251
8333
|
}
|
|
@@ -8256,7 +8338,7 @@ function sweepAbandonedScratch(directory = tmpdir2()) {
|
|
|
8256
8338
|
continue;
|
|
8257
8339
|
}
|
|
8258
8340
|
try {
|
|
8259
|
-
remove(
|
|
8341
|
+
remove(join7(directory, entry));
|
|
8260
8342
|
} catch {}
|
|
8261
8343
|
}
|
|
8262
8344
|
}
|
|
@@ -8294,7 +8376,7 @@ function tryAgain(path, attempt) {
|
|
|
8294
8376
|
lastTryOnExit();
|
|
8295
8377
|
}
|
|
8296
8378
|
const after = AGAIN_AFTER_MS[attempt];
|
|
8297
|
-
if (after === undefined || !
|
|
8379
|
+
if (after === undefined || !existsSync7(path)) {
|
|
8298
8380
|
waiting.delete(path);
|
|
8299
8381
|
if (after === undefined) {
|
|
8300
8382
|
atExit.add(path);
|
|
@@ -8341,8 +8423,8 @@ var init_scratch = __esm(() => {
|
|
|
8341
8423
|
});
|
|
8342
8424
|
|
|
8343
8425
|
// src/headless.ts
|
|
8344
|
-
import { existsSync as
|
|
8345
|
-
import { dirname as
|
|
8426
|
+
import { existsSync as existsSync8, readFileSync as readFileSync5, realpathSync as realpathSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8427
|
+
import { dirname as dirname5, join as join8 } from "node:path";
|
|
8346
8428
|
function snakeCased(params) {
|
|
8347
8429
|
const result = emptyRecord();
|
|
8348
8430
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -8397,7 +8479,7 @@ function refusalsIn(log) {
|
|
|
8397
8479
|
}
|
|
8398
8480
|
function scriptFault(problems, script) {
|
|
8399
8481
|
const spelled = (path) => `(${path.replaceAll("\\", "/").toLowerCase()}/`;
|
|
8400
|
-
const directories = [...new Set([spelled(
|
|
8482
|
+
const directories = [...new Set([spelled(dirname5(script)), spelled(resolvedOr(dirname5(script)))])];
|
|
8401
8483
|
return problems.find((entry) => {
|
|
8402
8484
|
const at = entry.detail.find((line) => line.startsWith("at: "))?.replaceAll("\\", "/").toLowerCase();
|
|
8403
8485
|
return entry.severity === "error" && at !== undefined && directories.some((directory) => at.includes(directory));
|
|
@@ -8422,7 +8504,7 @@ function librariesNotCopied(messages) {
|
|
|
8422
8504
|
}
|
|
8423
8505
|
function extensionRetryNote(libraries, stillNotLoaded = []) {
|
|
8424
8506
|
const second = stillNotLoaded.length === 0 ? "so the import was run again and loaded the extension, and the messages are from that second run." : `so the import was run again, and it still could not load ${stillNotLoaded.join(" and ")}: this answer is from an import without ${stillNotLoaded.length === 1 ? "it" : "them"}, and its messages say what the engine hit.`;
|
|
8425
|
-
return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + `editor's copy aside, ${second} The editor's copy is left beside the library as a ~RF….TMP file, which ` + "
|
|
8507
|
+
return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + `editor's copy aside, ${second} The editor's copy is left beside the library as a ~RF….TMP file, which ` + "the next import, export or editor this server starts on the project deletes once the editor has exited.";
|
|
8426
8508
|
}
|
|
8427
8509
|
async function runImport(godotPath, projectPath, options = {}) {
|
|
8428
8510
|
const once = options.once ?? importOnce;
|
|
@@ -8430,6 +8512,7 @@ async function runImport(godotPath, projectPath, options = {}) {
|
|
|
8430
8512
|
for (const target of options.forcing ?? []) {
|
|
8431
8513
|
forceNextImport(projectPath, target);
|
|
8432
8514
|
}
|
|
8515
|
+
freeLibraryCopies(projectPath);
|
|
8433
8516
|
return once(godotPath, projectPath);
|
|
8434
8517
|
};
|
|
8435
8518
|
const first = await pass();
|
|
@@ -8453,7 +8536,7 @@ async function importOnce(godotPath, projectPath) {
|
|
|
8453
8536
|
ran = await runEngine(godotPath, [
|
|
8454
8537
|
"--headless",
|
|
8455
8538
|
"--log-file",
|
|
8456
|
-
|
|
8539
|
+
join8(logDir, "engine.log"),
|
|
8457
8540
|
"--path",
|
|
8458
8541
|
projectPath,
|
|
8459
8542
|
"--import"
|
|
@@ -8473,13 +8556,13 @@ async function importOnce(godotPath, projectPath) {
|
|
|
8473
8556
|
}
|
|
8474
8557
|
async function runOperation(engine, operation, params, projectPath) {
|
|
8475
8558
|
const paramsDir = scratchDirectory("params");
|
|
8476
|
-
const paramsFile =
|
|
8477
|
-
const answerFile =
|
|
8559
|
+
const paramsFile = join8(paramsDir, `${operation}.json`);
|
|
8560
|
+
const answerFile = join8(paramsDir, "answer.json");
|
|
8478
8561
|
writeFileSync4(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
8479
8562
|
const args = [
|
|
8480
8563
|
"--headless",
|
|
8481
8564
|
"--log-file",
|
|
8482
|
-
|
|
8565
|
+
join8(paramsDir, "engine.log"),
|
|
8483
8566
|
"--path",
|
|
8484
8567
|
projectPath,
|
|
8485
8568
|
"--script",
|
|
@@ -8549,10 +8632,10 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
8549
8632
|
};
|
|
8550
8633
|
}
|
|
8551
8634
|
function answerIn(file) {
|
|
8552
|
-
if (!
|
|
8635
|
+
if (!existsSync8(file)) {
|
|
8553
8636
|
return { kind: "none" };
|
|
8554
8637
|
}
|
|
8555
|
-
const text =
|
|
8638
|
+
const text = readFileSync5(file, "utf8");
|
|
8556
8639
|
try {
|
|
8557
8640
|
const parsed = JSON.parse(text);
|
|
8558
8641
|
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { kind: "answer", payload: parsed } : { kind: "unreadable", why: `it is JSON but not an object: ${text.slice(0, 200)}` };
|
|
@@ -8566,14 +8649,15 @@ function answerIn(file) {
|
|
|
8566
8649
|
var AFTER_ANSWER_NOTE = "Printed after this operation had written its answer, while the engine ran the project's autoloads and shut down: Godot starts a project's autoloads in every script run and cannot be told not to. An error raised in a project script here is that autoload's own, and none of these changes the answer.", OWN_ERROR = "[ERROR] ", ANSWERED = "[gdharness] answered", MESSAGES_KEPT = 200, ERRORS_NAMED = 3;
|
|
8567
8650
|
var init_headless = __esm(() => {
|
|
8568
8651
|
init_engine_run();
|
|
8652
|
+
init_library_copies();
|
|
8569
8653
|
init_reimport();
|
|
8570
8654
|
init_scratch();
|
|
8571
8655
|
});
|
|
8572
8656
|
|
|
8573
8657
|
// src/update-check.ts
|
|
8574
|
-
import { existsSync as
|
|
8658
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
8575
8659
|
import { homedir as homedir3, tmpdir as tmpdir3 } from "node:os";
|
|
8576
|
-
import { join as
|
|
8660
|
+
import { join as join9 } from "node:path";
|
|
8577
8661
|
function packageUrl(root) {
|
|
8578
8662
|
return `${root.replace(/\/+$/, "")}/gdharness/latest`;
|
|
8579
8663
|
}
|
|
@@ -8595,28 +8679,28 @@ function cacheDirectory(environment) {
|
|
|
8595
8679
|
};
|
|
8596
8680
|
const home = set("HOME") ?? homedir3();
|
|
8597
8681
|
if (process.platform === "win32") {
|
|
8598
|
-
return
|
|
8682
|
+
return join9(set("LOCALAPPDATA") ?? home, "gdharness");
|
|
8599
8683
|
}
|
|
8600
8684
|
if (process.platform === "darwin") {
|
|
8601
|
-
return
|
|
8685
|
+
return join9(home, "Library", "Caches", "gdharness");
|
|
8602
8686
|
}
|
|
8603
|
-
return
|
|
8687
|
+
return join9(set("XDG_CACHE_HOME") ?? join9(home, ".cache"), "gdharness");
|
|
8604
8688
|
}
|
|
8605
8689
|
function cacheFile(environment = process.env) {
|
|
8606
8690
|
try {
|
|
8607
8691
|
const directory = cacheDirectory(environment);
|
|
8608
8692
|
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
8609
|
-
return
|
|
8693
|
+
return join9(directory, "update-check.json");
|
|
8610
8694
|
} catch {
|
|
8611
|
-
return
|
|
8695
|
+
return join9(tmpdir3(), "gdharness-update-check.json");
|
|
8612
8696
|
}
|
|
8613
8697
|
}
|
|
8614
8698
|
function readCache(path) {
|
|
8615
8699
|
try {
|
|
8616
|
-
if (!
|
|
8700
|
+
if (!existsSync9(path)) {
|
|
8617
8701
|
return null;
|
|
8618
8702
|
}
|
|
8619
|
-
const parsed = JSON.parse(
|
|
8703
|
+
const parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
8620
8704
|
if (typeof parsed !== "object" || parsed === null) {
|
|
8621
8705
|
return null;
|
|
8622
8706
|
}
|
|
@@ -8764,7 +8848,7 @@ var init_update_check = __esm(() => {
|
|
|
8764
8848
|
});
|
|
8765
8849
|
|
|
8766
8850
|
// src/server-version.ts
|
|
8767
|
-
import { readFileSync as
|
|
8851
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
8768
8852
|
function editorIsStale(addonVersion, serverVersion, addonDigest, shippedDigest) {
|
|
8769
8853
|
if (addonDigest !== undefined && addonDigest !== "" && shippedDigest !== undefined) {
|
|
8770
8854
|
return addonDigest !== shippedDigest;
|
|
@@ -8835,7 +8919,7 @@ var init_server_version = __esm(() => {
|
|
|
8835
8919
|
GODOT_DEBUG_MODE_DEFAULT = process.env["GODOT_DEBUG"] === "true" || DEBUG_MODE;
|
|
8836
8920
|
SERVER_VERSION = (() => {
|
|
8837
8921
|
try {
|
|
8838
|
-
const pkg = JSON.parse(
|
|
8922
|
+
const pkg = JSON.parse(readFileSync7(new URL("../package.json", import.meta.url), "utf8"));
|
|
8839
8923
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
8840
8924
|
} catch {
|
|
8841
8925
|
return "0.0.0";
|
|
@@ -9620,8 +9704,8 @@ var init_gdscript_source = __esm(() => {
|
|
|
9620
9704
|
});
|
|
9621
9705
|
|
|
9622
9706
|
// src/class-cache.ts
|
|
9623
|
-
import { existsSync as
|
|
9624
|
-
import { join as
|
|
9707
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
|
|
9708
|
+
import { join as join10, posix } from "node:path";
|
|
9625
9709
|
function isToolScript(source) {
|
|
9626
9710
|
return TOOL.test(codeOf(source));
|
|
9627
9711
|
}
|
|
@@ -9637,18 +9721,18 @@ function declaredClasses(projectPath) {
|
|
|
9637
9721
|
}
|
|
9638
9722
|
function eachScript(projectPath, each) {
|
|
9639
9723
|
const visit = (directory, prefix) => {
|
|
9640
|
-
if (
|
|
9724
|
+
if (existsSync10(join10(directory, ".gdignore"))) {
|
|
9641
9725
|
return;
|
|
9642
9726
|
}
|
|
9643
|
-
for (const entry of
|
|
9727
|
+
for (const entry of readdirSync5(directory, { withFileTypes: true })) {
|
|
9644
9728
|
if (entry.name.startsWith(".")) {
|
|
9645
9729
|
continue;
|
|
9646
9730
|
}
|
|
9647
|
-
const path =
|
|
9731
|
+
const path = join10(directory, entry.name);
|
|
9648
9732
|
if (entry.isDirectory()) {
|
|
9649
9733
|
visit(path, `${prefix}${entry.name}/`);
|
|
9650
9734
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
9651
|
-
each(`res://${prefix}${entry.name}`,
|
|
9735
|
+
each(`res://${prefix}${entry.name}`, readFileSync8(path, "utf8"));
|
|
9652
9736
|
}
|
|
9653
9737
|
}
|
|
9654
9738
|
};
|
|
@@ -9670,12 +9754,12 @@ function scriptsNaming(projectPath, classes) {
|
|
|
9670
9754
|
return naming.sort((a, b) => a.script.localeCompare(b.script));
|
|
9671
9755
|
}
|
|
9672
9756
|
function cachedClasses(projectPath) {
|
|
9673
|
-
const cache =
|
|
9674
|
-
if (!
|
|
9757
|
+
const cache = join10(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
9758
|
+
if (!existsSync10(cache)) {
|
|
9675
9759
|
return null;
|
|
9676
9760
|
}
|
|
9677
9761
|
const listed = new Map;
|
|
9678
|
-
const text =
|
|
9762
|
+
const text = readFileSync8(cache, "utf8");
|
|
9679
9763
|
for (const entry of text.matchAll(/"class":\s*&"([^"]+)"[\s\S]*?"path":\s*"([^"]+)"/g)) {
|
|
9680
9764
|
listed.set(entry[1] ?? "", entry[2] ?? "");
|
|
9681
9765
|
}
|
|
@@ -9941,7 +10025,7 @@ function uncachedClassNote(types) {
|
|
|
9941
10025
|
return `${types.join(", ")} ${one ? "is declared" : "are declared"} in this project and missing from ` + `.godot/global_script_class_cache.cfg, so a game launched now would not resolve ${one ? "it" : "them"} ` + "either. Try editor_rescan first: it asks the editor already running and starts nothing, and it " + "cleared this in 314ms in a second project with a 31-worker fan-out still importing. It is not " + "always enough, because a file another engine has already imported reads as settled and the " + "walk does not look inside it. project_import refresh_classes rewrites the cache from the " + "declarations on disk and does reach that case, at the cost of a short headless engine, which " + "is worth knowing when something else is already running against this project.";
|
|
9942
10026
|
}
|
|
9943
10027
|
function cacheWrittenAt(projectPath) {
|
|
9944
|
-
const cache =
|
|
10028
|
+
const cache = join10(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
9945
10029
|
try {
|
|
9946
10030
|
return statSync4(cache).mtimeMs;
|
|
9947
10031
|
} catch {
|
|
@@ -9955,14 +10039,14 @@ function declaredSince(projectPath, paths, names, since) {
|
|
|
9955
10039
|
return false;
|
|
9956
10040
|
}
|
|
9957
10041
|
try {
|
|
9958
|
-
return statSync4(
|
|
10042
|
+
return statSync4(join10(projectPath, path.slice("res://".length))).mtimeMs > since;
|
|
9959
10043
|
} catch {
|
|
9960
10044
|
return false;
|
|
9961
10045
|
}
|
|
9962
10046
|
});
|
|
9963
10047
|
}
|
|
9964
10048
|
function cachedAtMissingPaths(cached, projectPath) {
|
|
9965
|
-
return [...cached].filter(([, path]) => path.startsWith("res://") && !
|
|
10049
|
+
return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync10(join10(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
|
|
9966
10050
|
}
|
|
9967
10051
|
function staleAgainst(cached, projectPath) {
|
|
9968
10052
|
return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
|
|
@@ -9987,8 +10071,8 @@ var init_class_cache = __esm(() => {
|
|
|
9987
10071
|
});
|
|
9988
10072
|
|
|
9989
10073
|
// src/project-scan.ts
|
|
9990
|
-
import { existsSync as
|
|
9991
|
-
import { extname, join as
|
|
10074
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync9 } from "node:fs";
|
|
10075
|
+
import { extname, join as join11 } from "node:path";
|
|
9992
10076
|
function skipped(name) {
|
|
9993
10077
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
9994
10078
|
}
|
|
@@ -9996,10 +10080,10 @@ function steppedOver(directory) {
|
|
|
9996
10080
|
return gdignored(directory) || installedByUs(directory);
|
|
9997
10081
|
}
|
|
9998
10082
|
function gdignored(directory) {
|
|
9999
|
-
return
|
|
10083
|
+
return existsSync11(join11(directory, ".gdignore"));
|
|
10000
10084
|
}
|
|
10001
10085
|
function installedByUs(directory) {
|
|
10002
|
-
return
|
|
10086
|
+
return existsSync11(join11(directory, VERSION_MARKER));
|
|
10003
10087
|
}
|
|
10004
10088
|
function projectStructure(projectPath) {
|
|
10005
10089
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -10007,12 +10091,12 @@ function projectStructure(projectPath) {
|
|
|
10007
10091
|
if (steppedOver(directory)) {
|
|
10008
10092
|
return;
|
|
10009
10093
|
}
|
|
10010
|
-
for (const entry of
|
|
10094
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
10011
10095
|
if (skipped(entry.name)) {
|
|
10012
10096
|
continue;
|
|
10013
10097
|
}
|
|
10014
10098
|
if (entry.isDirectory()) {
|
|
10015
|
-
visit(
|
|
10099
|
+
visit(join11(directory, entry.name));
|
|
10016
10100
|
} else if (entry.isFile()) {
|
|
10017
10101
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10018
10102
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -10036,13 +10120,13 @@ function projectFiles(projectPath, extensions) {
|
|
|
10036
10120
|
if (steppedOver(directory)) {
|
|
10037
10121
|
return;
|
|
10038
10122
|
}
|
|
10039
|
-
for (const entry of
|
|
10123
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
10040
10124
|
if (skipped(entry.name)) {
|
|
10041
10125
|
continue;
|
|
10042
10126
|
}
|
|
10043
10127
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10044
10128
|
if (entry.isDirectory()) {
|
|
10045
|
-
visit(
|
|
10129
|
+
visit(join11(directory, entry.name), spelled);
|
|
10046
10130
|
} else if (entry.isFile() && extensions.includes(extname(entry.name).toLowerCase())) {
|
|
10047
10131
|
found.push(spelled);
|
|
10048
10132
|
}
|
|
@@ -10057,20 +10141,20 @@ function scriptsWithoutUid(projectPath) {
|
|
|
10057
10141
|
if (steppedOver(directory)) {
|
|
10058
10142
|
return;
|
|
10059
10143
|
}
|
|
10060
|
-
for (const entry of
|
|
10144
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
10061
10145
|
if (skipped(entry.name)) {
|
|
10062
10146
|
continue;
|
|
10063
10147
|
}
|
|
10064
10148
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10065
10149
|
if (entry.isDirectory()) {
|
|
10066
|
-
visit(
|
|
10150
|
+
visit(join11(directory, entry.name), spelled);
|
|
10067
10151
|
continue;
|
|
10068
10152
|
}
|
|
10069
10153
|
if (!entry.isFile()) {
|
|
10070
10154
|
continue;
|
|
10071
10155
|
}
|
|
10072
10156
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10073
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
10157
|
+
if (CARRIES_A_UID.has(extension) && !existsSync11(join11(directory, `${entry.name}.uid`))) {
|
|
10074
10158
|
found.push(spelled);
|
|
10075
10159
|
}
|
|
10076
10160
|
}
|
|
@@ -10087,11 +10171,11 @@ function projectTexts(projectPath) {
|
|
|
10087
10171
|
if (steppedOver(directory)) {
|
|
10088
10172
|
return;
|
|
10089
10173
|
}
|
|
10090
|
-
for (const entry of
|
|
10174
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
10091
10175
|
if (skipped(entry.name)) {
|
|
10092
10176
|
continue;
|
|
10093
10177
|
}
|
|
10094
|
-
const absolute =
|
|
10178
|
+
const absolute = join11(directory, entry.name);
|
|
10095
10179
|
const spelled = `${prefix}${entry.name}`;
|
|
10096
10180
|
if (entry.isDirectory()) {
|
|
10097
10181
|
visit(absolute, `${spelled}/`);
|
|
@@ -10101,7 +10185,7 @@ function projectTexts(projectPath) {
|
|
|
10101
10185
|
if (!entry.isFile() || BINARY_EXTENSIONS.has(extension)) {
|
|
10102
10186
|
continue;
|
|
10103
10187
|
}
|
|
10104
|
-
const bytes =
|
|
10188
|
+
const bytes = readFileSync9(absolute);
|
|
10105
10189
|
if (!isBinary(bytes)) {
|
|
10106
10190
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
10107
10191
|
}
|
|
@@ -10117,11 +10201,11 @@ function ignoredTexts(projectPath) {
|
|
|
10117
10201
|
return;
|
|
10118
10202
|
}
|
|
10119
10203
|
const under = ignored || gdignored(directory);
|
|
10120
|
-
for (const entry of
|
|
10204
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
10121
10205
|
if (skipped(entry.name)) {
|
|
10122
10206
|
continue;
|
|
10123
10207
|
}
|
|
10124
|
-
const absolute =
|
|
10208
|
+
const absolute = join11(directory, entry.name);
|
|
10125
10209
|
const spelled = `${prefix}${entry.name}`;
|
|
10126
10210
|
if (entry.isDirectory()) {
|
|
10127
10211
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -10131,7 +10215,7 @@ function ignoredTexts(projectPath) {
|
|
|
10131
10215
|
if (!under || !entry.isFile() || BINARY_EXTENSIONS.has(extension) || entry.name === ".gdignore") {
|
|
10132
10216
|
continue;
|
|
10133
10217
|
}
|
|
10134
|
-
const bytes =
|
|
10218
|
+
const bytes = readFileSync9(absolute);
|
|
10135
10219
|
if (!isBinary(bytes)) {
|
|
10136
10220
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
10137
10221
|
}
|
|
@@ -10154,14 +10238,14 @@ function searchProject(projectPath, options) {
|
|
|
10154
10238
|
if (steppedOver(directory)) {
|
|
10155
10239
|
return;
|
|
10156
10240
|
}
|
|
10157
|
-
for (const entry of
|
|
10241
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
10158
10242
|
if (full()) {
|
|
10159
10243
|
return;
|
|
10160
10244
|
}
|
|
10161
10245
|
if (skipped(entry.name)) {
|
|
10162
10246
|
continue;
|
|
10163
10247
|
}
|
|
10164
|
-
const entryPath =
|
|
10248
|
+
const entryPath = join11(directory, entry.name);
|
|
10165
10249
|
if (entry.isDirectory()) {
|
|
10166
10250
|
visit(entryPath);
|
|
10167
10251
|
continue;
|
|
@@ -10170,7 +10254,7 @@ function searchProject(projectPath, options) {
|
|
|
10170
10254
|
if (!entry.isFile() || extensions !== null && !extensions.has(extension)) {
|
|
10171
10255
|
continue;
|
|
10172
10256
|
}
|
|
10173
|
-
const bytes =
|
|
10257
|
+
const bytes = readFileSync9(entryPath);
|
|
10174
10258
|
if (extensions === null && isBinary(bytes)) {
|
|
10175
10259
|
continue;
|
|
10176
10260
|
}
|
|
@@ -10257,7 +10341,7 @@ var init_answer_json = __esm(() => {
|
|
|
10257
10341
|
});
|
|
10258
10342
|
|
|
10259
10343
|
// src/resources.ts
|
|
10260
|
-
import { readFileSync as
|
|
10344
|
+
import { readFileSync as readFileSync10, realpathSync as realpathSync3 } from "node:fs";
|
|
10261
10345
|
import { extname as extname2, resolve as resolve3 } from "node:path";
|
|
10262
10346
|
function ensureProjectPath(getProjectPath) {
|
|
10263
10347
|
const projectPath = getProjectPath();
|
|
@@ -10453,7 +10537,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
10453
10537
|
const parsedUri = parseGodotUri(uri);
|
|
10454
10538
|
if (parsedUri.kind === "project-info") {
|
|
10455
10539
|
const projectFilePath = resolveProjectFile(projectPath, "project.godot");
|
|
10456
|
-
const rawProject =
|
|
10540
|
+
const rawProject = readFileSync10(projectFilePath, "utf-8");
|
|
10457
10541
|
const parsedProject = parseProjectGodot(rawProject);
|
|
10458
10542
|
return {
|
|
10459
10543
|
mimeType: "application/json",
|
|
@@ -10462,7 +10546,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
10462
10546
|
}
|
|
10463
10547
|
const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
|
|
10464
10548
|
ensureAllowedExtension(parsedUri.kind, filePath);
|
|
10465
|
-
const text =
|
|
10549
|
+
const text = readFileSync10(filePath, "utf-8");
|
|
10466
10550
|
return {
|
|
10467
10551
|
mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
|
|
10468
10552
|
text
|
|
@@ -10607,16 +10691,16 @@ import { spawnSync } from "node:child_process";
|
|
|
10607
10691
|
import { createHash as createHash3 } from "node:crypto";
|
|
10608
10692
|
import {
|
|
10609
10693
|
cpSync,
|
|
10610
|
-
existsSync as
|
|
10694
|
+
existsSync as existsSync12,
|
|
10611
10695
|
mkdirSync as mkdirSync4,
|
|
10612
|
-
readdirSync as
|
|
10613
|
-
readFileSync as
|
|
10696
|
+
readdirSync as readdirSync7,
|
|
10697
|
+
readFileSync as readFileSync11,
|
|
10614
10698
|
rmdirSync,
|
|
10615
10699
|
rmSync as rmSync5,
|
|
10616
10700
|
statSync as statSync5,
|
|
10617
10701
|
writeFileSync as writeFileSync6
|
|
10618
10702
|
} from "node:fs";
|
|
10619
|
-
import { dirname as
|
|
10703
|
+
import { dirname as dirname6, join as join12 } from "node:path";
|
|
10620
10704
|
import { fileURLToPath } from "node:url";
|
|
10621
10705
|
function editorAddonDigest(from = shippedAddonsDirectory()) {
|
|
10622
10706
|
return addonsDigest(EDITOR_PLUGINS, from);
|
|
@@ -10627,25 +10711,25 @@ function runtimeAddonDigest(from = shippedAddonsDirectory()) {
|
|
|
10627
10711
|
function addonsDigest(names, from) {
|
|
10628
10712
|
const hash = createHash3("sha256");
|
|
10629
10713
|
const walk = (directory, relative) => {
|
|
10630
|
-
for (const name of
|
|
10714
|
+
for (const name of readdirSync7(directory).sort()) {
|
|
10631
10715
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
10632
10716
|
continue;
|
|
10633
10717
|
}
|
|
10634
|
-
const path =
|
|
10718
|
+
const path = join12(directory, name);
|
|
10635
10719
|
const inside = `${relative}/${name}`;
|
|
10636
10720
|
if (statSync5(path).isDirectory()) {
|
|
10637
10721
|
walk(path, inside);
|
|
10638
10722
|
} else {
|
|
10639
10723
|
hash.update(`${inside}
|
|
10640
10724
|
`);
|
|
10641
|
-
hash.update(
|
|
10725
|
+
hash.update(readFileSync11(path));
|
|
10642
10726
|
hash.update(`
|
|
10643
10727
|
`);
|
|
10644
10728
|
}
|
|
10645
10729
|
}
|
|
10646
10730
|
};
|
|
10647
10731
|
for (const name of names) {
|
|
10648
|
-
walk(
|
|
10732
|
+
walk(join12(from, name), name);
|
|
10649
10733
|
}
|
|
10650
10734
|
return hash.digest("hex");
|
|
10651
10735
|
}
|
|
@@ -10676,60 +10760,60 @@ function installedRuntimeDigest(projectPath) {
|
|
|
10676
10760
|
return digestMarker(projectPath, RUNTIME_ADDON);
|
|
10677
10761
|
}
|
|
10678
10762
|
function digestMarker(projectPath, addon) {
|
|
10679
|
-
const marker =
|
|
10680
|
-
if (!
|
|
10763
|
+
const marker = join12(projectPath, "addons", addon, DIGEST_MARKER);
|
|
10764
|
+
if (!existsSync12(marker)) {
|
|
10681
10765
|
return null;
|
|
10682
10766
|
}
|
|
10683
|
-
const said =
|
|
10767
|
+
const said = readFileSync11(marker, "utf8").trim();
|
|
10684
10768
|
return said === "" ? null : said;
|
|
10685
10769
|
}
|
|
10686
10770
|
function shippedAddonsDirectory() {
|
|
10687
|
-
return
|
|
10771
|
+
return join12(dirname6(fileURLToPath(import.meta.url)), "godot", "addons");
|
|
10688
10772
|
}
|
|
10689
10773
|
function shippedOperationsScript() {
|
|
10690
|
-
return
|
|
10774
|
+
return join12(dirname6(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
|
|
10691
10775
|
}
|
|
10692
10776
|
function installAddons(projectPath, from = shippedAddonsDirectory()) {
|
|
10693
10777
|
const installed = [];
|
|
10694
10778
|
for (const name of ADDONS) {
|
|
10695
|
-
const source =
|
|
10696
|
-
if (!
|
|
10779
|
+
const source = join12(from, name);
|
|
10780
|
+
if (!existsSync12(join12(source, name === RUNTIME_ADDON ? "runtime_autoload.gd" : "plugin.cfg"))) {
|
|
10697
10781
|
throw new Error(`The package holds no ${name} addon at ${source}.`);
|
|
10698
10782
|
}
|
|
10699
|
-
const target =
|
|
10700
|
-
const replaced =
|
|
10783
|
+
const target = join12(projectPath, "addons", name);
|
|
10784
|
+
const replaced = existsSync12(target);
|
|
10701
10785
|
rmSync5(target, { recursive: true, force: true });
|
|
10702
|
-
mkdirSync4(
|
|
10786
|
+
mkdirSync4(dirname6(target), { recursive: true });
|
|
10703
10787
|
cpSync(source, target, { recursive: true });
|
|
10704
|
-
writeFileSync6(
|
|
10788
|
+
writeFileSync6(join12(target, VERSION_MARKER), `${SERVER_VERSION}
|
|
10705
10789
|
`);
|
|
10706
10790
|
installed.push({ name, path: target, replaced });
|
|
10707
10791
|
}
|
|
10708
|
-
writeFileSync6(
|
|
10792
|
+
writeFileSync6(join12(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
|
|
10709
10793
|
`);
|
|
10710
|
-
writeFileSync6(
|
|
10794
|
+
writeFileSync6(join12(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
|
|
10711
10795
|
`);
|
|
10712
10796
|
return installed;
|
|
10713
10797
|
}
|
|
10714
10798
|
function installedAddonVersion(projectPath) {
|
|
10715
|
-
const marker =
|
|
10716
|
-
if (!
|
|
10799
|
+
const marker = join12(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
10800
|
+
if (!existsSync12(marker)) {
|
|
10717
10801
|
return null;
|
|
10718
10802
|
}
|
|
10719
|
-
const said =
|
|
10803
|
+
const said = readFileSync11(marker, "utf8").trim();
|
|
10720
10804
|
return said === "" ? null : said;
|
|
10721
10805
|
}
|
|
10722
10806
|
function removeAddons(projectPath) {
|
|
10723
10807
|
const removed = [];
|
|
10724
10808
|
for (const name of ADDONS) {
|
|
10725
|
-
const target =
|
|
10726
|
-
if (
|
|
10809
|
+
const target = join12(projectPath, "addons", name);
|
|
10810
|
+
if (existsSync12(target)) {
|
|
10727
10811
|
rmSync5(target, { recursive: true, force: true });
|
|
10728
10812
|
removed.push(target);
|
|
10729
10813
|
}
|
|
10730
10814
|
}
|
|
10731
|
-
const addons =
|
|
10732
|
-
if (removed.length > 0 &&
|
|
10815
|
+
const addons = join12(projectPath, "addons");
|
|
10816
|
+
if (removed.length > 0 && existsSync12(addons) && readdirSync7(addons).length === 0) {
|
|
10733
10817
|
rmdirSync(addons);
|
|
10734
10818
|
removed.push(addons);
|
|
10735
10819
|
}
|
|
@@ -10763,7 +10847,7 @@ function loaderAmong(projectPath, named) {
|
|
|
10763
10847
|
}
|
|
10764
10848
|
let source = null;
|
|
10765
10849
|
try {
|
|
10766
|
-
source =
|
|
10850
|
+
source = readFileSync11(join12(projectPath, path), "utf8");
|
|
10767
10851
|
} catch {}
|
|
10768
10852
|
if (source?.includes(RUNTIME_AUTOLOAD.path)) {
|
|
10769
10853
|
return { name: entry, path, exists: true };
|
|
@@ -10805,10 +10889,10 @@ function enabledPlugins(settings) {
|
|
|
10805
10889
|
function inspectProject(projectPath) {
|
|
10806
10890
|
const problems = [];
|
|
10807
10891
|
const addons = ADDONS.map((name) => {
|
|
10808
|
-
const directory =
|
|
10809
|
-
const marker =
|
|
10810
|
-
const installed =
|
|
10811
|
-
const version =
|
|
10892
|
+
const directory = join12(projectPath, "addons", name);
|
|
10893
|
+
const marker = join12(directory, VERSION_MARKER);
|
|
10894
|
+
const installed = existsSync12(directory);
|
|
10895
|
+
const version = existsSync12(marker) ? readFileSync11(marker, "utf8").trim() : null;
|
|
10812
10896
|
const current = version === SERVER_VERSION;
|
|
10813
10897
|
if (!installed) {
|
|
10814
10898
|
problems.push(`addons/${name} is not installed; run gdharness setup`);
|
|
@@ -10817,7 +10901,7 @@ function inspectProject(projectPath) {
|
|
|
10817
10901
|
}
|
|
10818
10902
|
return { name, installed, version, current };
|
|
10819
10903
|
});
|
|
10820
|
-
const settings = parseProjectGodot(
|
|
10904
|
+
const settings = parseProjectGodot(readFileSync11(join12(projectPath, "project.godot"), "utf8"));
|
|
10821
10905
|
const pluginsEnabled = enabledPlugins(settings);
|
|
10822
10906
|
for (const name of EDITOR_PLUGINS) {
|
|
10823
10907
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -10833,7 +10917,7 @@ function inspectProject(projectPath) {
|
|
|
10833
10917
|
const tracked = trackedByGit(projectPath, [...named.values()]);
|
|
10834
10918
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
10835
10919
|
for (const [name, path] of named) {
|
|
10836
|
-
if (!
|
|
10920
|
+
if (!existsSync12(join12(projectPath, path))) {
|
|
10837
10921
|
if (!addonMissing(path)) {
|
|
10838
10922
|
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`);
|
|
10839
10923
|
}
|
|
@@ -11898,7 +11982,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11898
11982
|
type: "array",
|
|
11899
11983
|
items: { type: "string" },
|
|
11900
11984
|
ops: ["start", "check"],
|
|
11901
|
-
description: `The game's own arguments, what OS.get_cmdline_user_args() answers, such as ["--level=2"]. The separator is added here, so a flag the engine reads goes to the game instead, and the answer says so under argsNote. A run with any is started by this server rather than by the editor, which fixes the game's command line when it opens the project, so the debug_* tools
|
|
11985
|
+
description: `The game's own arguments, what OS.get_cmdline_user_args() answers, such as ["--level=2"]. The separator is added here, so a flag the engine reads goes to the game instead, and the answer says so under argsNote. A run with any is started by this server rather than by the editor, which fixes the game's command line when it opens the project, so its debugger is the keeper holding it rather than the editor; the debug_* tools answer for it all the same.`
|
|
11902
11986
|
},
|
|
11903
11987
|
engineArgs: {
|
|
11904
11988
|
type: "array",
|
|
@@ -11915,7 +11999,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11915
11999
|
silent: {
|
|
11916
12000
|
type: "boolean",
|
|
11917
12001
|
ops: ["start"],
|
|
11918
|
-
description: "start: run the game with no sound out of the machine, by starting the engine with --audio-driver Dummy: every stream still plays and every playback position still advances, so what is playing can still be asked. The editor takes no engine flags for a game it plays, so a silent windowed run is started by this server,
|
|
12002
|
+
description: "start: run the game with no sound out of the machine, by starting the engine with --audio-driver Dummy: every stream still plays and every playback position still advances, so what is playing can still be asked. The editor takes no engine flags for a game it plays, so a silent windowed run is started by this server, whose keeper is its debugger rather than the editor. A headless run, check's included, is silent already. Default false, or true when GDHARNESS_SILENT is set in the server's environment to anything but 0 or false; silent: false then plays through the editor as before."
|
|
11919
12003
|
},
|
|
11920
12004
|
savesIn: {
|
|
11921
12005
|
type: "string",
|
|
@@ -12241,12 +12325,12 @@ var init_tool_definitions = __esm(() => {
|
|
|
12241
12325
|
},
|
|
12242
12326
|
metrics: {
|
|
12243
12327
|
effect: "read",
|
|
12244
|
-
summary: "frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count",
|
|
12328
|
+
summary: "frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. physics_steps_per_second is the physics steps taken a second over those frames, fewer than physics_ticks_per_second when a frame needs more than max_physics_steps_per_frame or the editor's Game view slows it. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count",
|
|
12245
12329
|
requires: []
|
|
12246
12330
|
},
|
|
12247
12331
|
accessibility: {
|
|
12248
12332
|
effect: "read",
|
|
12249
|
-
summary:
|
|
12333
|
+
summary: "what the game tells a screen reader, read through UI Automation as a Windows screen reader reads it, under windows: one tree per window, each element with its role (text for static text, button, check_box, group for a control holding others), name, value, description, placeholder, bounds in the window's pixels, the actions a reader is offered (click, focus, toggle, set_value, select, expand_collapse, scroll_into_view), and focused on the one with the focus. node is the path of the node that made the element; an element a node made as a part of itself has part_of instead, and part: announcement marks the text a window speaks announcements through. An empty name is read as nothing: static text takes it from accessibility_update_set_value, and _note counts the text elements on screen with none. tooltip is the node's tooltip_text, which a Windows reader is not told. With no control focused, the engine reports the first node under the root, GdharnessRuntime, as focused. Needs a windowed run on Windows started with engineArgs [\"--accessibility\", \"always\"] or accessibility_support at Always Active, since the engine builds the tree only when asked; reading takes a second or two",
|
|
12250
12334
|
requires: []
|
|
12251
12335
|
}
|
|
12252
12336
|
},
|
|
@@ -12596,7 +12680,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12596
12680
|
{
|
|
12597
12681
|
name: "debug_breakpoint",
|
|
12598
12682
|
title: "Breakpoints",
|
|
12599
|
-
description: "Sets or removes a breakpoint through the editor's debug adapter.
|
|
12683
|
+
description: "Sets or removes a breakpoint. Set them first, then editor_run, and the game stops where you asked. With the editor connected it goes through the editor's debug adapter; with none, it is held here. A game this server starts itself, which editor_run start does for a run given args, savesIn, env or silent, or with no editor, is handed every breakpoint held here as it starts, and one set while that game runs reaches it at once (runningGame says so). The editor keeps a breakpoint set this way for one play, so every breakpoint this server holds is sent again before each play editor_run starts, and the start answer lists them under breakpoints. The set is also kept in the project, for the server that comes after a reconnect: a breakpoint stays until it is removed here, whichever server set it. Every answer lists the whole set under held, and the breakpoints the user set in the editor's own gutter under setInEditor; setting one here never takes those away, and removing a line here clears it whoever set it. Godot clears every breakpoint in the editor when a debug session opens unless network/debug_adapter/sync_breakpoints is on, which the addon turns on as it loads; an editor running an older addon is said so under breakpointsAtRisk, here and in editor_status.",
|
|
12600
12684
|
parameters: {
|
|
12601
12685
|
projectPath: PROJECT_PATH,
|
|
12602
12686
|
scriptPath: SCRIPT_PATH,
|
|
@@ -12611,23 +12695,33 @@ var init_tool_definitions = __esm(() => {
|
|
|
12611
12695
|
{
|
|
12612
12696
|
name: "debug_control",
|
|
12613
12697
|
title: "Step the debugger",
|
|
12614
|
-
description: "
|
|
12698
|
+
description: "Pauses, continues or steps the debugged game. A step waits for the game to stop again and answers with the stack where it ended up; one that has not stopped the game again within ten seconds answers heldAgain false and no stack. A game the editor plays is moved through the editor's debug adapter, which cannot pause or step out: it answers a pause by reporting the game stopped and leaving it running, and implements no stepOut, so hold such a game with a breakpoint and step over or into from there. A game this server started itself has its keeper as its debugger, which does both: pause breaks into the game wherever it is, a script spinning in a loop included, which is how a game hung on its first frame is asked where it is.",
|
|
12615
12699
|
parameters: {},
|
|
12616
12700
|
requires: [],
|
|
12617
12701
|
operations: {
|
|
12702
|
+
pause: {
|
|
12703
|
+
effect: "additive",
|
|
12704
|
+
summary: "stop a game this server started where it is, answering with the stack. The game takes it between frames, and in a script every 2048 lines it runs, so a loop that sleeps between few lines can take seconds; the call waits thirty, and past that answers paused false with the request still standing. Stopped between frames, where no script runs, the game is stepped on to the next line of script it runs",
|
|
12705
|
+
requires: []
|
|
12706
|
+
},
|
|
12618
12707
|
continue: { effect: "additive", summary: "resume after a breakpoint", requires: [] },
|
|
12619
12708
|
step_over: { effect: "additive", summary: "run the current line", requires: [] },
|
|
12620
12709
|
step_into: {
|
|
12621
12710
|
effect: "additive",
|
|
12622
12711
|
summary: "run the current line, stopping inside whatever it calls",
|
|
12623
12712
|
requires: []
|
|
12713
|
+
},
|
|
12714
|
+
step_out: {
|
|
12715
|
+
effect: "additive",
|
|
12716
|
+
summary: "run to the end of the current function and stop in its caller; a game this server started only",
|
|
12717
|
+
requires: []
|
|
12624
12718
|
}
|
|
12625
12719
|
}
|
|
12626
12720
|
},
|
|
12627
12721
|
{
|
|
12628
12722
|
name: "debug_state",
|
|
12629
12723
|
title: "Debugger state",
|
|
12630
|
-
description: "Where the debugged game is stopped: the stack trace, what is in scope at a frame with the values, or the
|
|
12724
|
+
description: "Where the debugged game is stopped: the stack trace, what is in scope at a frame with the values, or the debugger's console output so far, its last thousand lines, with any earlier ones counted under notShown. For a game this server started, its keeper's debugger answers: each frame names its script as the project spells it under script and on this machine under path, and held says which thread stopped and why. For a game the editor plays, the editor's debug adapter answers. A stop is reported by the editor's adapter only to the sessions connected when it happened, so a server that connected afterwards, which is every server after a reconnect, is shown a thread and no frames about a game still sitting at its breakpoint. That server asks the runtime instead: a held game accepts a connection and never answers, and the refusal then says held rather than running, and that the stack cannot be read from here. debug_control continue lets it go, and the next stop is one this session is told about, with its stack.",
|
|
12631
12725
|
parameters: {
|
|
12632
12726
|
frameId: {
|
|
12633
12727
|
type: "number",
|
|
@@ -34412,10 +34506,10 @@ var init_stdio = __esm(() => {
|
|
|
34412
34506
|
});
|
|
34413
34507
|
|
|
34414
34508
|
// src/runtime-client.ts
|
|
34415
|
-
import { existsSync as
|
|
34509
|
+
import { existsSync as existsSync14, readdirSync as readdirSync9, readFileSync as readFileSync13, statSync as statSync6, unlinkSync as unlinkSync2 } from "node:fs";
|
|
34416
34510
|
import { createConnection } from "node:net";
|
|
34417
34511
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
34418
|
-
import { dirname as
|
|
34512
|
+
import { dirname as dirname9, join as join15, resolve as resolve4 } from "node:path";
|
|
34419
34513
|
import { setTimeout as delay } from "node:timers/promises";
|
|
34420
34514
|
function runtimeDirectory(variables = process.env) {
|
|
34421
34515
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -34423,20 +34517,20 @@ function runtimeDirectory(variables = process.env) {
|
|
|
34423
34517
|
return explicit;
|
|
34424
34518
|
}
|
|
34425
34519
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
34426
|
-
return
|
|
34520
|
+
return join15(perUser ?? tmpdir4(), "gdharness");
|
|
34427
34521
|
}
|
|
34428
34522
|
function runtimeDirectories(variables = process.env) {
|
|
34429
34523
|
const candidates = [runtimeDirectory(variables)];
|
|
34430
34524
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
34431
34525
|
for (const base of fallbacks) {
|
|
34432
|
-
candidates.push(
|
|
34526
|
+
candidates.push(join15(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
34433
34527
|
}
|
|
34434
34528
|
return [...new Set(candidates.map((path) => resolve4(path)))];
|
|
34435
34529
|
}
|
|
34436
34530
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
34437
34531
|
for (const directory of directories) {
|
|
34438
|
-
const path =
|
|
34439
|
-
if (
|
|
34532
|
+
const path = join15(directory, `runtime-${pid}.log`);
|
|
34533
|
+
if (existsSync14(path)) {
|
|
34440
34534
|
return path;
|
|
34441
34535
|
}
|
|
34442
34536
|
}
|
|
@@ -34467,7 +34561,7 @@ function reportAlreadyInTranscript(transcript, report) {
|
|
|
34467
34561
|
function parseAnnouncement(file, pid) {
|
|
34468
34562
|
let fields;
|
|
34469
34563
|
try {
|
|
34470
|
-
fields = asParams(JSON.parse(
|
|
34564
|
+
fields = asParams(JSON.parse(readFileSync13(file, "utf8")));
|
|
34471
34565
|
} catch {
|
|
34472
34566
|
return { kind: "rubbish" };
|
|
34473
34567
|
}
|
|
@@ -34533,7 +34627,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
|
|
|
34533
34627
|
function wentAway(file, pid) {
|
|
34534
34628
|
let project = "";
|
|
34535
34629
|
try {
|
|
34536
|
-
const fields = asParams(JSON.parse(
|
|
34630
|
+
const fields = asParams(JSON.parse(readFileSync13(file, "utf8")));
|
|
34537
34631
|
project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
|
|
34538
34632
|
} catch {
|
|
34539
34633
|
return;
|
|
@@ -34576,23 +34670,23 @@ function strangersAmong(candidates, now, startTimes = startTimesOf) {
|
|
|
34576
34670
|
}).map((one) => one.file));
|
|
34577
34671
|
}
|
|
34578
34672
|
function announcedIn(directory) {
|
|
34579
|
-
if (!
|
|
34673
|
+
if (!existsSync14(directory)) {
|
|
34580
34674
|
return [];
|
|
34581
34675
|
}
|
|
34582
34676
|
const found = [];
|
|
34583
34677
|
const candidates = [];
|
|
34584
34678
|
const entries = [];
|
|
34585
|
-
for (const entry of
|
|
34679
|
+
for (const entry of readdirSync9(directory)) {
|
|
34586
34680
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
34587
34681
|
if (report) {
|
|
34588
|
-
sweepErrorReport(
|
|
34682
|
+
sweepErrorReport(join15(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
34589
34683
|
continue;
|
|
34590
34684
|
}
|
|
34591
34685
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
34592
34686
|
if (!match) {
|
|
34593
34687
|
continue;
|
|
34594
34688
|
}
|
|
34595
|
-
const file =
|
|
34689
|
+
const file = join15(directory, entry);
|
|
34596
34690
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
34597
34691
|
const running = alive(pid);
|
|
34598
34692
|
entries.push({ file, pid, running });
|
|
@@ -34612,7 +34706,7 @@ function announcedIn(directory) {
|
|
|
34612
34706
|
}
|
|
34613
34707
|
wentAway(file, pid);
|
|
34614
34708
|
try {
|
|
34615
|
-
|
|
34709
|
+
unlinkSync2(file);
|
|
34616
34710
|
} catch {}
|
|
34617
34711
|
verdicts.delete(file);
|
|
34618
34712
|
continue;
|
|
@@ -34620,16 +34714,16 @@ function announcedIn(directory) {
|
|
|
34620
34714
|
found.push(announced);
|
|
34621
34715
|
}
|
|
34622
34716
|
const held = new Set(entries.map((one) => one.file));
|
|
34623
|
-
const here =
|
|
34717
|
+
const here = join15(directory, ".");
|
|
34624
34718
|
for (const file of verdicts.keys()) {
|
|
34625
|
-
if (
|
|
34719
|
+
if (dirname9(file) === here && !held.has(file)) {
|
|
34626
34720
|
verdicts.delete(file);
|
|
34627
34721
|
}
|
|
34628
34722
|
}
|
|
34629
34723
|
return found;
|
|
34630
34724
|
}
|
|
34631
34725
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
34632
|
-
const files = directories.map((directory) =>
|
|
34726
|
+
const files = directories.map((directory) => join15(directory, `runtime-${pid}.json`)).filter((file) => existsSync14(file));
|
|
34633
34727
|
const first = files[0];
|
|
34634
34728
|
if (first === undefined) {
|
|
34635
34729
|
return;
|
|
@@ -34645,7 +34739,7 @@ async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
|
34645
34739
|
wentAway(first, pid);
|
|
34646
34740
|
for (const file of files) {
|
|
34647
34741
|
try {
|
|
34648
|
-
|
|
34742
|
+
unlinkSync2(file);
|
|
34649
34743
|
} catch {}
|
|
34650
34744
|
verdicts.delete(file);
|
|
34651
34745
|
}
|
|
@@ -34656,7 +34750,7 @@ function sweepErrorReport(path, pid, now = Date.now()) {
|
|
|
34656
34750
|
}
|
|
34657
34751
|
try {
|
|
34658
34752
|
if (now - statSync6(path).mtimeMs >= ERROR_REPORT_KEEP_MS) {
|
|
34659
|
-
|
|
34753
|
+
unlinkSync2(path);
|
|
34660
34754
|
}
|
|
34661
34755
|
} catch {}
|
|
34662
34756
|
}
|
|
@@ -34930,14 +35024,14 @@ var init_runtime_client = __esm(() => {
|
|
|
34930
35024
|
});
|
|
34931
35025
|
|
|
34932
35026
|
// src/boot-note.ts
|
|
34933
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
34934
|
-
import { dirname as
|
|
35027
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "node:fs";
|
|
35028
|
+
import { dirname as dirname10, join as join16 } from "node:path";
|
|
34935
35029
|
function bootNotePath(projectPath) {
|
|
34936
|
-
return
|
|
35030
|
+
return join16(projectPath, ".godot", "gdharness-boot.json");
|
|
34937
35031
|
}
|
|
34938
35032
|
function readScenes(projectPath) {
|
|
34939
35033
|
try {
|
|
34940
|
-
return asParams(asParams(JSON.parse(
|
|
35034
|
+
return asParams(asParams(JSON.parse(readFileSync14(bootNotePath(projectPath), "utf8")))["scenes"]);
|
|
34941
35035
|
} catch {
|
|
34942
35036
|
return {};
|
|
34943
35037
|
}
|
|
@@ -34953,7 +35047,7 @@ function readBootNote(projectPath, scene) {
|
|
|
34953
35047
|
function writeBootNote(projectPath, scene, announcedAfterMs) {
|
|
34954
35048
|
try {
|
|
34955
35049
|
const path = bootNotePath(projectPath);
|
|
34956
|
-
mkdirSync6(
|
|
35050
|
+
mkdirSync6(dirname10(path), { recursive: true });
|
|
34957
35051
|
const scenes = {
|
|
34958
35052
|
...readScenes(projectPath),
|
|
34959
35053
|
[scene]: {
|
|
@@ -34979,10 +35073,10 @@ var init_boot_note = __esm(() => {
|
|
|
34979
35073
|
});
|
|
34980
35074
|
|
|
34981
35075
|
// src/breakpoint-note.ts
|
|
34982
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
34983
|
-
import { dirname as
|
|
35076
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
35077
|
+
import { dirname as dirname11, isAbsolute as isAbsolute3, join as join17, relative as relative3 } from "node:path";
|
|
34984
35078
|
function breakpointNotePath(projectPath) {
|
|
34985
|
-
return
|
|
35079
|
+
return join17(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
34986
35080
|
}
|
|
34987
35081
|
function writeBreakpointNote(projectPath, held) {
|
|
34988
35082
|
const path = breakpointNotePath(projectPath);
|
|
@@ -34999,7 +35093,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
34999
35093
|
lines: [...one.lines]
|
|
35000
35094
|
}))
|
|
35001
35095
|
};
|
|
35002
|
-
mkdirSync7(
|
|
35096
|
+
mkdirSync7(dirname11(path), { recursive: true });
|
|
35003
35097
|
writeFileSync9(path, `${JSON.stringify(note, null, 2)}
|
|
35004
35098
|
`, "utf8");
|
|
35005
35099
|
} catch (error) {
|
|
@@ -35008,7 +35102,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
35008
35102
|
}
|
|
35009
35103
|
function readBreakpointNote(projectPath) {
|
|
35010
35104
|
try {
|
|
35011
|
-
const parsed = JSON.parse(
|
|
35105
|
+
const parsed = JSON.parse(readFileSync15(breakpointNotePath(projectPath), "utf8"));
|
|
35012
35106
|
if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
|
|
35013
35107
|
return [];
|
|
35014
35108
|
}
|
|
@@ -35018,12 +35112,12 @@ function readBreakpointNote(projectPath) {
|
|
|
35018
35112
|
continue;
|
|
35019
35113
|
}
|
|
35020
35114
|
const { script, lines: listed } = entry;
|
|
35021
|
-
if (typeof script !== "string" ||
|
|
35115
|
+
if (typeof script !== "string" || isAbsolute3(script) || script.split("/").includes("..")) {
|
|
35022
35116
|
continue;
|
|
35023
35117
|
}
|
|
35024
35118
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
35025
35119
|
if (lines.length > 0) {
|
|
35026
|
-
held.push({ scriptPath: realPathOr(
|
|
35120
|
+
held.push({ scriptPath: realPathOr(join17(projectPath, script)), lines });
|
|
35027
35121
|
}
|
|
35028
35122
|
}
|
|
35029
35123
|
return held;
|
|
@@ -35033,7 +35127,7 @@ function readBreakpointNote(projectPath) {
|
|
|
35033
35127
|
}
|
|
35034
35128
|
function withinProject(projectPath, scriptPath) {
|
|
35035
35129
|
const rel = relative3(projectPath, scriptPath);
|
|
35036
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
35130
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute3(rel);
|
|
35037
35131
|
}
|
|
35038
35132
|
var init_breakpoint_note = __esm(() => {
|
|
35039
35133
|
init_errors();
|
|
@@ -35041,14 +35135,14 @@ var init_breakpoint_note = __esm(() => {
|
|
|
35041
35135
|
});
|
|
35042
35136
|
|
|
35043
35137
|
// src/class-note.ts
|
|
35044
|
-
import { mkdirSync as mkdirSync8, readFileSync as
|
|
35045
|
-
import { dirname as
|
|
35138
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "node:fs";
|
|
35139
|
+
import { dirname as dirname12, join as join18 } from "node:path";
|
|
35046
35140
|
function classNotePath(projectPath) {
|
|
35047
|
-
return
|
|
35141
|
+
return join18(projectPath, ".godot", "gdharness-classes.json");
|
|
35048
35142
|
}
|
|
35049
35143
|
function readClassNote(projectPath) {
|
|
35050
35144
|
try {
|
|
35051
|
-
const fields = asParams(JSON.parse(
|
|
35145
|
+
const fields = asParams(JSON.parse(readFileSync16(classNotePath(projectPath), "utf8")));
|
|
35052
35146
|
const rebuiltWith = readArray(fields, "rebuiltWith");
|
|
35053
35147
|
if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
|
|
35054
35148
|
return null;
|
|
@@ -35065,7 +35159,7 @@ function readClassNote(projectPath) {
|
|
|
35065
35159
|
function writeClassNote(projectPath, note) {
|
|
35066
35160
|
try {
|
|
35067
35161
|
const path = classNotePath(projectPath);
|
|
35068
|
-
mkdirSync8(
|
|
35162
|
+
mkdirSync8(dirname12(path), { recursive: true });
|
|
35069
35163
|
writeFileSync10(path, `${JSON.stringify(note, null, 2)}
|
|
35070
35164
|
`, "utf8");
|
|
35071
35165
|
} catch {}
|
|
@@ -35073,16 +35167,16 @@ function writeClassNote(projectPath, note) {
|
|
|
35073
35167
|
var init_class_note = () => {};
|
|
35074
35168
|
|
|
35075
35169
|
// src/config-pin.ts
|
|
35076
|
-
import { existsSync as
|
|
35077
|
-
import { join as
|
|
35170
|
+
import { existsSync as existsSync15, readFileSync as readFileSync17 } from "node:fs";
|
|
35171
|
+
import { join as join19 } from "node:path";
|
|
35078
35172
|
function versionsNamedInConfig(projectPath) {
|
|
35079
|
-
const config =
|
|
35080
|
-
if (!
|
|
35173
|
+
const config = join19(projectPath, ".mcp.json");
|
|
35174
|
+
if (!existsSync15(config)) {
|
|
35081
35175
|
return [];
|
|
35082
35176
|
}
|
|
35083
35177
|
let text;
|
|
35084
35178
|
try {
|
|
35085
|
-
text =
|
|
35179
|
+
text = readFileSync17(config, "utf8");
|
|
35086
35180
|
JSON.parse(text);
|
|
35087
35181
|
} catch {
|
|
35088
35182
|
return [];
|
|
@@ -35103,26 +35197,26 @@ var init_config_pin = __esm(() => {
|
|
|
35103
35197
|
});
|
|
35104
35198
|
|
|
35105
35199
|
// src/cut-test-run.ts
|
|
35106
|
-
import { existsSync as
|
|
35107
|
-
import { dirname as
|
|
35200
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
|
|
35201
|
+
import { dirname as dirname13, join as join20 } from "node:path";
|
|
35108
35202
|
function keptAt(projectPath) {
|
|
35109
|
-
return
|
|
35203
|
+
return join20(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
35110
35204
|
}
|
|
35111
35205
|
function keepCutTestRun(projectPath, run) {
|
|
35112
35206
|
const path = keptAt(projectPath);
|
|
35113
35207
|
try {
|
|
35114
|
-
mkdirSync9(
|
|
35208
|
+
mkdirSync9(dirname13(path), { recursive: true });
|
|
35115
35209
|
writeFileSync11(path, `${JSON.stringify(run, null, 2)}
|
|
35116
35210
|
`, "utf8");
|
|
35117
35211
|
} catch {}
|
|
35118
35212
|
}
|
|
35119
35213
|
function takeCutTestRun(projectPath) {
|
|
35120
35214
|
const path = keptAt(projectPath);
|
|
35121
|
-
if (!
|
|
35215
|
+
if (!existsSync16(path)) {
|
|
35122
35216
|
return null;
|
|
35123
35217
|
}
|
|
35124
35218
|
try {
|
|
35125
|
-
const run = JSON.parse(
|
|
35219
|
+
const run = JSON.parse(readFileSync18(path, "utf8"));
|
|
35126
35220
|
rmSync8(path, { force: true });
|
|
35127
35221
|
return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
|
|
35128
35222
|
} catch {
|
|
@@ -35698,6 +35792,18 @@ class GodotDAPClient {
|
|
|
35698
35792
|
lines: Array.from(lines).sort((a, b) => a - b)
|
|
35699
35793
|
}));
|
|
35700
35794
|
}
|
|
35795
|
+
forgetBreakpoint(scriptPath, line) {
|
|
35796
|
+
const key = fileKey(scriptPath);
|
|
35797
|
+
const lines = this.breakpoints.get(key);
|
|
35798
|
+
if (lines === undefined) {
|
|
35799
|
+
return;
|
|
35800
|
+
}
|
|
35801
|
+
lines.delete(line);
|
|
35802
|
+
if (lines.size === 0) {
|
|
35803
|
+
this.breakpoints.delete(key);
|
|
35804
|
+
this.spelled.delete(key);
|
|
35805
|
+
}
|
|
35806
|
+
}
|
|
35701
35807
|
holdBreakpoints(held) {
|
|
35702
35808
|
for (const { scriptPath, lines } of held) {
|
|
35703
35809
|
const key = fileKey(scriptPath);
|
|
@@ -35926,25 +36032,25 @@ var init_desktop = () => {};
|
|
|
35926
36032
|
// src/editor-log.ts
|
|
35927
36033
|
import {
|
|
35928
36034
|
closeSync,
|
|
35929
|
-
existsSync as
|
|
36035
|
+
existsSync as existsSync17,
|
|
35930
36036
|
mkdirSync as mkdirSync10,
|
|
35931
36037
|
openSync,
|
|
35932
|
-
readFileSync as
|
|
36038
|
+
readFileSync as readFileSync19,
|
|
35933
36039
|
readSync,
|
|
35934
36040
|
rmSync as rmSync9,
|
|
35935
36041
|
statSync as statSync7,
|
|
35936
36042
|
writeFileSync as writeFileSync12
|
|
35937
36043
|
} from "node:fs";
|
|
35938
|
-
import { dirname as
|
|
36044
|
+
import { dirname as dirname14, join as join21 } from "node:path";
|
|
35939
36045
|
function editorLogPath(projectPath) {
|
|
35940
|
-
return
|
|
36046
|
+
return join21(projectPath, ".godot", "gdharness-editor.log");
|
|
35941
36047
|
}
|
|
35942
36048
|
function editorLogNotePath(projectPath) {
|
|
35943
|
-
return
|
|
36049
|
+
return join21(projectPath, ".godot", "gdharness-editor.json");
|
|
35944
36050
|
}
|
|
35945
36051
|
function readEditorLogNote(projectPath) {
|
|
35946
36052
|
try {
|
|
35947
|
-
const fields = asParams(JSON.parse(
|
|
36053
|
+
const fields = asParams(JSON.parse(readFileSync19(editorLogNotePath(projectPath), "utf8")));
|
|
35948
36054
|
const pid = readNumber(fields, "pid");
|
|
35949
36055
|
const startedAt = readString(fields, "startedAt");
|
|
35950
36056
|
return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
|
|
@@ -35955,7 +36061,7 @@ function readEditorLogNote(projectPath) {
|
|
|
35955
36061
|
function clearEditorLog(projectPath) {
|
|
35956
36062
|
try {
|
|
35957
36063
|
const path = editorLogPath(projectPath);
|
|
35958
|
-
mkdirSync10(
|
|
36064
|
+
mkdirSync10(dirname14(path), { recursive: true });
|
|
35959
36065
|
rmSync9(path, { force: true });
|
|
35960
36066
|
rmSync9(editorLogNotePath(projectPath), { force: true });
|
|
35961
36067
|
} catch {}
|
|
@@ -35963,7 +36069,7 @@ function clearEditorLog(projectPath) {
|
|
|
35963
36069
|
function writeEditorLogNote(projectPath, pid) {
|
|
35964
36070
|
try {
|
|
35965
36071
|
const path = editorLogNotePath(projectPath);
|
|
35966
|
-
mkdirSync10(
|
|
36072
|
+
mkdirSync10(dirname14(path), { recursive: true });
|
|
35967
36073
|
writeFileSync12(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
35968
36074
|
} catch {}
|
|
35969
36075
|
}
|
|
@@ -35985,7 +36091,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
35985
36091
|
return { kind: "older server", notedPid: noted?.pid ?? null };
|
|
35986
36092
|
}
|
|
35987
36093
|
const path = editorLogPath(projectPath);
|
|
35988
|
-
if (!
|
|
36094
|
+
if (!existsSync17(path)) {
|
|
35989
36095
|
return { kind: "not written" };
|
|
35990
36096
|
}
|
|
35991
36097
|
const log = new GameLog;
|
|
@@ -36043,7 +36149,7 @@ function foldProgress(text) {
|
|
|
36043
36149
|
function theFirstOf(path) {
|
|
36044
36150
|
const whole = statSync7(path).size;
|
|
36045
36151
|
if (whole <= MOST_CONSOLE_BYTES) {
|
|
36046
|
-
return { text:
|
|
36152
|
+
return { text: readFileSync19(path, "utf8") };
|
|
36047
36153
|
}
|
|
36048
36154
|
const handle = openSync(path, "r");
|
|
36049
36155
|
try {
|
|
@@ -36203,6 +36309,26 @@ var init_editor_log = __esm(() => {
|
|
|
36203
36309
|
SLOTS = [/"[^"]*"/g, /'[^']*'/g, /\b(?:res|user):\/\/\S+/g, /(?<=\.gd|\.tscn|\.cs):\d+(?::\d+)?/g];
|
|
36204
36310
|
});
|
|
36205
36311
|
|
|
36312
|
+
// src/godot-variant.ts
|
|
36313
|
+
var FLAG_64, MOST_MESSAGE_BYTES;
|
|
36314
|
+
var init_godot_variant = __esm(() => {
|
|
36315
|
+
FLAG_64 = 1 << 16;
|
|
36316
|
+
MOST_MESSAGE_BYTES = (8 << 20) + 4;
|
|
36317
|
+
});
|
|
36318
|
+
|
|
36319
|
+
// src/game-debugger.ts
|
|
36320
|
+
var PAUSE_MS = 30000, STEP_MS = 1e4, READ_MS = 5000, REPLIES;
|
|
36321
|
+
var init_game_debugger = __esm(() => {
|
|
36322
|
+
init_godot_variant();
|
|
36323
|
+
REPLIES = new Set([
|
|
36324
|
+
"debug_enter",
|
|
36325
|
+
"debug_exit",
|
|
36326
|
+
"stack_dump",
|
|
36327
|
+
"stack_frame_vars",
|
|
36328
|
+
"stack_frame_var"
|
|
36329
|
+
]);
|
|
36330
|
+
});
|
|
36331
|
+
|
|
36206
36332
|
// node_modules/ws/lib/constants.js
|
|
36207
36333
|
var require_constants = __commonJS(function(exports, module) {
|
|
36208
36334
|
var BINARY_TYPES = ["nodebuffer", "arraybuffer", "fragments"];
|
|
@@ -40167,7 +40293,30 @@ var init_junit = __esm(() => {
|
|
|
40167
40293
|
|
|
40168
40294
|
// src/keeper-channel.ts
|
|
40169
40295
|
import { connect as connect2, createServer as createServer2 } from "node:net";
|
|
40170
|
-
function askTheKeeperToStop(address, withinMs) {
|
|
40296
|
+
async function askTheKeeperToStop(address, withinMs) {
|
|
40297
|
+
const answered = await askTheKeeper(address, "stop", withinMs);
|
|
40298
|
+
if (typeof answered !== "string") {
|
|
40299
|
+
return answered;
|
|
40300
|
+
}
|
|
40301
|
+
return answered === "signalled" || answered === "gone" ? answered : { unreached: `it answered ${JSON.stringify(answered)}` };
|
|
40302
|
+
}
|
|
40303
|
+
async function askTheKeepersDebugger(address, request, withinMs) {
|
|
40304
|
+
const answered = await askTheKeeper(address, `${DEBUG_PREFIX}${JSON.stringify(request)}`, withinMs);
|
|
40305
|
+
if (typeof answered !== "string") {
|
|
40306
|
+
return answered;
|
|
40307
|
+
}
|
|
40308
|
+
if (answered === "unknown") {
|
|
40309
|
+
return { unreached: "its keeper was started by a gdharness from before game debuggers, and has none" };
|
|
40310
|
+
}
|
|
40311
|
+
try {
|
|
40312
|
+
const parsed = JSON.parse(answered);
|
|
40313
|
+
if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
|
|
40314
|
+
return parsed;
|
|
40315
|
+
}
|
|
40316
|
+
} catch {}
|
|
40317
|
+
return { unreached: `it answered ${JSON.stringify(answered.slice(0, 200))}` };
|
|
40318
|
+
}
|
|
40319
|
+
function askTheKeeper(address, line, withinMs) {
|
|
40171
40320
|
return new Promise((resolve) => {
|
|
40172
40321
|
let said = "";
|
|
40173
40322
|
let settled = false;
|
|
@@ -40185,16 +40334,15 @@ function askTheKeeperToStop(address, withinMs) {
|
|
|
40185
40334
|
}, withinMs);
|
|
40186
40335
|
socket.setEncoding("utf8");
|
|
40187
40336
|
socket.on("connect", () => {
|
|
40188
|
-
socket.write(
|
|
40337
|
+
socket.write(`${line}
|
|
40189
40338
|
`);
|
|
40190
40339
|
});
|
|
40191
40340
|
socket.on("data", (chunk) => {
|
|
40192
40341
|
said += chunk;
|
|
40193
|
-
const line = said.split(`
|
|
40194
|
-
`)[0] ?? "";
|
|
40195
40342
|
if (said.includes(`
|
|
40196
40343
|
`)) {
|
|
40197
|
-
settle(
|
|
40344
|
+
settle(said.split(`
|
|
40345
|
+
`)[0] ?? "");
|
|
40198
40346
|
}
|
|
40199
40347
|
});
|
|
40200
40348
|
socket.on("error", (error) => {
|
|
@@ -40205,22 +40353,23 @@ function askTheKeeperToStop(address, withinMs) {
|
|
|
40205
40353
|
});
|
|
40206
40354
|
});
|
|
40207
40355
|
}
|
|
40356
|
+
var DEBUG_PREFIX = "debug ";
|
|
40208
40357
|
var init_keeper_channel = () => {};
|
|
40209
40358
|
|
|
40210
40359
|
// src/lifecycle-log.ts
|
|
40211
|
-
import { appendFileSync, mkdirSync as mkdirSync11, renameSync, statSync as statSync8 } from "node:fs";
|
|
40212
|
-
import { dirname as
|
|
40360
|
+
import { appendFileSync, mkdirSync as mkdirSync11, renameSync as renameSync2, statSync as statSync8 } from "node:fs";
|
|
40361
|
+
import { dirname as dirname15, join as join22 } from "node:path";
|
|
40213
40362
|
import process10 from "node:process";
|
|
40214
40363
|
function lifecycleLogPath() {
|
|
40215
|
-
return
|
|
40364
|
+
return join22(runtimeDirectory(), "servers.log");
|
|
40216
40365
|
}
|
|
40217
40366
|
function writeLifecycle(event, fields = {}) {
|
|
40218
40367
|
const path = lifecycleLogPath();
|
|
40219
40368
|
try {
|
|
40220
|
-
mkdirSync11(
|
|
40369
|
+
mkdirSync11(dirname15(path), { recursive: true });
|
|
40221
40370
|
if ((statSync8(path, { throwIfNoEntry: false })?.size ?? 0) > LIFECYCLE_LOG_LIMIT) {
|
|
40222
40371
|
try {
|
|
40223
|
-
|
|
40372
|
+
renameSync2(path, `${path}.1`);
|
|
40224
40373
|
} catch {}
|
|
40225
40374
|
}
|
|
40226
40375
|
const line = {
|
|
@@ -40255,10 +40404,10 @@ var init_lifecycle_log = __esm(() => {
|
|
|
40255
40404
|
});
|
|
40256
40405
|
|
|
40257
40406
|
// src/lsp_client.ts
|
|
40258
|
-
import { existsSync as
|
|
40407
|
+
import { existsSync as existsSync18, realpathSync as realpathSync4 } from "node:fs";
|
|
40259
40408
|
import { readFile, realpath } from "node:fs/promises";
|
|
40260
40409
|
import { createConnection as createConnection3 } from "node:net";
|
|
40261
|
-
import { dirname as
|
|
40410
|
+
import { dirname as dirname16, join as join23, resolve as resolve5 } from "node:path";
|
|
40262
40411
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
40263
40412
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
40264
40413
|
function initializeTimeoutMs() {
|
|
@@ -40420,7 +40569,7 @@ class GodotLSPClient {
|
|
|
40420
40569
|
if (this.initialized) {
|
|
40421
40570
|
return;
|
|
40422
40571
|
}
|
|
40423
|
-
const rootPath = this.rootPath ??
|
|
40572
|
+
const rootPath = this.rootPath ?? dirname16(resolve5(filePath));
|
|
40424
40573
|
await this.initialize(rootPath);
|
|
40425
40574
|
}
|
|
40426
40575
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -40921,7 +41070,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
40921
41070
|
} catch {
|
|
40922
41071
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
40923
41072
|
}
|
|
40924
|
-
if (!
|
|
41073
|
+
if (!existsSync18(join23(projectPath, "project.godot"))) {
|
|
40925
41074
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
40926
41075
|
}
|
|
40927
41076
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -41361,8 +41510,8 @@ var init_prompts = __esm(() => {
|
|
|
41361
41510
|
});
|
|
41362
41511
|
|
|
41363
41512
|
// src/rename.ts
|
|
41364
|
-
import { existsSync as
|
|
41365
|
-
import { dirname as
|
|
41513
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync12, readFileSync as readFileSync20, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync13 } from "node:fs";
|
|
41514
|
+
import { dirname as dirname17, posix as posix2 } from "node:path";
|
|
41366
41515
|
|
|
41367
41516
|
class Script {
|
|
41368
41517
|
path;
|
|
@@ -42063,26 +42212,26 @@ function rewritesOf(plan, texts, move) {
|
|
|
42063
42212
|
}
|
|
42064
42213
|
function applyRewrites(rewrites) {
|
|
42065
42214
|
for (const rewrite of rewrites) {
|
|
42066
|
-
const now =
|
|
42215
|
+
const now = readFileSync20(rewrite.absolute, "utf8");
|
|
42067
42216
|
if (now !== rewrite.before) {
|
|
42068
42217
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
42069
42218
|
}
|
|
42070
|
-
if (rewrite.writeTo !== rewrite.absolute &&
|
|
42219
|
+
if (rewrite.writeTo !== rewrite.absolute && existsSync19(rewrite.writeTo)) {
|
|
42071
42220
|
throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
|
|
42072
42221
|
}
|
|
42073
42222
|
}
|
|
42074
42223
|
const done = [];
|
|
42075
42224
|
try {
|
|
42076
42225
|
for (const rewrite of rewrites) {
|
|
42077
|
-
mkdirSync12(
|
|
42226
|
+
mkdirSync12(dirname17(rewrite.writeTo), { recursive: true });
|
|
42078
42227
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
42079
42228
|
writeFileSync13(staging, rewrite.after, "utf8");
|
|
42080
|
-
|
|
42229
|
+
renameSync3(staging, rewrite.writeTo);
|
|
42081
42230
|
let movedUid = false;
|
|
42082
42231
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
42083
|
-
|
|
42084
|
-
if (
|
|
42085
|
-
|
|
42232
|
+
unlinkSync3(rewrite.absolute);
|
|
42233
|
+
if (existsSync19(`${rewrite.absolute}.uid`)) {
|
|
42234
|
+
renameSync3(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
|
|
42086
42235
|
movedUid = true;
|
|
42087
42236
|
}
|
|
42088
42237
|
}
|
|
@@ -42093,9 +42242,9 @@ function applyRewrites(rewrites) {
|
|
|
42093
42242
|
try {
|
|
42094
42243
|
writeFileSync13(rewrite.absolute, rewrite.before, "utf8");
|
|
42095
42244
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
42096
|
-
|
|
42245
|
+
unlinkSync3(rewrite.writeTo);
|
|
42097
42246
|
if (movedUid) {
|
|
42098
|
-
|
|
42247
|
+
renameSync3(`${rewrite.writeTo}.uid`, `${rewrite.absolute}.uid`);
|
|
42099
42248
|
}
|
|
42100
42249
|
}
|
|
42101
42250
|
} catch {}
|
|
@@ -42127,8 +42276,8 @@ var init_rename = __esm(() => {
|
|
|
42127
42276
|
});
|
|
42128
42277
|
|
|
42129
42278
|
// src/rename-symbol.ts
|
|
42130
|
-
import { existsSync as
|
|
42131
|
-
import { basename as
|
|
42279
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync13, readdirSync as readdirSync10, realpathSync as realpathSync5, rmSync as rmSync10, writeFileSync as writeFileSync14 } from "node:fs";
|
|
42280
|
+
import { basename as basename6, dirname as dirname18, join as join24, relative as relative4, resolve as resolve6 } from "node:path";
|
|
42132
42281
|
function resourcePathOf(projectPath, absolute) {
|
|
42133
42282
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
42134
42283
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -42141,13 +42290,13 @@ function onDiskSpelling(path) {
|
|
|
42141
42290
|
let at = resolve6(path);
|
|
42142
42291
|
for (;; ) {
|
|
42143
42292
|
try {
|
|
42144
|
-
return
|
|
42293
|
+
return join24(realpathSync5.native(at), ...missing.reverse());
|
|
42145
42294
|
} catch {
|
|
42146
|
-
const parent =
|
|
42295
|
+
const parent = dirname18(at);
|
|
42147
42296
|
if (parent === at) {
|
|
42148
42297
|
return resolve6(path);
|
|
42149
42298
|
}
|
|
42150
|
-
missing.push(
|
|
42299
|
+
missing.push(basename6(at));
|
|
42151
42300
|
at = parent;
|
|
42152
42301
|
}
|
|
42153
42302
|
}
|
|
@@ -42194,7 +42343,7 @@ async function renameSymbol(request, services) {
|
|
|
42194
42343
|
reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
|
|
42195
42344
|
};
|
|
42196
42345
|
}
|
|
42197
|
-
if (
|
|
42346
|
+
if (existsSync20(request.newScriptPath)) {
|
|
42198
42347
|
return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
|
|
42199
42348
|
}
|
|
42200
42349
|
}
|
|
@@ -42370,7 +42519,7 @@ async function renameSymbol(request, services) {
|
|
|
42370
42519
|
scripts,
|
|
42371
42520
|
from: declaringPath,
|
|
42372
42521
|
to: moveTo,
|
|
42373
|
-
exists: (path) =>
|
|
42522
|
+
exists: (path) => existsSync20(`${projectPath}/${path.slice("res://".length)}`)
|
|
42374
42523
|
}, plan);
|
|
42375
42524
|
}
|
|
42376
42525
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? null : declaringPath);
|
|
@@ -42569,15 +42718,15 @@ ${mention.kind}`;
|
|
|
42569
42718
|
};
|
|
42570
42719
|
}
|
|
42571
42720
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
42572
|
-
const directory =
|
|
42721
|
+
const directory = join24(projectPath, ".godot", "gdharness-renames");
|
|
42573
42722
|
mkdirSync13(directory, { recursive: true });
|
|
42574
42723
|
detailWrites += 1;
|
|
42575
|
-
const path =
|
|
42724
|
+
const path = join24(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
42576
42725
|
writeFileSync14(path, `${JSON.stringify(detail, null, 2)}
|
|
42577
42726
|
`, "utf8");
|
|
42578
|
-
const kept =
|
|
42727
|
+
const kept = readdirSync10(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
42579
42728
|
for (const old of kept.slice(10)) {
|
|
42580
|
-
rmSync10(
|
|
42729
|
+
rmSync10(join24(directory, old), { force: true });
|
|
42581
42730
|
}
|
|
42582
42731
|
return path;
|
|
42583
42732
|
}
|
|
@@ -42617,15 +42766,15 @@ var init_rename_symbol = __esm(() => {
|
|
|
42617
42766
|
});
|
|
42618
42767
|
|
|
42619
42768
|
// src/restart-note.ts
|
|
42620
|
-
import { mkdirSync as mkdirSync14, readFileSync as
|
|
42621
|
-
import { dirname as
|
|
42769
|
+
import { mkdirSync as mkdirSync14, readFileSync as readFileSync21, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
|
|
42770
|
+
import { dirname as dirname19, join as join25 } from "node:path";
|
|
42622
42771
|
function restartNotePath(projectPath) {
|
|
42623
|
-
return
|
|
42772
|
+
return join25(projectPath, ".godot", "gdharness-restart.json");
|
|
42624
42773
|
}
|
|
42625
42774
|
function noteRestartBegun(note) {
|
|
42626
42775
|
const path = restartNotePath(note.projectPath);
|
|
42627
42776
|
try {
|
|
42628
|
-
mkdirSync14(
|
|
42777
|
+
mkdirSync14(dirname19(path), { recursive: true });
|
|
42629
42778
|
writeFileSync15(path, `${JSON.stringify(note, null, 2)}
|
|
42630
42779
|
`, "utf8");
|
|
42631
42780
|
} catch (error) {
|
|
@@ -42634,7 +42783,7 @@ function noteRestartBegun(note) {
|
|
|
42634
42783
|
}
|
|
42635
42784
|
function restartOwed(projectPath) {
|
|
42636
42785
|
try {
|
|
42637
|
-
const parsed = JSON.parse(
|
|
42786
|
+
const parsed = JSON.parse(readFileSync21(restartNotePath(projectPath), "utf8"));
|
|
42638
42787
|
if (typeof parsed !== "object" || parsed === null) {
|
|
42639
42788
|
return null;
|
|
42640
42789
|
}
|
|
@@ -42659,7 +42808,7 @@ var init_restart_note = __esm(() => {
|
|
|
42659
42808
|
});
|
|
42660
42809
|
|
|
42661
42810
|
// src/whole-file.ts
|
|
42662
|
-
import { renameSync as
|
|
42811
|
+
import { renameSync as renameSync4, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "node:fs";
|
|
42663
42812
|
function writeWhole(path, text) {
|
|
42664
42813
|
const partial = `${path}.${process.pid}.partial`;
|
|
42665
42814
|
try {
|
|
@@ -42670,7 +42819,7 @@ function writeWhole(path, text) {
|
|
|
42670
42819
|
const until = Date.now() + HELD_FILE_MS;
|
|
42671
42820
|
for (;; ) {
|
|
42672
42821
|
try {
|
|
42673
|
-
|
|
42822
|
+
renameSync4(partial, path);
|
|
42674
42823
|
return true;
|
|
42675
42824
|
} catch (error) {
|
|
42676
42825
|
const code = error.code ?? "";
|
|
@@ -42692,24 +42841,24 @@ var init_whole_file = __esm(() => {
|
|
|
42692
42841
|
// src/run-record.ts
|
|
42693
42842
|
import { execFile as execFile4 } from "node:child_process";
|
|
42694
42843
|
import { createHash as createHash4 } from "node:crypto";
|
|
42695
|
-
import { mkdirSync as mkdirSync15, openSync as openSync2, readdirSync as
|
|
42696
|
-
import { basename as
|
|
42844
|
+
import { mkdirSync as mkdirSync15, openSync as openSync2, readdirSync as readdirSync11, readFileSync as readFileSync22, realpathSync as realpathSync6, rmSync as rmSync13, statSync as statSync9 } from "node:fs";
|
|
42845
|
+
import { basename as basename7, dirname as dirname20, join as join26, resolve as resolve7 } from "node:path";
|
|
42697
42846
|
import { promisify as promisify4 } from "node:util";
|
|
42698
42847
|
function runsDirectory() {
|
|
42699
|
-
return
|
|
42848
|
+
return join26(runtimeDirectory(), "runs");
|
|
42700
42849
|
}
|
|
42701
42850
|
function realAsFarAsItExists(path) {
|
|
42702
42851
|
const missing = [];
|
|
42703
42852
|
let at = path;
|
|
42704
42853
|
for (;; ) {
|
|
42705
42854
|
try {
|
|
42706
|
-
return
|
|
42855
|
+
return join26(realpathSync6.native(at), ...missing);
|
|
42707
42856
|
} catch {
|
|
42708
|
-
const parent =
|
|
42857
|
+
const parent = dirname20(at);
|
|
42709
42858
|
if (parent === at) {
|
|
42710
42859
|
return path;
|
|
42711
42860
|
}
|
|
42712
|
-
missing.unshift(
|
|
42861
|
+
missing.unshift(basename7(at));
|
|
42713
42862
|
at = parent;
|
|
42714
42863
|
}
|
|
42715
42864
|
}
|
|
@@ -42720,10 +42869,10 @@ function projectKey(projectPath) {
|
|
|
42720
42869
|
return createHash4("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
42721
42870
|
}
|
|
42722
42871
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
42723
|
-
return
|
|
42872
|
+
return join26(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
42724
42873
|
}
|
|
42725
42874
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
42726
|
-
return
|
|
42875
|
+
return join26(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
42727
42876
|
}
|
|
42728
42877
|
function writeLatestRun(servedBy, latest) {
|
|
42729
42878
|
try {
|
|
@@ -42733,7 +42882,7 @@ function writeLatestRun(servedBy, latest) {
|
|
|
42733
42882
|
}
|
|
42734
42883
|
function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
42735
42884
|
try {
|
|
42736
|
-
const fields = JSON.parse(
|
|
42885
|
+
const fields = JSON.parse(readFileSync22(latestRunPath(servedBy, directory), "utf8"));
|
|
42737
42886
|
if (typeof fields !== "object" || fields === null) {
|
|
42738
42887
|
return null;
|
|
42739
42888
|
}
|
|
@@ -42748,17 +42897,17 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
42748
42897
|
}
|
|
42749
42898
|
function openTranscript(startedAt) {
|
|
42750
42899
|
mkdirSync15(runsDirectory(), { recursive: true });
|
|
42751
|
-
const path =
|
|
42900
|
+
const path = join26(runsDirectory(), `run-${startedAt}.log`);
|
|
42752
42901
|
return { path, fd: openSync2(path, "a") };
|
|
42753
42902
|
}
|
|
42754
42903
|
function profilePath(startedAt) {
|
|
42755
|
-
return
|
|
42904
|
+
return join26(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
42756
42905
|
}
|
|
42757
42906
|
function noteIsAbout(note, playing) {
|
|
42758
42907
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
42759
42908
|
}
|
|
42760
42909
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
42761
|
-
return
|
|
42910
|
+
return join26(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
42762
42911
|
}
|
|
42763
42912
|
function writeEditorRunNote(note) {
|
|
42764
42913
|
mkdirSync15(runsDirectory(), { recursive: true });
|
|
@@ -42769,7 +42918,7 @@ function writeEditorRunNote(note) {
|
|
|
42769
42918
|
}
|
|
42770
42919
|
function readEditorRunNote(projectPath) {
|
|
42771
42920
|
for (const directory of runtimeDirectories()) {
|
|
42772
|
-
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(
|
|
42921
|
+
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join26(directory, "runs", "editor-run.json"));
|
|
42773
42922
|
if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
|
|
42774
42923
|
return note;
|
|
42775
42924
|
}
|
|
@@ -42779,7 +42928,7 @@ function readEditorRunNote(projectPath) {
|
|
|
42779
42928
|
function editorNoteAt(path) {
|
|
42780
42929
|
let parsed;
|
|
42781
42930
|
try {
|
|
42782
|
-
parsed = JSON.parse(
|
|
42931
|
+
parsed = JSON.parse(readFileSync22(path, "utf8"));
|
|
42783
42932
|
} catch {
|
|
42784
42933
|
return null;
|
|
42785
42934
|
}
|
|
@@ -42803,7 +42952,7 @@ function readRunRecord(projectPath) {
|
|
|
42803
42952
|
if (own !== null) {
|
|
42804
42953
|
return own;
|
|
42805
42954
|
}
|
|
42806
|
-
const shared = recordAt(
|
|
42955
|
+
const shared = recordAt(join26(directory, "runs", SHARED_NOTE));
|
|
42807
42956
|
if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
|
|
42808
42957
|
return shared;
|
|
42809
42958
|
}
|
|
@@ -42815,13 +42964,13 @@ function everyRunRecord() {
|
|
|
42815
42964
|
for (const directory of runtimeDirectories()) {
|
|
42816
42965
|
let entries;
|
|
42817
42966
|
try {
|
|
42818
|
-
entries =
|
|
42967
|
+
entries = readdirSync11(join26(directory, "runs"));
|
|
42819
42968
|
} catch {
|
|
42820
42969
|
continue;
|
|
42821
42970
|
}
|
|
42822
42971
|
for (const entry of entries) {
|
|
42823
42972
|
if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
42824
|
-
const record = recordAt(
|
|
42973
|
+
const record = recordAt(join26(directory, "runs", entry));
|
|
42825
42974
|
if (record !== null) {
|
|
42826
42975
|
found.push(record);
|
|
42827
42976
|
}
|
|
@@ -42833,7 +42982,7 @@ function everyRunRecord() {
|
|
|
42833
42982
|
function recordAt(path) {
|
|
42834
42983
|
let parsed;
|
|
42835
42984
|
try {
|
|
42836
|
-
parsed = JSON.parse(
|
|
42985
|
+
parsed = JSON.parse(readFileSync22(path, "utf8"));
|
|
42837
42986
|
} catch {
|
|
42838
42987
|
return null;
|
|
42839
42988
|
}
|
|
@@ -42886,7 +43035,7 @@ async function runningAs(pid) {
|
|
|
42886
43035
|
const began = await startedAt(pid, null);
|
|
42887
43036
|
const when = began === null ? {} : { startedAt: began };
|
|
42888
43037
|
if (process.platform === "linux") {
|
|
42889
|
-
const raw =
|
|
43038
|
+
const raw = readFileSync22(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
42890
43039
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
42891
43040
|
}
|
|
42892
43041
|
const args = (await execFileAsync("ps", ["-p", String(pid), "-o", "args="], { encoding: "utf8", timeout: 15000 })).stdout.trim();
|
|
@@ -42992,7 +43141,7 @@ function whyNotTheRun(record, running, needed) {
|
|
|
42992
43141
|
if (after > SAME_RUN_WINDOW_MS) {
|
|
42993
43142
|
return `that process started ${Math.round(after / 1000)} seconds after the run had`;
|
|
42994
43143
|
}
|
|
42995
|
-
const engine = record.command === undefined ? null :
|
|
43144
|
+
const engine = record.command === undefined ? null : basename7(record.command);
|
|
42996
43145
|
const said = process.platform === "win32" ? running.text.toLowerCase() : running.text;
|
|
42997
43146
|
const wanted = engine === null || process.platform !== "win32" ? engine : engine.toLowerCase();
|
|
42998
43147
|
if (wanted !== null && !said.includes(wanted)) {
|
|
@@ -43031,7 +43180,7 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
|
|
|
43031
43180
|
}
|
|
43032
43181
|
function clearRunRecord(projectPath) {
|
|
43033
43182
|
for (const directory of runtimeDirectories()) {
|
|
43034
|
-
const shared =
|
|
43183
|
+
const shared = join26(directory, "runs", SHARED_NOTE);
|
|
43035
43184
|
const sharedRecord = recordAt(shared);
|
|
43036
43185
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
43037
43186
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
@@ -43056,7 +43205,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
43056
43205
|
]));
|
|
43057
43206
|
let entries;
|
|
43058
43207
|
try {
|
|
43059
|
-
entries =
|
|
43208
|
+
entries = readdirSync11(directory);
|
|
43060
43209
|
} catch {
|
|
43061
43210
|
return;
|
|
43062
43211
|
}
|
|
@@ -43065,7 +43214,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
43065
43214
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
43066
43215
|
continue;
|
|
43067
43216
|
}
|
|
43068
|
-
const path =
|
|
43217
|
+
const path = join26(directory, entry);
|
|
43069
43218
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
43070
43219
|
continue;
|
|
43071
43220
|
}
|
|
@@ -43086,7 +43235,7 @@ var init_run_record = __esm(() => {
|
|
|
43086
43235
|
});
|
|
43087
43236
|
|
|
43088
43237
|
// src/script-origins.ts
|
|
43089
|
-
import { join as
|
|
43238
|
+
import { join as join27 } from "node:path";
|
|
43090
43239
|
function unresolvedClassIn(message) {
|
|
43091
43240
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
43092
43241
|
}
|
|
@@ -43139,7 +43288,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
43139
43288
|
const ran = await runEngine(godotPath, [
|
|
43140
43289
|
"--headless",
|
|
43141
43290
|
"--log-file",
|
|
43142
|
-
|
|
43291
|
+
join27(logDir, "engine.log"),
|
|
43143
43292
|
"--path",
|
|
43144
43293
|
projectPath,
|
|
43145
43294
|
"--check-only",
|
|
@@ -43183,18 +43332,11 @@ var init_script_origins = __esm(() => {
|
|
|
43183
43332
|
RELOADED_AT = /GDScript::reload \((res:\/\/.+):(\d+)\)$/;
|
|
43184
43333
|
});
|
|
43185
43334
|
|
|
43186
|
-
// src/godot-variant.ts
|
|
43187
|
-
var FLAG_64, MOST_MESSAGE_BYTES;
|
|
43188
|
-
var init_godot_variant = __esm(() => {
|
|
43189
|
-
FLAG_64 = 1 << 16;
|
|
43190
|
-
MOST_MESSAGE_BYTES = (8 << 20) + 4;
|
|
43191
|
-
});
|
|
43192
|
-
|
|
43193
43335
|
// src/script-profile.ts
|
|
43194
|
-
import { readFileSync as
|
|
43336
|
+
import { readFileSync as readFileSync23 } from "node:fs";
|
|
43195
43337
|
function readProfileFile(path) {
|
|
43196
43338
|
try {
|
|
43197
|
-
const parsed = JSON.parse(
|
|
43339
|
+
const parsed = JSON.parse(readFileSync23(path, "utf8"));
|
|
43198
43340
|
return typeof parsed === "object" && typeof parsed.connected === "boolean" ? parsed : null;
|
|
43199
43341
|
} catch {
|
|
43200
43342
|
return null;
|
|
@@ -43356,7 +43498,7 @@ function serverInstructions() {
|
|
|
43356
43498
|
"Call editor_status first: it says whether an editor is connected and whether its addon matches this server.",
|
|
43357
43499
|
"Start the game with editor_run start, never by spawning an engine, and read editor_output after every run:",
|
|
43358
43500
|
"its clean verdict and its error entries are how a run is judged, and editor_run wait is how to wait for one.",
|
|
43359
|
-
"The runtime_* tools work on the running game and the debug_* tools on
|
|
43501
|
+
"The runtime_* tools work on the running game and the debug_* tools on its debugger.",
|
|
43360
43502
|
"A refusal names what would have worked, so read it before calling again."
|
|
43361
43503
|
].join(" ");
|
|
43362
43504
|
}
|
|
@@ -43372,17 +43514,17 @@ import {
|
|
|
43372
43514
|
closeSync as closeSync2,
|
|
43373
43515
|
constants,
|
|
43374
43516
|
copyFileSync,
|
|
43375
|
-
existsSync as
|
|
43517
|
+
existsSync as existsSync21,
|
|
43376
43518
|
fstatSync,
|
|
43377
43519
|
mkdirSync as mkdirSync16,
|
|
43378
43520
|
openSync as openSync3,
|
|
43379
|
-
readdirSync as
|
|
43380
|
-
readFileSync as
|
|
43521
|
+
readdirSync as readdirSync12,
|
|
43522
|
+
readFileSync as readFileSync24,
|
|
43381
43523
|
readSync as readSync2,
|
|
43382
43524
|
rmdirSync as rmdirSync2,
|
|
43383
43525
|
statSync as statSync10
|
|
43384
43526
|
} from "node:fs";
|
|
43385
|
-
import { basename as
|
|
43527
|
+
import { basename as basename8, dirname as dirname21, extname as extname3, isAbsolute as isAbsolute4, join as join28, normalize as normalize2, relative as relative5 } from "node:path";
|
|
43386
43528
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
43387
43529
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
43388
43530
|
import { promisify as promisify5 } from "node:util";
|
|
@@ -43413,22 +43555,22 @@ function writtenFileRefusal(path, rule) {
|
|
|
43413
43555
|
if (/^(res|user):\/\//.test(path)) {
|
|
43414
43556
|
return `${named} ${path} is a Godot path; ${what} is saved outside the project, at an absolute path on this machine.`;
|
|
43415
43557
|
}
|
|
43416
|
-
if (!
|
|
43558
|
+
if (!isAbsolute4(path)) {
|
|
43417
43559
|
return `${named} ${path} is not absolute; name the whole path, so where it is written does not depend on this server's directory.`;
|
|
43418
43560
|
}
|
|
43419
43561
|
if (!rule.extensions.includes(extname3(path).toLowerCase())) {
|
|
43420
43562
|
return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
|
|
43421
43563
|
}
|
|
43422
|
-
const directory = realPathOr(
|
|
43423
|
-
if (!
|
|
43424
|
-
return `${named}'s directory ${
|
|
43564
|
+
const directory = realPathOr(dirname21(path));
|
|
43565
|
+
if (!existsSync21(directory)) {
|
|
43566
|
+
return `${named}'s directory ${dirname21(path)} does not exist; ${what} creates no directories.`;
|
|
43425
43567
|
}
|
|
43426
|
-
const where =
|
|
43568
|
+
const where = join28(directory, basename8(path));
|
|
43427
43569
|
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
43428
43570
|
if (inside !== undefined) {
|
|
43429
43571
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
43430
43572
|
}
|
|
43431
|
-
const taken = rule.writes(where).find((file) =>
|
|
43573
|
+
const taken = rule.writes(where).find((file) => existsSync21(file));
|
|
43432
43574
|
if (taken !== undefined) {
|
|
43433
43575
|
return taken === where ? `${named} ${path} already exists; ${what} is not written over a file. Name a new one.` : `${named} ${path} would write ${taken}, which already exists; ${what} is not written over a file. Name a new one.`;
|
|
43434
43576
|
}
|
|
@@ -43486,6 +43628,30 @@ function longerWaitNote(tool, op, waitedMs) {
|
|
|
43486
43628
|
const how = tool === "runtime_wait" && op === "frames" ? `the wait is counted from the number of frames, at ${SLOWEST_FRAME_RATE} a second on top of GDHARNESS_RUNTIME_TIMEOUT_MS: a game drawing frames more slowly than that is waited on in fewer frames at a time, and a game inside one long frame, such as a long _ready, answers no call however few frames are asked for` : "the wait is GDHARNESS_RUNTIME_TIMEOUT_MS, which is set in the environment the server is started with";
|
|
43487
43629
|
return `${tool} ${op} takes no timeoutMs: it was given ${waitedMs}ms, and ${how}.`;
|
|
43488
43630
|
}
|
|
43631
|
+
function keptGameRequest(tool, op, args) {
|
|
43632
|
+
if (tool === "debug_state") {
|
|
43633
|
+
if (op === "output") {
|
|
43634
|
+
return { op: "output" };
|
|
43635
|
+
}
|
|
43636
|
+
if (op === "variables") {
|
|
43637
|
+
const frame = args["frameId"];
|
|
43638
|
+
return { op: "variables", frame: typeof frame === "number" ? frame : 0 };
|
|
43639
|
+
}
|
|
43640
|
+
return { op: "stack" };
|
|
43641
|
+
}
|
|
43642
|
+
switch (op) {
|
|
43643
|
+
case "pause":
|
|
43644
|
+
return { op: "pause" };
|
|
43645
|
+
case "step_over":
|
|
43646
|
+
return { op: "resume", how: "next" };
|
|
43647
|
+
case "step_into":
|
|
43648
|
+
return { op: "resume", how: "step" };
|
|
43649
|
+
case "step_out":
|
|
43650
|
+
return { op: "resume", how: "out" };
|
|
43651
|
+
default:
|
|
43652
|
+
return { op: "resume", how: "continue" };
|
|
43653
|
+
}
|
|
43654
|
+
}
|
|
43489
43655
|
function projectSpelling(projectPath, absolutePath) {
|
|
43490
43656
|
return `res://${relative5(realPathOr(projectPath), realPathOr(absolutePath)).replaceAll("\\", "/")}`;
|
|
43491
43657
|
}
|
|
@@ -43900,6 +44066,7 @@ function notRunNote(notRun, failFast) {
|
|
|
43900
44066
|
function exportAnswer(ending, asked, before, after) {
|
|
43901
44067
|
const { log } = ending;
|
|
43902
44068
|
const written = after !== null && after !== before;
|
|
44069
|
+
const notCopied = librariesNotCopied(log.all);
|
|
43903
44070
|
const verdict = {
|
|
43904
44071
|
exported: ending.exitCode === 0 && ending.failure === null && log.count("error") === 0 && written,
|
|
43905
44072
|
...asked,
|
|
@@ -43908,6 +44075,10 @@ function exportAnswer(ending, asked, before, after) {
|
|
|
43908
44075
|
failure: ending.failure ?? undefined,
|
|
43909
44076
|
errors: log.count("error"),
|
|
43910
44077
|
warnings: log.count("warning"),
|
|
44078
|
+
...notCopied.length === 0 ? {} : {
|
|
44079
|
+
librariesNotLoaded: notCopied,
|
|
44080
|
+
extensionNote: `The export could not load ${notCopied.join(" and ")}: on Windows an engine running as the editor, which an export is, loads a GDExtension from a ~ copy beside the library, and another engine on this project held that copy: it could not be moved aside before the export, or another engine made a new one in between. Export again once that engine has loaded or exited.`
|
|
44081
|
+
},
|
|
43911
44082
|
...foldedEntriesAnswer(log)
|
|
43912
44083
|
};
|
|
43913
44084
|
if (verdict.exported) {
|
|
@@ -43945,7 +44116,7 @@ function engineFlagsNote(args) {
|
|
|
43945
44116
|
return `${named} ${among.length === 1 ? "is a flag" : "are flags"} the engine reads, and args goes to the game after --, where the engine does not read ${among.length === 1 ? "it" : "them"}. engineArgs hands the engine its own flags; ${among.length === 1 ? "leave it" : "leave them"} in args only for a game that reads ${among.length === 1 ? "that flag" : "those flags"} itself.`;
|
|
43946
44117
|
}
|
|
43947
44118
|
function editorAddonMissing(projectPath) {
|
|
43948
|
-
if (!
|
|
44119
|
+
if (!existsSync21(join28(projectPath, "project.godot"))) {
|
|
43949
44120
|
return null;
|
|
43950
44121
|
}
|
|
43951
44122
|
const report = inspectProject(projectPath);
|
|
@@ -44181,7 +44352,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
44181
44352
|
let projectGodot = null;
|
|
44182
44353
|
if (projectPath !== null) {
|
|
44183
44354
|
try {
|
|
44184
|
-
projectGodot =
|
|
44355
|
+
projectGodot = readFileSync24(join28(projectPath, "project.godot"), "utf8");
|
|
44185
44356
|
} catch {}
|
|
44186
44357
|
}
|
|
44187
44358
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -44205,7 +44376,7 @@ function projectSourceOf(projectPath) {
|
|
|
44205
44376
|
let source = null;
|
|
44206
44377
|
if (inside.ok) {
|
|
44207
44378
|
try {
|
|
44208
|
-
source =
|
|
44379
|
+
source = readFileSync24(inside.absolutePath, "utf8");
|
|
44209
44380
|
} catch {}
|
|
44210
44381
|
}
|
|
44211
44382
|
read.set(script, source);
|
|
@@ -44355,7 +44526,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
44355
44526
|
}
|
|
44356
44527
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
44357
44528
|
try {
|
|
44358
|
-
return settingKeys(
|
|
44529
|
+
return settingKeys(readFileSync24(join28(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
44359
44530
|
} catch {
|
|
44360
44531
|
return true;
|
|
44361
44532
|
}
|
|
@@ -44370,13 +44541,13 @@ function uncaughtNote(uncaught) {
|
|
|
44370
44541
|
}
|
|
44371
44542
|
function elsewhereIn(projectPath, asked) {
|
|
44372
44543
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
44373
|
-
if (wanted === "" ||
|
|
44544
|
+
if (wanted === "" || existsSync21(join28(projectPath, wanted))) {
|
|
44374
44545
|
return "";
|
|
44375
44546
|
}
|
|
44376
44547
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
44377
44548
|
let near = [];
|
|
44378
44549
|
try {
|
|
44379
|
-
near =
|
|
44550
|
+
near = readdirSync12(projectPath, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.toLowerCase() !== root).map((entry) => entry.name).filter((name) => name.toLowerCase().startsWith("test") || root.startsWith(name.toLowerCase()));
|
|
44380
44551
|
} catch {
|
|
44381
44552
|
return "";
|
|
44382
44553
|
}
|
|
@@ -44466,17 +44637,17 @@ function isDirectory(path) {
|
|
|
44466
44637
|
}
|
|
44467
44638
|
}
|
|
44468
44639
|
function scriptsUnder(directory, prefix) {
|
|
44469
|
-
if (
|
|
44640
|
+
if (existsSync21(join28(directory, ".gdignore"))) {
|
|
44470
44641
|
return [];
|
|
44471
44642
|
}
|
|
44472
44643
|
const found = [];
|
|
44473
|
-
for (const entry of
|
|
44644
|
+
for (const entry of readdirSync12(directory, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : 1)) {
|
|
44474
44645
|
if (entry.name.startsWith(".")) {
|
|
44475
44646
|
continue;
|
|
44476
44647
|
}
|
|
44477
44648
|
const path = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
44478
44649
|
if (entry.isDirectory()) {
|
|
44479
|
-
found.push(...scriptsUnder(
|
|
44650
|
+
found.push(...scriptsUnder(join28(directory, entry.name), path));
|
|
44480
44651
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
44481
44652
|
found.push(path);
|
|
44482
44653
|
}
|
|
@@ -44484,7 +44655,7 @@ function scriptsUnder(directory, prefix) {
|
|
|
44484
44655
|
return found;
|
|
44485
44656
|
}
|
|
44486
44657
|
function hasMainScene(projectFile) {
|
|
44487
|
-
const scene = parseProjectGodot(
|
|
44658
|
+
const scene = parseProjectGodot(readFileSync24(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
44488
44659
|
return typeof scene === "string" && scene !== "";
|
|
44489
44660
|
}
|
|
44490
44661
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -44492,13 +44663,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
44492
44663
|
return false;
|
|
44493
44664
|
}
|
|
44494
44665
|
try {
|
|
44495
|
-
return
|
|
44666
|
+
return readFileSync24(join28(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
44496
44667
|
} catch {
|
|
44497
44668
|
return false;
|
|
44498
44669
|
}
|
|
44499
44670
|
}
|
|
44500
44671
|
function editorPlaysHeadless(projectFile) {
|
|
44501
|
-
const runArgs = parseProjectGodot(
|
|
44672
|
+
const runArgs = parseProjectGodot(readFileSync24(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
44502
44673
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
44503
44674
|
}
|
|
44504
44675
|
function camelCased(params) {
|
|
@@ -44517,7 +44688,7 @@ function needsItsOwnProcess(asked) {
|
|
|
44517
44688
|
class GodotServer {
|
|
44518
44689
|
stdio = null;
|
|
44519
44690
|
locator = new GodotLocator;
|
|
44520
|
-
operationsScript =
|
|
44691
|
+
operationsScript = join28(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
44521
44692
|
godotBridge;
|
|
44522
44693
|
tools = buildToolDefinitions();
|
|
44523
44694
|
activeProcess = null;
|
|
@@ -44542,6 +44713,7 @@ class GodotServer {
|
|
|
44542
44713
|
launchedDropped = null;
|
|
44543
44714
|
closedHere = null;
|
|
44544
44715
|
inFlight = new Map;
|
|
44716
|
+
callsOnGames = new Set;
|
|
44545
44717
|
callsTaken = 0;
|
|
44546
44718
|
clientNoted = null;
|
|
44547
44719
|
parentCommand = null;
|
|
@@ -44773,14 +44945,14 @@ class GodotServer {
|
|
|
44773
44945
|
}
|
|
44774
44946
|
noteTheStart() {
|
|
44775
44947
|
const cwd = process.cwd();
|
|
44776
|
-
const mcpJson =
|
|
44948
|
+
const mcpJson = join28(cwd, ".mcp.json");
|
|
44777
44949
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
44778
44950
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
44779
44951
|
writeLifecycle("started", {
|
|
44780
44952
|
argv: process.argv.slice(1),
|
|
44781
44953
|
cwd,
|
|
44782
44954
|
project: this.ownProject,
|
|
44783
|
-
mcpJson:
|
|
44955
|
+
mcpJson: existsSync21(mcpJson) ? mcpJson : null
|
|
44784
44956
|
});
|
|
44785
44957
|
if (predecessor !== null) {
|
|
44786
44958
|
writeLifecycle("replaced", { project: this.ownProject, predecessor });
|
|
@@ -44921,7 +45093,7 @@ class GodotServer {
|
|
|
44921
45093
|
return checked.response;
|
|
44922
45094
|
}
|
|
44923
45095
|
const named = args["projectPath"];
|
|
44924
|
-
if (typeof named === "string" &&
|
|
45096
|
+
if (typeof named === "string" && existsSync21(join28(named, "project.godot"))) {
|
|
44925
45097
|
this.lastProjectPath = named;
|
|
44926
45098
|
}
|
|
44927
45099
|
this.updates.refresh();
|
|
@@ -45392,28 +45564,70 @@ class GodotServer {
|
|
|
45392
45564
|
return this.createErrorResponse(located.reason, PATH_SOLUTIONS);
|
|
45393
45565
|
}
|
|
45394
45566
|
const session = this.dapHoldingBreakpointsOf(project.value.path);
|
|
45395
|
-
const
|
|
45396
|
-
|
|
45397
|
-
|
|
45398
|
-
|
|
45399
|
-
|
|
45400
|
-
|
|
45567
|
+
const scriptPath = realPathOr(located.absolutePath);
|
|
45568
|
+
const line = readPositiveNumber(args, "line");
|
|
45569
|
+
if (line === undefined) {
|
|
45570
|
+
return this.createErrorResponse("line is required: the one-based line to stop on.");
|
|
45571
|
+
}
|
|
45572
|
+
const editor = this.godotBridge.isConnected();
|
|
45573
|
+
let answer;
|
|
45574
|
+
if (editor) {
|
|
45575
|
+
const answered = await this.handleDAP(op === "set" ? "dap_set_breakpoint" : "dap_remove_breakpoint", {
|
|
45576
|
+
...args,
|
|
45577
|
+
scriptPath
|
|
45578
|
+
});
|
|
45579
|
+
if (answered.isError === true) {
|
|
45580
|
+
return answered;
|
|
45581
|
+
}
|
|
45582
|
+
answer = asParams(JSON.parse(answered.content[0]?.text ?? "{}"));
|
|
45583
|
+
} else {
|
|
45584
|
+
if (op === "set") {
|
|
45585
|
+
session.holdBreakpoints([{ scriptPath, lines: [line] }]);
|
|
45586
|
+
} else {
|
|
45587
|
+
session.forgetBreakpoint(scriptPath, line);
|
|
45588
|
+
}
|
|
45589
|
+
answer = { [op === "set" ? "set" : "removed"]: true };
|
|
45401
45590
|
}
|
|
45402
45591
|
const held = session.breakpointsHeld();
|
|
45403
45592
|
writeBreakpointNote(project.value.path, held);
|
|
45593
|
+
const kept = this.keptRun();
|
|
45594
|
+
const told = kept === null ? null : await askTheKeepersDebugger(kept.keeper, {
|
|
45595
|
+
op: "breakpoint",
|
|
45596
|
+
script: projectSpelling(project.value.path, scriptPath),
|
|
45597
|
+
line,
|
|
45598
|
+
set: op === "set"
|
|
45599
|
+
}, KEEPER_DEBUG_MS);
|
|
45404
45600
|
return this.jsonTextResponse({
|
|
45405
|
-
...
|
|
45601
|
+
...answer,
|
|
45406
45602
|
held: this.breakpointsAsProjectSpells(project.value.path, held),
|
|
45407
|
-
|
|
45408
|
-
|
|
45409
|
-
|
|
45603
|
+
...editor ? {
|
|
45604
|
+
setInEditor: this.breakpointsAsProjectSpells(project.value.path, session.breakpointsInEditor())
|
|
45605
|
+
} : {},
|
|
45606
|
+
...told === null ? {} : ("unreached" in told) || typeof told["refused"] === "string" ? {
|
|
45607
|
+
runningGame: `The game this server started is running, and its debugger was not told: ${String("unreached" in told ? told.unreached : told["refused"])}. It takes the line when it is started again.`
|
|
45608
|
+
} : {
|
|
45609
|
+
runningGame: `Told to the running game (pid ${kept?.pid ?? "?"}), which this server started.`
|
|
45610
|
+
},
|
|
45611
|
+
note: editor ? "The editor keeps a breakpoint set this way for one play. Every breakpoint held here is sent again before each play editor_run starts, handed to every game this server starts itself, and kept in the project for the server after a reconnect." : "No editor is connected, so the set under held is kept here: every game this server starts is handed it as it starts, an editor that connects is sent it before its first play, and it is kept in the project for the server after a reconnect.",
|
|
45612
|
+
...editor ? this.breakpointsAtRisk() : {}
|
|
45410
45613
|
});
|
|
45411
45614
|
}
|
|
45412
45615
|
case "debug_control":
|
|
45413
45616
|
case "debug_state": {
|
|
45617
|
+
const kept = this.keptRun();
|
|
45618
|
+
if (kept !== null) {
|
|
45619
|
+
return await this.askTheKeptGame(kept.keeper, keptGameRequest(tool, op, args), kept.projectPath ?? "");
|
|
45620
|
+
}
|
|
45414
45621
|
if (tool === "debug_state" && op === "output") {
|
|
45415
45622
|
return await this.handleDAP("dap_get_output", args);
|
|
45416
45623
|
}
|
|
45624
|
+
const played = this.activeProcess;
|
|
45625
|
+
if ((op === "pause" || op === "step_out") && played?.throughEditor === true && !exited(played)) {
|
|
45626
|
+
return this.createErrorResponse(op === "pause" ? "A game the editor plays cannot be paused from here: Godot's debug adapter answers a pause by reporting the game stopped and leaving it running." : "A game the editor plays cannot be stepped out of a function from here: Godot's debug adapter implements no stepOut.", [
|
|
45627
|
+
"Set a breakpoint where it should stop, then step over or into from there",
|
|
45628
|
+
"A game this server starts, as editor_run start does with args, savesIn or env, has a debugger here that pauses and steps out"
|
|
45629
|
+
]);
|
|
45630
|
+
}
|
|
45417
45631
|
const held = await this.debuggedGame(tool === "debug_control" ? "move" : "read");
|
|
45418
45632
|
if (!held.ok) {
|
|
45419
45633
|
return held.response;
|
|
@@ -45437,8 +45651,8 @@ class GodotServer {
|
|
|
45437
45651
|
if (path === undefined) {
|
|
45438
45652
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
45439
45653
|
}
|
|
45440
|
-
const file =
|
|
45441
|
-
if (!
|
|
45654
|
+
const file = join28(path, "project.godot");
|
|
45655
|
+
if (!existsSync21(file)) {
|
|
45442
45656
|
return {
|
|
45443
45657
|
ok: false,
|
|
45444
45658
|
response: this.createErrorResponse(`Not a Godot project: ${path}`, [
|
|
@@ -45484,7 +45698,7 @@ class GodotServer {
|
|
|
45484
45698
|
}
|
|
45485
45699
|
runEnvironment(args) {
|
|
45486
45700
|
const savesIn = readNonEmptyString(args, "savesIn");
|
|
45487
|
-
if (savesIn !== undefined && !
|
|
45701
|
+
if (savesIn !== undefined && !isAbsolute4(savesIn)) {
|
|
45488
45702
|
return {
|
|
45489
45703
|
ok: false,
|
|
45490
45704
|
response: this.createErrorResponse(`savesIn must be an absolute path, not "${savesIn}".`, [
|
|
@@ -45557,8 +45771,8 @@ class GodotServer {
|
|
|
45557
45771
|
if (!game.throughEditor) {
|
|
45558
45772
|
return {
|
|
45559
45773
|
ok: false,
|
|
45560
|
-
response: this.createErrorResponse("The running game
|
|
45561
|
-
"editor_run start
|
|
45774
|
+
response: this.createErrorResponse("The running game has no debugger: it was started with none, or by a keeper that could not take questions, so breakpoints never hit and there is no stack to read.", [
|
|
45775
|
+
"editor_run start starts it again, and the keeper holding a game this server starts is its debugger",
|
|
45562
45776
|
"editor_status says whether an editor has reached this server"
|
|
45563
45777
|
])
|
|
45564
45778
|
};
|
|
@@ -46068,11 +46282,11 @@ class GodotServer {
|
|
|
46068
46282
|
if (!engine.ok) {
|
|
46069
46283
|
return engine.response;
|
|
46070
46284
|
}
|
|
46071
|
-
const application = parseProjectGodot(
|
|
46285
|
+
const application = parseProjectGodot(readFileSync24(project.value.file, "utf8"))["application"] ?? {};
|
|
46072
46286
|
const name = application["config/name"];
|
|
46073
46287
|
const mainScene = application["run/main_scene"];
|
|
46074
46288
|
const info = {
|
|
46075
|
-
name: typeof name === "string" && name !== "" ? name :
|
|
46289
|
+
name: typeof name === "string" && name !== "" ? name : basename8(project.value.path),
|
|
46076
46290
|
path: project.value.path,
|
|
46077
46291
|
mainScene: typeof mainScene === "string" && mainScene !== "" ? mainScene : null,
|
|
46078
46292
|
godotVersion: await this.godotVersion(engine.value),
|
|
@@ -46124,18 +46338,18 @@ class GodotServer {
|
|
|
46124
46338
|
if (!engine.ok) {
|
|
46125
46339
|
return engine.response;
|
|
46126
46340
|
}
|
|
46127
|
-
const folder =
|
|
46341
|
+
const folder = dirname21(output.absolutePath);
|
|
46128
46342
|
try {
|
|
46129
46343
|
mkdirSync16(folder, { recursive: true });
|
|
46130
46344
|
} catch (error) {
|
|
46131
|
-
return this.createErrorResponse(`Cannot create ${
|
|
46345
|
+
return this.createErrorResponse(`Cannot create ${dirname21(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
46132
46346
|
}
|
|
46133
46347
|
const debug = readBoolean(args, "debug") ?? false;
|
|
46134
46348
|
const exportLogs = scratchDirectory("export");
|
|
46135
46349
|
const exportArgs = [
|
|
46136
46350
|
"--headless",
|
|
46137
46351
|
"--log-file",
|
|
46138
|
-
|
|
46352
|
+
join28(exportLogs, "engine.log"),
|
|
46139
46353
|
"--path",
|
|
46140
46354
|
project.value.path,
|
|
46141
46355
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -46143,8 +46357,12 @@ class GodotServer {
|
|
|
46143
46357
|
output.absolutePath
|
|
46144
46358
|
];
|
|
46145
46359
|
this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
|
|
46146
|
-
const writtenAt = () =>
|
|
46360
|
+
const writtenAt = () => existsSync21(output.absolutePath) ? statSync10(output.absolutePath).mtimeMs : null;
|
|
46147
46361
|
const before = writtenAt();
|
|
46362
|
+
const freed = freeLibraryCopies(project.value.path);
|
|
46363
|
+
if (freed.setAside.length > 0 || freed.swept.length > 0) {
|
|
46364
|
+
this.logDebug(`Before the export: set aside ${freed.setAside.join(", ")}; swept ${freed.swept.join(", ")}`);
|
|
46365
|
+
}
|
|
46148
46366
|
let ending;
|
|
46149
46367
|
try {
|
|
46150
46368
|
ending = await runEngine(engine.value, exportArgs, { timeout: { ms: 300000, said: "five minutes" } });
|
|
@@ -46207,10 +46425,10 @@ class GodotServer {
|
|
|
46207
46425
|
if (movie !== null) {
|
|
46208
46426
|
return this.createErrorResponse(movie);
|
|
46209
46427
|
}
|
|
46210
|
-
const missing = paths.value.filter((path) => !
|
|
46428
|
+
const missing = paths.value.filter((path) => !existsSync21(join28(project.value.path, path.slice("res://".length))));
|
|
46211
46429
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
46212
46430
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
46213
|
-
const noRunner = !
|
|
46431
|
+
const noRunner = !existsSync21(join28(project.value.path, runner));
|
|
46214
46432
|
if (asked.length === 0) {
|
|
46215
46433
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
46216
46434
|
const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
|
|
@@ -46301,21 +46519,21 @@ class GodotServer {
|
|
|
46301
46519
|
await untilGone([run.process.pid]);
|
|
46302
46520
|
}
|
|
46303
46521
|
await run.settled;
|
|
46304
|
-
const reportsDir =
|
|
46522
|
+
const reportsDir = join28(project.value.path, ".godot", ...ours.split("/"));
|
|
46305
46523
|
let report = null;
|
|
46306
46524
|
let reportProblem = null;
|
|
46307
46525
|
try {
|
|
46308
|
-
const written =
|
|
46526
|
+
const written = existsSync21(reportsDir) ? readdirSync12(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
46309
46527
|
const newest = written.at(-1);
|
|
46310
46528
|
if (newest !== undefined) {
|
|
46311
|
-
report = parseJUnit(
|
|
46529
|
+
report = parseJUnit(readFileSync24(join28(reportsDir, newest, "results.xml"), "utf8"));
|
|
46312
46530
|
}
|
|
46313
46531
|
} catch (error) {
|
|
46314
46532
|
reportProblem = errorMessage(error);
|
|
46315
46533
|
} finally {
|
|
46316
46534
|
discard(reportsDir, userData);
|
|
46317
46535
|
try {
|
|
46318
|
-
rmdirSync2(
|
|
46536
|
+
rmdirSync2(dirname21(reportsDir));
|
|
46319
46537
|
} catch {}
|
|
46320
46538
|
}
|
|
46321
46539
|
const engineEntries = foldedRepeats(run.log.select({
|
|
@@ -46575,11 +46793,11 @@ class GodotServer {
|
|
|
46575
46793
|
const cached = cachedClasses(projectPath);
|
|
46576
46794
|
const sourceOf = (resourcePath) => {
|
|
46577
46795
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
46578
|
-
if (!contained.ok || !
|
|
46796
|
+
if (!contained.ok || !existsSync21(contained.absolutePath)) {
|
|
46579
46797
|
return null;
|
|
46580
46798
|
}
|
|
46581
46799
|
try {
|
|
46582
|
-
return
|
|
46800
|
+
return readFileSync24(contained.absolutePath, "utf8");
|
|
46583
46801
|
} catch {
|
|
46584
46802
|
return null;
|
|
46585
46803
|
}
|
|
@@ -46644,11 +46862,11 @@ class GodotServer {
|
|
|
46644
46862
|
}
|
|
46645
46863
|
sourceOfClass(projectPath, resourcePath) {
|
|
46646
46864
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
46647
|
-
if (!contained.ok || !
|
|
46865
|
+
if (!contained.ok || !existsSync21(contained.absolutePath)) {
|
|
46648
46866
|
return null;
|
|
46649
46867
|
}
|
|
46650
46868
|
try {
|
|
46651
|
-
return
|
|
46869
|
+
return readFileSync24(contained.absolutePath, "utf8");
|
|
46652
46870
|
} catch {
|
|
46653
46871
|
return null;
|
|
46654
46872
|
}
|
|
@@ -46769,6 +46987,38 @@ class GodotServer {
|
|
|
46769
46987
|
breakpointsAtRisk: status.syncsBreakpoints === false ? "This editor clears every breakpoint in its script editor whenever a debug session opens on its adapter, which this server does for its first breakpoint or stack read: network/debug_adapter/sync_breakpoints is off. The installed addon turns it on as it loads, so the editor is running an older one; editor_launch restart puts the installed addon in, or turn the setting on in Editor Settings > Network > Debug Adapter." : "Whether this editor keeps its breakpoints when a debug session opens cannot be told: the addon it is running predates the report. Godot clears them unless network/debug_adapter/sync_breakpoints is on, which the installed addon turns on as it loads; editor_launch restart puts the installed addon in."
|
|
46770
46988
|
};
|
|
46771
46989
|
}
|
|
46990
|
+
breakpointsForAGame(projectPath) {
|
|
46991
|
+
return this.dapHoldingBreakpointsOf(projectPath).breakpointsHeld().flatMap((one) => one.lines.map((line) => `${projectSpelling(projectPath, one.scriptPath)}:${line}`));
|
|
46992
|
+
}
|
|
46993
|
+
keptRun() {
|
|
46994
|
+
const game = this.activeProcess;
|
|
46995
|
+
if (game === null || game.throughEditor || exited(game) || game.keeper === undefined) {
|
|
46996
|
+
return null;
|
|
46997
|
+
}
|
|
46998
|
+
return { ...game, keeper: game.keeper };
|
|
46999
|
+
}
|
|
47000
|
+
async askTheKeptGame(keeper, request, projectPath) {
|
|
47001
|
+
const answered = await askTheKeepersDebugger(keeper, request, KEEPER_DEBUG_MS);
|
|
47002
|
+
if ("unreached" in answered) {
|
|
47003
|
+
return this.createErrorResponse(`The debugger of the running game did not answer: ${String(answered.unreached)}.`, [
|
|
47004
|
+
"editor_run start starts the game again, with a debugger",
|
|
47005
|
+
"editor_status says whether it is still running"
|
|
47006
|
+
]);
|
|
47007
|
+
}
|
|
47008
|
+
if (typeof answered["refused"] === "string") {
|
|
47009
|
+
return this.createErrorResponse(answered["refused"]);
|
|
47010
|
+
}
|
|
47011
|
+
const stack = answered["stack"];
|
|
47012
|
+
return this.jsonTextResponse({
|
|
47013
|
+
...answered,
|
|
47014
|
+
...Array.isArray(stack) ? {
|
|
47015
|
+
stack: stack.map((frame) => ({
|
|
47016
|
+
...frame,
|
|
47017
|
+
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join28(projectPath, frame["script"].slice("res://".length)) } : {}
|
|
47018
|
+
}))
|
|
47019
|
+
} : {}
|
|
47020
|
+
});
|
|
47021
|
+
}
|
|
46772
47022
|
breakpointsAsProjectSpells(projectPath, held) {
|
|
46773
47023
|
return held.map((one) => ({
|
|
46774
47024
|
scriptPath: projectSpelling(projectPath, one.scriptPath),
|
|
@@ -47034,7 +47284,7 @@ class GodotServer {
|
|
|
47034
47284
|
return null;
|
|
47035
47285
|
}
|
|
47036
47286
|
try {
|
|
47037
|
-
return settingKeys(
|
|
47287
|
+
return settingKeys(readFileSync24(join28(projectPath, "project.godot"), "utf8"));
|
|
47038
47288
|
} catch {
|
|
47039
47289
|
return null;
|
|
47040
47290
|
}
|
|
@@ -47299,6 +47549,7 @@ class GodotServer {
|
|
|
47299
47549
|
}
|
|
47300
47550
|
async openAnEditor(engine, projectPath, ports, hidden) {
|
|
47301
47551
|
clearEditorLog(projectPath);
|
|
47552
|
+
freeLibraryCopies(projectPath);
|
|
47302
47553
|
const launched = await launchOutsideTheTree({
|
|
47303
47554
|
command: engine,
|
|
47304
47555
|
args: editorArguments(projectPath, ports, editorLogPath(projectPath), hidden),
|
|
@@ -47430,9 +47681,10 @@ class GodotServer {
|
|
|
47430
47681
|
started.announcedBefore = alreadyPlaying;
|
|
47431
47682
|
started.bootScene = runtimeWaitMs.scene;
|
|
47432
47683
|
this.activeProcess = started;
|
|
47433
|
-
const spawnedInstead = this.godotBridge.isConnected() && editorWouldPlay && (given.value.length > 0 || flags.value.length > 0) ?
|
|
47684
|
+
const spawnedInstead = this.godotBridge.isConnected() && editorWouldPlay && (given.value.length > 0 || flags.value.length > 0) ? ` The editor cannot be handed arguments for a game it plays, so this one was started here.${OWN_DEBUGGER}` : this.godotBridge.isConnected() && editorWouldPlay && needsItsOwnProcess(asked.value) ? ` The editor plays a game with its own environment, so a run given one was started here.${OWN_DEBUGGER}` : this.godotBridge.isConnected() && editorWouldPlay && silent ? " The editor cannot play a game silent, which takes an engine flag, so this one was " + `started here${args["silent"] === true ? "" : ", silent because GDHARNESS_SILENT says so"}, and silent: false plays it through the editor.${OWN_DEBUGGER}` : "";
|
|
47434
47685
|
const endedForThis = endedToStartThis(ended);
|
|
47435
47686
|
const stranded = wereHereFirst.filter((one) => one.pid !== ended?.pid && alive(one.pid));
|
|
47687
|
+
const handed = this.dapHoldingBreakpointsOf(project.value.path).breakpointsHeld();
|
|
47436
47688
|
const alsoRunning = leftRunningNote(stranded);
|
|
47437
47689
|
return this.jsonTextResponse({
|
|
47438
47690
|
started: true,
|
|
@@ -47442,6 +47694,7 @@ class GodotServer {
|
|
|
47442
47694
|
arguments: cmdArgs,
|
|
47443
47695
|
argsNote: engineFlagsNote(given.value),
|
|
47444
47696
|
transcript: started.transcript,
|
|
47697
|
+
breakpoints: started.keeper === undefined || handed.length === 0 ? undefined : this.breakpointsAsProjectSpells(project.value.path, handed),
|
|
47445
47698
|
profile: profileOnStart(profiled, "gdharness", runtimeAddonRegistered(project.value.path)),
|
|
47446
47699
|
refreshedClasses: refreshed.value,
|
|
47447
47700
|
...scanWaitAnswer(scanned),
|
|
@@ -47450,12 +47703,12 @@ class GodotServer {
|
|
|
47450
47703
|
previousRunLeft: previousRunLeft(ended),
|
|
47451
47704
|
...previousRunHow(ended),
|
|
47452
47705
|
runtime: await this.runtimeUp(project.value.path, alreadyPlaying, runtimeWaitMs, given.value.length > 0),
|
|
47453
|
-
message: `Use editor_output for what it prints and editor_run stop to end it.${spawnedInstead}${endedForThis}${alsoRunning}`
|
|
47706
|
+
message: `Use editor_output for what it prints and editor_run stop to end it.${started.keeper !== undefined && handed.length > 0 ? " It stops at the lines under breakpoints, and debug_state stack reads where it is held." : ""}${spawnedInstead}${endedForThis}${alsoRunning}`
|
|
47454
47707
|
});
|
|
47455
47708
|
}
|
|
47456
47709
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
47457
47710
|
const budgetMs = budget.ms;
|
|
47458
|
-
if (!
|
|
47711
|
+
if (!existsSync21(join28(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
47459
47712
|
return runtimeVerdict(null, {
|
|
47460
47713
|
addon: false,
|
|
47461
47714
|
budgetMs,
|
|
@@ -47734,7 +47987,7 @@ class GodotServer {
|
|
|
47734
47987
|
return 0;
|
|
47735
47988
|
}
|
|
47736
47989
|
try {
|
|
47737
|
-
return reportAlreadyInTranscript(
|
|
47990
|
+
return reportAlreadyInTranscript(readFileSync24(game.transcript), readFileSync24(report));
|
|
47738
47991
|
} catch (error) {
|
|
47739
47992
|
this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
|
|
47740
47993
|
return 0;
|
|
@@ -47977,7 +48230,8 @@ class GodotServer {
|
|
|
47977
48230
|
startedAt,
|
|
47978
48231
|
projectPath,
|
|
47979
48232
|
...this.servedBy(),
|
|
47980
|
-
...profile === undefined ? {} : { profile }
|
|
48233
|
+
...profile === undefined ? {} : { profile },
|
|
48234
|
+
breakpoints: this.breakpointsForAGame(projectPath)
|
|
47981
48235
|
},
|
|
47982
48236
|
...desktop === undefined ? {} : { desktop }
|
|
47983
48237
|
});
|
|
@@ -48413,7 +48667,7 @@ class GodotServer {
|
|
|
48413
48667
|
}
|
|
48414
48668
|
const going = await this.runStillGoing(run);
|
|
48415
48669
|
const projectPath = run.projectPath;
|
|
48416
|
-
const projectGone = projectPath !== null && !
|
|
48670
|
+
const projectGone = projectPath !== null && !existsSync21(join28(projectPath, "project.godot"));
|
|
48417
48671
|
if (projectGone && callersName !== undefined) {
|
|
48418
48672
|
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"]);
|
|
48419
48673
|
}
|
|
@@ -48701,7 +48955,7 @@ class GodotServer {
|
|
|
48701
48955
|
notes.push(endedWithoutACode({
|
|
48702
48956
|
throughEditor: run.throughEditor,
|
|
48703
48957
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
48704
|
-
couldAnnounce: run.projectPath !== null &&
|
|
48958
|
+
couldAnnounce: run.projectPath !== null && existsSync21(join28(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
48705
48959
|
endedHere: typeof run.endedHere === "string",
|
|
48706
48960
|
pickedUp: run.pickedUp === true
|
|
48707
48961
|
}));
|
|
@@ -49266,7 +49520,7 @@ class GodotServer {
|
|
|
49266
49520
|
if (!scriptPath.ok) {
|
|
49267
49521
|
return this.createErrorResponse(scriptPath.reason);
|
|
49268
49522
|
}
|
|
49269
|
-
if (!
|
|
49523
|
+
if (!existsSync21(scriptPath.absolutePath)) {
|
|
49270
49524
|
return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
|
|
49271
49525
|
}
|
|
49272
49526
|
const asked = readNonEmptyString(args, "newScriptPath");
|
|
@@ -49594,7 +49848,7 @@ class GodotServer {
|
|
|
49594
49848
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
49595
49849
|
const run = await this.currentRun();
|
|
49596
49850
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
49597
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !
|
|
49851
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync21(join28(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
49598
49852
|
return null;
|
|
49599
49853
|
}
|
|
49600
49854
|
const already = runtimesAnnounced().running;
|
|
@@ -49679,12 +49933,25 @@ class GodotServer {
|
|
|
49679
49933
|
}
|
|
49680
49934
|
}
|
|
49681
49935
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
49682
|
-
const screenshotPath = screenshotDir ?
|
|
49936
|
+
const screenshotPath = screenshotDir ? join28(screenshotDir, "capture.png") : null;
|
|
49683
49937
|
const staleNote = this.runtimeStaleNote(choice.endpoint);
|
|
49684
49938
|
const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
|
|
49685
49939
|
const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
|
|
49686
49940
|
|
|
49687
49941
|
${staleNote}`);
|
|
49942
|
+
const onGame = {
|
|
49943
|
+
pid: choice.endpoint.pid,
|
|
49944
|
+
what: `${asked.tool} ${asked.op}`,
|
|
49945
|
+
alongside: new Set
|
|
49946
|
+
};
|
|
49947
|
+
for (const other of this.callsOnGames) {
|
|
49948
|
+
if (other.pid === onGame.pid) {
|
|
49949
|
+
other.alongside.add(onGame.what);
|
|
49950
|
+
onGame.alongside.add(other.what);
|
|
49951
|
+
}
|
|
49952
|
+
}
|
|
49953
|
+
this.callsOnGames.add(onGame);
|
|
49954
|
+
const alongside = () => onGame.alongside.size === 0 ? {} : { ranAlongside: [...onGame.alongside], alongsideNote: ALONGSIDE_NOTE };
|
|
49688
49955
|
try {
|
|
49689
49956
|
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs, !expectsScreenshot);
|
|
49690
49957
|
if (!reply.ok && reply.requestId !== undefined) {
|
|
@@ -49695,7 +49962,8 @@ ${staleNote}`);
|
|
|
49695
49962
|
waitedMs: timeoutMs,
|
|
49696
49963
|
note: `${reply.message} ${longerWaitNote(asked.tool, asked.op, timeoutMs)}`,
|
|
49697
49964
|
...answeredBy,
|
|
49698
|
-
...stale
|
|
49965
|
+
...stale,
|
|
49966
|
+
...alongside()
|
|
49699
49967
|
});
|
|
49700
49968
|
}
|
|
49701
49969
|
if (!reply.ok) {
|
|
@@ -49703,7 +49971,7 @@ ${staleNote}`);
|
|
|
49703
49971
|
}
|
|
49704
49972
|
const { id: _id, ...payload } = reply.payload;
|
|
49705
49973
|
if (!expectsScreenshot) {
|
|
49706
|
-
return this.jsonTextResponse({ ...payload, ...answeredBy, ...stale });
|
|
49974
|
+
return this.jsonTextResponse({ ...payload, ...answeredBy, ...stale, ...alongside() });
|
|
49707
49975
|
}
|
|
49708
49976
|
const returnedPath = readString(payload, "path");
|
|
49709
49977
|
if (readString(payload, "type") !== "screenshot_file" || !returnedPath || !screenshotPath) {
|
|
@@ -49720,18 +49988,22 @@ ${staleNote}`);
|
|
|
49720
49988
|
return this.createErrorResponse(`The capture was taken and could not be saved at ${saveTo}: ${errorMessage(error)}`);
|
|
49721
49989
|
}
|
|
49722
49990
|
}
|
|
49991
|
+
const ranWith = [...onGame.alongside];
|
|
49723
49992
|
return {
|
|
49724
49993
|
content: [
|
|
49725
49994
|
{
|
|
49726
49995
|
type: "text",
|
|
49727
|
-
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}${captureDetail(payload)}${saveTo === null ? "" : `. Saved to ${saveTo}`}${
|
|
49996
|
+
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}${captureDetail(payload)}${saveTo === null ? "" : `. Saved to ${saveTo}`}${ranWith.length === 0 ? "" : `
|
|
49997
|
+
|
|
49998
|
+
Ran alongside ${ranWith.join(", ")}. ${ALONGSIDE_NOTE}`}${staleNote === undefined ? "" : `
|
|
49728
49999
|
|
|
49729
50000
|
${staleNote}`}`
|
|
49730
50001
|
},
|
|
49731
|
-
{ type: "image", data:
|
|
50002
|
+
{ type: "image", data: readFileSync24(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
49732
50003
|
]
|
|
49733
50004
|
};
|
|
49734
50005
|
} finally {
|
|
50006
|
+
this.callsOnGames.delete(onGame);
|
|
49735
50007
|
discard(screenshotDir);
|
|
49736
50008
|
}
|
|
49737
50009
|
}
|
|
@@ -49830,7 +50102,7 @@ async function runGodotServer() {
|
|
|
49830
50102
|
const server = new GodotServer;
|
|
49831
50103
|
await server.run();
|
|
49832
50104
|
}
|
|
49833
|
-
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, EDITOR_FREE_INSTEAD, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
50105
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __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, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, EDITOR_FREE_INSTEAD, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
49834
50106
|
var init_server = __esm(() => {
|
|
49835
50107
|
init_dist();
|
|
49836
50108
|
init_stdio();
|
|
@@ -49849,6 +50121,7 @@ var init_server = __esm(() => {
|
|
|
49849
50121
|
init_editor_log();
|
|
49850
50122
|
init_engine_run();
|
|
49851
50123
|
init_errors();
|
|
50124
|
+
init_game_debugger();
|
|
49852
50125
|
init_game_log();
|
|
49853
50126
|
init_gdscript_source();
|
|
49854
50127
|
init_godot_bridge();
|
|
@@ -49859,6 +50132,7 @@ var init_server = __esm(() => {
|
|
|
49859
50132
|
init_junit();
|
|
49860
50133
|
init_keeper_channel();
|
|
49861
50134
|
init_launch();
|
|
50135
|
+
init_library_copies();
|
|
49862
50136
|
init_lifecycle_log();
|
|
49863
50137
|
init_lsp_client();
|
|
49864
50138
|
init_outside();
|
|
@@ -49885,7 +50159,8 @@ var init_server = __esm(() => {
|
|
|
49885
50159
|
init_tool_reference();
|
|
49886
50160
|
init_update_check();
|
|
49887
50161
|
run4 = promisify5(execFile5);
|
|
49888
|
-
__dirname2 =
|
|
50162
|
+
__dirname2 = dirname21(fileURLToPath5(import.meta.url));
|
|
50163
|
+
KEEPER_DEBUG_MS = PAUSE_MS + STEP_MS + 2 * READ_MS;
|
|
49889
50164
|
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
49890
50165
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
49891
50166
|
VALUE_NAMES = {
|
|
@@ -49943,8 +50218,8 @@ var init_server = __esm(() => {
|
|
|
49943
50218
|
init_bridge_announce();
|
|
49944
50219
|
init_errors();
|
|
49945
50220
|
init_godot_path();
|
|
49946
|
-
import { existsSync as
|
|
49947
|
-
import { join as
|
|
50221
|
+
import { existsSync as existsSync22 } from "node:fs";
|
|
50222
|
+
import { join as join29, resolve as resolve8 } from "node:path";
|
|
49948
50223
|
|
|
49949
50224
|
// src/harnesses.ts
|
|
49950
50225
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -50909,10 +51184,10 @@ init_setup();
|
|
|
50909
51184
|
init_setup();
|
|
50910
51185
|
init_tool_definitions();
|
|
50911
51186
|
init_tool_reference();
|
|
50912
|
-
import { existsSync as
|
|
50913
|
-
import { basename as
|
|
51187
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync8, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
51188
|
+
import { basename as basename5, dirname as dirname7, join as join13 } from "node:path";
|
|
50914
51189
|
var SKILL_NAME = "gdharness";
|
|
50915
|
-
var SHARED_SKILLS =
|
|
51190
|
+
var SHARED_SKILLS = join13(".agents", "skills");
|
|
50916
51191
|
function skillMarkdown(version) {
|
|
50917
51192
|
return `---
|
|
50918
51193
|
name: ${SKILL_NAME}
|
|
@@ -51118,27 +51393,27 @@ comes back separately, under \`engine_messages_after_answer\`: none of it change
|
|
|
51118
51393
|
function skillFiles(version) {
|
|
51119
51394
|
return new Map([
|
|
51120
51395
|
["SKILL.md", skillMarkdown(version)],
|
|
51121
|
-
[
|
|
51396
|
+
[join13("references", "tools.md"), renderToolsMarkdown()]
|
|
51122
51397
|
]);
|
|
51123
51398
|
}
|
|
51124
51399
|
function skillDirectories(harnesses, projectPath, exists) {
|
|
51125
|
-
const directories = [
|
|
51400
|
+
const directories = [join13(projectPath, SHARED_SKILLS, SKILL_NAME)];
|
|
51126
51401
|
for (const harness of harnesses) {
|
|
51127
51402
|
if (harness.skills === undefined) {
|
|
51128
51403
|
continue;
|
|
51129
51404
|
}
|
|
51130
|
-
const own =
|
|
51131
|
-
if ((!harness.skills.shared || exists(own)) && !directories.includes(
|
|
51132
|
-
directories.push(
|
|
51405
|
+
const own = join13(projectPath, harness.skills.dir);
|
|
51406
|
+
if ((!harness.skills.shared || exists(own)) && !directories.includes(join13(own, SKILL_NAME))) {
|
|
51407
|
+
directories.push(join13(own, SKILL_NAME));
|
|
51133
51408
|
}
|
|
51134
51409
|
}
|
|
51135
51410
|
return directories;
|
|
51136
51411
|
}
|
|
51137
51412
|
function everySkillDirectory(projectPath, harnesses) {
|
|
51138
|
-
const directories = new Set([
|
|
51413
|
+
const directories = new Set([join13(projectPath, SHARED_SKILLS, SKILL_NAME)]);
|
|
51139
51414
|
for (const harness of harnesses) {
|
|
51140
51415
|
if (harness.skills !== undefined) {
|
|
51141
|
-
directories.add(
|
|
51416
|
+
directories.add(join13(projectPath, harness.skills.dir, SKILL_NAME));
|
|
51142
51417
|
}
|
|
51143
51418
|
}
|
|
51144
51419
|
return [...directories];
|
|
@@ -51146,33 +51421,33 @@ function everySkillDirectory(projectPath, harnesses) {
|
|
|
51146
51421
|
function removeSkill(directories, projectPath) {
|
|
51147
51422
|
const removed = [];
|
|
51148
51423
|
for (const directory of directories) {
|
|
51149
|
-
if (
|
|
51424
|
+
if (existsSync13(directory)) {
|
|
51150
51425
|
rmSync6(directory, { recursive: true, force: true });
|
|
51151
|
-
pruneEmpty(
|
|
51426
|
+
pruneEmpty(dirname7(directory), projectPath);
|
|
51152
51427
|
removed.push(directory);
|
|
51153
51428
|
}
|
|
51154
51429
|
}
|
|
51155
51430
|
return removed;
|
|
51156
51431
|
}
|
|
51157
51432
|
function pruneEmpty(directory, projectPath) {
|
|
51158
|
-
const shared =
|
|
51433
|
+
const shared = join13(projectPath, ".agents");
|
|
51159
51434
|
let path = directory;
|
|
51160
|
-
while (path !== projectPath && (
|
|
51161
|
-
if (
|
|
51435
|
+
while (path !== projectPath && (basename5(path) === "skills" || path === shared)) {
|
|
51436
|
+
if (readdirSync8(path).length > 0) {
|
|
51162
51437
|
return;
|
|
51163
51438
|
}
|
|
51164
51439
|
rmSync6(path, { recursive: true, force: true });
|
|
51165
|
-
path =
|
|
51440
|
+
path = dirname7(path);
|
|
51166
51441
|
}
|
|
51167
51442
|
}
|
|
51168
51443
|
function writeSkill(directories, version) {
|
|
51169
51444
|
const files = skillFiles(version);
|
|
51170
51445
|
return directories.map((directory) => {
|
|
51171
|
-
const replaced =
|
|
51446
|
+
const replaced = existsSync13(directory);
|
|
51172
51447
|
rmSync6(directory, { recursive: true, force: true });
|
|
51173
51448
|
for (const [name, contents] of files) {
|
|
51174
|
-
const path =
|
|
51175
|
-
mkdirSync5(
|
|
51449
|
+
const path = join13(directory, name);
|
|
51450
|
+
mkdirSync5(dirname7(path), { recursive: true });
|
|
51176
51451
|
writeFileSync7(path, contents, "utf8");
|
|
51177
51452
|
}
|
|
51178
51453
|
return { path: directory, replaced };
|
|
@@ -51180,18 +51455,18 @@ function writeSkill(directories, version) {
|
|
|
51180
51455
|
}
|
|
51181
51456
|
|
|
51182
51457
|
// src/version.ts
|
|
51183
|
-
import { readFileSync as
|
|
51184
|
-
import { dirname as
|
|
51458
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
51459
|
+
import { dirname as dirname8, join as join14 } from "node:path";
|
|
51185
51460
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
51186
51461
|
function getLocalVersion() {
|
|
51187
|
-
const currentDirectory =
|
|
51462
|
+
const currentDirectory = dirname8(fileURLToPath2(import.meta.url));
|
|
51188
51463
|
const packageCandidates = [
|
|
51189
|
-
|
|
51190
|
-
|
|
51464
|
+
join14(currentDirectory, "..", "package.json"),
|
|
51465
|
+
join14(currentDirectory, "..", "..", "package.json")
|
|
51191
51466
|
];
|
|
51192
51467
|
for (const packagePath of packageCandidates) {
|
|
51193
51468
|
try {
|
|
51194
|
-
const value = JSON.parse(
|
|
51469
|
+
const value = JSON.parse(readFileSync12(packagePath, "utf-8"));
|
|
51195
51470
|
if (typeof value === "object" && value !== null && "name" in value && value.name === "gdharness" && "version" in value && typeof value.version === "string") {
|
|
51196
51471
|
return value.version;
|
|
51197
51472
|
}
|
|
@@ -51209,7 +51484,7 @@ class UsageError extends Refusal {
|
|
|
51209
51484
|
function projectArgument(at) {
|
|
51210
51485
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
51211
51486
|
const projectPath = resolve8(named ?? ".");
|
|
51212
|
-
if (!
|
|
51487
|
+
if (!existsSync22(join29(projectPath, "project.godot"))) {
|
|
51213
51488
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
51214
51489
|
}
|
|
51215
51490
|
return projectPath;
|
|
@@ -51254,7 +51529,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
51254
51529
|
return;
|
|
51255
51530
|
}
|
|
51256
51531
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
51257
|
-
if (!
|
|
51532
|
+
if (!existsSync22(join29(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
51258
51533
|
console.log(` that file is not in this project, so it boots with a missing script: restore it, or point the entry at res://${RUNTIME_AUTOLOAD.path}`);
|
|
51259
51534
|
}
|
|
51260
51535
|
}
|
|
@@ -51386,7 +51661,7 @@ async function setup() {
|
|
|
51386
51661
|
console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
|
|
51387
51662
|
}
|
|
51388
51663
|
if (!args.includes("--no-skill")) {
|
|
51389
|
-
const directories = skillDirectories(chosen.harnesses, projectPath,
|
|
51664
|
+
const directories = skillDirectories(chosen.harnesses, projectPath, existsSync22);
|
|
51390
51665
|
for (const written of writeSkill(directories, getLocalVersion())) {
|
|
51391
51666
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
51392
51667
|
}
|
|
@@ -51500,7 +51775,7 @@ async function upgrade() {
|
|
|
51500
51775
|
moved.push(took.from);
|
|
51501
51776
|
}
|
|
51502
51777
|
}
|
|
51503
|
-
for (const written of writeSkill(skillDirectories(already, projectPath,
|
|
51778
|
+
for (const written of writeSkill(skillDirectories(already, projectPath, existsSync22), version)) {
|
|
51504
51779
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
51505
51780
|
}
|
|
51506
51781
|
console.log(`
|