gdharness 1.2.37 → 1.2.39
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 +943 -491
- package/build/godot/addons/gdharness_editor/headless_check.gd +41 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +86 -23
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +20 -7
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +44 -6
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +7 -4
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +10 -0
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +8 -4
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +5 -0
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +2 -2
- package/build/godot/operations/godot_operations.gd +10 -3
- package/build/index.js +4521 -4072
- package/build/keeper.js +1 -0
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -8181,6 +8181,9 @@ class GameLog {
|
|
|
8181
8181
|
count(severity) {
|
|
8182
8182
|
return this.entries.filter((entry) => entry.severity === severity).length;
|
|
8183
8183
|
}
|
|
8184
|
+
crashReport() {
|
|
8185
|
+
return this.entries.find((entry) => CRASH.test(entry.text));
|
|
8186
|
+
}
|
|
8184
8187
|
everything() {
|
|
8185
8188
|
return this.entries;
|
|
8186
8189
|
}
|
|
@@ -8260,21 +8263,98 @@ function runEngine(executable, args, options = {}) {
|
|
|
8260
8263
|
});
|
|
8261
8264
|
});
|
|
8262
8265
|
}
|
|
8266
|
+
function exitCodeText(code) {
|
|
8267
|
+
if (code === null) {
|
|
8268
|
+
return "exit code unknown";
|
|
8269
|
+
}
|
|
8270
|
+
if (code < 3221225472) {
|
|
8271
|
+
return `exit code ${code}`;
|
|
8272
|
+
}
|
|
8273
|
+
const hex = `0x${code.toString(16).toUpperCase()}`;
|
|
8274
|
+
return `exit code ${hex}, ${WINDOWS_STATUSES.get(code) ?? "a Windows error status"}`;
|
|
8275
|
+
}
|
|
8263
8276
|
function howItEnded(run) {
|
|
8264
8277
|
if (run.failure !== null) {
|
|
8265
8278
|
return run.exitSignal === null ? run.failure : `ended by ${run.exitSignal}, because ${run.failure}`;
|
|
8266
8279
|
}
|
|
8267
|
-
return run.exitSignal === null ?
|
|
8280
|
+
return run.exitSignal === null ? exitCodeText(run.exitCode) : `ended by ${run.exitSignal}`;
|
|
8268
8281
|
}
|
|
8282
|
+
var WINDOWS_STATUSES;
|
|
8269
8283
|
var init_engine_run = __esm(() => {
|
|
8270
8284
|
init_call_signal();
|
|
8271
8285
|
init_game_log();
|
|
8272
8286
|
init_spawn();
|
|
8287
|
+
WINDOWS_STATUSES = new Map([
|
|
8288
|
+
[3221225477, "an access violation, so it crashed"],
|
|
8289
|
+
[3221225501, "an illegal instruction, so it crashed"],
|
|
8290
|
+
[3221225620, "an integer division by zero, so it crashed"],
|
|
8291
|
+
[3221225725, "a stack overflow, so it crashed"],
|
|
8292
|
+
[3221225781, "a library it needs was not found, so it never started"],
|
|
8293
|
+
[3221225786, "its console was closed or sent Ctrl+C"],
|
|
8294
|
+
[3221226356, "a corrupted heap, so it crashed"],
|
|
8295
|
+
[3221226505, "a stack buffer overrun or a fast fail, so it crashed"]
|
|
8296
|
+
]);
|
|
8273
8297
|
});
|
|
8274
8298
|
|
|
8275
|
-
// src/
|
|
8276
|
-
import { existsSync as existsSync5, readdirSync as readdirSync3,
|
|
8299
|
+
// src/doc-cache.ts
|
|
8300
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
8301
|
+
import { homedir as homedir3 } from "node:os";
|
|
8277
8302
|
import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute2, join as join5 } from "node:path";
|
|
8303
|
+
import process5 from "node:process";
|
|
8304
|
+
function editorCacheDirectory(godotPath, platform = process5.platform, env = process5.env, home = homedir3()) {
|
|
8305
|
+
const beside = dirname4(godotPath);
|
|
8306
|
+
const bundle = platform === "darwin" && basename3(beside) === "MacOS" && basename3(dirname4(beside)) === "Contents" ? dirname4(dirname4(dirname4(beside))) : null;
|
|
8307
|
+
for (const folder of bundle === null ? [beside] : [beside, bundle]) {
|
|
8308
|
+
if (existsSync5(join5(folder, "._sc_")) || existsSync5(join5(folder, "_sc_"))) {
|
|
8309
|
+
return join5(folder, "editor_data", "cache");
|
|
8310
|
+
}
|
|
8311
|
+
}
|
|
8312
|
+
if (platform === "win32") {
|
|
8313
|
+
const set = (name) => env[name] === "" ? undefined : env[name];
|
|
8314
|
+
return join5(set("LOCALAPPDATA") ?? set("TEMP") ?? join5(home, "AppData", "Local"), "Godot");
|
|
8315
|
+
}
|
|
8316
|
+
if (platform === "darwin") {
|
|
8317
|
+
return join5(home, "Library", "Caches", "Godot");
|
|
8318
|
+
}
|
|
8319
|
+
const xdg = env["XDG_CACHE_HOME"];
|
|
8320
|
+
return join5(xdg !== undefined && isAbsolute2(xdg) ? xdg : join5(home, ".cache"), "godot");
|
|
8321
|
+
}
|
|
8322
|
+
function docCachesWritten(directory) {
|
|
8323
|
+
const written = new Map;
|
|
8324
|
+
let names;
|
|
8325
|
+
try {
|
|
8326
|
+
names = readdirSync3(directory);
|
|
8327
|
+
} catch {
|
|
8328
|
+
return written;
|
|
8329
|
+
}
|
|
8330
|
+
for (const name of names.filter((one) => CACHE_FILE.test(one))) {
|
|
8331
|
+
try {
|
|
8332
|
+
written.set(join5(directory, name), statSync4(join5(directory, name)).mtimeMs);
|
|
8333
|
+
} catch {}
|
|
8334
|
+
}
|
|
8335
|
+
return written;
|
|
8336
|
+
}
|
|
8337
|
+
function docCacheCrash(ran, before, after, what) {
|
|
8338
|
+
const report = ran.log.crashReport();
|
|
8339
|
+
if (ran.exitCode === 0 && ran.exitSignal === null && report === undefined || ran.failure !== null) {
|
|
8340
|
+
return null;
|
|
8341
|
+
}
|
|
8342
|
+
const rewritten = [...after].find(([path, at]) => before.get(path) !== at)?.[0];
|
|
8343
|
+
if (rewritten === undefined) {
|
|
8344
|
+
return null;
|
|
8345
|
+
}
|
|
8346
|
+
const said = report === undefined ? "" : `, and its crash handler printed "${report.text}"`;
|
|
8347
|
+
return `The first ${what} ended badly (${howItEnded(ran)}${said}) after rebuilding the editor's class reference ` + `cache, ${rewritten}. Godot crashes as an import or an export exits when it finds that cache ` + "stale, which it is whenever another build of the same minor version wrote it last, such as 4.7.1 " + `beside 4.7.2, and the crashing run writes a fresh one. So the ${what} was run again, and this answer ` + "is from that second run.";
|
|
8348
|
+
}
|
|
8349
|
+
var CACHE_FILE;
|
|
8350
|
+
var init_doc_cache = __esm(() => {
|
|
8351
|
+
init_engine_run();
|
|
8352
|
+
CACHE_FILE = /^editor_doc_cache-\d+\.\d+\.res$/;
|
|
8353
|
+
});
|
|
8354
|
+
|
|
8355
|
+
// src/library-copies.ts
|
|
8356
|
+
import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync3, renameSync, unlinkSync } from "node:fs";
|
|
8357
|
+
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute3, join as join6 } from "node:path";
|
|
8278
8358
|
function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
8279
8359
|
const setAside = [];
|
|
8280
8360
|
const swept = [];
|
|
@@ -8282,26 +8362,26 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
8282
8362
|
return { setAside, swept };
|
|
8283
8363
|
}
|
|
8284
8364
|
for (const library of windowsLibraries(projectPath)) {
|
|
8285
|
-
const folder =
|
|
8286
|
-
const copy = `~${
|
|
8287
|
-
if (
|
|
8288
|
-
const aside =
|
|
8365
|
+
const folder = dirname5(library);
|
|
8366
|
+
const copy = `~${basename4(library)}`;
|
|
8367
|
+
if (existsSync6(join6(folder, copy))) {
|
|
8368
|
+
const aside = join6(folder, `${copy}.aside-${process.pid}-${Date.now()}`);
|
|
8289
8369
|
try {
|
|
8290
|
-
renameSync(
|
|
8370
|
+
renameSync(join6(folder, copy), aside);
|
|
8291
8371
|
setAside.push(aside);
|
|
8292
8372
|
} catch {}
|
|
8293
8373
|
}
|
|
8294
8374
|
let names;
|
|
8295
8375
|
try {
|
|
8296
|
-
names =
|
|
8376
|
+
names = readdirSync4(folder);
|
|
8297
8377
|
} catch {
|
|
8298
8378
|
continue;
|
|
8299
8379
|
}
|
|
8300
8380
|
for (const name of names) {
|
|
8301
8381
|
if (name.length > copy.length && name.toLowerCase().startsWith(copy.toLowerCase())) {
|
|
8302
8382
|
try {
|
|
8303
|
-
unlinkSync(
|
|
8304
|
-
swept.push(
|
|
8383
|
+
unlinkSync(join6(folder, name));
|
|
8384
|
+
swept.push(join6(folder, name));
|
|
8305
8385
|
} catch {}
|
|
8306
8386
|
}
|
|
8307
8387
|
}
|
|
@@ -8309,7 +8389,7 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
8309
8389
|
return { setAside, swept };
|
|
8310
8390
|
}
|
|
8311
8391
|
function windowsLibraries(projectPath) {
|
|
8312
|
-
const listed = readOrNull(
|
|
8392
|
+
const listed = readOrNull(join6(projectPath, ".godot", "extension_list.cfg"));
|
|
8313
8393
|
if (listed === null) {
|
|
8314
8394
|
return [];
|
|
8315
8395
|
}
|
|
@@ -8328,7 +8408,7 @@ function windowsLibraries(projectPath) {
|
|
|
8328
8408
|
continue;
|
|
8329
8409
|
}
|
|
8330
8410
|
const named = inLibraries ? /^\s*windows[\w.]*\s*=\s*"([^"]+)"/.exec(row)?.[1] : undefined;
|
|
8331
|
-
const library = named === undefined ? null : resolveResource(projectPath,
|
|
8411
|
+
const library = named === undefined ? null : resolveResource(projectPath, dirname5(manifest), named);
|
|
8332
8412
|
if (library !== null) {
|
|
8333
8413
|
libraries.add(library);
|
|
8334
8414
|
}
|
|
@@ -8341,9 +8421,9 @@ function resolveResource(projectPath, relativeTo, path) {
|
|
|
8341
8421
|
return null;
|
|
8342
8422
|
}
|
|
8343
8423
|
if (path.startsWith("res://")) {
|
|
8344
|
-
return
|
|
8424
|
+
return join6(projectPath, path.slice("res://".length));
|
|
8345
8425
|
}
|
|
8346
|
-
return
|
|
8426
|
+
return isAbsolute3(path) ? path : join6(relativeTo, path);
|
|
8347
8427
|
}
|
|
8348
8428
|
function readOrNull(path) {
|
|
8349
8429
|
try {
|
|
@@ -8356,33 +8436,33 @@ var init_library_copies = () => {};
|
|
|
8356
8436
|
|
|
8357
8437
|
// src/reimport.ts
|
|
8358
8438
|
import { createHash } from "node:crypto";
|
|
8359
|
-
import { existsSync as
|
|
8360
|
-
import { basename as
|
|
8439
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4, rmSync as rmSync3, statSync as statSync5, utimesSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8440
|
+
import { basename as basename5, join as join7 } from "node:path";
|
|
8361
8441
|
function forceNextImport(projectPath, resourcePath) {
|
|
8362
8442
|
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
8363
|
-
const sidecar =
|
|
8364
|
-
if (!
|
|
8443
|
+
const sidecar = join7(projectPath, `${relative}.import`);
|
|
8444
|
+
if (!existsSync7(sidecar)) {
|
|
8365
8445
|
return;
|
|
8366
8446
|
}
|
|
8367
|
-
const { atime, mtimeMs } =
|
|
8447
|
+
const { atime, mtimeMs } = statSync5(sidecar);
|
|
8368
8448
|
writeFileSync3(sidecar, readFileSync4(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
8369
8449
|
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
8370
8450
|
rmSync3(importRecord(projectPath, resourcePath), { force: true });
|
|
8371
8451
|
}
|
|
8372
8452
|
function importRecord(projectPath, resourcePath) {
|
|
8373
8453
|
const digest = createHash("md5").update(resourcePath).digest("hex");
|
|
8374
|
-
return
|
|
8454
|
+
return join7(projectPath, ".godot", "imported", `${basename5(resourcePath)}-${digest}.md5`);
|
|
8375
8455
|
}
|
|
8376
8456
|
var init_reimport = () => {};
|
|
8377
8457
|
|
|
8378
8458
|
// src/alive.ts
|
|
8379
|
-
import
|
|
8459
|
+
import process6 from "node:process";
|
|
8380
8460
|
function alive(pid) {
|
|
8381
8461
|
if (pid === undefined || pid === null) {
|
|
8382
8462
|
return false;
|
|
8383
8463
|
}
|
|
8384
8464
|
try {
|
|
8385
|
-
|
|
8465
|
+
process6.kill(pid, 0);
|
|
8386
8466
|
return true;
|
|
8387
8467
|
} catch (error) {
|
|
8388
8468
|
return error.code === "EPERM";
|
|
@@ -8391,17 +8471,17 @@ function alive(pid) {
|
|
|
8391
8471
|
var init_alive = () => {};
|
|
8392
8472
|
|
|
8393
8473
|
// src/scratch.ts
|
|
8394
|
-
import { existsSync as
|
|
8474
|
+
import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync5, rmSync as rmSync4 } from "node:fs";
|
|
8395
8475
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
8396
|
-
import { join as
|
|
8397
|
-
import
|
|
8476
|
+
import { join as join8 } from "node:path";
|
|
8477
|
+
import process7 from "node:process";
|
|
8398
8478
|
function scratchDirectory(kind) {
|
|
8399
|
-
return mkdtempSync(
|
|
8479
|
+
return mkdtempSync(join8(tmpdir2(), `gdharness-${kind}-${process7.pid}-`));
|
|
8400
8480
|
}
|
|
8401
8481
|
function sweepAbandonedScratch(directory = tmpdir2()) {
|
|
8402
8482
|
let entries;
|
|
8403
8483
|
try {
|
|
8404
|
-
entries =
|
|
8484
|
+
entries = readdirSync5(directory);
|
|
8405
8485
|
} catch {
|
|
8406
8486
|
return;
|
|
8407
8487
|
}
|
|
@@ -8412,7 +8492,7 @@ function sweepAbandonedScratch(directory = tmpdir2()) {
|
|
|
8412
8492
|
continue;
|
|
8413
8493
|
}
|
|
8414
8494
|
try {
|
|
8415
|
-
remove(
|
|
8495
|
+
remove(join8(directory, entry));
|
|
8416
8496
|
} catch {}
|
|
8417
8497
|
}
|
|
8418
8498
|
}
|
|
@@ -8450,7 +8530,7 @@ function tryAgain(path, attempt) {
|
|
|
8450
8530
|
lastTryOnExit();
|
|
8451
8531
|
}
|
|
8452
8532
|
const after = AGAIN_AFTER_MS[attempt];
|
|
8453
|
-
if (after === undefined || !
|
|
8533
|
+
if (after === undefined || !existsSync8(path)) {
|
|
8454
8534
|
waiting.delete(path);
|
|
8455
8535
|
if (after === undefined) {
|
|
8456
8536
|
atExit.add(path);
|
|
@@ -8471,7 +8551,7 @@ function lastTryOnExit() {
|
|
|
8471
8551
|
return;
|
|
8472
8552
|
}
|
|
8473
8553
|
lastTryWatched = true;
|
|
8474
|
-
|
|
8554
|
+
process7.once("exit", () => {
|
|
8475
8555
|
for (const path of [...waiting, ...atExit]) {
|
|
8476
8556
|
try {
|
|
8477
8557
|
remove(path);
|
|
@@ -8498,8 +8578,8 @@ var init_scratch = __esm(() => {
|
|
|
8498
8578
|
});
|
|
8499
8579
|
|
|
8500
8580
|
// src/headless.ts
|
|
8501
|
-
import { existsSync as
|
|
8502
|
-
import { dirname as
|
|
8581
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, realpathSync as realpathSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8582
|
+
import { dirname as dirname6, join as join9 } from "node:path";
|
|
8503
8583
|
function snakeCased(params) {
|
|
8504
8584
|
const result = emptyRecord();
|
|
8505
8585
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -8560,7 +8640,7 @@ function refusalsIn(log) {
|
|
|
8560
8640
|
}
|
|
8561
8641
|
function scriptFault(problems, script) {
|
|
8562
8642
|
const spelled = (path) => `(${path.replaceAll("\\", "/").toLowerCase()}/`;
|
|
8563
|
-
const directories = [...new Set([spelled(
|
|
8643
|
+
const directories = [...new Set([spelled(dirname6(script)), spelled(resolvedOr(dirname6(script)))])];
|
|
8564
8644
|
return problems.find((entry) => {
|
|
8565
8645
|
const at = entry.detail.find((line) => line.startsWith("at: "))?.replaceAll("\\", "/").toLowerCase();
|
|
8566
8646
|
return entry.severity === "error" && at !== undefined && directories.some((directory) => at.includes(directory));
|
|
@@ -8598,26 +8678,33 @@ async function runImport(godotPath, projectPath, options = {}) {
|
|
|
8598
8678
|
};
|
|
8599
8679
|
const first = await pass();
|
|
8600
8680
|
const collided = librariesNotCopied(first.messages);
|
|
8601
|
-
|
|
8681
|
+
const crash = first.docCacheNote;
|
|
8682
|
+
if (collided.length === 0 && crash === undefined) {
|
|
8602
8683
|
return first;
|
|
8603
8684
|
}
|
|
8604
|
-
const
|
|
8685
|
+
const { docCacheNote: _, ...again } = await pass();
|
|
8686
|
+
const second = again.ok || crash === undefined ? again : { ...again, message: `${again.message.replace(/[.\s]+$/, "")}. ${crash}` };
|
|
8605
8687
|
const still = librariesNotCopied(second.messages);
|
|
8606
8688
|
return {
|
|
8607
8689
|
...second,
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8690
|
+
...collided.length === 0 ? {} : {
|
|
8691
|
+
librariesRetried: collided,
|
|
8692
|
+
...still.length === 0 ? {} : { librariesNotLoaded: still },
|
|
8693
|
+
extensionNote: extensionRetryNote(collided, still)
|
|
8694
|
+
},
|
|
8695
|
+
...crash === undefined ? {} : { docCacheNote: crash }
|
|
8611
8696
|
};
|
|
8612
8697
|
}
|
|
8613
8698
|
async function importOnce(godotPath, projectPath) {
|
|
8614
8699
|
const logDir = scratchDirectory("import");
|
|
8700
|
+
const caches = editorCacheDirectory(godotPath);
|
|
8701
|
+
const cachesBefore = docCachesWritten(caches);
|
|
8615
8702
|
let ran;
|
|
8616
8703
|
try {
|
|
8617
8704
|
ran = await runEngine(godotPath, [
|
|
8618
8705
|
"--headless",
|
|
8619
8706
|
"--log-file",
|
|
8620
|
-
|
|
8707
|
+
join9(logDir, "engine.log"),
|
|
8621
8708
|
"--path",
|
|
8622
8709
|
projectPath,
|
|
8623
8710
|
"--import"
|
|
@@ -8626,24 +8713,26 @@ async function importOnce(godotPath, projectPath) {
|
|
|
8626
8713
|
discard(logDir);
|
|
8627
8714
|
}
|
|
8628
8715
|
const problems = problemsIn(ran.log);
|
|
8716
|
+
const crash = docCacheCrash(ran, cachesBefore, docCachesWritten(caches), "import");
|
|
8629
8717
|
if (ran.exitCode === 0 && ran.failure === null) {
|
|
8630
|
-
return { ok: true, ...reported2(problems) };
|
|
8718
|
+
return { ok: true, ...reported2(problems), ...crash === null ? {} : { docCacheNote: crash } };
|
|
8631
8719
|
}
|
|
8632
8720
|
return {
|
|
8633
8721
|
ok: false,
|
|
8634
8722
|
message: `the import pass failed (${howItEnded(ran)}): ${reason(ran.log, problems)}`,
|
|
8635
|
-
...reported2(problems)
|
|
8723
|
+
...reported2(problems),
|
|
8724
|
+
...crash === null ? {} : { docCacheNote: crash }
|
|
8636
8725
|
};
|
|
8637
8726
|
}
|
|
8638
8727
|
async function runOperation(engine, operation, params, projectPath) {
|
|
8639
8728
|
const paramsDir = scratchDirectory("params");
|
|
8640
|
-
const paramsFile =
|
|
8641
|
-
const answerFile =
|
|
8729
|
+
const paramsFile = join9(paramsDir, `${operation}.json`);
|
|
8730
|
+
const answerFile = join9(paramsDir, "answer.json");
|
|
8642
8731
|
writeFileSync4(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
8643
8732
|
const args = [
|
|
8644
8733
|
"--headless",
|
|
8645
8734
|
"--log-file",
|
|
8646
|
-
|
|
8735
|
+
join9(paramsDir, "engine.log"),
|
|
8647
8736
|
"--path",
|
|
8648
8737
|
projectPath,
|
|
8649
8738
|
"--script",
|
|
@@ -8731,7 +8820,7 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
8731
8820
|
};
|
|
8732
8821
|
}
|
|
8733
8822
|
function answerIn(file) {
|
|
8734
|
-
if (!
|
|
8823
|
+
if (!existsSync9(file)) {
|
|
8735
8824
|
return { kind: "none" };
|
|
8736
8825
|
}
|
|
8737
8826
|
const text = readFileSync5(file, "utf8");
|
|
@@ -8747,6 +8836,7 @@ function answerIn(file) {
|
|
|
8747
8836
|
}
|
|
8748
8837
|
var AFTER_ANSWER_NOTE = "Printed after this operation had written its answer, while the engine ran the project's autoloads and shut down: Godot starts a project's autoloads in every script run and cannot be told not to. An error raised in a project script here is that autoload's own, and none of these changes the answer.", BEFORE_OPERATION_NOTE = "Printed while the engine started the project's autoloads, before this operation ran: an operation that compiles the project's scripts waits for them, because the engine knows an autoload's name only once it is in the tree. An error raised in a project script here is that autoload's own.", OWN_ERROR = "[ERROR] ", ANSWERED = "[gdharness] answered", WAITING = "[gdharness] waiting for the autoloads", BEGUN = "[gdharness] begun", MESSAGES_KEPT = 200, ERRORS_NAMED = 3;
|
|
8749
8838
|
var init_headless = __esm(() => {
|
|
8839
|
+
init_doc_cache();
|
|
8750
8840
|
init_engine_run();
|
|
8751
8841
|
init_library_copies();
|
|
8752
8842
|
init_reimport();
|
|
@@ -8754,9 +8844,9 @@ var init_headless = __esm(() => {
|
|
|
8754
8844
|
});
|
|
8755
8845
|
|
|
8756
8846
|
// src/update-check.ts
|
|
8757
|
-
import { existsSync as
|
|
8758
|
-
import { homedir as
|
|
8759
|
-
import { join as
|
|
8847
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
8848
|
+
import { homedir as homedir4, tmpdir as tmpdir3 } from "node:os";
|
|
8849
|
+
import { join as join10 } from "node:path";
|
|
8760
8850
|
function packageUrl(root) {
|
|
8761
8851
|
return `${root.replace(/\/+$/, "")}/gdharness/latest`;
|
|
8762
8852
|
}
|
|
@@ -8776,27 +8866,27 @@ function cacheDirectory(environment) {
|
|
|
8776
8866
|
const value = environment[name];
|
|
8777
8867
|
return value !== undefined && value !== "" ? value : null;
|
|
8778
8868
|
};
|
|
8779
|
-
const home = set("HOME") ??
|
|
8869
|
+
const home = set("HOME") ?? homedir4();
|
|
8780
8870
|
if (process.platform === "win32") {
|
|
8781
|
-
return
|
|
8871
|
+
return join10(set("LOCALAPPDATA") ?? home, "gdharness");
|
|
8782
8872
|
}
|
|
8783
8873
|
if (process.platform === "darwin") {
|
|
8784
|
-
return
|
|
8874
|
+
return join10(home, "Library", "Caches", "gdharness");
|
|
8785
8875
|
}
|
|
8786
|
-
return
|
|
8876
|
+
return join10(set("XDG_CACHE_HOME") ?? join10(home, ".cache"), "gdharness");
|
|
8787
8877
|
}
|
|
8788
8878
|
function cacheFile(environment = process.env) {
|
|
8789
8879
|
try {
|
|
8790
8880
|
const directory = cacheDirectory(environment);
|
|
8791
8881
|
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
8792
|
-
return
|
|
8882
|
+
return join10(directory, "update-check.json");
|
|
8793
8883
|
} catch {
|
|
8794
|
-
return
|
|
8884
|
+
return join10(tmpdir3(), "gdharness-update-check.json");
|
|
8795
8885
|
}
|
|
8796
8886
|
}
|
|
8797
8887
|
function readCache(path) {
|
|
8798
8888
|
try {
|
|
8799
|
-
if (!
|
|
8889
|
+
if (!existsSync10(path)) {
|
|
8800
8890
|
return null;
|
|
8801
8891
|
}
|
|
8802
8892
|
const parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
@@ -9031,7 +9121,7 @@ var init_server_version = __esm(() => {
|
|
|
9031
9121
|
|
|
9032
9122
|
// src/issues.ts
|
|
9033
9123
|
import { createHash as createHash2 } from "node:crypto";
|
|
9034
|
-
import
|
|
9124
|
+
import process8 from "node:process";
|
|
9035
9125
|
function feedbackNotice(every) {
|
|
9036
9126
|
return JSON.stringify({
|
|
9037
9127
|
gdharness_feedback: {
|
|
@@ -9042,9 +9132,9 @@ function feedbackNotice(every) {
|
|
|
9042
9132
|
}, null, 2);
|
|
9043
9133
|
}
|
|
9044
9134
|
function runtime() {
|
|
9045
|
-
const versions =
|
|
9135
|
+
const versions = process8.versions;
|
|
9046
9136
|
const bun = versions["bun"];
|
|
9047
|
-
return bun === undefined ? `node ${
|
|
9137
|
+
return bun === undefined ? `node ${process8.versions.node}` : `bun ${bun}`;
|
|
9048
9138
|
}
|
|
9049
9139
|
function defectSignature(where, message) {
|
|
9050
9140
|
const normalised = `${where}
|
|
@@ -9056,7 +9146,7 @@ function facts(where, message, godotVersion) {
|
|
|
9056
9146
|
["Where", where],
|
|
9057
9147
|
["Error", message],
|
|
9058
9148
|
["Version", `gdharness ${SERVER_VERSION}`],
|
|
9059
|
-
["Runtime", `${runtime()}, ${
|
|
9149
|
+
["Runtime", `${runtime()}, ${process8.platform} ${process8.arch}`],
|
|
9060
9150
|
["Godot", godotVersion ?? "not known at the point this failed"],
|
|
9061
9151
|
["Signature", defectSignature(where, message)]
|
|
9062
9152
|
];
|
|
@@ -9066,7 +9156,7 @@ function filledTemplate(where, message, godotVersion) {
|
|
|
9066
9156
|
`**gdharness version**: ${SERVER_VERSION}`,
|
|
9067
9157
|
`**Godot version**: ${godotVersion ?? ""}`,
|
|
9068
9158
|
`**Bun version**: ${runtime()}`,
|
|
9069
|
-
`**OS**: ${
|
|
9159
|
+
`**OS**: ${process8.platform} ${process8.arch}`,
|
|
9070
9160
|
"**MCP client**:",
|
|
9071
9161
|
"",
|
|
9072
9162
|
"## What you did",
|
|
@@ -9806,8 +9896,8 @@ var init_gdscript_source = __esm(() => {
|
|
|
9806
9896
|
});
|
|
9807
9897
|
|
|
9808
9898
|
// src/class-cache.ts
|
|
9809
|
-
import { existsSync as
|
|
9810
|
-
import { join as
|
|
9899
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync8, statSync as statSync6 } from "node:fs";
|
|
9900
|
+
import { join as join11, posix } from "node:path";
|
|
9811
9901
|
function isToolScript(source) {
|
|
9812
9902
|
return TOOL.test(codeOf(source));
|
|
9813
9903
|
}
|
|
@@ -9823,14 +9913,14 @@ function declaredClasses(projectPath) {
|
|
|
9823
9913
|
}
|
|
9824
9914
|
function eachScript(projectPath, each) {
|
|
9825
9915
|
const visit = (directory, prefix) => {
|
|
9826
|
-
if (
|
|
9916
|
+
if (existsSync11(join11(directory, ".gdignore"))) {
|
|
9827
9917
|
return;
|
|
9828
9918
|
}
|
|
9829
|
-
for (const entry of
|
|
9919
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
9830
9920
|
if (entry.name.startsWith(".")) {
|
|
9831
9921
|
continue;
|
|
9832
9922
|
}
|
|
9833
|
-
const path =
|
|
9923
|
+
const path = join11(directory, entry.name);
|
|
9834
9924
|
if (entry.isDirectory()) {
|
|
9835
9925
|
visit(path, `${prefix}${entry.name}/`);
|
|
9836
9926
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
@@ -9856,8 +9946,8 @@ function scriptsNaming(projectPath, classes) {
|
|
|
9856
9946
|
return naming.sort((a, b) => a.script.localeCompare(b.script));
|
|
9857
9947
|
}
|
|
9858
9948
|
function cachedClasses(projectPath) {
|
|
9859
|
-
const cache =
|
|
9860
|
-
if (!
|
|
9949
|
+
const cache = join11(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
9950
|
+
if (!existsSync11(cache)) {
|
|
9861
9951
|
return null;
|
|
9862
9952
|
}
|
|
9863
9953
|
const listed = new Map;
|
|
@@ -10127,9 +10217,9 @@ function uncachedClassNote(types) {
|
|
|
10127
10217
|
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.";
|
|
10128
10218
|
}
|
|
10129
10219
|
function cacheWrittenAt(projectPath) {
|
|
10130
|
-
const cache =
|
|
10220
|
+
const cache = join11(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
10131
10221
|
try {
|
|
10132
|
-
return
|
|
10222
|
+
return statSync6(cache).mtimeMs;
|
|
10133
10223
|
} catch {
|
|
10134
10224
|
return null;
|
|
10135
10225
|
}
|
|
@@ -10141,14 +10231,14 @@ function declaredSince(projectPath, paths, names, since) {
|
|
|
10141
10231
|
return false;
|
|
10142
10232
|
}
|
|
10143
10233
|
try {
|
|
10144
|
-
return
|
|
10234
|
+
return statSync6(join11(projectPath, path.slice("res://".length))).mtimeMs > since;
|
|
10145
10235
|
} catch {
|
|
10146
10236
|
return false;
|
|
10147
10237
|
}
|
|
10148
10238
|
});
|
|
10149
10239
|
}
|
|
10150
10240
|
function cachedAtMissingPaths(cached, projectPath) {
|
|
10151
|
-
return [...cached].filter(([, path]) => path.startsWith("res://") && !
|
|
10241
|
+
return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync11(join11(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
|
|
10152
10242
|
}
|
|
10153
10243
|
function staleAgainst(cached, projectPath) {
|
|
10154
10244
|
return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
|
|
@@ -10173,8 +10263,8 @@ var init_class_cache = __esm(() => {
|
|
|
10173
10263
|
});
|
|
10174
10264
|
|
|
10175
10265
|
// src/project-scan.ts
|
|
10176
|
-
import { existsSync as
|
|
10177
|
-
import { extname, join as
|
|
10266
|
+
import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync9 } from "node:fs";
|
|
10267
|
+
import { extname, join as join12 } from "node:path";
|
|
10178
10268
|
function skipped(name) {
|
|
10179
10269
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
10180
10270
|
}
|
|
@@ -10182,10 +10272,10 @@ function steppedOver(directory) {
|
|
|
10182
10272
|
return gdignored(directory) || installedByUs(directory);
|
|
10183
10273
|
}
|
|
10184
10274
|
function gdignored(directory) {
|
|
10185
|
-
return
|
|
10275
|
+
return existsSync12(join12(directory, ".gdignore"));
|
|
10186
10276
|
}
|
|
10187
10277
|
function installedByUs(directory) {
|
|
10188
|
-
return
|
|
10278
|
+
return existsSync12(join12(directory, VERSION_MARKER));
|
|
10189
10279
|
}
|
|
10190
10280
|
function projectStructure(projectPath) {
|
|
10191
10281
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -10193,12 +10283,12 @@ function projectStructure(projectPath) {
|
|
|
10193
10283
|
if (steppedOver(directory)) {
|
|
10194
10284
|
return;
|
|
10195
10285
|
}
|
|
10196
|
-
for (const entry of
|
|
10286
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10197
10287
|
if (skipped(entry.name)) {
|
|
10198
10288
|
continue;
|
|
10199
10289
|
}
|
|
10200
10290
|
if (entry.isDirectory()) {
|
|
10201
|
-
visit(
|
|
10291
|
+
visit(join12(directory, entry.name));
|
|
10202
10292
|
} else if (entry.isFile()) {
|
|
10203
10293
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10204
10294
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -10222,13 +10312,13 @@ function projectFiles(projectPath, extensions) {
|
|
|
10222
10312
|
if (steppedOver(directory)) {
|
|
10223
10313
|
return;
|
|
10224
10314
|
}
|
|
10225
|
-
for (const entry of
|
|
10315
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10226
10316
|
if (skipped(entry.name)) {
|
|
10227
10317
|
continue;
|
|
10228
10318
|
}
|
|
10229
10319
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10230
10320
|
if (entry.isDirectory()) {
|
|
10231
|
-
visit(
|
|
10321
|
+
visit(join12(directory, entry.name), spelled);
|
|
10232
10322
|
} else if (entry.isFile() && extensions.includes(extname(entry.name).toLowerCase())) {
|
|
10233
10323
|
found.push(spelled);
|
|
10234
10324
|
}
|
|
@@ -10243,20 +10333,20 @@ function scriptsWithoutUid(projectPath) {
|
|
|
10243
10333
|
if (steppedOver(directory)) {
|
|
10244
10334
|
return;
|
|
10245
10335
|
}
|
|
10246
|
-
for (const entry of
|
|
10336
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10247
10337
|
if (skipped(entry.name)) {
|
|
10248
10338
|
continue;
|
|
10249
10339
|
}
|
|
10250
10340
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10251
10341
|
if (entry.isDirectory()) {
|
|
10252
|
-
visit(
|
|
10342
|
+
visit(join12(directory, entry.name), spelled);
|
|
10253
10343
|
continue;
|
|
10254
10344
|
}
|
|
10255
10345
|
if (!entry.isFile()) {
|
|
10256
10346
|
continue;
|
|
10257
10347
|
}
|
|
10258
10348
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10259
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
10349
|
+
if (CARRIES_A_UID.has(extension) && !existsSync12(join12(directory, `${entry.name}.uid`))) {
|
|
10260
10350
|
found.push(spelled);
|
|
10261
10351
|
}
|
|
10262
10352
|
}
|
|
@@ -10273,11 +10363,11 @@ function projectTexts(projectPath) {
|
|
|
10273
10363
|
if (steppedOver(directory)) {
|
|
10274
10364
|
return;
|
|
10275
10365
|
}
|
|
10276
|
-
for (const entry of
|
|
10366
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10277
10367
|
if (skipped(entry.name)) {
|
|
10278
10368
|
continue;
|
|
10279
10369
|
}
|
|
10280
|
-
const absolute =
|
|
10370
|
+
const absolute = join12(directory, entry.name);
|
|
10281
10371
|
const spelled = `${prefix}${entry.name}`;
|
|
10282
10372
|
if (entry.isDirectory()) {
|
|
10283
10373
|
visit(absolute, `${spelled}/`);
|
|
@@ -10303,11 +10393,11 @@ function ignoredTexts(projectPath) {
|
|
|
10303
10393
|
return;
|
|
10304
10394
|
}
|
|
10305
10395
|
const under = ignored || gdignored(directory);
|
|
10306
|
-
for (const entry of
|
|
10396
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10307
10397
|
if (skipped(entry.name)) {
|
|
10308
10398
|
continue;
|
|
10309
10399
|
}
|
|
10310
|
-
const absolute =
|
|
10400
|
+
const absolute = join12(directory, entry.name);
|
|
10311
10401
|
const spelled = `${prefix}${entry.name}`;
|
|
10312
10402
|
if (entry.isDirectory()) {
|
|
10313
10403
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -10340,14 +10430,14 @@ function searchProject(projectPath, options) {
|
|
|
10340
10430
|
if (steppedOver(directory)) {
|
|
10341
10431
|
return;
|
|
10342
10432
|
}
|
|
10343
|
-
for (const entry of
|
|
10433
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10344
10434
|
if (full()) {
|
|
10345
10435
|
return;
|
|
10346
10436
|
}
|
|
10347
10437
|
if (skipped(entry.name)) {
|
|
10348
10438
|
continue;
|
|
10349
10439
|
}
|
|
10350
|
-
const entryPath =
|
|
10440
|
+
const entryPath = join12(directory, entry.name);
|
|
10351
10441
|
if (entry.isDirectory()) {
|
|
10352
10442
|
visit(entryPath);
|
|
10353
10443
|
continue;
|
|
@@ -10792,16 +10882,16 @@ function readParams(params, key) {
|
|
|
10792
10882
|
import { createHash as createHash3 } from "node:crypto";
|
|
10793
10883
|
import {
|
|
10794
10884
|
cpSync,
|
|
10795
|
-
existsSync as
|
|
10885
|
+
existsSync as existsSync13,
|
|
10796
10886
|
mkdirSync as mkdirSync4,
|
|
10797
|
-
readdirSync as
|
|
10887
|
+
readdirSync as readdirSync8,
|
|
10798
10888
|
readFileSync as readFileSync11,
|
|
10799
10889
|
rmdirSync,
|
|
10800
10890
|
rmSync as rmSync5,
|
|
10801
|
-
statSync as
|
|
10891
|
+
statSync as statSync7,
|
|
10802
10892
|
writeFileSync as writeFileSync6
|
|
10803
10893
|
} from "node:fs";
|
|
10804
|
-
import { dirname as
|
|
10894
|
+
import { dirname as dirname7, join as join13 } from "node:path";
|
|
10805
10895
|
import { fileURLToPath } from "node:url";
|
|
10806
10896
|
function editorAddonDigest(from = shippedAddonsDirectory()) {
|
|
10807
10897
|
return addonsDigest(EDITOR_PLUGINS, from);
|
|
@@ -10812,13 +10902,13 @@ function runtimeAddonDigest(from = shippedAddonsDirectory()) {
|
|
|
10812
10902
|
function addonsDigest(names, from) {
|
|
10813
10903
|
const hash = createHash3("sha256");
|
|
10814
10904
|
const walk = (directory, relative) => {
|
|
10815
|
-
for (const name of
|
|
10905
|
+
for (const name of readdirSync8(directory).sort()) {
|
|
10816
10906
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
10817
10907
|
continue;
|
|
10818
10908
|
}
|
|
10819
|
-
const path =
|
|
10909
|
+
const path = join13(directory, name);
|
|
10820
10910
|
const inside = `${relative}/${name}`;
|
|
10821
|
-
if (
|
|
10911
|
+
if (statSync7(path).isDirectory()) {
|
|
10822
10912
|
walk(path, inside);
|
|
10823
10913
|
} else {
|
|
10824
10914
|
hash.update(`${inside}
|
|
@@ -10830,7 +10920,7 @@ function addonsDigest(names, from) {
|
|
|
10830
10920
|
}
|
|
10831
10921
|
};
|
|
10832
10922
|
for (const name of names) {
|
|
10833
|
-
walk(
|
|
10923
|
+
walk(join13(from, name), name);
|
|
10834
10924
|
}
|
|
10835
10925
|
return hash.digest("hex");
|
|
10836
10926
|
}
|
|
@@ -10861,44 +10951,44 @@ function installedRuntimeDigest(projectPath) {
|
|
|
10861
10951
|
return digestMarker(projectPath, RUNTIME_ADDON);
|
|
10862
10952
|
}
|
|
10863
10953
|
function digestMarker(projectPath, addon) {
|
|
10864
|
-
const marker =
|
|
10865
|
-
if (!
|
|
10954
|
+
const marker = join13(projectPath, "addons", addon, DIGEST_MARKER);
|
|
10955
|
+
if (!existsSync13(marker)) {
|
|
10866
10956
|
return null;
|
|
10867
10957
|
}
|
|
10868
10958
|
const said = readFileSync11(marker, "utf8").trim();
|
|
10869
10959
|
return said === "" ? null : said;
|
|
10870
10960
|
}
|
|
10871
10961
|
function shippedAddonsDirectory() {
|
|
10872
|
-
return
|
|
10962
|
+
return join13(dirname7(fileURLToPath(import.meta.url)), "godot", "addons");
|
|
10873
10963
|
}
|
|
10874
10964
|
function shippedOperationsScript() {
|
|
10875
|
-
return
|
|
10965
|
+
return join13(dirname7(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
|
|
10876
10966
|
}
|
|
10877
10967
|
function installAddons(projectPath, from = shippedAddonsDirectory()) {
|
|
10878
10968
|
const installed = [];
|
|
10879
10969
|
for (const name of ADDONS) {
|
|
10880
|
-
const source =
|
|
10881
|
-
if (!
|
|
10970
|
+
const source = join13(from, name);
|
|
10971
|
+
if (!existsSync13(join13(source, name === RUNTIME_ADDON ? "runtime_autoload.gd" : "plugin.cfg"))) {
|
|
10882
10972
|
throw new Error(`The package holds no ${name} addon at ${source}.`);
|
|
10883
10973
|
}
|
|
10884
|
-
const target =
|
|
10885
|
-
const replaced =
|
|
10974
|
+
const target = join13(projectPath, "addons", name);
|
|
10975
|
+
const replaced = existsSync13(target);
|
|
10886
10976
|
rmSync5(target, { recursive: true, force: true });
|
|
10887
|
-
mkdirSync4(
|
|
10977
|
+
mkdirSync4(dirname7(target), { recursive: true });
|
|
10888
10978
|
cpSync(source, target, { recursive: true });
|
|
10889
|
-
writeFileSync6(
|
|
10979
|
+
writeFileSync6(join13(target, VERSION_MARKER), `${SERVER_VERSION}
|
|
10890
10980
|
`);
|
|
10891
10981
|
installed.push({ name, path: target, replaced });
|
|
10892
10982
|
}
|
|
10893
|
-
writeFileSync6(
|
|
10983
|
+
writeFileSync6(join13(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
|
|
10894
10984
|
`);
|
|
10895
|
-
writeFileSync6(
|
|
10985
|
+
writeFileSync6(join13(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
|
|
10896
10986
|
`);
|
|
10897
10987
|
return installed;
|
|
10898
10988
|
}
|
|
10899
10989
|
function installedAddonVersion(projectPath) {
|
|
10900
|
-
const marker =
|
|
10901
|
-
if (!
|
|
10990
|
+
const marker = join13(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
10991
|
+
if (!existsSync13(marker)) {
|
|
10902
10992
|
return null;
|
|
10903
10993
|
}
|
|
10904
10994
|
const said = readFileSync11(marker, "utf8").trim();
|
|
@@ -10907,14 +10997,14 @@ function installedAddonVersion(projectPath) {
|
|
|
10907
10997
|
function removeAddons(projectPath) {
|
|
10908
10998
|
const removed = [];
|
|
10909
10999
|
for (const name of ADDONS) {
|
|
10910
|
-
const target =
|
|
10911
|
-
if (
|
|
11000
|
+
const target = join13(projectPath, "addons", name);
|
|
11001
|
+
if (existsSync13(target)) {
|
|
10912
11002
|
rmSync5(target, { recursive: true, force: true });
|
|
10913
11003
|
removed.push(target);
|
|
10914
11004
|
}
|
|
10915
11005
|
}
|
|
10916
|
-
const addons =
|
|
10917
|
-
if (removed.length > 0 &&
|
|
11006
|
+
const addons = join13(projectPath, "addons");
|
|
11007
|
+
if (removed.length > 0 && existsSync13(addons) && readdirSync8(addons).length === 0) {
|
|
10918
11008
|
rmdirSync(addons);
|
|
10919
11009
|
removed.push(addons);
|
|
10920
11010
|
}
|
|
@@ -10948,7 +11038,7 @@ function loaderAmong(projectPath, named) {
|
|
|
10948
11038
|
}
|
|
10949
11039
|
let source = null;
|
|
10950
11040
|
try {
|
|
10951
|
-
source = readFileSync11(
|
|
11041
|
+
source = readFileSync11(join13(projectPath, path), "utf8");
|
|
10952
11042
|
} catch {}
|
|
10953
11043
|
if (source?.includes(RUNTIME_AUTOLOAD.path)) {
|
|
10954
11044
|
return { name: entry, path, exists: true };
|
|
@@ -10990,10 +11080,10 @@ function enabledPlugins(settings) {
|
|
|
10990
11080
|
function inspectProject(projectPath) {
|
|
10991
11081
|
const problems = [];
|
|
10992
11082
|
const addons = ADDONS.map((name) => {
|
|
10993
|
-
const directory =
|
|
10994
|
-
const marker =
|
|
10995
|
-
const installed =
|
|
10996
|
-
const version =
|
|
11083
|
+
const directory = join13(projectPath, "addons", name);
|
|
11084
|
+
const marker = join13(directory, VERSION_MARKER);
|
|
11085
|
+
const installed = existsSync13(directory);
|
|
11086
|
+
const version = existsSync13(marker) ? readFileSync11(marker, "utf8").trim() : null;
|
|
10997
11087
|
const current = version === SERVER_VERSION;
|
|
10998
11088
|
if (!installed) {
|
|
10999
11089
|
problems.push(`addons/${name} is not installed; run gdharness setup`);
|
|
@@ -11002,7 +11092,7 @@ function inspectProject(projectPath) {
|
|
|
11002
11092
|
}
|
|
11003
11093
|
return { name, installed, version, current };
|
|
11004
11094
|
});
|
|
11005
|
-
const settings = parseProjectGodot(readFileSync11(
|
|
11095
|
+
const settings = parseProjectGodot(readFileSync11(join13(projectPath, "project.godot"), "utf8"));
|
|
11006
11096
|
const pluginsEnabled = enabledPlugins(settings);
|
|
11007
11097
|
for (const name of EDITOR_PLUGINS) {
|
|
11008
11098
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -11018,7 +11108,7 @@ function inspectProject(projectPath) {
|
|
|
11018
11108
|
const tracked = trackedByGit(projectPath, [...named.values()]);
|
|
11019
11109
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
11020
11110
|
for (const [name, path] of named) {
|
|
11021
|
-
if (!
|
|
11111
|
+
if (!existsSync13(join13(projectPath, path))) {
|
|
11022
11112
|
if (!addonMissing(path)) {
|
|
11023
11113
|
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`);
|
|
11024
11114
|
}
|
|
@@ -11334,7 +11424,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11334
11424
|
name: "project_info",
|
|
11335
11425
|
title: "Project info",
|
|
11336
11426
|
effect: "read",
|
|
11337
|
-
description: "What a project is: its name and main scene from project.godot, the Godot that answers, and how many scenes, scripts and assets it holds, with optional sections on top.",
|
|
11427
|
+
description: "What a project is: its name and main scene from project.godot, the Godot that answers, and how many scenes, scripts and assets it holds, other counting every remaining file, leaving out the addons gdharness installed and any folder holding a .gdignore, with optional sections on top.",
|
|
11338
11428
|
parameters: {
|
|
11339
11429
|
projectPath: PROJECT_PATH,
|
|
11340
11430
|
include: {
|
|
@@ -11576,7 +11666,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11576
11666
|
},
|
|
11577
11667
|
reimport: {
|
|
11578
11668
|
effect: "destructive",
|
|
11579
|
-
summary: "reimport what status says is outdated or failed, one resource with resourcePath, or what is current as well with force. Through the open editor when one serves the project, so it reloads what it holds, waiting for it to finish; otherwise through the engine's own import pass. The editor scans first, since its reimport passes over a file it has not taken in, such as one an installer has just written; what that scan imported is named under importedByTheScan and is not imported again unless force asks. Answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, whether or not anything needed reimporting; one resourcePath is not a walk of the project and leaves it out. stillImporting is an editor that had not finished within timeoutMs",
|
|
11669
|
+
summary: "reimport what status says is outdated or failed, one resource with resourcePath, or what is current as well with force. Through the open editor when one serves the project, so it reloads what it holds, waiting for it to finish; otherwise through the engine's own import pass. The editor scans first, since its reimport passes over a file it has not taken in, such as one an installer has just written; what that scan imported is named under importedByTheScan and is not imported again unless force asks. Answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. When nothing needs reimporting, nothing runs, and the answer has the two lists empty, a note saying so, and no via. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, whether or not anything needed reimporting; one resourcePath is not a walk of the project and leaves it out. stillImporting is an editor that had not finished within timeoutMs",
|
|
11580
11670
|
requires: []
|
|
11581
11671
|
},
|
|
11582
11672
|
uid: {
|
|
@@ -11660,7 +11750,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11660
11750
|
timeoutMs: {
|
|
11661
11751
|
type: "integer",
|
|
11662
11752
|
minimum: 1,
|
|
11663
|
-
description: "How long the run may take before it is killed. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path."
|
|
11753
|
+
description: "How long the run may take before it is killed. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path, unless it ended repeating one script error, which the verdict names instead. A run whose autoload did not compile is stopped as soon as the engine says so, and names the script that did not under scriptErrors."
|
|
11664
11754
|
},
|
|
11665
11755
|
printed: {
|
|
11666
11756
|
type: "string",
|
|
@@ -11679,7 +11769,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11679
11769
|
rootNodeType: {
|
|
11680
11770
|
type: "string",
|
|
11681
11771
|
ops: ["create"],
|
|
11682
|
-
description: "create: the root node class. Default Node."
|
|
11772
|
+
description: "create: the root node class, an engine class or a class_name a project script declares, which the root is made as with that script attached. Default Node."
|
|
11683
11773
|
},
|
|
11684
11774
|
rootNodeName: {
|
|
11685
11775
|
type: "string",
|
|
@@ -11717,7 +11807,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11717
11807
|
{
|
|
11718
11808
|
name: "scene_node",
|
|
11719
11809
|
title: "Scene nodes",
|
|
11720
|
-
description: `One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {"name": "Boss"} renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. ${WITHOUT_AN_EDITOR}`,
|
|
11810
|
+
description: `One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type or script class can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {"name": "Boss"} renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. ${WITHOUT_AN_EDITOR}`,
|
|
11721
11811
|
parameters: {
|
|
11722
11812
|
projectPath: PROJECT_PATH,
|
|
11723
11813
|
scenePath: SCENE_PATH,
|
|
@@ -11730,7 +11820,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11730
11820
|
nodeType: {
|
|
11731
11821
|
type: "string",
|
|
11732
11822
|
ops: ["add"],
|
|
11733
|
-
description: 'add: the node class, such as "CharacterBody2D". Give this or instancePath.'
|
|
11823
|
+
description: 'add: the node class, such as "CharacterBody2D", or a class_name a project script declares, which the node is made as with that script attached. Give this or instancePath.'
|
|
11734
11824
|
},
|
|
11735
11825
|
instancePath: {
|
|
11736
11826
|
type: "string",
|
|
@@ -12075,7 +12165,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12075
12165
|
resourceType: {
|
|
12076
12166
|
type: "string",
|
|
12077
12167
|
ops: ["create"],
|
|
12078
|
-
description: 'create: the resource class, such as "PhysicsMaterial" or "StandardMaterial3D".'
|
|
12168
|
+
description: 'create: the resource class, such as "PhysicsMaterial" or "StandardMaterial3D", or a class_name a project script declares, which the resource is made as with that script attached.'
|
|
12079
12169
|
},
|
|
12080
12170
|
properties: { ...PROPERTIES, ops: ["create", "modify"] },
|
|
12081
12171
|
script: {
|
|
@@ -12269,6 +12359,17 @@ var init_tool_definitions = __esm(() => {
|
|
|
12269
12359
|
ops: ["profile"],
|
|
12270
12360
|
description: "profile: also answer, under callers, where the project's scripts name this function in code: its name, or Class.name for the functions of one class. Each place has its script, line, the named function it is written in (within), whether the function is called there or passed on as a Callable (called), and the profile's calls, self and total milliseconds for the function it is in, those taking the most time first, forty at most. Found by name in the source, since the engine's profiler records nothing about who made a call, so a method of the same name on another class is listed too, except, when Class.name was asked, one called through another class of the project by name or through a parameter, local or class variable declared as one, and a call made through a string is not. A parameter, local or class variable of the same name is not the function and is not listed. A property's get or set block, which the profile names Class.@name_getter or Class.@name_setter, is asked about by that name or by the property's: the places are where the property is named, each saying instead of called whether it writes the property (access write), which runs the set block, or reads it (access read), which runs the get block, and the rows are both blocks'. 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 places."
|
|
12271
12361
|
},
|
|
12362
|
+
mark: {
|
|
12363
|
+
type: "boolean",
|
|
12364
|
+
ops: ["profile"],
|
|
12365
|
+
description: `profile: answer as usual and set the run's mark at these totals, so a later profile with since: "mark" answers for one action, a press or a commit, apart from the boot and everything before it. The mark is kept beside the run's profile, so a server that replaces this one reads it too, and setting it again moves it. Given with since, the answer counts from the mark before it moves, which times one span after another. Default false.`
|
|
12366
|
+
},
|
|
12367
|
+
since: {
|
|
12368
|
+
type: "string",
|
|
12369
|
+
enum: ["mark"],
|
|
12370
|
+
ops: ["profile"],
|
|
12371
|
+
description: 'profile: "mark" answers only what was added after the mark that mark: true set in this run: per function the calls and self and total milliseconds since it, with the functions that added no call left out, frames and coveredMs since it, and callersOf read off the same difference. Refused when no mark was set in this run, or when the profile holds less than it did at the mark, which is a profiler started again since.'
|
|
12372
|
+
},
|
|
12272
12373
|
limit: {
|
|
12273
12374
|
type: "integer",
|
|
12274
12375
|
minimum: 1,
|
|
@@ -12320,7 +12421,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12320
12421
|
requires: []
|
|
12321
12422
|
}
|
|
12322
12423
|
},
|
|
12323
|
-
answer: "stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked.",
|
|
12424
|
+
answer: "stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. The runtime addon's own functions, gdharness answering its tools inside the game, are not among them: their self time is runtimeAddonMs, over runtimeAddonFunctions functions. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. frames counts the frames of the profile that arrived, and the engine's totals also hold the work after the last of them, so once complete a function run every frame can show a call more than frames. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked. A profile with mark answers marked: true; one with since answers since: \"mark\" and markSetMsAgo, and lessThanAtMark counts the functions left out for reading less than at the mark, which the engine's own totals, capped, can do when they replace the frames' sum.",
|
|
12324
12425
|
defaultOperation: "start"
|
|
12325
12426
|
},
|
|
12326
12427
|
{
|
|
@@ -32796,7 +32897,7 @@ var init_ajvProvider_CEoC__sr = __esm(() => {
|
|
|
32796
32897
|
});
|
|
32797
32898
|
|
|
32798
32899
|
// node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs
|
|
32799
|
-
import
|
|
32900
|
+
import process10 from "node:process";
|
|
32800
32901
|
var init_shimsNode = __esm(() => {
|
|
32801
32902
|
init_ajvProvider_CEoC__sr();
|
|
32802
32903
|
});
|
|
@@ -34610,7 +34711,7 @@ var swallowsErrorsAfterClose, StdioServerTransport = class {
|
|
|
34610
34711
|
_readBuffer;
|
|
34611
34712
|
_started = false;
|
|
34612
34713
|
_closed = false;
|
|
34613
|
-
constructor(_stdin =
|
|
34714
|
+
constructor(_stdin = process10.stdin, _stdout = process10.stdout, options) {
|
|
34614
34715
|
this._stdin = _stdin;
|
|
34615
34716
|
this._stdout = _stdout;
|
|
34616
34717
|
this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
|
|
@@ -34798,11 +34899,51 @@ var init_stdio = __esm(() => {
|
|
|
34798
34899
|
swallowsErrorsAfterClose = Symbol("swallowsErrorsAfterClose");
|
|
34799
34900
|
});
|
|
34800
34901
|
|
|
34902
|
+
// src/autoload-failure.ts
|
|
34903
|
+
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
|
|
34904
|
+
import { join as join16 } from "node:path";
|
|
34905
|
+
function scriptsAutoloaded(projectPath) {
|
|
34906
|
+
const scripts = new Map;
|
|
34907
|
+
let settings;
|
|
34908
|
+
try {
|
|
34909
|
+
settings = parseProjectGodot(readFileSync13(join16(projectPath, "project.godot"), "utf8"));
|
|
34910
|
+
} catch {
|
|
34911
|
+
return scripts;
|
|
34912
|
+
}
|
|
34913
|
+
for (const [name, path] of autoloadPaths(settings)) {
|
|
34914
|
+
if (!path.endsWith(".tscn")) {
|
|
34915
|
+
scripts.set(`res://${path}`, name);
|
|
34916
|
+
continue;
|
|
34917
|
+
}
|
|
34918
|
+
const scene = join16(projectPath, path);
|
|
34919
|
+
if (!existsSync15(scene)) {
|
|
34920
|
+
continue;
|
|
34921
|
+
}
|
|
34922
|
+
try {
|
|
34923
|
+
for (const named of readFileSync13(scene, "utf8").matchAll(/^\[ext_resource\b[^\]]*\btype="Script"[^\]]*\bpath="(res:\/\/[^"]+)"/gm)) {
|
|
34924
|
+
scripts.set(named[1] ?? "", name);
|
|
34925
|
+
}
|
|
34926
|
+
} catch {}
|
|
34927
|
+
}
|
|
34928
|
+
return scripts;
|
|
34929
|
+
}
|
|
34930
|
+
function autoloadNotCompiled(entry, autoloaded) {
|
|
34931
|
+
const path = NOT_COMPILED.exec(entry.text)?.[1];
|
|
34932
|
+
const name = path === undefined ? undefined : autoloaded.get(path);
|
|
34933
|
+
return path === undefined || name === undefined ? null : { name, path };
|
|
34934
|
+
}
|
|
34935
|
+
var NOT_COMPILED;
|
|
34936
|
+
var init_autoload_failure = __esm(() => {
|
|
34937
|
+
init_resources();
|
|
34938
|
+
init_setup();
|
|
34939
|
+
NOT_COMPILED = /^Failed to load script "(res:\/\/[^"]+)" with error "Compilation failed"/;
|
|
34940
|
+
});
|
|
34941
|
+
|
|
34801
34942
|
// src/runtime-client.ts
|
|
34802
|
-
import { existsSync as
|
|
34943
|
+
import { existsSync as existsSync16, readdirSync as readdirSync10, readFileSync as readFileSync14, statSync as statSync8, unlinkSync as unlinkSync2 } from "node:fs";
|
|
34803
34944
|
import { createConnection } from "node:net";
|
|
34804
34945
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
34805
|
-
import { dirname as
|
|
34946
|
+
import { dirname as dirname10, join as join17, resolve as resolve4 } from "node:path";
|
|
34806
34947
|
import { setTimeout as delay } from "node:timers/promises";
|
|
34807
34948
|
function runtimeDirectory(variables = process.env) {
|
|
34808
34949
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -34810,20 +34951,20 @@ function runtimeDirectory(variables = process.env) {
|
|
|
34810
34951
|
return explicit;
|
|
34811
34952
|
}
|
|
34812
34953
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
34813
|
-
return
|
|
34954
|
+
return join17(perUser ?? tmpdir4(), "gdharness");
|
|
34814
34955
|
}
|
|
34815
34956
|
function runtimeDirectories(variables = process.env) {
|
|
34816
34957
|
const candidates = [runtimeDirectory(variables)];
|
|
34817
34958
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
34818
34959
|
for (const base of fallbacks) {
|
|
34819
|
-
candidates.push(
|
|
34960
|
+
candidates.push(join17(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
34820
34961
|
}
|
|
34821
34962
|
return [...new Set(candidates.map((path) => resolve4(path)))];
|
|
34822
34963
|
}
|
|
34823
34964
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
34824
34965
|
for (const directory of directories) {
|
|
34825
|
-
const path =
|
|
34826
|
-
if (
|
|
34966
|
+
const path = join17(directory, `runtime-${pid}.log`);
|
|
34967
|
+
if (existsSync16(path)) {
|
|
34827
34968
|
return path;
|
|
34828
34969
|
}
|
|
34829
34970
|
}
|
|
@@ -34854,7 +34995,7 @@ function reportAlreadyInTranscript(transcript, report) {
|
|
|
34854
34995
|
function parseAnnouncement(file, pid) {
|
|
34855
34996
|
let fields;
|
|
34856
34997
|
try {
|
|
34857
|
-
fields = asParams(JSON.parse(
|
|
34998
|
+
fields = asParams(JSON.parse(readFileSync14(file, "utf8")));
|
|
34858
34999
|
} catch {
|
|
34859
35000
|
return { kind: "rubbish" };
|
|
34860
35001
|
}
|
|
@@ -34871,7 +35012,7 @@ function parseAnnouncement(file, pid) {
|
|
|
34871
35012
|
};
|
|
34872
35013
|
let announcedAt;
|
|
34873
35014
|
try {
|
|
34874
|
-
announcedAt =
|
|
35015
|
+
announcedAt = statSync8(file).mtimeMs;
|
|
34875
35016
|
} catch {}
|
|
34876
35017
|
const when = announcedAt === undefined ? {} : { announcedAt };
|
|
34877
35018
|
if (protocol !== RUNTIME_PROTOCOL) {
|
|
@@ -34920,7 +35061,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
|
|
|
34920
35061
|
function wentAway(file, pid) {
|
|
34921
35062
|
let project = "";
|
|
34922
35063
|
try {
|
|
34923
|
-
const fields = asParams(JSON.parse(
|
|
35064
|
+
const fields = asParams(JSON.parse(readFileSync14(file, "utf8")));
|
|
34924
35065
|
project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
|
|
34925
35066
|
} catch {
|
|
34926
35067
|
return;
|
|
@@ -34963,29 +35104,29 @@ function strangersAmong(candidates, now, startTimes = startTimesOf) {
|
|
|
34963
35104
|
}).map((one) => one.file));
|
|
34964
35105
|
}
|
|
34965
35106
|
function announcedIn(directory) {
|
|
34966
|
-
if (!
|
|
35107
|
+
if (!existsSync16(directory)) {
|
|
34967
35108
|
return [];
|
|
34968
35109
|
}
|
|
34969
35110
|
const found = [];
|
|
34970
35111
|
const candidates = [];
|
|
34971
35112
|
const entries = [];
|
|
34972
|
-
for (const entry of
|
|
35113
|
+
for (const entry of readdirSync10(directory)) {
|
|
34973
35114
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
34974
35115
|
if (report) {
|
|
34975
|
-
sweepErrorReport(
|
|
35116
|
+
sweepErrorReport(join17(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
34976
35117
|
continue;
|
|
34977
35118
|
}
|
|
34978
35119
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
34979
35120
|
if (!match) {
|
|
34980
35121
|
continue;
|
|
34981
35122
|
}
|
|
34982
|
-
const file =
|
|
35123
|
+
const file = join17(directory, entry);
|
|
34983
35124
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
34984
35125
|
const running = alive(pid);
|
|
34985
35126
|
entries.push({ file, pid, running });
|
|
34986
35127
|
if (running) {
|
|
34987
35128
|
try {
|
|
34988
|
-
candidates.push({ file, pid, writtenAt:
|
|
35129
|
+
candidates.push({ file, pid, writtenAt: statSync8(file).mtimeMs });
|
|
34989
35130
|
} catch {}
|
|
34990
35131
|
}
|
|
34991
35132
|
}
|
|
@@ -35007,16 +35148,16 @@ function announcedIn(directory) {
|
|
|
35007
35148
|
found.push(announced);
|
|
35008
35149
|
}
|
|
35009
35150
|
const held = new Set(entries.map((one) => one.file));
|
|
35010
|
-
const here =
|
|
35151
|
+
const here = join17(directory, ".");
|
|
35011
35152
|
for (const file of verdicts.keys()) {
|
|
35012
|
-
if (
|
|
35153
|
+
if (dirname10(file) === here && !held.has(file)) {
|
|
35013
35154
|
verdicts.delete(file);
|
|
35014
35155
|
}
|
|
35015
35156
|
}
|
|
35016
35157
|
return found;
|
|
35017
35158
|
}
|
|
35018
35159
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
35019
|
-
const files = directories.map((directory) =>
|
|
35160
|
+
const files = directories.map((directory) => join17(directory, `runtime-${pid}.json`)).filter((file) => existsSync16(file));
|
|
35020
35161
|
const first = files[0];
|
|
35021
35162
|
if (first === undefined) {
|
|
35022
35163
|
return;
|
|
@@ -35042,7 +35183,7 @@ function sweepErrorReport(path, pid, now = Date.now()) {
|
|
|
35042
35183
|
return;
|
|
35043
35184
|
}
|
|
35044
35185
|
try {
|
|
35045
|
-
if (now -
|
|
35186
|
+
if (now - statSync8(path).mtimeMs >= ERROR_REPORT_KEEP_MS) {
|
|
35046
35187
|
unlinkSync2(path);
|
|
35047
35188
|
}
|
|
35048
35189
|
} catch {}
|
|
@@ -35180,7 +35321,36 @@ function keepLate(id, answer) {
|
|
|
35180
35321
|
function lateAnswer(id) {
|
|
35181
35322
|
return lateAnswers.get(id);
|
|
35182
35323
|
}
|
|
35183
|
-
function
|
|
35324
|
+
function placeOf(endpoint) {
|
|
35325
|
+
return `${dialAddress(endpoint.address)}:${endpoint.port}`;
|
|
35326
|
+
}
|
|
35327
|
+
function waitForAPlace(game, granted) {
|
|
35328
|
+
const line = untaken.get(game) ?? { held: 0, waiting: [] };
|
|
35329
|
+
untaken.set(game, line);
|
|
35330
|
+
const grant = () => {
|
|
35331
|
+
line.held += 1;
|
|
35332
|
+
let given = false;
|
|
35333
|
+
granted(() => {
|
|
35334
|
+
if (given) {
|
|
35335
|
+
return;
|
|
35336
|
+
}
|
|
35337
|
+
given = true;
|
|
35338
|
+
line.held -= 1;
|
|
35339
|
+
const next = line.waiting.shift();
|
|
35340
|
+
if (next !== undefined) {
|
|
35341
|
+
next();
|
|
35342
|
+
} else if (line.held === 0 && untaken.get(game) === line) {
|
|
35343
|
+
untaken.delete(game);
|
|
35344
|
+
}
|
|
35345
|
+
});
|
|
35346
|
+
};
|
|
35347
|
+
if (line.held < UNTAKEN_PER_GAME) {
|
|
35348
|
+
grant();
|
|
35349
|
+
} else {
|
|
35350
|
+
line.waiting.push(grant);
|
|
35351
|
+
}
|
|
35352
|
+
}
|
|
35353
|
+
function runtimeRequest(endpoint, command, params, timeoutMs, afterTheWait = "drop") {
|
|
35184
35354
|
const id = nextRequestId++;
|
|
35185
35355
|
const sentAt = Date.now();
|
|
35186
35356
|
return new Promise((settle) => {
|
|
@@ -35188,16 +35358,32 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35188
35358
|
let late = false;
|
|
35189
35359
|
let buffered = "";
|
|
35190
35360
|
let welcomed = false;
|
|
35191
|
-
|
|
35361
|
+
let socket = null;
|
|
35362
|
+
let refusals = 0;
|
|
35363
|
+
let redial = null;
|
|
35364
|
+
let giveBackThePlace = null;
|
|
35365
|
+
const taken = () => {
|
|
35366
|
+
giveBackThePlace?.();
|
|
35367
|
+
giveBackThePlace = null;
|
|
35368
|
+
};
|
|
35192
35369
|
let lateTimer = null;
|
|
35193
35370
|
const finish = (reply) => {
|
|
35371
|
+
taken();
|
|
35372
|
+
if (redial !== null) {
|
|
35373
|
+
clearTimeout(redial);
|
|
35374
|
+
redial = null;
|
|
35375
|
+
}
|
|
35194
35376
|
if (late) {
|
|
35195
35377
|
if (lateTimer !== null) {
|
|
35196
35378
|
clearTimeout(lateTimer);
|
|
35197
35379
|
}
|
|
35198
|
-
socket
|
|
35199
|
-
keepLate(id, reply.ok || reply.reason === "error" ? { state: "answered", command, sentAt, answeredAt: Date.now(), reply } : { state: "never", command, sentAt, why: reply.message });
|
|
35380
|
+
socket?.destroy();
|
|
35200
35381
|
late = false;
|
|
35382
|
+
if (typeof afterTheWait === "function") {
|
|
35383
|
+
afterTheWait(reply);
|
|
35384
|
+
return;
|
|
35385
|
+
}
|
|
35386
|
+
keepLate(id, reply.ok || reply.reason === "error" ? { state: "answered", command, sentAt, answeredAt: Date.now(), reply } : { state: "never", command, sentAt, why: reply.message });
|
|
35201
35387
|
return;
|
|
35202
35388
|
}
|
|
35203
35389
|
if (done) {
|
|
@@ -35205,12 +35391,24 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35205
35391
|
}
|
|
35206
35392
|
done = true;
|
|
35207
35393
|
clearTimeout(timer);
|
|
35208
|
-
socket
|
|
35394
|
+
socket?.destroy();
|
|
35209
35395
|
settle(reply);
|
|
35210
35396
|
};
|
|
35211
35397
|
const timer = setTimeout(() => {
|
|
35398
|
+
if (socket === null) {
|
|
35399
|
+
finish(refusals > 0 ? {
|
|
35400
|
+
ok: false,
|
|
35401
|
+
reason: "refused",
|
|
35402
|
+
message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: it refused ${refusals === 1 ? "the connection" : `all ${refusals} connections tried`} within ${timeoutMs}ms. Nothing listens there while a game is starting or shutting down, and the engine refuses connections while more are waiting for the game than the eight it queues, which several servers calling one game at once, or a long frame or a breakpoint holding it, can make it do. Nothing was sent, so nothing ran.`
|
|
35403
|
+
} : {
|
|
35404
|
+
ok: false,
|
|
35405
|
+
reason: "busy",
|
|
35406
|
+
message: `'${command}' was not sent to the game (${describe2(endpoint)}) within ${timeoutMs}ms: ${UNTAKEN_PER_GAME} connections from this server were already waiting for the game to take them, which it does between frames, so a long frame or a breakpoint holds them. Nothing was sent, so nothing ran.`
|
|
35407
|
+
});
|
|
35408
|
+
return;
|
|
35409
|
+
}
|
|
35212
35410
|
const still = welcomed ? `The game (${describe2(endpoint)}) has the request and did not answer '${command}' within ${timeoutMs}ms.` : `The game (${describe2(endpoint)}) did not answer '${command}' within ${timeoutMs}ms and had not taken the request: its frame has not ended since it was sent, which a long frame or a breakpoint does, and the request runs when it does.`;
|
|
35213
|
-
if (
|
|
35411
|
+
if (afterTheWait === "drop") {
|
|
35214
35412
|
finish({
|
|
35215
35413
|
ok: false,
|
|
35216
35414
|
reason: "busy",
|
|
@@ -35220,7 +35418,9 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35220
35418
|
}
|
|
35221
35419
|
done = true;
|
|
35222
35420
|
late = true;
|
|
35223
|
-
|
|
35421
|
+
if (afterTheWait === "keep") {
|
|
35422
|
+
keepLate(id, { state: "waiting", command, sentAt });
|
|
35423
|
+
}
|
|
35224
35424
|
lateTimer = setTimeout(() => {
|
|
35225
35425
|
finish({
|
|
35226
35426
|
ok: false,
|
|
@@ -35230,6 +35430,15 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35230
35430
|
}, LATE_ANSWER_MS);
|
|
35231
35431
|
lateTimer.unref();
|
|
35232
35432
|
socket.unref();
|
|
35433
|
+
if (afterTheWait !== "keep") {
|
|
35434
|
+
settle({
|
|
35435
|
+
ok: false,
|
|
35436
|
+
reason: "busy",
|
|
35437
|
+
stillWithTheGame: true,
|
|
35438
|
+
message: `${still} Nothing was cancelled: the game runs it when it gets to it, and what it answers then is not kept, so ask again once the game answers. A game held at a breakpoint instead is one debug_state stack answers about.`
|
|
35439
|
+
});
|
|
35440
|
+
return;
|
|
35441
|
+
}
|
|
35233
35442
|
settle({
|
|
35234
35443
|
ok: false,
|
|
35235
35444
|
reason: "busy",
|
|
@@ -35237,71 +35446,112 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
|
|
|
35237
35446
|
message: `${still} Nothing was cancelled: a call that takes longer than the wait goes on and does everything it was asked, so read the state back rather than repeating it. runtime_invoke op result with requestId ${id} answers with its reply once it comes. A game held at a breakpoint instead is one debug_state stack answers about.`
|
|
35238
35447
|
});
|
|
35239
35448
|
}, timeoutMs);
|
|
35240
|
-
|
|
35241
|
-
|
|
35242
|
-
socket
|
|
35449
|
+
const open = () => {
|
|
35450
|
+
const opened = createConnection({ port: endpoint.port, host: dialAddress(endpoint.address) });
|
|
35451
|
+
socket = opened;
|
|
35452
|
+
opened.setEncoding("utf8");
|
|
35453
|
+
opened.on("connect", () => {
|
|
35454
|
+
opened.write(`${JSON.stringify({ id, command, params })}
|
|
35243
35455
|
`);
|
|
35244
|
-
|
|
35245
|
-
|
|
35246
|
-
|
|
35247
|
-
|
|
35456
|
+
});
|
|
35457
|
+
opened.on("data", (chunk) => {
|
|
35458
|
+
buffered += chunk;
|
|
35459
|
+
let newline = buffered.indexOf(`
|
|
35248
35460
|
`);
|
|
35249
|
-
|
|
35250
|
-
|
|
35251
|
-
|
|
35252
|
-
|
|
35461
|
+
while (newline !== -1 && (!done || late)) {
|
|
35462
|
+
const line = buffered.slice(0, newline).trim();
|
|
35463
|
+
buffered = buffered.slice(newline + 1);
|
|
35464
|
+
newline = buffered.indexOf(`
|
|
35253
35465
|
`);
|
|
35254
|
-
|
|
35255
|
-
|
|
35256
|
-
|
|
35257
|
-
|
|
35258
|
-
|
|
35259
|
-
|
|
35260
|
-
|
|
35261
|
-
|
|
35262
|
-
|
|
35263
|
-
|
|
35264
|
-
|
|
35265
|
-
|
|
35266
|
-
|
|
35267
|
-
|
|
35268
|
-
|
|
35269
|
-
|
|
35270
|
-
|
|
35271
|
-
|
|
35272
|
-
|
|
35273
|
-
|
|
35466
|
+
if (line === "") {
|
|
35467
|
+
continue;
|
|
35468
|
+
}
|
|
35469
|
+
let message;
|
|
35470
|
+
try {
|
|
35471
|
+
message = asParams(JSON.parse(line));
|
|
35472
|
+
} catch {
|
|
35473
|
+
continue;
|
|
35474
|
+
}
|
|
35475
|
+
if (!welcomed) {
|
|
35476
|
+
welcomed = true;
|
|
35477
|
+
taken();
|
|
35478
|
+
if (readString(message, "type") === "welcome") {
|
|
35479
|
+
const spoken = readNumber(message, "protocol");
|
|
35480
|
+
if (spoken !== RUNTIME_PROTOCOL) {
|
|
35481
|
+
finish({
|
|
35482
|
+
ok: false,
|
|
35483
|
+
reason: "protocol",
|
|
35484
|
+
message: `The game (${describe2(endpoint)}) speaks runtime protocol ${spoken ?? "unknown"} and this server speaks ${RUNTIME_PROTOCOL}. Reinstall the runtime addon into the project.`
|
|
35485
|
+
});
|
|
35486
|
+
return;
|
|
35487
|
+
}
|
|
35488
|
+
continue;
|
|
35274
35489
|
}
|
|
35490
|
+
}
|
|
35491
|
+
if (message["id"] !== id) {
|
|
35275
35492
|
continue;
|
|
35276
35493
|
}
|
|
35494
|
+
if (readString(message, "type") === "error") {
|
|
35495
|
+
finish({
|
|
35496
|
+
ok: false,
|
|
35497
|
+
reason: "error",
|
|
35498
|
+
message: readString(message, "message") ?? "unknown error"
|
|
35499
|
+
});
|
|
35500
|
+
return;
|
|
35501
|
+
}
|
|
35502
|
+
finish({ ok: true, payload: message, endpoint });
|
|
35277
35503
|
}
|
|
35278
|
-
|
|
35279
|
-
|
|
35504
|
+
});
|
|
35505
|
+
opened.on("error", (error) => {
|
|
35506
|
+
if (socket !== opened) {
|
|
35507
|
+
return;
|
|
35280
35508
|
}
|
|
35281
|
-
if (
|
|
35282
|
-
|
|
35509
|
+
if (error.code === "ECONNREFUSED" && !done) {
|
|
35510
|
+
if (alive(endpoint.pid)) {
|
|
35511
|
+
refusals += 1;
|
|
35512
|
+
socket = null;
|
|
35513
|
+
opened.destroy();
|
|
35514
|
+
redial = setTimeout(() => {
|
|
35515
|
+
redial = null;
|
|
35516
|
+
open();
|
|
35517
|
+
}, REDIAL_MS);
|
|
35518
|
+
return;
|
|
35519
|
+
}
|
|
35520
|
+
finish({
|
|
35521
|
+
ok: false,
|
|
35522
|
+
reason: "refused",
|
|
35523
|
+
message: `The game (${describe2(endpoint)}) has ended: its port refused the connection and its process is gone.`
|
|
35524
|
+
});
|
|
35283
35525
|
return;
|
|
35284
35526
|
}
|
|
35285
|
-
finish({
|
|
35286
|
-
|
|
35287
|
-
|
|
35288
|
-
|
|
35289
|
-
|
|
35290
|
-
ok: false,
|
|
35291
|
-
reason: "refused",
|
|
35292
|
-
message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: ${error.code ?? error.message}. It may still be starting, or be shutting down.`
|
|
35527
|
+
finish({
|
|
35528
|
+
ok: false,
|
|
35529
|
+
reason: "refused",
|
|
35530
|
+
message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: ${error.code ?? error.message}. It may still be starting or be shutting down.`
|
|
35531
|
+
});
|
|
35293
35532
|
});
|
|
35294
|
-
|
|
35295
|
-
|
|
35296
|
-
|
|
35297
|
-
|
|
35298
|
-
|
|
35299
|
-
|
|
35533
|
+
opened.on("close", () => {
|
|
35534
|
+
if (socket !== opened) {
|
|
35535
|
+
return;
|
|
35536
|
+
}
|
|
35537
|
+
finish({
|
|
35538
|
+
ok: false,
|
|
35539
|
+
reason: "refused",
|
|
35540
|
+
message: `The game (${describe2(endpoint)}) closed the connection before answering '${command}'.`
|
|
35541
|
+
});
|
|
35300
35542
|
});
|
|
35543
|
+
};
|
|
35544
|
+
waitForAPlace(placeOf(endpoint), (giveBack) => {
|
|
35545
|
+
if (done) {
|
|
35546
|
+
giveBack();
|
|
35547
|
+
return;
|
|
35548
|
+
}
|
|
35549
|
+
giveBackThePlace = giveBack;
|
|
35550
|
+
open();
|
|
35301
35551
|
});
|
|
35302
35552
|
});
|
|
35303
35553
|
}
|
|
35304
|
-
var RUNTIME_PROTOCOL = 2, ANNOUNCEMENT_PATTERN, ERROR_REPORT_PATTERN, CONSOLE_BOUNDARY = "[gdharness] runtime listening on ", ERROR_REPORT_KEEP_MS, GONE_WINDOW_MS = 120000, GONE_KEPT = 32, gone, STARTED_AFTER_ANNOUNCING_MS = 2000, CONFIRMED_FOR_MS = 60000, JUDGED_AFTER_MS = 60000, verdicts, judging = null, ENDED_WITHIN_MS = 3000, LOOK_EVERY_MS = 50, ANNOUNCE_BUDGET_MS = 5000, nextRequestId = 1, LATE_ANSWER_MS, LATE_KEPT = 50, lateAnswers;
|
|
35554
|
+
var RUNTIME_PROTOCOL = 2, ANNOUNCEMENT_PATTERN, ERROR_REPORT_PATTERN, CONSOLE_BOUNDARY = "[gdharness] runtime listening on ", ERROR_REPORT_KEEP_MS, GONE_WINDOW_MS = 120000, GONE_KEPT = 32, gone, STARTED_AFTER_ANNOUNCING_MS = 2000, CONFIRMED_FOR_MS = 60000, JUDGED_AFTER_MS = 60000, verdicts, judging = null, ENDED_WITHIN_MS = 3000, LOOK_EVERY_MS = 50, ANNOUNCE_BUDGET_MS = 5000, nextRequestId = 1, LATE_ANSWER_MS, LATE_KEPT = 50, lateAnswers, UNTAKEN_PER_GAME = 4, REDIAL_MS = 50, untaken;
|
|
35305
35555
|
var init_runtime_client = __esm(() => {
|
|
35306
35556
|
init_alive();
|
|
35307
35557
|
init_launch();
|
|
@@ -35314,17 +35564,18 @@ var init_runtime_client = __esm(() => {
|
|
|
35314
35564
|
verdicts = new Map;
|
|
35315
35565
|
LATE_ANSWER_MS = 30 * 60000;
|
|
35316
35566
|
lateAnswers = new Map;
|
|
35567
|
+
untaken = new Map;
|
|
35317
35568
|
});
|
|
35318
35569
|
|
|
35319
35570
|
// src/boot-note.ts
|
|
35320
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
35321
|
-
import { dirname as
|
|
35571
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
|
|
35572
|
+
import { dirname as dirname11, join as join18 } from "node:path";
|
|
35322
35573
|
function bootNotePath(projectPath) {
|
|
35323
|
-
return
|
|
35574
|
+
return join18(projectPath, ".godot", "gdharness-boot.json");
|
|
35324
35575
|
}
|
|
35325
35576
|
function readScenes(projectPath) {
|
|
35326
35577
|
try {
|
|
35327
|
-
return asParams(asParams(JSON.parse(
|
|
35578
|
+
return asParams(asParams(JSON.parse(readFileSync15(bootNotePath(projectPath), "utf8")))["scenes"]);
|
|
35328
35579
|
} catch {
|
|
35329
35580
|
return {};
|
|
35330
35581
|
}
|
|
@@ -35340,7 +35591,7 @@ function readBootNote(projectPath, scene) {
|
|
|
35340
35591
|
function writeBootNote(projectPath, scene, announcedAfterMs) {
|
|
35341
35592
|
try {
|
|
35342
35593
|
const path = bootNotePath(projectPath);
|
|
35343
|
-
mkdirSync6(
|
|
35594
|
+
mkdirSync6(dirname11(path), { recursive: true });
|
|
35344
35595
|
const scenes = {
|
|
35345
35596
|
...readScenes(projectPath),
|
|
35346
35597
|
[scene]: {
|
|
@@ -35366,10 +35617,10 @@ var init_boot_note = __esm(() => {
|
|
|
35366
35617
|
});
|
|
35367
35618
|
|
|
35368
35619
|
// src/breakpoint-note.ts
|
|
35369
|
-
import { mkdirSync as mkdirSync7, readFileSync as
|
|
35370
|
-
import { dirname as
|
|
35620
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync16, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
35621
|
+
import { dirname as dirname12, isAbsolute as isAbsolute4, join as join19, relative as relative3 } from "node:path";
|
|
35371
35622
|
function breakpointNotePath(projectPath) {
|
|
35372
|
-
return
|
|
35623
|
+
return join19(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
35373
35624
|
}
|
|
35374
35625
|
function writeBreakpointNote(projectPath, held) {
|
|
35375
35626
|
const path = breakpointNotePath(projectPath);
|
|
@@ -35386,7 +35637,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
35386
35637
|
lines: [...one.lines]
|
|
35387
35638
|
}))
|
|
35388
35639
|
};
|
|
35389
|
-
mkdirSync7(
|
|
35640
|
+
mkdirSync7(dirname12(path), { recursive: true });
|
|
35390
35641
|
writeFileSync9(path, `${JSON.stringify(note, null, 2)}
|
|
35391
35642
|
`, "utf8");
|
|
35392
35643
|
} catch (error) {
|
|
@@ -35395,7 +35646,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
35395
35646
|
}
|
|
35396
35647
|
function readBreakpointNote(projectPath) {
|
|
35397
35648
|
try {
|
|
35398
|
-
const parsed = JSON.parse(
|
|
35649
|
+
const parsed = JSON.parse(readFileSync16(breakpointNotePath(projectPath), "utf8"));
|
|
35399
35650
|
if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
|
|
35400
35651
|
return [];
|
|
35401
35652
|
}
|
|
@@ -35405,12 +35656,12 @@ function readBreakpointNote(projectPath) {
|
|
|
35405
35656
|
continue;
|
|
35406
35657
|
}
|
|
35407
35658
|
const { script, lines: listed } = entry;
|
|
35408
|
-
if (typeof script !== "string" ||
|
|
35659
|
+
if (typeof script !== "string" || isAbsolute4(script) || script.split("/").includes("..")) {
|
|
35409
35660
|
continue;
|
|
35410
35661
|
}
|
|
35411
35662
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
35412
35663
|
if (lines.length > 0) {
|
|
35413
|
-
held.push({ scriptPath: realPathOr(
|
|
35664
|
+
held.push({ scriptPath: realPathOr(join19(projectPath, script)), lines });
|
|
35414
35665
|
}
|
|
35415
35666
|
}
|
|
35416
35667
|
return held;
|
|
@@ -35420,7 +35671,7 @@ function readBreakpointNote(projectPath) {
|
|
|
35420
35671
|
}
|
|
35421
35672
|
function withinProject(projectPath, scriptPath) {
|
|
35422
35673
|
const rel = relative3(projectPath, scriptPath);
|
|
35423
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
35674
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute4(rel);
|
|
35424
35675
|
}
|
|
35425
35676
|
var init_breakpoint_note = __esm(() => {
|
|
35426
35677
|
init_errors();
|
|
@@ -35459,14 +35710,14 @@ var init_whole_file = __esm(() => {
|
|
|
35459
35710
|
});
|
|
35460
35711
|
|
|
35461
35712
|
// src/class-note.ts
|
|
35462
|
-
import { mkdirSync as mkdirSync8, readFileSync as
|
|
35463
|
-
import { dirname as
|
|
35713
|
+
import { mkdirSync as mkdirSync8, readFileSync as readFileSync17 } from "node:fs";
|
|
35714
|
+
import { dirname as dirname13, join as join20 } from "node:path";
|
|
35464
35715
|
function classNotePath(projectPath) {
|
|
35465
|
-
return
|
|
35716
|
+
return join20(projectPath, ".godot", "gdharness-classes.json");
|
|
35466
35717
|
}
|
|
35467
35718
|
function readClassNote(projectPath) {
|
|
35468
35719
|
try {
|
|
35469
|
-
const fields = asParams(JSON.parse(
|
|
35720
|
+
const fields = asParams(JSON.parse(readFileSync17(classNotePath(projectPath), "utf8")));
|
|
35470
35721
|
const rebuiltWith = readArray(fields, "rebuiltWith");
|
|
35471
35722
|
if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
|
|
35472
35723
|
return null;
|
|
@@ -35496,7 +35747,7 @@ function unfinishedScanIn(value) {
|
|
|
35496
35747
|
function writeClassNote(projectPath, note) {
|
|
35497
35748
|
const path = classNotePath(projectPath);
|
|
35498
35749
|
try {
|
|
35499
|
-
mkdirSync8(
|
|
35750
|
+
mkdirSync8(dirname13(path), { recursive: true });
|
|
35500
35751
|
} catch {
|
|
35501
35752
|
return;
|
|
35502
35753
|
}
|
|
@@ -35508,16 +35759,16 @@ var init_class_note = __esm(() => {
|
|
|
35508
35759
|
});
|
|
35509
35760
|
|
|
35510
35761
|
// src/config-pin.ts
|
|
35511
|
-
import { existsSync as
|
|
35512
|
-
import { join as
|
|
35762
|
+
import { existsSync as existsSync17, readFileSync as readFileSync18 } from "node:fs";
|
|
35763
|
+
import { join as join21 } from "node:path";
|
|
35513
35764
|
function versionsNamedInConfig(projectPath) {
|
|
35514
|
-
const config =
|
|
35515
|
-
if (!
|
|
35765
|
+
const config = join21(projectPath, ".mcp.json");
|
|
35766
|
+
if (!existsSync17(config)) {
|
|
35516
35767
|
return [];
|
|
35517
35768
|
}
|
|
35518
35769
|
let text;
|
|
35519
35770
|
try {
|
|
35520
|
-
text =
|
|
35771
|
+
text = readFileSync18(config, "utf8");
|
|
35521
35772
|
JSON.parse(text);
|
|
35522
35773
|
} catch {
|
|
35523
35774
|
return [];
|
|
@@ -35538,26 +35789,26 @@ var init_config_pin = __esm(() => {
|
|
|
35538
35789
|
});
|
|
35539
35790
|
|
|
35540
35791
|
// src/cut-test-run.ts
|
|
35541
|
-
import { existsSync as
|
|
35542
|
-
import { dirname as
|
|
35792
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync19, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "node:fs";
|
|
35793
|
+
import { dirname as dirname14, join as join22 } from "node:path";
|
|
35543
35794
|
function keptAt(projectPath) {
|
|
35544
|
-
return
|
|
35795
|
+
return join22(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
35545
35796
|
}
|
|
35546
35797
|
function keepCutTestRun(projectPath, run) {
|
|
35547
35798
|
const path = keptAt(projectPath);
|
|
35548
35799
|
try {
|
|
35549
|
-
mkdirSync9(
|
|
35800
|
+
mkdirSync9(dirname14(path), { recursive: true });
|
|
35550
35801
|
writeFileSync11(path, `${JSON.stringify(run, null, 2)}
|
|
35551
35802
|
`, "utf8");
|
|
35552
35803
|
} catch {}
|
|
35553
35804
|
}
|
|
35554
35805
|
function takeCutTestRun(projectPath) {
|
|
35555
35806
|
const path = keptAt(projectPath);
|
|
35556
|
-
if (!
|
|
35807
|
+
if (!existsSync18(path)) {
|
|
35557
35808
|
return null;
|
|
35558
35809
|
}
|
|
35559
35810
|
try {
|
|
35560
|
-
const run = JSON.parse(
|
|
35811
|
+
const run = JSON.parse(readFileSync19(path, "utf8"));
|
|
35561
35812
|
rmSync9(path, { force: true });
|
|
35562
35813
|
return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
|
|
35563
35814
|
} catch {
|
|
@@ -36370,27 +36621,27 @@ var init_dap_client = __esm(() => {
|
|
|
36370
36621
|
import { createHash as createHash4 } from "node:crypto";
|
|
36371
36622
|
import {
|
|
36372
36623
|
closeSync,
|
|
36373
|
-
existsSync as
|
|
36624
|
+
existsSync as existsSync19,
|
|
36374
36625
|
mkdtempSync as mkdtempSync2,
|
|
36375
36626
|
openSync,
|
|
36376
|
-
readFileSync as
|
|
36627
|
+
readFileSync as readFileSync20,
|
|
36377
36628
|
rmSync as rmSync10,
|
|
36378
|
-
statSync as
|
|
36629
|
+
statSync as statSync9,
|
|
36379
36630
|
writeFileSync as writeFileSync12,
|
|
36380
36631
|
writeSync
|
|
36381
36632
|
} from "node:fs";
|
|
36382
36633
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
36383
|
-
import { join as
|
|
36634
|
+
import { join as join23 } from "node:path";
|
|
36384
36635
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
36385
36636
|
function helperAssembly(directory = tmpdir5()) {
|
|
36386
|
-
return
|
|
36637
|
+
return join23(directory, `gdharness-desktop-${DIGEST}.dll`);
|
|
36387
36638
|
}
|
|
36388
36639
|
async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COMPILE_MS, wait = true) {
|
|
36389
36640
|
const assembly = helperAssembly(directory);
|
|
36390
36641
|
const lock = `${assembly}.compiling`;
|
|
36391
36642
|
const until = Date.now() + budgetMs;
|
|
36392
36643
|
while (Date.now() < until) {
|
|
36393
|
-
if (
|
|
36644
|
+
if (existsSync19(assembly)) {
|
|
36394
36645
|
return null;
|
|
36395
36646
|
}
|
|
36396
36647
|
let mine = false;
|
|
@@ -36408,8 +36659,8 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
|
|
|
36408
36659
|
}
|
|
36409
36660
|
}
|
|
36410
36661
|
try {
|
|
36411
|
-
const holder = Number(
|
|
36412
|
-
const old = Date.now() -
|
|
36662
|
+
const holder = Number(readFileSync20(lock, "utf8").trim());
|
|
36663
|
+
const old = Date.now() - statSync9(lock).mtimeMs > budgetMs;
|
|
36413
36664
|
if (old || Number.isInteger(holder) && holder > 0 && !alive(holder)) {
|
|
36414
36665
|
rmSync10(lock, { force: true });
|
|
36415
36666
|
continue;
|
|
@@ -36423,8 +36674,8 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
|
|
|
36423
36674
|
return `the desktop helper was not compiled within ${String(budgetMs)} ms`;
|
|
36424
36675
|
}
|
|
36425
36676
|
async function compileHelper(directory, assembly, budgetMs) {
|
|
36426
|
-
const scratch = mkdtempSync2(
|
|
36427
|
-
const source =
|
|
36677
|
+
const scratch = mkdtempSync2(join23(directory, "gdharness-desktop-source-"));
|
|
36678
|
+
const source = join23(scratch, "helper.cs");
|
|
36428
36679
|
writeFileSync12(source, SOURCE, "utf8");
|
|
36429
36680
|
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
36430
36681
|
const script = [
|
|
@@ -36462,7 +36713,7 @@ async function compileHelper(directory, assembly, budgetMs) {
|
|
|
36462
36713
|
});
|
|
36463
36714
|
compiler.once("close", (code) => {
|
|
36464
36715
|
clearTimeout(timer);
|
|
36465
|
-
resolve(
|
|
36716
|
+
resolve(existsSync19(assembly) ? null : `Windows PowerShell could not compile it${code === null ? " in time" : ""}: ${complained.trim() || `exit code ${String(code)}`}`);
|
|
36466
36717
|
});
|
|
36467
36718
|
});
|
|
36468
36719
|
} finally {
|
|
@@ -36629,25 +36880,25 @@ var init_desktop = __esm(() => {
|
|
|
36629
36880
|
// src/editor-log.ts
|
|
36630
36881
|
import {
|
|
36631
36882
|
closeSync as closeSync2,
|
|
36632
|
-
existsSync as
|
|
36883
|
+
existsSync as existsSync20,
|
|
36633
36884
|
mkdirSync as mkdirSync10,
|
|
36634
36885
|
openSync as openSync2,
|
|
36635
|
-
readFileSync as
|
|
36886
|
+
readFileSync as readFileSync21,
|
|
36636
36887
|
readSync,
|
|
36637
36888
|
rmSync as rmSync11,
|
|
36638
|
-
statSync as
|
|
36889
|
+
statSync as statSync10,
|
|
36639
36890
|
writeFileSync as writeFileSync13
|
|
36640
36891
|
} from "node:fs";
|
|
36641
|
-
import { dirname as
|
|
36892
|
+
import { dirname as dirname15, join as join24 } from "node:path";
|
|
36642
36893
|
function editorLogPath(projectPath) {
|
|
36643
|
-
return
|
|
36894
|
+
return join24(projectPath, ".godot", "gdharness-editor.log");
|
|
36644
36895
|
}
|
|
36645
36896
|
function editorLogNotePath(projectPath) {
|
|
36646
|
-
return
|
|
36897
|
+
return join24(projectPath, ".godot", "gdharness-editor.json");
|
|
36647
36898
|
}
|
|
36648
36899
|
function readEditorLogNote(projectPath) {
|
|
36649
36900
|
try {
|
|
36650
|
-
const fields = asParams(JSON.parse(
|
|
36901
|
+
const fields = asParams(JSON.parse(readFileSync21(editorLogNotePath(projectPath), "utf8")));
|
|
36651
36902
|
const pid = readNumber(fields, "pid");
|
|
36652
36903
|
const startedAt = readString(fields, "startedAt");
|
|
36653
36904
|
return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
|
|
@@ -36658,7 +36909,7 @@ function readEditorLogNote(projectPath) {
|
|
|
36658
36909
|
function clearEditorLog(projectPath) {
|
|
36659
36910
|
try {
|
|
36660
36911
|
const path = editorLogPath(projectPath);
|
|
36661
|
-
mkdirSync10(
|
|
36912
|
+
mkdirSync10(dirname15(path), { recursive: true });
|
|
36662
36913
|
rmSync11(path, { force: true });
|
|
36663
36914
|
rmSync11(editorLogNotePath(projectPath), { force: true });
|
|
36664
36915
|
} catch {}
|
|
@@ -36666,7 +36917,7 @@ function clearEditorLog(projectPath) {
|
|
|
36666
36917
|
function writeEditorLogNote(projectPath, pid) {
|
|
36667
36918
|
try {
|
|
36668
36919
|
const path = editorLogNotePath(projectPath);
|
|
36669
|
-
mkdirSync10(
|
|
36920
|
+
mkdirSync10(dirname15(path), { recursive: true });
|
|
36670
36921
|
writeFileSync13(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
36671
36922
|
} catch {}
|
|
36672
36923
|
}
|
|
@@ -36688,7 +36939,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
36688
36939
|
return { kind: "older server", notedPid: noted?.pid ?? null };
|
|
36689
36940
|
}
|
|
36690
36941
|
const path = editorLogPath(projectPath);
|
|
36691
|
-
if (!
|
|
36942
|
+
if (!existsSync20(path)) {
|
|
36692
36943
|
return { kind: "not written" };
|
|
36693
36944
|
}
|
|
36694
36945
|
const log = new GameLog;
|
|
@@ -36744,9 +36995,9 @@ function foldProgress(text) {
|
|
|
36744
36995
|
`);
|
|
36745
36996
|
}
|
|
36746
36997
|
function theFirstOf(path) {
|
|
36747
|
-
const whole =
|
|
36998
|
+
const whole = statSync10(path).size;
|
|
36748
36999
|
if (whole <= MOST_CONSOLE_BYTES) {
|
|
36749
|
-
return { text:
|
|
37000
|
+
return { text: readFileSync21(path, "utf8") };
|
|
36750
37001
|
}
|
|
36751
37002
|
const handle = openSync2(path, "r");
|
|
36752
37003
|
try {
|
|
@@ -36907,8 +37158,8 @@ var init_editor_log = __esm(() => {
|
|
|
36907
37158
|
});
|
|
36908
37159
|
|
|
36909
37160
|
// src/rename.ts
|
|
36910
|
-
import { existsSync as
|
|
36911
|
-
import { dirname as
|
|
37161
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync22, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "node:fs";
|
|
37162
|
+
import { dirname as dirname16, posix as posix2 } from "node:path";
|
|
36912
37163
|
|
|
36913
37164
|
class Script {
|
|
36914
37165
|
path;
|
|
@@ -37612,25 +37863,25 @@ function rewritesOf(plan, texts, move) {
|
|
|
37612
37863
|
}
|
|
37613
37864
|
function applyRewrites(rewrites) {
|
|
37614
37865
|
for (const rewrite of rewrites) {
|
|
37615
|
-
const now =
|
|
37866
|
+
const now = readFileSync22(rewrite.absolute, "utf8");
|
|
37616
37867
|
if (now !== rewrite.before) {
|
|
37617
37868
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
37618
37869
|
}
|
|
37619
|
-
if (rewrite.writeTo !== rewrite.absolute &&
|
|
37870
|
+
if (rewrite.writeTo !== rewrite.absolute && existsSync21(rewrite.writeTo)) {
|
|
37620
37871
|
throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
|
|
37621
37872
|
}
|
|
37622
37873
|
}
|
|
37623
37874
|
const done = [];
|
|
37624
37875
|
try {
|
|
37625
37876
|
for (const rewrite of rewrites) {
|
|
37626
|
-
mkdirSync11(
|
|
37877
|
+
mkdirSync11(dirname16(rewrite.writeTo), { recursive: true });
|
|
37627
37878
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
37628
37879
|
writeFileSync14(staging, rewrite.after, "utf8");
|
|
37629
37880
|
renameSync3(staging, rewrite.writeTo);
|
|
37630
37881
|
let movedUid = false;
|
|
37631
37882
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
37632
37883
|
unlinkSync3(rewrite.absolute);
|
|
37633
|
-
if (
|
|
37884
|
+
if (existsSync21(`${rewrite.absolute}.uid`)) {
|
|
37634
37885
|
renameSync3(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
|
|
37635
37886
|
movedUid = true;
|
|
37636
37887
|
}
|
|
@@ -37676,10 +37927,10 @@ var init_rename = __esm(() => {
|
|
|
37676
37927
|
});
|
|
37677
37928
|
|
|
37678
37929
|
// src/lsp_client.ts
|
|
37679
|
-
import { existsSync as
|
|
37930
|
+
import { existsSync as existsSync22, realpathSync as realpathSync4 } from "node:fs";
|
|
37680
37931
|
import { readFile, realpath } from "node:fs/promises";
|
|
37681
37932
|
import { createConnection as createConnection3 } from "node:net";
|
|
37682
|
-
import { dirname as
|
|
37933
|
+
import { dirname as dirname17, join as join25, resolve as resolve5 } from "node:path";
|
|
37683
37934
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
37684
37935
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
37685
37936
|
function initializeTimeoutMs() {
|
|
@@ -37841,7 +38092,7 @@ class GodotLSPClient {
|
|
|
37841
38092
|
if (this.initialized) {
|
|
37842
38093
|
return;
|
|
37843
38094
|
}
|
|
37844
|
-
const rootPath = this.rootPath ??
|
|
38095
|
+
const rootPath = this.rootPath ?? dirname17(resolve5(filePath));
|
|
37845
38096
|
await this.initialize(rootPath);
|
|
37846
38097
|
}
|
|
37847
38098
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -38357,7 +38608,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
38357
38608
|
} catch {
|
|
38358
38609
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
38359
38610
|
}
|
|
38360
|
-
if (!
|
|
38611
|
+
if (!existsSync22(join25(projectPath, "project.godot"))) {
|
|
38361
38612
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
38362
38613
|
}
|
|
38363
38614
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -38517,8 +38768,8 @@ var init_lsp_client = __esm(() => {
|
|
|
38517
38768
|
});
|
|
38518
38769
|
|
|
38519
38770
|
// src/rename-symbol.ts
|
|
38520
|
-
import { existsSync as
|
|
38521
|
-
import { basename as
|
|
38771
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readdirSync as readdirSync11, realpathSync as realpathSync5, rmSync as rmSync12, writeFileSync as writeFileSync15 } from "node:fs";
|
|
38772
|
+
import { basename as basename7, dirname as dirname18, join as join26, relative as relative4, resolve as resolve6 } from "node:path";
|
|
38522
38773
|
function resourcePathOf(projectPath, absolute) {
|
|
38523
38774
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
38524
38775
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -38531,13 +38782,13 @@ function onDiskSpelling(path) {
|
|
|
38531
38782
|
let at = resolve6(path);
|
|
38532
38783
|
for (;; ) {
|
|
38533
38784
|
try {
|
|
38534
|
-
return
|
|
38785
|
+
return join26(realpathSync5.native(at), ...missing.reverse());
|
|
38535
38786
|
} catch {
|
|
38536
|
-
const parent =
|
|
38787
|
+
const parent = dirname18(at);
|
|
38537
38788
|
if (parent === at) {
|
|
38538
38789
|
return resolve6(path);
|
|
38539
38790
|
}
|
|
38540
|
-
missing.push(
|
|
38791
|
+
missing.push(basename7(at));
|
|
38541
38792
|
at = parent;
|
|
38542
38793
|
}
|
|
38543
38794
|
}
|
|
@@ -38584,7 +38835,7 @@ async function renameSymbol(request, services) {
|
|
|
38584
38835
|
reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
|
|
38585
38836
|
};
|
|
38586
38837
|
}
|
|
38587
|
-
if (
|
|
38838
|
+
if (existsSync23(request.newScriptPath)) {
|
|
38588
38839
|
return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
|
|
38589
38840
|
}
|
|
38590
38841
|
}
|
|
@@ -38760,7 +39011,7 @@ async function renameSymbol(request, services) {
|
|
|
38760
39011
|
scripts,
|
|
38761
39012
|
moves: new Map([[declaringPath, moveTo]]),
|
|
38762
39013
|
named: [declaringPath],
|
|
38763
|
-
exists: (path) =>
|
|
39014
|
+
exists: (path) => existsSync23(`${projectPath}/${path.slice("res://".length)}`)
|
|
38764
39015
|
}, plan);
|
|
38765
39016
|
}
|
|
38766
39017
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? [] : [declaringPath]);
|
|
@@ -38959,15 +39210,15 @@ ${mention.kind}`;
|
|
|
38959
39210
|
};
|
|
38960
39211
|
}
|
|
38961
39212
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
38962
|
-
const directory =
|
|
39213
|
+
const directory = join26(projectPath, ".godot", "gdharness-renames");
|
|
38963
39214
|
mkdirSync12(directory, { recursive: true });
|
|
38964
39215
|
detailWrites += 1;
|
|
38965
|
-
const path =
|
|
39216
|
+
const path = join26(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
38966
39217
|
writeFileSync15(path, `${JSON.stringify(detail, null, 2)}
|
|
38967
39218
|
`, "utf8");
|
|
38968
|
-
const kept =
|
|
39219
|
+
const kept = readdirSync11(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
38969
39220
|
for (const old of kept.slice(10)) {
|
|
38970
|
-
rmSync12(
|
|
39221
|
+
rmSync12(join26(directory, old), { force: true });
|
|
38971
39222
|
}
|
|
38972
39223
|
return path;
|
|
38973
39224
|
}
|
|
@@ -39008,25 +39259,25 @@ var init_rename_symbol = __esm(() => {
|
|
|
39008
39259
|
|
|
39009
39260
|
// src/file-move.ts
|
|
39010
39261
|
import {
|
|
39011
|
-
existsSync as
|
|
39262
|
+
existsSync as existsSync24,
|
|
39012
39263
|
mkdirSync as mkdirSync13,
|
|
39013
|
-
readdirSync as
|
|
39014
|
-
readFileSync as
|
|
39264
|
+
readdirSync as readdirSync12,
|
|
39265
|
+
readFileSync as readFileSync23,
|
|
39015
39266
|
renameSync as renameSync4,
|
|
39016
|
-
statSync as
|
|
39267
|
+
statSync as statSync11,
|
|
39017
39268
|
writeFileSync as writeFileSync16
|
|
39018
39269
|
} from "node:fs";
|
|
39019
|
-
import { basename as
|
|
39270
|
+
import { basename as basename8, dirname as dirname19, extname as extname3, join as join27, relative as relative5 } from "node:path";
|
|
39020
39271
|
function filesUnder(directory) {
|
|
39021
|
-
return
|
|
39022
|
-
const path =
|
|
39272
|
+
return readdirSync12(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
39273
|
+
const path = join27(directory, entry.name);
|
|
39023
39274
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
39024
39275
|
});
|
|
39025
39276
|
}
|
|
39026
39277
|
function sameFile(one, other) {
|
|
39027
39278
|
try {
|
|
39028
|
-
const a =
|
|
39029
|
-
const b =
|
|
39279
|
+
const a = statSync11(one);
|
|
39280
|
+
const b = statSync11(other);
|
|
39030
39281
|
return a.ino === b.ino && a.dev === b.dev;
|
|
39031
39282
|
} catch {
|
|
39032
39283
|
return false;
|
|
@@ -39040,8 +39291,8 @@ function unseenByTheEngine(projectPath, path) {
|
|
|
39040
39291
|
return `${path} is under ${step}, and the engine passes over every directory whose name starts with a dot`;
|
|
39041
39292
|
}
|
|
39042
39293
|
if (at < steps.length - 1) {
|
|
39043
|
-
directory =
|
|
39044
|
-
if (
|
|
39294
|
+
directory = join27(directory, step);
|
|
39295
|
+
if (existsSync24(join27(directory, ".gdignore"))) {
|
|
39045
39296
|
return `${path} is under a directory holding a .gdignore, which the engine does not load`;
|
|
39046
39297
|
}
|
|
39047
39298
|
}
|
|
@@ -39053,21 +39304,21 @@ function installedAddonOf(projectPath, path) {
|
|
|
39053
39304
|
if (steps[0] !== "addons" || steps[1] === undefined) {
|
|
39054
39305
|
return null;
|
|
39055
39306
|
}
|
|
39056
|
-
return
|
|
39307
|
+
return existsSync24(join27(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
|
|
39057
39308
|
}
|
|
39058
39309
|
function binaryResources(projectPath) {
|
|
39059
39310
|
const found = [];
|
|
39060
39311
|
const visit = (directory, prefix) => {
|
|
39061
|
-
if (
|
|
39312
|
+
if (existsSync24(join27(directory, ".gdignore"))) {
|
|
39062
39313
|
return;
|
|
39063
39314
|
}
|
|
39064
|
-
for (const entry of
|
|
39315
|
+
for (const entry of readdirSync12(directory, { withFileTypes: true })) {
|
|
39065
39316
|
if (entry.name.startsWith(".")) {
|
|
39066
39317
|
continue;
|
|
39067
39318
|
}
|
|
39068
39319
|
const path = `${prefix}${entry.name}`;
|
|
39069
39320
|
if (entry.isDirectory()) {
|
|
39070
|
-
visit(
|
|
39321
|
+
visit(join27(directory, entry.name), `${path}/`);
|
|
39071
39322
|
} else if (/\.(scn|res)$/i.test(entry.name)) {
|
|
39072
39323
|
found.push(path);
|
|
39073
39324
|
}
|
|
@@ -39077,7 +39328,7 @@ function binaryResources(projectPath) {
|
|
|
39077
39328
|
return found;
|
|
39078
39329
|
}
|
|
39079
39330
|
function targetPathOf(projectPath, absolute) {
|
|
39080
|
-
const inside = relative5(onDiskSpelling(projectPath),
|
|
39331
|
+
const inside = relative5(onDiskSpelling(projectPath), join27(onDiskSpelling(dirname19(absolute)), basename8(absolute)));
|
|
39081
39332
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
39082
39333
|
return null;
|
|
39083
39334
|
}
|
|
@@ -39095,13 +39346,13 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39095
39346
|
if (to === null) {
|
|
39096
39347
|
return { ok: false, reason: `newPath must be a path inside the project, and ${absoluteTo} is not.` };
|
|
39097
39348
|
}
|
|
39098
|
-
if (!
|
|
39349
|
+
if (!existsSync24(absoluteFrom)) {
|
|
39099
39350
|
return { ok: false, reason: `There is nothing at ${from} to move.` };
|
|
39100
39351
|
}
|
|
39101
39352
|
if (from === to) {
|
|
39102
39353
|
return { ok: false, reason: `${from} is already there; nothing to move.` };
|
|
39103
39354
|
}
|
|
39104
|
-
const isDirectory =
|
|
39355
|
+
const isDirectory = statSync11(absoluteFrom).isDirectory();
|
|
39105
39356
|
if (from === "res://project.godot") {
|
|
39106
39357
|
return {
|
|
39107
39358
|
ok: false,
|
|
@@ -39124,7 +39375,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39124
39375
|
if (isDirectory && to.startsWith(`${from}/`)) {
|
|
39125
39376
|
return { ok: false, reason: `${from} cannot be moved into itself.` };
|
|
39126
39377
|
}
|
|
39127
|
-
if (
|
|
39378
|
+
if (existsSync24(absoluteTo) && !sameFile(absoluteFrom, absoluteTo)) {
|
|
39128
39379
|
return { ok: false, reason: `${to} already exists, so nothing was moved there.` };
|
|
39129
39380
|
}
|
|
39130
39381
|
const unseen = unseenByTheEngine(projectPath, to);
|
|
@@ -39132,7 +39383,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39132
39383
|
return { ok: false, reason: `${unseen}, so nothing there could load what was moved.` };
|
|
39133
39384
|
}
|
|
39134
39385
|
if (!isDirectory) {
|
|
39135
|
-
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null &&
|
|
39386
|
+
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync24(join27(projectPath, path.slice("res://".length))));
|
|
39136
39387
|
if (owner !== undefined && owner !== null) {
|
|
39137
39388
|
return { ok: false, reason: `${from} belongs to ${owner} and moves with it: move ${owner} instead.` };
|
|
39138
39389
|
}
|
|
@@ -39154,7 +39405,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39154
39405
|
} else {
|
|
39155
39406
|
moves.set(from, to);
|
|
39156
39407
|
for (const sidecar of SIDECARS) {
|
|
39157
|
-
if (
|
|
39408
|
+
if (existsSync24(`${absoluteFrom}${sidecar}`)) {
|
|
39158
39409
|
moves.set(`${from}${sidecar}`, `${to}${sidecar}`);
|
|
39159
39410
|
}
|
|
39160
39411
|
}
|
|
@@ -39166,12 +39417,12 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39166
39417
|
scripts,
|
|
39167
39418
|
moves,
|
|
39168
39419
|
named: [from],
|
|
39169
|
-
exists: (path) =>
|
|
39420
|
+
exists: (path) => existsSync24(join27(projectPath, path.slice("res://".length)))
|
|
39170
39421
|
}, { edits: [], mentions: [] });
|
|
39171
39422
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
|
|
39172
39423
|
const binaries = [];
|
|
39173
39424
|
for (const path of binaryResources(projectPath)) {
|
|
39174
|
-
const bytes =
|
|
39425
|
+
const bytes = readFileSync23(join27(projectPath, path.slice("res://".length)));
|
|
39175
39426
|
const names = [...moves.keys()].find((moving) => bytes.includes(moving));
|
|
39176
39427
|
if (names !== undefined) {
|
|
39177
39428
|
binaries.push({
|
|
@@ -39190,7 +39441,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39190
39441
|
}));
|
|
39191
39442
|
const left = leftAloneReport(mentions);
|
|
39192
39443
|
const rewrites = rewritesOf(plan, texts, null);
|
|
39193
|
-
const absoluteOf = (path) =>
|
|
39444
|
+
const absoluteOf = (path) => join27(projectPath, path.slice("res://".length));
|
|
39194
39445
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
39195
39446
|
const moved = [...moves].map(([source, target]) => {
|
|
39196
39447
|
const rewrite = rewritten.get(source);
|
|
@@ -39232,24 +39483,24 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39232
39483
|
}
|
|
39233
39484
|
function applyFileMove(outcome) {
|
|
39234
39485
|
for (const rewrite of outcome.inPlace) {
|
|
39235
|
-
if (
|
|
39486
|
+
if (readFileSync23(rewrite.absolute, "utf8") !== rewrite.before) {
|
|
39236
39487
|
throw new Error(`${rewrite.path} changed on disk while the move was being worked out; nothing was written`);
|
|
39237
39488
|
}
|
|
39238
39489
|
}
|
|
39239
39490
|
for (const file of outcome.moved) {
|
|
39240
|
-
if (!
|
|
39491
|
+
if (!existsSync24(file.absoluteFrom)) {
|
|
39241
39492
|
throw new Error(`${file.from} is gone from disk; nothing was written`);
|
|
39242
39493
|
}
|
|
39243
|
-
if (file.text !== null &&
|
|
39494
|
+
if (file.text !== null && readFileSync23(file.absoluteFrom, "utf8") !== file.text.before) {
|
|
39244
39495
|
throw new Error(`${file.from} changed on disk while the move was being worked out; nothing was written`);
|
|
39245
39496
|
}
|
|
39246
|
-
if (
|
|
39497
|
+
if (existsSync24(file.absoluteTo) && !sameFile(file.absoluteFrom, file.absoluteTo)) {
|
|
39247
39498
|
throw new Error(`${file.to} already exists; nothing was written`);
|
|
39248
39499
|
}
|
|
39249
39500
|
}
|
|
39250
39501
|
const undo = [];
|
|
39251
39502
|
const rename = (source, target) => {
|
|
39252
|
-
mkdirSync13(
|
|
39503
|
+
mkdirSync13(dirname19(target), { recursive: true });
|
|
39253
39504
|
renameSync4(source, target);
|
|
39254
39505
|
undo.push(() => {
|
|
39255
39506
|
renameSync4(target, source);
|
|
@@ -43435,25 +43686,25 @@ var DEBUG_PREFIX = "debug ";
|
|
|
43435
43686
|
var init_keeper_channel = () => {};
|
|
43436
43687
|
|
|
43437
43688
|
// src/lifecycle-log.ts
|
|
43438
|
-
import { appendFileSync, mkdirSync as mkdirSync14, renameSync as renameSync5, statSync as
|
|
43439
|
-
import { dirname as
|
|
43440
|
-
import
|
|
43689
|
+
import { appendFileSync, mkdirSync as mkdirSync14, renameSync as renameSync5, statSync as statSync12 } from "node:fs";
|
|
43690
|
+
import { dirname as dirname20, join as join28 } from "node:path";
|
|
43691
|
+
import process11 from "node:process";
|
|
43441
43692
|
function lifecycleLogPath() {
|
|
43442
|
-
return
|
|
43693
|
+
return join28(runtimeDirectory(), "servers.log");
|
|
43443
43694
|
}
|
|
43444
43695
|
function writeLifecycle(event, fields = {}) {
|
|
43445
43696
|
const path = lifecycleLogPath();
|
|
43446
43697
|
try {
|
|
43447
|
-
mkdirSync14(
|
|
43448
|
-
if ((
|
|
43698
|
+
mkdirSync14(dirname20(path), { recursive: true });
|
|
43699
|
+
if ((statSync12(path, { throwIfNoEntry: false })?.size ?? 0) > LIFECYCLE_LOG_LIMIT) {
|
|
43449
43700
|
try {
|
|
43450
43701
|
renameSync5(path, `${path}.1`);
|
|
43451
43702
|
} catch {}
|
|
43452
43703
|
}
|
|
43453
43704
|
const line = {
|
|
43454
43705
|
at: new Date().toISOString(),
|
|
43455
|
-
pid:
|
|
43456
|
-
ppid:
|
|
43706
|
+
pid: process11.pid,
|
|
43707
|
+
ppid: process11.ppid,
|
|
43457
43708
|
version: SERVER_VERSION,
|
|
43458
43709
|
event
|
|
43459
43710
|
};
|
|
@@ -43759,15 +44010,15 @@ var init_prompts = __esm(() => {
|
|
|
43759
44010
|
});
|
|
43760
44011
|
|
|
43761
44012
|
// src/restart-note.ts
|
|
43762
|
-
import { mkdirSync as mkdirSync15, readFileSync as
|
|
43763
|
-
import { dirname as
|
|
44013
|
+
import { mkdirSync as mkdirSync15, readFileSync as readFileSync24, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
|
|
44014
|
+
import { dirname as dirname21, join as join29 } from "node:path";
|
|
43764
44015
|
function restartNotePath(projectPath) {
|
|
43765
|
-
return
|
|
44016
|
+
return join29(projectPath, ".godot", "gdharness-restart.json");
|
|
43766
44017
|
}
|
|
43767
44018
|
function noteRestartBegun(note) {
|
|
43768
44019
|
const path = restartNotePath(note.projectPath);
|
|
43769
44020
|
try {
|
|
43770
|
-
mkdirSync15(
|
|
44021
|
+
mkdirSync15(dirname21(path), { recursive: true });
|
|
43771
44022
|
writeFileSync17(path, `${JSON.stringify(note, null, 2)}
|
|
43772
44023
|
`, "utf8");
|
|
43773
44024
|
} catch (error) {
|
|
@@ -43776,7 +44027,7 @@ function noteRestartBegun(note) {
|
|
|
43776
44027
|
}
|
|
43777
44028
|
function restartOwed(projectPath) {
|
|
43778
44029
|
try {
|
|
43779
|
-
const parsed = JSON.parse(
|
|
44030
|
+
const parsed = JSON.parse(readFileSync24(restartNotePath(projectPath), "utf8"));
|
|
43780
44031
|
if (typeof parsed !== "object" || parsed === null) {
|
|
43781
44032
|
return null;
|
|
43782
44033
|
}
|
|
@@ -43802,23 +44053,23 @@ var init_restart_note = __esm(() => {
|
|
|
43802
44053
|
|
|
43803
44054
|
// src/run-record.ts
|
|
43804
44055
|
import { createHash as createHash5 } from "node:crypto";
|
|
43805
|
-
import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as
|
|
43806
|
-
import { basename as
|
|
44056
|
+
import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync13, readFileSync as readFileSync25, realpathSync as realpathSync6, rmSync as rmSync14, statSync as statSync13 } from "node:fs";
|
|
44057
|
+
import { basename as basename9, dirname as dirname22, join as join30, resolve as resolve7 } from "node:path";
|
|
43807
44058
|
function runsDirectory() {
|
|
43808
|
-
return
|
|
44059
|
+
return join30(runtimeDirectory(), "runs");
|
|
43809
44060
|
}
|
|
43810
44061
|
function realAsFarAsItExists(path) {
|
|
43811
44062
|
const missing = [];
|
|
43812
44063
|
let at = path;
|
|
43813
44064
|
for (;; ) {
|
|
43814
44065
|
try {
|
|
43815
|
-
return
|
|
44066
|
+
return join30(realpathSync6.native(at), ...missing);
|
|
43816
44067
|
} catch {
|
|
43817
|
-
const parent =
|
|
44068
|
+
const parent = dirname22(at);
|
|
43818
44069
|
if (parent === at) {
|
|
43819
44070
|
return path;
|
|
43820
44071
|
}
|
|
43821
|
-
missing.unshift(
|
|
44072
|
+
missing.unshift(basename9(at));
|
|
43822
44073
|
at = parent;
|
|
43823
44074
|
}
|
|
43824
44075
|
}
|
|
@@ -43829,10 +44080,10 @@ function projectKey(projectPath) {
|
|
|
43829
44080
|
return createHash5("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
43830
44081
|
}
|
|
43831
44082
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
43832
|
-
return
|
|
44083
|
+
return join30(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
43833
44084
|
}
|
|
43834
44085
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
43835
|
-
return
|
|
44086
|
+
return join30(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
43836
44087
|
}
|
|
43837
44088
|
function writeLatestRun(servedBy, latest) {
|
|
43838
44089
|
try {
|
|
@@ -43842,7 +44093,7 @@ function writeLatestRun(servedBy, latest) {
|
|
|
43842
44093
|
}
|
|
43843
44094
|
function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
43844
44095
|
try {
|
|
43845
|
-
const fields = JSON.parse(
|
|
44096
|
+
const fields = JSON.parse(readFileSync25(latestRunPath(servedBy, directory), "utf8"));
|
|
43846
44097
|
if (typeof fields !== "object" || fields === null) {
|
|
43847
44098
|
return null;
|
|
43848
44099
|
}
|
|
@@ -43857,17 +44108,20 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
43857
44108
|
}
|
|
43858
44109
|
function openTranscript(startedAt) {
|
|
43859
44110
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
43860
|
-
const path =
|
|
44111
|
+
const path = join30(runsDirectory(), `run-${startedAt}.log`);
|
|
43861
44112
|
return { path, fd: openSync3(path, "a") };
|
|
43862
44113
|
}
|
|
43863
44114
|
function profilePath(startedAt) {
|
|
43864
|
-
return
|
|
44115
|
+
return join30(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
44116
|
+
}
|
|
44117
|
+
function profileMarkPath(startedAt) {
|
|
44118
|
+
return join30(runsDirectory(), `run-${startedAt}.profile-mark.json`);
|
|
43865
44119
|
}
|
|
43866
44120
|
function noteIsAbout(note, playing) {
|
|
43867
44121
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
43868
44122
|
}
|
|
43869
44123
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
43870
|
-
return
|
|
44124
|
+
return join30(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
43871
44125
|
}
|
|
43872
44126
|
function writeEditorRunNote(note) {
|
|
43873
44127
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
@@ -43878,7 +44132,7 @@ function writeEditorRunNote(note) {
|
|
|
43878
44132
|
}
|
|
43879
44133
|
function readEditorRunNote(projectPath) {
|
|
43880
44134
|
for (const directory of runtimeDirectories()) {
|
|
43881
|
-
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(
|
|
44135
|
+
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join30(directory, "runs", "editor-run.json"));
|
|
43882
44136
|
if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
|
|
43883
44137
|
return note;
|
|
43884
44138
|
}
|
|
@@ -43888,7 +44142,7 @@ function readEditorRunNote(projectPath) {
|
|
|
43888
44142
|
function editorNoteAt(path) {
|
|
43889
44143
|
let parsed;
|
|
43890
44144
|
try {
|
|
43891
|
-
parsed = JSON.parse(
|
|
44145
|
+
parsed = JSON.parse(readFileSync25(path, "utf8"));
|
|
43892
44146
|
} catch {
|
|
43893
44147
|
return null;
|
|
43894
44148
|
}
|
|
@@ -43912,7 +44166,7 @@ function readRunRecord(projectPath) {
|
|
|
43912
44166
|
if (own !== null) {
|
|
43913
44167
|
return own;
|
|
43914
44168
|
}
|
|
43915
|
-
const shared = recordAt(
|
|
44169
|
+
const shared = recordAt(join30(directory, "runs", SHARED_NOTE));
|
|
43916
44170
|
if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
|
|
43917
44171
|
return shared;
|
|
43918
44172
|
}
|
|
@@ -43924,13 +44178,13 @@ function everyRunRecord() {
|
|
|
43924
44178
|
for (const directory of runtimeDirectories()) {
|
|
43925
44179
|
let entries;
|
|
43926
44180
|
try {
|
|
43927
|
-
entries =
|
|
44181
|
+
entries = readdirSync13(join30(directory, "runs"));
|
|
43928
44182
|
} catch {
|
|
43929
44183
|
continue;
|
|
43930
44184
|
}
|
|
43931
44185
|
for (const entry of entries) {
|
|
43932
44186
|
if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
43933
|
-
const record = recordAt(
|
|
44187
|
+
const record = recordAt(join30(directory, "runs", entry));
|
|
43934
44188
|
if (record !== null) {
|
|
43935
44189
|
found.push(record);
|
|
43936
44190
|
}
|
|
@@ -43942,7 +44196,7 @@ function everyRunRecord() {
|
|
|
43942
44196
|
function recordAt(path) {
|
|
43943
44197
|
let parsed;
|
|
43944
44198
|
try {
|
|
43945
|
-
parsed = JSON.parse(
|
|
44199
|
+
parsed = JSON.parse(readFileSync25(path, "utf8"));
|
|
43946
44200
|
} catch {
|
|
43947
44201
|
return null;
|
|
43948
44202
|
}
|
|
@@ -43995,7 +44249,7 @@ async function runningAs(pid) {
|
|
|
43995
44249
|
const began = await startedAt(pid, null);
|
|
43996
44250
|
const when = began === null ? {} : { startedAt: began };
|
|
43997
44251
|
if (process.platform === "linux") {
|
|
43998
|
-
const raw =
|
|
44252
|
+
const raw = readFileSync25(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
43999
44253
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
44000
44254
|
}
|
|
44001
44255
|
const args = (await runHidden("ps", ["-p", String(pid), "-o", "args="], { timeout: 15000 })).stdout.trim();
|
|
@@ -44099,7 +44353,7 @@ function whyNotTheRun(record, running, needed) {
|
|
|
44099
44353
|
if (after > SAME_RUN_WINDOW_MS) {
|
|
44100
44354
|
return `that process started ${Math.round(after / 1000)} seconds after the run had`;
|
|
44101
44355
|
}
|
|
44102
|
-
const engine = record.command === undefined ? null :
|
|
44356
|
+
const engine = record.command === undefined ? null : basename9(record.command);
|
|
44103
44357
|
const said = process.platform === "win32" ? running.text.toLowerCase() : running.text;
|
|
44104
44358
|
const wanted = engine === null || process.platform !== "win32" ? engine : engine.toLowerCase();
|
|
44105
44359
|
if (wanted !== null && !said.includes(wanted)) {
|
|
@@ -44138,7 +44392,7 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
|
|
|
44138
44392
|
}
|
|
44139
44393
|
function clearRunRecord(projectPath) {
|
|
44140
44394
|
for (const directory of runtimeDirectories()) {
|
|
44141
|
-
const shared =
|
|
44395
|
+
const shared = join30(directory, "runs", SHARED_NOTE);
|
|
44142
44396
|
const sharedRecord = recordAt(shared);
|
|
44143
44397
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
44144
44398
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
@@ -44150,7 +44404,7 @@ function clearRunRecord(projectPath) {
|
|
|
44150
44404
|
}
|
|
44151
44405
|
function ageMs(path, now) {
|
|
44152
44406
|
try {
|
|
44153
|
-
return now -
|
|
44407
|
+
return now - statSync13(path).mtimeMs;
|
|
44154
44408
|
} catch {
|
|
44155
44409
|
return 0;
|
|
44156
44410
|
}
|
|
@@ -44159,20 +44413,21 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
44159
44413
|
const directory = runsDirectory();
|
|
44160
44414
|
const kept = new Set(everyRunRecord().flatMap((record) => [
|
|
44161
44415
|
record.transcript,
|
|
44416
|
+
profileMarkPath(record.startedAt),
|
|
44162
44417
|
...record.profile === undefined ? [] : [record.profile]
|
|
44163
44418
|
]));
|
|
44164
44419
|
let entries;
|
|
44165
44420
|
try {
|
|
44166
|
-
entries =
|
|
44421
|
+
entries = readdirSync13(directory);
|
|
44167
44422
|
} catch {
|
|
44168
44423
|
return;
|
|
44169
44424
|
}
|
|
44170
44425
|
for (const entry of entries) {
|
|
44171
|
-
const transcript = entry.startsWith("run-") && (entry.endsWith(".log") || entry.endsWith(".profile.json"));
|
|
44426
|
+
const transcript = entry.startsWith("run-") && (entry.endsWith(".log") || entry.endsWith(".profile.json") || entry.endsWith(".profile-mark.json"));
|
|
44172
44427
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
44173
44428
|
continue;
|
|
44174
44429
|
}
|
|
44175
|
-
const path =
|
|
44430
|
+
const path = join30(directory, entry);
|
|
44176
44431
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
44177
44432
|
continue;
|
|
44178
44433
|
}
|
|
@@ -44193,7 +44448,7 @@ var init_run_record = __esm(() => {
|
|
|
44193
44448
|
});
|
|
44194
44449
|
|
|
44195
44450
|
// src/script-origins.ts
|
|
44196
|
-
import { join as
|
|
44451
|
+
import { join as join31 } from "node:path";
|
|
44197
44452
|
function unresolvedClassIn(message) {
|
|
44198
44453
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
44199
44454
|
}
|
|
@@ -44230,6 +44485,12 @@ async function originsOf(errors, declared, check) {
|
|
|
44230
44485
|
}
|
|
44231
44486
|
return { origins, unconfirmed };
|
|
44232
44487
|
}
|
|
44488
|
+
function compileErrorsIn(entries) {
|
|
44489
|
+
return entries.flatMap((entry) => {
|
|
44490
|
+
const at = entry.severity === "error" ? reloadedAt(entry) : null;
|
|
44491
|
+
return at === null ? [] : [{ ...at, message: entry.text }];
|
|
44492
|
+
});
|
|
44493
|
+
}
|
|
44233
44494
|
function reloadedAt(entry) {
|
|
44234
44495
|
for (const line of entry.detail) {
|
|
44235
44496
|
const at = RELOADED_AT.exec(line.trim());
|
|
@@ -44246,7 +44507,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
44246
44507
|
const ran = await runEngine(godotPath, [
|
|
44247
44508
|
"--headless",
|
|
44248
44509
|
"--log-file",
|
|
44249
|
-
|
|
44510
|
+
join31(logDir, "engine.log"),
|
|
44250
44511
|
"--path",
|
|
44251
44512
|
projectPath,
|
|
44252
44513
|
"--check-only",
|
|
@@ -44291,10 +44552,10 @@ var init_script_origins = __esm(() => {
|
|
|
44291
44552
|
});
|
|
44292
44553
|
|
|
44293
44554
|
// src/script-profile.ts
|
|
44294
|
-
import { readFileSync as
|
|
44555
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
44295
44556
|
function readProfileFile(path) {
|
|
44296
44557
|
try {
|
|
44297
|
-
const parsed = JSON.parse(
|
|
44558
|
+
const parsed = JSON.parse(readFileSync26(path, "utf8"));
|
|
44298
44559
|
return typeof parsed === "object" && typeof parsed.connected === "boolean" ? parsed : null;
|
|
44299
44560
|
} catch {
|
|
44300
44561
|
return null;
|
|
@@ -44322,12 +44583,16 @@ function signatureParts(signature) {
|
|
|
44322
44583
|
function profiledFunctions(totals, limit, sourceOf = () => null) {
|
|
44323
44584
|
const perCallUs = perCallCostOf(totals);
|
|
44324
44585
|
const rows = rowsOf(totals, sourceOf).map((row) => withOverhead(row, perCallUs));
|
|
44325
|
-
const
|
|
44586
|
+
const called = Object.entries(totals.functions).filter(([signature, one]) => one.calls > 0 && !isCalibration(signature));
|
|
44587
|
+
const addon = called.filter(([signature]) => isRuntimeAddon(signature));
|
|
44588
|
+
const selfOf = (entries) => ms(entries.reduce((sum, [, one]) => sum + one.selfSeconds, 0));
|
|
44326
44589
|
return {
|
|
44327
44590
|
functions: rows.slice(0, limit).map((row) => placedLambda(row, sourceOf)),
|
|
44328
44591
|
omitted: Math.max(0, rows.length - limit),
|
|
44329
|
-
scriptMs,
|
|
44592
|
+
scriptMs: selfOf(called.filter(([signature]) => !isRuntimeAddon(signature))),
|
|
44330
44593
|
counted: rows.length,
|
|
44594
|
+
addonMs: selfOf(addon),
|
|
44595
|
+
addonFunctions: addon.length,
|
|
44331
44596
|
superCallers: rows.filter((row) => row.selfIncludesSuper === true).length,
|
|
44332
44597
|
perCallUs,
|
|
44333
44598
|
overheadDominated: rows.filter((row) => row.overheadMs !== undefined).length,
|
|
@@ -44335,10 +44600,59 @@ function profiledFunctions(totals, limit, sourceOf = () => null) {
|
|
|
44335
44600
|
overheadReach: rows.findLastIndex((row) => row.overheadMs !== undefined) + 1
|
|
44336
44601
|
};
|
|
44337
44602
|
}
|
|
44603
|
+
function isRuntimeAddon(signature) {
|
|
44604
|
+
return signatureParts(signature).script.startsWith(RUNTIME_ADDON2);
|
|
44605
|
+
}
|
|
44338
44606
|
function isCalibration(signature) {
|
|
44339
44607
|
const parts = signatureParts(signature);
|
|
44340
44608
|
return parts.script.endsWith(RUNTIME_SCRIPT) && (parts.function === PROFILER_PROBE || parts.function === PROFILER_CALIBRATOR);
|
|
44341
44609
|
}
|
|
44610
|
+
function writeProfileMark(path, mark) {
|
|
44611
|
+
return writeWhole(path, JSON.stringify(mark));
|
|
44612
|
+
}
|
|
44613
|
+
function readProfileMark(path) {
|
|
44614
|
+
try {
|
|
44615
|
+
const parsed = JSON.parse(readFileSync26(path, "utf8"));
|
|
44616
|
+
return typeof parsed === "object" && typeof parsed.totals === "object" ? parsed : null;
|
|
44617
|
+
} catch {
|
|
44618
|
+
return null;
|
|
44619
|
+
}
|
|
44620
|
+
}
|
|
44621
|
+
function totalsSince(now, mark) {
|
|
44622
|
+
if (now.frames < mark.frames || now.startedAt !== mark.startedAt) {
|
|
44623
|
+
return null;
|
|
44624
|
+
}
|
|
44625
|
+
let shrunk = 0;
|
|
44626
|
+
const functions = {};
|
|
44627
|
+
for (const [signature, sum] of Object.entries(now.functions)) {
|
|
44628
|
+
const before = mark.functions[signature];
|
|
44629
|
+
if (isCalibration(signature) || before === undefined) {
|
|
44630
|
+
functions[signature] = sum;
|
|
44631
|
+
continue;
|
|
44632
|
+
}
|
|
44633
|
+
const added = {
|
|
44634
|
+
calls: sum.calls - before.calls,
|
|
44635
|
+
selfSeconds: sum.selfSeconds - before.selfSeconds,
|
|
44636
|
+
totalSeconds: sum.totalSeconds - before.totalSeconds
|
|
44637
|
+
};
|
|
44638
|
+
if (added.calls < 0 || added.selfSeconds < 0 || added.totalSeconds < 0) {
|
|
44639
|
+
shrunk += 1;
|
|
44640
|
+
} else if (added.calls > 0) {
|
|
44641
|
+
functions[signature] = added;
|
|
44642
|
+
}
|
|
44643
|
+
}
|
|
44644
|
+
shrunk += Object.keys(mark.functions).filter((signature) => now.functions[signature] === undefined && (mark.functions[signature]?.calls ?? 0) > 0).length;
|
|
44645
|
+
return {
|
|
44646
|
+
totals: {
|
|
44647
|
+
...now,
|
|
44648
|
+
frames: now.frames - mark.frames,
|
|
44649
|
+
unreadable: Math.max(0, now.unreadable - mark.unreadable),
|
|
44650
|
+
...now.cappedFrames === undefined ? {} : { cappedFrames: Math.max(0, now.cappedFrames - (mark.cappedFrames ?? 0)) },
|
|
44651
|
+
functions
|
|
44652
|
+
},
|
|
44653
|
+
shrunk
|
|
44654
|
+
};
|
|
44655
|
+
}
|
|
44342
44656
|
function perCallCostOf(totals) {
|
|
44343
44657
|
for (const [signature, sum] of Object.entries(totals.functions)) {
|
|
44344
44658
|
const parts = signatureParts(signature);
|
|
@@ -44364,7 +44678,7 @@ function rowsOf(totals, sourceOf) {
|
|
|
44364
44678
|
}
|
|
44365
44679
|
return codes.get(script) ?? null;
|
|
44366
44680
|
};
|
|
44367
|
-
return Object.entries(totals.functions).filter(([signature, sum]) => sum.calls > 0 && !isCalibration(signature)).map(([signature, sum]) => {
|
|
44681
|
+
return Object.entries(totals.functions).filter(([signature, sum]) => sum.calls > 0 && !isRuntimeAddon(signature) && !isCalibration(signature)).map(([signature, sum]) => {
|
|
44368
44682
|
const parts = signatureParts(signature);
|
|
44369
44683
|
const code = codeOfScript(parts.script);
|
|
44370
44684
|
const property = propertyOf(parts.function);
|
|
@@ -44440,7 +44754,7 @@ function callersOf(totals, name, scripts) {
|
|
|
44440
44754
|
property
|
|
44441
44755
|
};
|
|
44442
44756
|
}
|
|
44443
|
-
var QUEUE_SETTING = "limits/debugger/max_queued_messages", QUEUE_DEFAULT = 2048, QUEUE_HEADROOM = 512, UNNAMED_PREFIX = "#", PROFILE_FROM_START = "GDHARNESS_PROFILE_FROM_START", ms = (seconds) => Math.round(seconds * 1e6) / 1000, RUNTIME_SCRIPT = "gdharness_runtime/runtime_autoload.gd", PROFILER_PROBE = "_profiler_probe", PROFILER_CALIBRATOR = "_calibrate_the_profiler", CALL_SITES = 40;
|
|
44757
|
+
var QUEUE_SETTING = "limits/debugger/max_queued_messages", QUEUE_DEFAULT = 2048, QUEUE_HEADROOM = 512, UNNAMED_PREFIX = "#", PROFILE_FROM_START = "GDHARNESS_PROFILE_FROM_START", ms = (seconds) => Math.round(seconds * 1e6) / 1000, RUNTIME_SCRIPT = "gdharness_runtime/runtime_autoload.gd", PROFILER_PROBE = "_profiler_probe", PROFILER_CALIBRATOR = "_calibrate_the_profiler", RUNTIME_ADDON2 = "res://addons/gdharness_runtime/", CALL_SITES = 40;
|
|
44444
44758
|
var init_script_profile = __esm(() => {
|
|
44445
44759
|
init_gdscript_source();
|
|
44446
44760
|
init_godot_variant();
|
|
@@ -44471,18 +44785,18 @@ import {
|
|
|
44471
44785
|
closeSync as closeSync3,
|
|
44472
44786
|
constants,
|
|
44473
44787
|
copyFileSync,
|
|
44474
|
-
existsSync as
|
|
44788
|
+
existsSync as existsSync25,
|
|
44475
44789
|
fstatSync,
|
|
44476
44790
|
mkdirSync as mkdirSync17,
|
|
44477
44791
|
openSync as openSync4,
|
|
44478
|
-
readdirSync as
|
|
44479
|
-
readFileSync as
|
|
44792
|
+
readdirSync as readdirSync14,
|
|
44793
|
+
readFileSync as readFileSync27,
|
|
44480
44794
|
readSync as readSync2,
|
|
44481
44795
|
rmdirSync as rmdirSync2,
|
|
44482
|
-
statSync as
|
|
44796
|
+
statSync as statSync14,
|
|
44483
44797
|
writeFileSync as writeFileSync18
|
|
44484
44798
|
} from "node:fs";
|
|
44485
|
-
import { basename as
|
|
44799
|
+
import { basename as basename10, dirname as dirname23, extname as extname4, isAbsolute as isAbsolute5, join as join32, normalize as normalize2, relative as relative6 } from "node:path";
|
|
44486
44800
|
import { setTimeout as delay5 } from "node:timers/promises";
|
|
44487
44801
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
44488
44802
|
function editorRestartTimeoutMs() {
|
|
@@ -44512,22 +44826,22 @@ function writtenFileRefusal(path, rule) {
|
|
|
44512
44826
|
if (/^(res|user):\/\//.test(path)) {
|
|
44513
44827
|
return `${named} ${path} is a Godot path; ${what} is saved outside the project, at an absolute path on this machine.`;
|
|
44514
44828
|
}
|
|
44515
|
-
if (!
|
|
44829
|
+
if (!isAbsolute5(path)) {
|
|
44516
44830
|
return `${named} ${path} is not absolute; name the whole path, so where it is written does not depend on this server's directory.`;
|
|
44517
44831
|
}
|
|
44518
44832
|
if (!rule.extensions.includes(extname4(path).toLowerCase())) {
|
|
44519
44833
|
return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
|
|
44520
44834
|
}
|
|
44521
|
-
const directory = realPathOr(
|
|
44522
|
-
if (!
|
|
44523
|
-
return `${named}'s directory ${
|
|
44835
|
+
const directory = realPathOr(dirname23(path));
|
|
44836
|
+
if (!existsSync25(directory)) {
|
|
44837
|
+
return `${named}'s directory ${dirname23(path)} does not exist; ${what} creates no directories.`;
|
|
44524
44838
|
}
|
|
44525
|
-
const where =
|
|
44839
|
+
const where = join32(directory, basename10(path));
|
|
44526
44840
|
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
44527
44841
|
if (inside !== undefined) {
|
|
44528
44842
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
44529
44843
|
}
|
|
44530
|
-
const taken = rule.writes(where).find((file) =>
|
|
44844
|
+
const taken = rule.writes(where).find((file) => existsSync25(file));
|
|
44531
44845
|
if (taken !== undefined) {
|
|
44532
44846
|
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.`;
|
|
44533
44847
|
}
|
|
@@ -44636,7 +44950,7 @@ function exited(run) {
|
|
|
44636
44950
|
return run.exitCode !== null || run.exitSignal !== null;
|
|
44637
44951
|
}
|
|
44638
44952
|
function howItExited(run) {
|
|
44639
|
-
return run.exitSignal === null ?
|
|
44953
|
+
return run.exitSignal === null ? exitCodeText(run.exitCode) : `ended by ${run.exitSignal}, with no exit code`;
|
|
44640
44954
|
}
|
|
44641
44955
|
function errorsUnread(run) {
|
|
44642
44956
|
return run.throughEditor && typeof run.errorReport !== "string";
|
|
@@ -44744,7 +45058,7 @@ function endedWithoutACode(run) {
|
|
|
44744
45058
|
function ourRunEnded(run, now) {
|
|
44745
45059
|
const which = `${run.throughEditor ? "played in the editor" : "started"}${run.pid === null ? "" : ` (pid ${run.pid})`}`;
|
|
44746
45060
|
const when = run.endedAt === undefined ? "" : ` ${Math.max(0, Math.round((now - run.endedAt) / 1000))}s ago`;
|
|
44747
|
-
const how = run.endedThrough === "close" ? "this server asked it to close" : run.endedThrough === "keeper" ? "this server ended it through the keeper holding it" : run.endedThrough === "signal" ? "this server signalled it" : run.endedThrough === "editor" ? "this server asked the editor to stop it" : run.exitCode !== null ? `it exited by itself with
|
|
45061
|
+
const how = run.endedThrough === "close" ? "this server asked it to close" : run.endedThrough === "keeper" ? "this server ended it through the keeper holding it" : run.endedThrough === "signal" ? "this server signalled it" : run.endedThrough === "editor" ? "this server asked the editor to stop it" : run.exitCode !== null ? `it exited by itself with ${exitCodeText(run.exitCode)}` : run.exitSignal === null ? "it is no longer running" : `it was ended by ${run.exitSignal}`;
|
|
44748
45062
|
return `No game with the runtime addon is running: the game this server ${which} ended${when}, and ${how}. editor_output has what it printed, and editor_run start runs it again.`;
|
|
44749
45063
|
}
|
|
44750
45064
|
function stopVerdict(stop) {
|
|
@@ -44999,7 +45313,12 @@ function unlistedValues(schema, value, path) {
|
|
|
44999
45313
|
const fields = schema;
|
|
45000
45314
|
const listed = fields["enum"];
|
|
45001
45315
|
if (Array.isArray(listed) && typeof value === "string") {
|
|
45002
|
-
|
|
45316
|
+
if (listed.includes(value)) {
|
|
45317
|
+
return [];
|
|
45318
|
+
}
|
|
45319
|
+
return [
|
|
45320
|
+
listed.length === 1 ? `${path} only as ${String(listed[0])}, not ${JSON.stringify(value)}` : `${path} as one of ${listed.join(", ")}, not ${JSON.stringify(value)}`
|
|
45321
|
+
];
|
|
45003
45322
|
}
|
|
45004
45323
|
if (Array.isArray(value)) {
|
|
45005
45324
|
return value.flatMap((element, index) => unlistedValues(fields["items"], element, `${path}[${index}]`));
|
|
@@ -45063,7 +45382,7 @@ function notRunNote(notRun, failFast) {
|
|
|
45063
45382
|
function exportPresetRefusal(projectPath, preset) {
|
|
45064
45383
|
let text;
|
|
45065
45384
|
try {
|
|
45066
|
-
text =
|
|
45385
|
+
text = readFileSync27(join32(projectPath, "export_presets.cfg"), "utf8");
|
|
45067
45386
|
} catch {
|
|
45068
45387
|
return `This project has no export_presets.cfg, so it has no preset '${preset}' to export with. project_export add_preset writes one, as the editor's Project > Export does.`;
|
|
45069
45388
|
}
|
|
@@ -45085,7 +45404,7 @@ function exportPresetRefusal(projectPath, preset) {
|
|
|
45085
45404
|
}
|
|
45086
45405
|
return names.length === 0 ? `export_presets.cfg names no preset, so there is no '${preset}' to export with. project_export add_preset adds one.` : `This project has no export preset named '${preset}'. Its presets are ${names.map((name) => `'${name}'`).join(", ")}.`;
|
|
45087
45406
|
}
|
|
45088
|
-
function exportAnswer(ending, asked, before, after, beside = []) {
|
|
45407
|
+
function exportAnswer(ending, asked, before, after, beside = [], docCacheNote = null) {
|
|
45089
45408
|
const { log } = ending;
|
|
45090
45409
|
const written = after !== null && after !== before;
|
|
45091
45410
|
const notCopied = librariesNotCopied(log.all);
|
|
@@ -45102,6 +45421,7 @@ function exportAnswer(ending, asked, before, after, beside = []) {
|
|
|
45102
45421
|
librariesNotLoaded: notCopied,
|
|
45103
45422
|
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.`
|
|
45104
45423
|
},
|
|
45424
|
+
...docCacheNote === null ? {} : { docCacheNote },
|
|
45105
45425
|
...foldedEntriesAnswer(log)
|
|
45106
45426
|
};
|
|
45107
45427
|
if (verdict.exported) {
|
|
@@ -45126,9 +45446,9 @@ function writtenBeside(before, after, output, within) {
|
|
|
45126
45446
|
function filesIn(folder) {
|
|
45127
45447
|
const found = new Map;
|
|
45128
45448
|
try {
|
|
45129
|
-
for (const entry of
|
|
45449
|
+
for (const entry of readdirSync14(folder, { withFileTypes: true })) {
|
|
45130
45450
|
if (entry.isFile()) {
|
|
45131
|
-
found.set(entry.name,
|
|
45451
|
+
found.set(entry.name, statSync14(join32(folder, entry.name)).mtimeMs);
|
|
45132
45452
|
}
|
|
45133
45453
|
}
|
|
45134
45454
|
} catch {}
|
|
@@ -45158,7 +45478,7 @@ function unsavedInEditorNote(scripts) {
|
|
|
45158
45478
|
return `${named} ${one ? "is" : "are"} open in the editor's script ${one ? "tab" : "tabs"} with unsaved edits, and saving ${one ? "that tab" : "them"} there writes ${one ? "its" : "their"} text over what was written here: close ${one ? "it" : "them"} without saving, or save first and edit again.`;
|
|
45159
45479
|
}
|
|
45160
45480
|
function editorAddonMissing(projectPath) {
|
|
45161
|
-
if (!
|
|
45481
|
+
if (!existsSync25(join32(projectPath, "project.godot"))) {
|
|
45162
45482
|
return null;
|
|
45163
45483
|
}
|
|
45164
45484
|
const report = inspectProject(projectPath);
|
|
@@ -45324,8 +45644,34 @@ function scriptErrorsNote(errors, {
|
|
|
45324
45644
|
const opening = unnamed === 0 ? `No tests ran: gdUnit4 could not load ${scripts === 1 ? "one script" : `${scripts} scripts`} while looking for suites, and it runs no suite at all when one fails to load.` : `No tests ran: ${scripts + unnamed} scripts did not compile, and gdUnit4 runs no suite at all when one fails to load. It could not load ${scripts === 1 ? "one" : scripts} of them while looking for suites, and ${unnamed === 1 ? "the one it did not name comes" : `the ${unnamed} it did not name come`} first.`;
|
|
45325
45645
|
return [opening, named, following, rest, after].filter((part) => part !== "").join(" ");
|
|
45326
45646
|
}
|
|
45327
|
-
function
|
|
45647
|
+
function autoloadNotCompiledNote(at, errors, stoppedEarly) {
|
|
45648
|
+
const first = errors.scriptErrors[0];
|
|
45649
|
+
const where = first === undefined ? "" : `, and the first error the engine raised compiling was at ${first.path}${first.line === null ? "" : `:${first.line}`}: ${first.message}`;
|
|
45650
|
+
const others = errors.scriptErrors.length - 1;
|
|
45651
|
+
const more = others > 0 ? ` ${others === 1 ? "One more error is" : `${others} more errors are`} under scriptErrors.` : "";
|
|
45652
|
+
const through = errors.failedThroughDependency.length;
|
|
45653
|
+
const dependents = through > 0 ? ` ${through === 1 ? "One script" : `${through} scripts`} failed only through ${first === undefined ? "it" : "that"}, listed under failedThroughDependency.` : "";
|
|
45654
|
+
const ended = stoppedEarly ? "The run was stopped as soon as the engine said so rather than left to its timeout" : "The run ended before that was read, and what its suites said is set aside";
|
|
45655
|
+
const stop = /[.!?]\)?$/.test(where) ? "" : ".";
|
|
45656
|
+
return `No result counts: the autoload ${at.name} (${at.path}) did not compile${where}${stop}${more}${dependents} Every suite runs with the autoloads, so a case calling into a script that did not compile fails the same way on every pass, and one waiting in a loop for the call to succeed never ends. ${ended}. script_diagnostics checks a script without running anything.`;
|
|
45657
|
+
}
|
|
45658
|
+
function repeatingScriptError(entries, byScript) {
|
|
45659
|
+
const last = entries.at(-1);
|
|
45660
|
+
if (last === undefined || !byScript(last)) {
|
|
45661
|
+
return null;
|
|
45662
|
+
}
|
|
45663
|
+
const where = last.detail[0] ?? null;
|
|
45664
|
+
const times = entries.filter((entry) => entry.text === last.text && (entry.detail[0] ?? null) === where && byScript(entry)).length;
|
|
45665
|
+
return times >= LOOPING_AFTER ? { text: last.text, where: where?.replace(/^at: /, "") ?? null, times } : null;
|
|
45666
|
+
}
|
|
45667
|
+
function timedOutVerdict(timeoutMs, silentForMs, repeating = null) {
|
|
45328
45668
|
const quiet = Math.round(silentForMs);
|
|
45669
|
+
if (repeating !== null) {
|
|
45670
|
+
return {
|
|
45671
|
+
verdict: `timed out after ${timeoutMs} ms repeating one script error, printed ${repeating.times} times: ${repeating.text}${repeating.where === null ? "" : ` (${repeating.where})`}. A line that fails on every pass of a loop does this, and a longer timeoutMs would not end it`,
|
|
45672
|
+
hung: false
|
|
45673
|
+
};
|
|
45674
|
+
}
|
|
45329
45675
|
return quiet >= Math.min(STALLED_AFTER_MS, timeoutMs / 2) ? {
|
|
45330
45676
|
verdict: `hung: killed after ${timeoutMs} ms, having printed nothing for the last ${quiet} ms`,
|
|
45331
45677
|
hung: true
|
|
@@ -45396,7 +45742,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
45396
45742
|
let projectGodot = null;
|
|
45397
45743
|
if (projectPath !== null) {
|
|
45398
45744
|
try {
|
|
45399
|
-
projectGodot =
|
|
45745
|
+
projectGodot = readFileSync27(join32(projectPath, "project.godot"), "utf8");
|
|
45400
45746
|
} catch {}
|
|
45401
45747
|
}
|
|
45402
45748
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -45420,7 +45766,7 @@ function projectSourceOf(projectPath) {
|
|
|
45420
45766
|
let source = null;
|
|
45421
45767
|
if (inside.ok) {
|
|
45422
45768
|
try {
|
|
45423
|
-
source =
|
|
45769
|
+
source = readFileSync27(inside.absolutePath, "utf8");
|
|
45424
45770
|
} catch {}
|
|
45425
45771
|
}
|
|
45426
45772
|
read.set(script, source);
|
|
@@ -45471,6 +45817,9 @@ function profileAnswer(reading, limit) {
|
|
|
45471
45817
|
if (reading.coveredMs > 0 && table.scriptMs > reading.coveredMs) {
|
|
45472
45818
|
notes.push(`scriptMs, ${table.scriptMs}ms, is more than the ${reading.coveredMs}ms the profile covers.${table.superCallers > 0 ? " The super calls above, counted twice, can account for some or all of that; whatever they do not is" : " One thread cannot spend more time than passes, so that is"} functions running on several threads at once, through WorkerThreadPool or Thread. The engine adds every thread's time to the same function and does not say which thread a call ran on, so a function's selfMs is summed over every thread it ran on rather than taken out of frames, and a function that waits for the others, through wait_for_group_task_completion or wait_to_finish, has the wait in its own selfMs as well.`);
|
|
45473
45819
|
}
|
|
45820
|
+
if (table.addonFunctions > 0) {
|
|
45821
|
+
notes.push(`The gdharness runtime addon's own functions took ${table.addonMs}ms of self time over ${table.addonFunctions === 1 ? "one function" : `${table.addonFunctions} functions`}, answering tools such as runtime_invoke and sending the profile. They are left out of functions, functionsCounted and scriptMs and summed under runtimeAddonMs, since they are not the game's work, though that time was spent in the game's frames.`);
|
|
45822
|
+
}
|
|
45474
45823
|
notes.push(...cappedNotes(totals));
|
|
45475
45824
|
if (totals.unreadable > 0) {
|
|
45476
45825
|
notes.push(`${totals.unreadable} message${totals.unreadable === 1 ? "" : "s"} from the game could not be read, so the totals have that many gaps.`);
|
|
@@ -45485,6 +45834,8 @@ function profileAnswer(reading, limit) {
|
|
|
45485
45834
|
scriptMs: table.scriptMs,
|
|
45486
45835
|
perCallOverheadUs: table.perCallUs ?? undefined,
|
|
45487
45836
|
functionsCounted: table.counted,
|
|
45837
|
+
runtimeAddonMs: table.addonFunctions > 0 ? table.addonMs : undefined,
|
|
45838
|
+
runtimeAddonFunctions: table.addonFunctions > 0 ? table.addonFunctions : undefined,
|
|
45488
45839
|
omitted: table.omitted > 0 ? table.omitted : undefined,
|
|
45489
45840
|
functions: table.functions,
|
|
45490
45841
|
callers: reading.callersOf === undefined ? undefined : callersAnswer(totals, reading.callersOf),
|
|
@@ -45570,7 +45921,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
45570
45921
|
}
|
|
45571
45922
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
45572
45923
|
try {
|
|
45573
|
-
return settingKeys(
|
|
45924
|
+
return settingKeys(readFileSync27(join32(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
45574
45925
|
} catch {
|
|
45575
45926
|
return true;
|
|
45576
45927
|
}
|
|
@@ -45585,13 +45936,13 @@ function uncaughtNote(uncaught) {
|
|
|
45585
45936
|
}
|
|
45586
45937
|
function elsewhereIn(projectPath, asked) {
|
|
45587
45938
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
45588
|
-
if (wanted === "" ||
|
|
45939
|
+
if (wanted === "" || existsSync25(join32(projectPath, wanted))) {
|
|
45589
45940
|
return "";
|
|
45590
45941
|
}
|
|
45591
45942
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
45592
45943
|
let near = [];
|
|
45593
45944
|
try {
|
|
45594
|
-
near =
|
|
45945
|
+
near = readdirSync14(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()));
|
|
45595
45946
|
} catch {
|
|
45596
45947
|
return "";
|
|
45597
45948
|
}
|
|
@@ -45682,23 +46033,23 @@ function engineChecked(why) {
|
|
|
45682
46033
|
}
|
|
45683
46034
|
function isDirectory(path) {
|
|
45684
46035
|
try {
|
|
45685
|
-
return
|
|
46036
|
+
return statSync14(path).isDirectory();
|
|
45686
46037
|
} catch {
|
|
45687
46038
|
return false;
|
|
45688
46039
|
}
|
|
45689
46040
|
}
|
|
45690
46041
|
function scriptsUnder(directory, prefix) {
|
|
45691
|
-
if (
|
|
46042
|
+
if (existsSync25(join32(directory, ".gdignore"))) {
|
|
45692
46043
|
return [];
|
|
45693
46044
|
}
|
|
45694
46045
|
const found = [];
|
|
45695
|
-
for (const entry of
|
|
46046
|
+
for (const entry of readdirSync14(directory, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : 1)) {
|
|
45696
46047
|
if (entry.name.startsWith(".")) {
|
|
45697
46048
|
continue;
|
|
45698
46049
|
}
|
|
45699
46050
|
const path = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
45700
46051
|
if (entry.isDirectory()) {
|
|
45701
|
-
found.push(...scriptsUnder(
|
|
46052
|
+
found.push(...scriptsUnder(join32(directory, entry.name), path));
|
|
45702
46053
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
45703
46054
|
found.push(path);
|
|
45704
46055
|
}
|
|
@@ -45706,7 +46057,7 @@ function scriptsUnder(directory, prefix) {
|
|
|
45706
46057
|
return found;
|
|
45707
46058
|
}
|
|
45708
46059
|
function hasMainScene(projectFile) {
|
|
45709
|
-
const scene = parseProjectGodot(
|
|
46060
|
+
const scene = parseProjectGodot(readFileSync27(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
45710
46061
|
return typeof scene === "string" && scene !== "";
|
|
45711
46062
|
}
|
|
45712
46063
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -45714,13 +46065,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
45714
46065
|
return false;
|
|
45715
46066
|
}
|
|
45716
46067
|
try {
|
|
45717
|
-
return
|
|
46068
|
+
return readFileSync27(join32(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
45718
46069
|
} catch {
|
|
45719
46070
|
return false;
|
|
45720
46071
|
}
|
|
45721
46072
|
}
|
|
45722
46073
|
function editorPlaysHeadless(projectFile) {
|
|
45723
|
-
const runArgs = parseProjectGodot(
|
|
46074
|
+
const runArgs = parseProjectGodot(readFileSync27(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
45724
46075
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
45725
46076
|
}
|
|
45726
46077
|
function camelCased(params) {
|
|
@@ -45739,7 +46090,7 @@ function needsItsOwnProcess(asked) {
|
|
|
45739
46090
|
class GodotServer {
|
|
45740
46091
|
stdio = null;
|
|
45741
46092
|
locator = new GodotLocator;
|
|
45742
|
-
operationsScript =
|
|
46093
|
+
operationsScript = join32(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
45743
46094
|
godotBridge;
|
|
45744
46095
|
tools = buildToolDefinitions();
|
|
45745
46096
|
activeProcess = null;
|
|
@@ -46000,14 +46351,14 @@ class GodotServer {
|
|
|
46000
46351
|
}
|
|
46001
46352
|
noteTheStart() {
|
|
46002
46353
|
const cwd = process.cwd();
|
|
46003
|
-
const mcpJson =
|
|
46354
|
+
const mcpJson = join32(cwd, ".mcp.json");
|
|
46004
46355
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
46005
46356
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
46006
46357
|
writeLifecycle("started", {
|
|
46007
46358
|
argv: process.argv.slice(1),
|
|
46008
46359
|
cwd,
|
|
46009
46360
|
project: this.ownProject,
|
|
46010
|
-
mcpJson:
|
|
46361
|
+
mcpJson: existsSync25(mcpJson) ? mcpJson : null
|
|
46011
46362
|
});
|
|
46012
46363
|
if (predecessor !== null) {
|
|
46013
46364
|
writeLifecycle("replaced", { project: this.ownProject, predecessor });
|
|
@@ -46148,7 +46499,7 @@ class GodotServer {
|
|
|
46148
46499
|
return checked.response;
|
|
46149
46500
|
}
|
|
46150
46501
|
const named = args["projectPath"];
|
|
46151
|
-
if (typeof named === "string" &&
|
|
46502
|
+
if (typeof named === "string" && existsSync25(join32(named, "project.godot"))) {
|
|
46152
46503
|
this.lastProjectPath = named;
|
|
46153
46504
|
}
|
|
46154
46505
|
this.updates.refresh();
|
|
@@ -46184,7 +46535,7 @@ class GodotServer {
|
|
|
46184
46535
|
let notSaved;
|
|
46185
46536
|
try {
|
|
46186
46537
|
savedAt = answer.content.map((item, index) => {
|
|
46187
|
-
const file =
|
|
46538
|
+
const file = join32(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
|
|
46188
46539
|
writeFileSync18(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
|
|
46189
46540
|
return file;
|
|
46190
46541
|
});
|
|
@@ -46802,8 +47153,8 @@ class GodotServer {
|
|
|
46802
47153
|
if (path === undefined) {
|
|
46803
47154
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
46804
47155
|
}
|
|
46805
|
-
const file =
|
|
46806
|
-
if (!
|
|
47156
|
+
const file = join32(path, "project.godot");
|
|
47157
|
+
if (!existsSync25(file)) {
|
|
46807
47158
|
return {
|
|
46808
47159
|
ok: false,
|
|
46809
47160
|
response: this.createErrorResponse(`Not a Godot project: ${path}`, [
|
|
@@ -46849,7 +47200,7 @@ class GodotServer {
|
|
|
46849
47200
|
}
|
|
46850
47201
|
runEnvironment(args) {
|
|
46851
47202
|
const savesIn = readNonEmptyString(args, "savesIn");
|
|
46852
|
-
if (savesIn !== undefined && !
|
|
47203
|
+
if (savesIn !== undefined && !isAbsolute5(savesIn)) {
|
|
46853
47204
|
return {
|
|
46854
47205
|
ok: false,
|
|
46855
47206
|
response: this.createErrorResponse(`savesIn must be an absolute path, not "${savesIn}".`, [
|
|
@@ -47108,7 +47459,8 @@ class GodotServer {
|
|
|
47108
47459
|
uidsCreated: given,
|
|
47109
47460
|
stillWithoutUid: after,
|
|
47110
47461
|
note: uidsLeftNote(after.length),
|
|
47111
|
-
...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote }
|
|
47462
|
+
...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote },
|
|
47463
|
+
...imported.docCacheNote === undefined ? {} : { docCacheNote: imported.docCacheNote }
|
|
47112
47464
|
}
|
|
47113
47465
|
}, scanned));
|
|
47114
47466
|
}
|
|
@@ -47347,6 +47699,9 @@ class GodotServer {
|
|
|
47347
47699
|
if (imported.extensionNote !== undefined) {
|
|
47348
47700
|
extra["extensionNote"] = imported.extensionNote;
|
|
47349
47701
|
}
|
|
47702
|
+
if (imported.docCacheNote !== undefined) {
|
|
47703
|
+
extra["docCacheNote"] = imported.docCacheNote;
|
|
47704
|
+
}
|
|
47350
47705
|
}
|
|
47351
47706
|
const after = await this.operation("get_import_status", statusAsk, projectPath);
|
|
47352
47707
|
if (!after.ok) {
|
|
@@ -47459,11 +47814,11 @@ class GodotServer {
|
|
|
47459
47814
|
if (!engine.ok) {
|
|
47460
47815
|
return engine.response;
|
|
47461
47816
|
}
|
|
47462
|
-
const application = parseProjectGodot(
|
|
47817
|
+
const application = parseProjectGodot(readFileSync27(project.value.file, "utf8"))["application"] ?? {};
|
|
47463
47818
|
const name = application["config/name"];
|
|
47464
47819
|
const mainScene = application["run/main_scene"];
|
|
47465
47820
|
const info = {
|
|
47466
|
-
name: typeof name === "string" && name !== "" ? name :
|
|
47821
|
+
name: typeof name === "string" && name !== "" ? name : basename10(project.value.path),
|
|
47467
47822
|
path: project.value.path,
|
|
47468
47823
|
mainScene: typeof mainScene === "string" && mainScene !== "" ? mainScene : null,
|
|
47469
47824
|
godotVersion: await this.godotVersion(engine.value),
|
|
@@ -47546,18 +47901,18 @@ class GodotServer {
|
|
|
47546
47901
|
if (!engine.ok) {
|
|
47547
47902
|
return engine.response;
|
|
47548
47903
|
}
|
|
47549
|
-
const folder =
|
|
47904
|
+
const folder = dirname23(output.absolutePath);
|
|
47550
47905
|
try {
|
|
47551
47906
|
mkdirSync17(folder, { recursive: true });
|
|
47552
47907
|
} catch (error) {
|
|
47553
|
-
return this.createErrorResponse(`Cannot create ${
|
|
47908
|
+
return this.createErrorResponse(`Cannot create ${dirname23(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
47554
47909
|
}
|
|
47555
47910
|
const debug = readBoolean(args, "debug") ?? false;
|
|
47556
47911
|
const exportLogs = scratchDirectory("export");
|
|
47557
47912
|
const exportArgs = [
|
|
47558
47913
|
"--headless",
|
|
47559
47914
|
"--log-file",
|
|
47560
|
-
|
|
47915
|
+
join32(exportLogs, "engine.log"),
|
|
47561
47916
|
"--path",
|
|
47562
47917
|
project.value.path,
|
|
47563
47918
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -47565,24 +47920,37 @@ class GodotServer {
|
|
|
47565
47920
|
output.absolutePath
|
|
47566
47921
|
];
|
|
47567
47922
|
this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
|
|
47568
|
-
const writtenAt = () =>
|
|
47569
|
-
const
|
|
47570
|
-
const
|
|
47571
|
-
|
|
47572
|
-
|
|
47573
|
-
|
|
47574
|
-
|
|
47575
|
-
|
|
47923
|
+
const writtenAt = () => existsSync25(output.absolutePath) ? statSync14(output.absolutePath).mtimeMs : null;
|
|
47924
|
+
const caches = editorCacheDirectory(engine.value);
|
|
47925
|
+
const exportOnce = async () => {
|
|
47926
|
+
const before = writtenAt();
|
|
47927
|
+
const listedBefore = filesIn(folder);
|
|
47928
|
+
const cachesBefore = docCachesWritten(caches);
|
|
47929
|
+
const freed = freeLibraryCopies(project.value.path);
|
|
47930
|
+
if (freed.setAside.length > 0 || freed.swept.length > 0) {
|
|
47931
|
+
this.logDebug(`Before the export: set aside ${freed.setAside.join(", ")}; swept ${freed.swept.join(", ")}`);
|
|
47932
|
+
}
|
|
47933
|
+
const ending = await runEngine(engine.value, exportArgs, {
|
|
47934
|
+
timeout: { ms: 300000, said: "five minutes" }
|
|
47935
|
+
});
|
|
47936
|
+
const crash = docCacheCrash(ending, cachesBefore, docCachesWritten(caches), "export");
|
|
47937
|
+
return { ending, before, listedBefore, crash };
|
|
47938
|
+
};
|
|
47939
|
+
let run;
|
|
47940
|
+
let docCacheNote;
|
|
47576
47941
|
try {
|
|
47577
|
-
|
|
47942
|
+
const first = await exportOnce();
|
|
47943
|
+
docCacheNote = first.crash;
|
|
47944
|
+
run = first.crash === null ? first : await exportOnce();
|
|
47578
47945
|
} finally {
|
|
47579
47946
|
discard(exportLogs);
|
|
47580
47947
|
}
|
|
47948
|
+
const { ending, before, listedBefore } = run;
|
|
47581
47949
|
if (ending.exitSignal === null && ending.exitCode === null) {
|
|
47582
47950
|
return this.createErrorResponse(`Export could not be run: ${ending.failure ?? "the engine gave no exit status"}`);
|
|
47583
47951
|
}
|
|
47584
|
-
const beside = writtenBeside(listedBefore, filesIn(folder),
|
|
47585
|
-
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt(), beside);
|
|
47952
|
+
const beside = writtenBeside(listedBefore, filesIn(folder), basename10(output.absolutePath), dirname23(output.relativePath).replaceAll("\\", "/"));
|
|
47953
|
+
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt(), beside, docCacheNote);
|
|
47586
47954
|
}
|
|
47587
47955
|
reportTestProgress(log) {
|
|
47588
47956
|
const progress = callProgress();
|
|
@@ -47635,10 +48003,10 @@ class GodotServer {
|
|
|
47635
48003
|
if (movie !== null) {
|
|
47636
48004
|
return this.createErrorResponse(movie);
|
|
47637
48005
|
}
|
|
47638
|
-
const missing = paths.value.filter((path) => !
|
|
48006
|
+
const missing = paths.value.filter((path) => !existsSync25(join32(project.value.path, path.slice("res://".length))));
|
|
47639
48007
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
47640
48008
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
47641
|
-
const noRunner = !
|
|
48009
|
+
const noRunner = !existsSync25(join32(project.value.path, runner));
|
|
47642
48010
|
if (asked.length === 0) {
|
|
47643
48011
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
47644
48012
|
const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
|
|
@@ -47708,42 +48076,57 @@ class GodotServer {
|
|
|
47708
48076
|
lastSaid = Date.now();
|
|
47709
48077
|
});
|
|
47710
48078
|
const reporting = this.reportTestProgress(run.log);
|
|
48079
|
+
const autoloaded = scriptsAutoloaded(project.value.path);
|
|
47711
48080
|
let silentForMs = 0;
|
|
48081
|
+
const stopped = { for: null };
|
|
47712
48082
|
const timedOut = await new Promise((resolve) => {
|
|
48083
|
+
let read = 0;
|
|
48084
|
+
const watching = setInterval(() => {
|
|
48085
|
+
const entries = run.log.all;
|
|
48086
|
+
for (const entry of entries.slice(read)) {
|
|
48087
|
+
stopped.for ??= autoloadNotCompiled(entry, autoloaded);
|
|
48088
|
+
}
|
|
48089
|
+
read = entries.length;
|
|
48090
|
+
if (stopped.for !== null) {
|
|
48091
|
+
clearInterval(watching);
|
|
48092
|
+
run.process.kill();
|
|
48093
|
+
}
|
|
48094
|
+
}, 100);
|
|
47713
48095
|
const timer = setTimeout(() => {
|
|
48096
|
+
clearInterval(watching);
|
|
47714
48097
|
silentForMs = Date.now() - lastSaid;
|
|
47715
48098
|
run.process.kill();
|
|
47716
48099
|
resolve(true);
|
|
47717
48100
|
}, timeoutMs);
|
|
47718
|
-
|
|
48101
|
+
const ended = () => {
|
|
48102
|
+
clearInterval(watching);
|
|
47719
48103
|
clearTimeout(timer);
|
|
47720
48104
|
resolve(false);
|
|
47721
|
-
}
|
|
47722
|
-
run.process.once("
|
|
47723
|
-
|
|
47724
|
-
resolve(false);
|
|
47725
|
-
});
|
|
48105
|
+
};
|
|
48106
|
+
run.process.once("exit", ended);
|
|
48107
|
+
run.process.once("error", ended);
|
|
47726
48108
|
});
|
|
47727
48109
|
clearInterval(reporting);
|
|
47728
|
-
if (timedOut && run.process.pid !== undefined) {
|
|
48110
|
+
if ((timedOut || stopped.for !== null) && run.process.pid !== undefined) {
|
|
47729
48111
|
await untilGone([run.process.pid]);
|
|
47730
48112
|
}
|
|
47731
48113
|
await run.settled;
|
|
47732
|
-
const
|
|
48114
|
+
const notCompiled = stopped.for ?? run.log.all.map((entry) => autoloadNotCompiled(entry, autoloaded)).find((found) => found !== null) ?? null;
|
|
48115
|
+
const reportsDir = join32(project.value.path, ".godot", ...ours.split("/"));
|
|
47733
48116
|
let report = null;
|
|
47734
48117
|
let reportProblem = null;
|
|
47735
48118
|
try {
|
|
47736
|
-
const written =
|
|
48119
|
+
const written = existsSync25(reportsDir) && notCompiled === null ? readdirSync14(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
47737
48120
|
const newest = written.at(-1);
|
|
47738
48121
|
if (newest !== undefined) {
|
|
47739
|
-
report = parseJUnit(
|
|
48122
|
+
report = parseJUnit(readFileSync27(join32(reportsDir, newest, "results.xml"), "utf8"));
|
|
47740
48123
|
}
|
|
47741
48124
|
} catch (error) {
|
|
47742
48125
|
reportProblem = errorMessage(error);
|
|
47743
48126
|
} finally {
|
|
47744
48127
|
discard(reportsDir, userData);
|
|
47745
48128
|
try {
|
|
47746
|
-
rmdirSync2(
|
|
48129
|
+
rmdirSync2(dirname23(reportsDir));
|
|
47747
48130
|
} catch {}
|
|
47748
48131
|
}
|
|
47749
48132
|
const engineEntries = foldedRepeats(run.log.select({
|
|
@@ -47768,14 +48151,14 @@ class GodotServer {
|
|
|
47768
48151
|
limit: Number.POSITIVE_INFINITY
|
|
47769
48152
|
}).entries;
|
|
47770
48153
|
const said = printed.map((entry) => entry.text);
|
|
47771
|
-
const nothingRan = timedOut ? null : whyNoReport(said, asked.join(" and "));
|
|
47772
|
-
const cutShort = timedOut ? timedOutVerdict(timeoutMs, silentForMs) : null;
|
|
48154
|
+
const nothingRan = timedOut || notCompiled !== null ? null : whyNoReport(said, asked.join(" and "));
|
|
48155
|
+
const cutShort = timedOut && notCompiled === null ? timedOutVerdict(timeoutMs, silentForMs, repeatingScriptError(run.log.all, (entry) => run.log.raisedByScript(entry))) : null;
|
|
47773
48156
|
const hung = cutShort?.hung ?? false;
|
|
47774
|
-
const printedErrors = timedOut || exitCode !== 105 ? [] : scriptErrorsPrinted(said);
|
|
48157
|
+
const printedErrors = notCompiled !== null ? compileErrorsIn(run.log.all) : timedOut || exitCode !== 105 ? [] : scriptErrorsPrinted(said);
|
|
47775
48158
|
const declaring = printedErrors.some((error) => unresolvedClassIn(error.message) !== null) ? await originsOf(printedErrors, declaredClasses(project.value.path), checkWithEngine(engine.value, project.value.path)) : { origins: [], unconfirmed: [] };
|
|
47776
48159
|
const scriptErrors = [...declaring.origins, ...printedErrors];
|
|
47777
48160
|
const brokenScripts = new Set(scriptErrors.map((error) => error.path)).size;
|
|
47778
|
-
const ranAs = cutShort !== null ? cutShort.verdict : nothingRan ?? (brokenScripts > 0 ? `script errors in ${brokenScripts === 1 ? "one script" : `${brokenScripts} scripts`}, so no suite ran` : undefined) ?? (uncaught.length > 0 ? `${uncaught.length === 1 ? "one script error" : `${uncaught.length} script errors`} that failed no case` : undefined) ?? (exitCode === null ? undefined : verdicts[exitCode]) ??
|
|
48161
|
+
const ranAs = notCompiled !== null ? `the autoload ${notCompiled.name} (${notCompiled.path}) did not compile, so no result counts` : cutShort !== null ? cutShort.verdict : nothingRan ?? (brokenScripts > 0 ? `script errors in ${brokenScripts === 1 ? "one script" : `${brokenScripts} scripts`}, so no suite ran` : undefined) ?? (uncaught.length > 0 ? `${uncaught.length === 1 ? "one script error" : `${uncaught.length} script errors`} that failed no case` : undefined) ?? (exitCode === null ? undefined : verdicts[exitCode]) ?? howItExited(run);
|
|
47779
48162
|
const nothingAt = missing.join(" and ");
|
|
47780
48163
|
const verdict = missing.length === 0 ? ranAs : `${ranAs}; nothing at ${nothingAt}`;
|
|
47781
48164
|
const missingNote = missing.length === 0 ? undefined : `Nothing at ${nothingAt}, so ${missing.length === 1 ? "it was" : "they were"} left out and only ${asked.join(", ")} ran; a run of fewer paths than were asked for is not called a pass.${elsewhereIn(project.value.path, missing[0] ?? "")}`;
|
|
@@ -47803,7 +48186,7 @@ class GodotServer {
|
|
|
47803
48186
|
}),
|
|
47804
48187
|
...running === null ? {} : { suiteRunning: running }
|
|
47805
48188
|
};
|
|
47806
|
-
const said = nothingRan !== null ? `No tests ran: ${ranAs}.` : scriptErrors.length > 0 ? scriptErrorsNote(printedErrors, declaring) : reportProblem !== null ? `The test run's report could not be read (${reportProblem}); the engine's own verdict was ${ranAs}.` : `The test run wrote no report (${ranAs}).${finishedBeforeTheEnd(finished.length, failing.length, running)}`;
|
|
48189
|
+
const said = notCompiled !== null ? autoloadNotCompiledNote(notCompiled, foldedScriptErrors(scriptErrors), stopped.for !== null) : nothingRan !== null ? `No tests ran: ${ranAs}.` : scriptErrors.length > 0 ? scriptErrorsNote(printedErrors, declaring) : reportProblem !== null ? `The test run's report could not be read (${reportProblem}); the engine's own verdict was ${ranAs}.` : `The test run wrote no report (${ranAs}).${finishedBeforeTheEnd(finished.length, failing.length, running)}`;
|
|
47807
48190
|
const note = missingNote === undefined ? said : `${said} ${missingNote}`;
|
|
47808
48191
|
if (callSignal()?.aborted === true) {
|
|
47809
48192
|
keepCutTestRun(project.value.path, { cutAt: new Date().toISOString(), paths: asked, ...atTheEnd });
|
|
@@ -48102,11 +48485,11 @@ class GodotServer {
|
|
|
48102
48485
|
const cached = cachedClasses(projectPath);
|
|
48103
48486
|
const sourceOf = (resourcePath) => {
|
|
48104
48487
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
48105
|
-
if (!contained.ok || !
|
|
48488
|
+
if (!contained.ok || !existsSync25(contained.absolutePath)) {
|
|
48106
48489
|
return null;
|
|
48107
48490
|
}
|
|
48108
48491
|
try {
|
|
48109
|
-
return
|
|
48492
|
+
return readFileSync27(contained.absolutePath, "utf8");
|
|
48110
48493
|
} catch {
|
|
48111
48494
|
return null;
|
|
48112
48495
|
}
|
|
@@ -48196,11 +48579,11 @@ class GodotServer {
|
|
|
48196
48579
|
}
|
|
48197
48580
|
sourceOfClass(projectPath, resourcePath) {
|
|
48198
48581
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
48199
|
-
if (!contained.ok || !
|
|
48582
|
+
if (!contained.ok || !existsSync25(contained.absolutePath)) {
|
|
48200
48583
|
return null;
|
|
48201
48584
|
}
|
|
48202
48585
|
try {
|
|
48203
|
-
return
|
|
48586
|
+
return readFileSync27(contained.absolutePath, "utf8");
|
|
48204
48587
|
} catch {
|
|
48205
48588
|
return null;
|
|
48206
48589
|
}
|
|
@@ -48358,7 +48741,7 @@ class GodotServer {
|
|
|
48358
48741
|
...Array.isArray(stack) ? {
|
|
48359
48742
|
stack: stack.map((frame) => ({
|
|
48360
48743
|
...frame,
|
|
48361
|
-
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path:
|
|
48744
|
+
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join32(projectPath, frame["script"].slice("res://".length)) } : {}
|
|
48362
48745
|
}))
|
|
48363
48746
|
} : {}
|
|
48364
48747
|
});
|
|
@@ -48637,7 +49020,7 @@ class GodotServer {
|
|
|
48637
49020
|
return null;
|
|
48638
49021
|
}
|
|
48639
49022
|
try {
|
|
48640
|
-
return settingKeys(
|
|
49023
|
+
return settingKeys(readFileSync27(join32(projectPath, "project.godot"), "utf8"));
|
|
48641
49024
|
} catch {
|
|
48642
49025
|
return null;
|
|
48643
49026
|
}
|
|
@@ -49074,7 +49457,7 @@ class GodotServer {
|
|
|
49074
49457
|
}
|
|
49075
49458
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
49076
49459
|
const budgetMs = budget.ms;
|
|
49077
|
-
if (!
|
|
49460
|
+
if (!existsSync25(join32(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
49078
49461
|
return runtimeVerdict(null, {
|
|
49079
49462
|
addon: false,
|
|
49080
49463
|
budgetMs,
|
|
@@ -49160,7 +49543,7 @@ class GodotServer {
|
|
|
49160
49543
|
}
|
|
49161
49544
|
let announcedAt;
|
|
49162
49545
|
try {
|
|
49163
|
-
announcedAt =
|
|
49546
|
+
announcedAt = statSync14(endpoint.file).mtimeMs;
|
|
49164
49547
|
} catch {
|
|
49165
49548
|
return;
|
|
49166
49549
|
}
|
|
@@ -49354,7 +49737,7 @@ class GodotServer {
|
|
|
49354
49737
|
return 0;
|
|
49355
49738
|
}
|
|
49356
49739
|
try {
|
|
49357
|
-
return reportAlreadyInTranscript(
|
|
49740
|
+
return reportAlreadyInTranscript(readFileSync27(game.transcript), readFileSync27(report));
|
|
49358
49741
|
} catch (error) {
|
|
49359
49742
|
this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
|
|
49360
49743
|
return 0;
|
|
@@ -50050,9 +50433,13 @@ class GodotServer {
|
|
|
50050
50433
|
if (callersName !== undefined && !callersName.replace(/@([^.@]+)_(?:getter|setter)$/, "$1").split(".").every(isIdentifier)) {
|
|
50051
50434
|
return this.createErrorResponse(`callersOf takes a function's name, or Class.name for the functions of one class, and ${JSON.stringify(callersName)} is neither.`, ["editor_run profile names each function as it answers; pass one of those"]);
|
|
50052
50435
|
}
|
|
50436
|
+
const asked = {
|
|
50437
|
+
mark: readBoolean(args, "mark") === true,
|
|
50438
|
+
since: readString(args, "since") === "mark"
|
|
50439
|
+
};
|
|
50053
50440
|
const going = await this.runStillGoing(run);
|
|
50054
50441
|
const projectPath = run.projectPath;
|
|
50055
|
-
const projectGone = projectPath !== null && !
|
|
50442
|
+
const projectGone = projectPath !== null && !existsSync25(join32(projectPath, "project.godot"));
|
|
50056
50443
|
if (projectGone && callersName !== undefined) {
|
|
50057
50444
|
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"]);
|
|
50058
50445
|
}
|
|
@@ -50062,7 +50449,7 @@ class GodotServer {
|
|
|
50062
50449
|
...callersName === undefined ? {} : { callersOf: { name: callersName, scripts: () => projectScripts(projectPath) } }
|
|
50063
50450
|
};
|
|
50064
50451
|
if (run.throughEditor) {
|
|
50065
|
-
return await this.profileOfThePlayedGame(
|
|
50452
|
+
return await this.profileOfThePlayedGame(run, going, fromProject, limit, asked);
|
|
50066
50453
|
}
|
|
50067
50454
|
const path = run.profile;
|
|
50068
50455
|
if (path === undefined) {
|
|
@@ -50075,15 +50462,16 @@ class GodotServer {
|
|
|
50075
50462
|
]);
|
|
50076
50463
|
}
|
|
50077
50464
|
const totals = file.connected ? file : null;
|
|
50078
|
-
return this.
|
|
50465
|
+
return this.profileReply(run, {
|
|
50079
50466
|
through: "gdharness",
|
|
50080
50467
|
running: going,
|
|
50081
50468
|
totals,
|
|
50082
50469
|
coveredMs: totals?.lastFrameAt == null ? 0 : totals.lastFrameAt - totals.startedAt,
|
|
50083
50470
|
...fromProject
|
|
50084
|
-
}, limit)
|
|
50471
|
+
}, limit, asked);
|
|
50085
50472
|
}
|
|
50086
|
-
async profileOfThePlayedGame(
|
|
50473
|
+
async profileOfThePlayedGame(run, going, fromProject, limit, asked) {
|
|
50474
|
+
const { projectPath } = run;
|
|
50087
50475
|
const read = await this.handleViaBridge("profile_read", {});
|
|
50088
50476
|
if (read.isError === true) {
|
|
50089
50477
|
return read;
|
|
@@ -50105,12 +50493,13 @@ class GodotServer {
|
|
|
50105
50493
|
return this.createErrorResponse("The editor says it is playing and has no debugger session for the game, so there is nothing to switch the profiler on in yet.", ["Ask again in a moment, once the game is up"]);
|
|
50106
50494
|
}
|
|
50107
50495
|
const on = await this.firstProfiledFrame();
|
|
50496
|
+
const unmarked = asked.mark || asked.since ? " Nothing was marked, and nothing is counted from a mark: the profile begins now, so an answer without since covers the same span." : "";
|
|
50108
50497
|
return this.jsonTextResponse({
|
|
50109
50498
|
through: "editor",
|
|
50110
50499
|
running: true,
|
|
50111
50500
|
started: true,
|
|
50112
50501
|
functions: [],
|
|
50113
|
-
note: on ?
|
|
50502
|
+
note: on ? `The profiler was switched on now, for the game the editor is playing, and its first frame has arrived, so it covers what the game does from here on. Ask again for the functions; editor_run start with profile: true covers a game from its start.${unmarked}` : `The profiler was switched on for the game the editor is playing, and no frame of it had arrived ${PROFILER_ON_MS}ms later: what the game does before the first one is not counted. Ask again for the functions; editor_run start with profile: true covers a game from its start.${unmarked}`
|
|
50114
50503
|
});
|
|
50115
50504
|
}
|
|
50116
50505
|
const functions = asParams(held["functions"]);
|
|
@@ -50133,13 +50522,70 @@ class GodotServer {
|
|
|
50133
50522
|
];
|
|
50134
50523
|
}))
|
|
50135
50524
|
} : null;
|
|
50136
|
-
return this.
|
|
50525
|
+
return this.profileReply(run, {
|
|
50137
50526
|
through: "editor",
|
|
50138
50527
|
running: going,
|
|
50139
50528
|
totals,
|
|
50140
50529
|
coveredMs: readNonNegativeNumber(held, "coveredMs") ?? 0,
|
|
50141
50530
|
...fromProject
|
|
50142
|
-
}, limit)
|
|
50531
|
+
}, limit, asked);
|
|
50532
|
+
}
|
|
50533
|
+
profileReply(run, reading, limit, asked) {
|
|
50534
|
+
const markPath = profileMarkPath(run.startedAt);
|
|
50535
|
+
const now = Date.now();
|
|
50536
|
+
let shown = reading;
|
|
50537
|
+
const lead = [];
|
|
50538
|
+
let sinceFields = {};
|
|
50539
|
+
if (asked.since) {
|
|
50540
|
+
const mark = readProfileMark(markPath);
|
|
50541
|
+
if (mark === null || mark.runStartedAt !== run.startedAt || mark.projectPath !== run.projectPath) {
|
|
50542
|
+
return this.createErrorResponse("No mark has been set in this run, so there is nothing to count from.", ["editor_run profile with mark: true sets one at the totals it answers with"]);
|
|
50543
|
+
}
|
|
50544
|
+
const added = reading.totals === null ? null : totalsSince(reading.totals, mark.totals);
|
|
50545
|
+
if (added === null) {
|
|
50546
|
+
return this.createErrorResponse("The profile holds less than it did at the mark, so the profiler was started again since, and what it holds now cannot be counted from the mark.", ["editor_run profile with mark: true sets a mark in the profile as it is now"]);
|
|
50547
|
+
}
|
|
50548
|
+
shown = {
|
|
50549
|
+
...reading,
|
|
50550
|
+
totals: added.totals,
|
|
50551
|
+
coveredMs: Math.max(0, reading.coveredMs - mark.coveredMs)
|
|
50552
|
+
};
|
|
50553
|
+
const ago = now - mark.takenAt;
|
|
50554
|
+
sinceFields = {
|
|
50555
|
+
since: "mark",
|
|
50556
|
+
markSetMsAgo: ago,
|
|
50557
|
+
...added.shrunk > 0 ? { lessThanAtMark: added.shrunk } : {}
|
|
50558
|
+
};
|
|
50559
|
+
lead.push(`Counted from the mark set ${ago}ms ago: each function's calls and milliseconds are what it added after the mark, frames and coveredMs are those since it, and a function that added no call is left out.`);
|
|
50560
|
+
if (added.shrunk > 0) {
|
|
50561
|
+
const one = added.shrunk === 1;
|
|
50562
|
+
lead.push(`${one ? "One function reads" : `${added.shrunk} functions read`} less than at the mark and ${one ? "is" : "are"} left out, counted under lessThanAtMark: the engine's own totals arrived after the mark and replaced the frames' sum, and capped totals leave out the functions with the least time.`);
|
|
50563
|
+
}
|
|
50564
|
+
}
|
|
50565
|
+
if (asked.mark) {
|
|
50566
|
+
if (reading.totals === null) {
|
|
50567
|
+
return this.createErrorResponse("There is nothing to mark yet: the game has not connected to the profiler.", ["Ask again in a moment, once the game is up"]);
|
|
50568
|
+
}
|
|
50569
|
+
const mark = {
|
|
50570
|
+
takenAt: now,
|
|
50571
|
+
projectPath: run.projectPath,
|
|
50572
|
+
runStartedAt: run.startedAt,
|
|
50573
|
+
coveredMs: reading.coveredMs,
|
|
50574
|
+
totals: reading.totals
|
|
50575
|
+
};
|
|
50576
|
+
if (!writeProfileMark(markPath, mark)) {
|
|
50577
|
+
return this.createErrorResponse(`The mark could not be written to ${markPath}, so none is set.`);
|
|
50578
|
+
}
|
|
50579
|
+
lead.push(asked.since ? 'The mark is moved to the totals as they are now, so the next since: "mark" counts from here.' : 'The mark is set at these totals: editor_run profile with since: "mark" answers only what is added after it.');
|
|
50580
|
+
}
|
|
50581
|
+
const answer = profileAnswer(shown, limit);
|
|
50582
|
+
const note = [...lead, ...typeof answer["note"] === "string" ? [answer["note"]] : []].join(" ");
|
|
50583
|
+
return this.jsonTextResponse({
|
|
50584
|
+
...answer,
|
|
50585
|
+
...sinceFields,
|
|
50586
|
+
...asked.mark ? { marked: true } : {},
|
|
50587
|
+
note: note === "" ? undefined : note
|
|
50588
|
+
});
|
|
50143
50589
|
}
|
|
50144
50590
|
async firstProfiledFrame() {
|
|
50145
50591
|
const deadline = Date.now() + PROFILER_ON_MS;
|
|
@@ -50340,7 +50786,7 @@ class GodotServer {
|
|
|
50340
50786
|
notes.push(endedWithoutACode({
|
|
50341
50787
|
throughEditor: run.throughEditor,
|
|
50342
50788
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
50343
|
-
couldAnnounce: run.projectPath !== null &&
|
|
50789
|
+
couldAnnounce: run.projectPath !== null && existsSync25(join32(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
50344
50790
|
endedHere: typeof run.endedHere === "string",
|
|
50345
50791
|
pickedUp: run.pickedUp === true
|
|
50346
50792
|
}));
|
|
@@ -50970,7 +51416,7 @@ class GodotServer {
|
|
|
50970
51416
|
if (!scriptPath.ok) {
|
|
50971
51417
|
return this.createErrorResponse(scriptPath.reason);
|
|
50972
51418
|
}
|
|
50973
|
-
if (!
|
|
51419
|
+
if (!existsSync25(scriptPath.absolutePath)) {
|
|
50974
51420
|
return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
|
|
50975
51421
|
}
|
|
50976
51422
|
const asked = readNonEmptyString(args, "newScriptPath");
|
|
@@ -51384,7 +51830,7 @@ class GodotServer {
|
|
|
51384
51830
|
}
|
|
51385
51831
|
const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
51386
51832
|
const declared = new RegExp(`^\\s*(?:@\\w+(?:\\([^)]*\\))?\\s+)*class_name\\s+${escaped}\\b`, "m");
|
|
51387
|
-
if (declared.test(
|
|
51833
|
+
if (declared.test(readFileSync27(scriptPath, "utf8"))) {
|
|
51388
51834
|
return null;
|
|
51389
51835
|
}
|
|
51390
51836
|
const checked = await this.classesTheEditorCannotSee(args);
|
|
@@ -51552,7 +51998,7 @@ class GodotServer {
|
|
|
51552
51998
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
51553
51999
|
const run = await this.currentRun();
|
|
51554
52000
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
51555
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !
|
|
52001
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync25(join32(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
51556
52002
|
return null;
|
|
51557
52003
|
}
|
|
51558
52004
|
const already = runtimesAnnounced().running;
|
|
@@ -51561,7 +52007,7 @@ class GodotServer {
|
|
|
51561
52007
|
return !run.announcedBefore.has(one.pid);
|
|
51562
52008
|
}
|
|
51563
52009
|
try {
|
|
51564
|
-
return
|
|
52010
|
+
return statSync14(one.file).mtimeMs >= run.startedAt;
|
|
51565
52011
|
} catch {
|
|
51566
52012
|
return false;
|
|
51567
52013
|
}
|
|
@@ -51642,7 +52088,7 @@ class GodotServer {
|
|
|
51642
52088
|
}
|
|
51643
52089
|
}
|
|
51644
52090
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
51645
|
-
const screenshotPath = screenshotDir ?
|
|
52091
|
+
const screenshotPath = screenshotDir ? join32(screenshotDir, "capture.png") : null;
|
|
51646
52092
|
const staleNote = this.runtimeStaleNote(choice.endpoint);
|
|
51647
52093
|
const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
|
|
51648
52094
|
const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
|
|
@@ -51661,8 +52107,10 @@ ${staleNote}`);
|
|
|
51661
52107
|
}
|
|
51662
52108
|
this.callsOnGames.add(onGame);
|
|
51663
52109
|
const alongside = () => onGame.alongside.size === 0 ? {} : { ranAlongside: [...onGame.alongside], alongsideNote: ALONGSIDE_NOTE };
|
|
52110
|
+
let savedLater = false;
|
|
51664
52111
|
try {
|
|
51665
|
-
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs,
|
|
52112
|
+
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs, expectsScreenshot ? () => discard(screenshotDir) : "keep");
|
|
52113
|
+
savedLater = !reply.ok && reply.stillWithTheGame === true;
|
|
51666
52114
|
if (!reply.ok && reply.requestId !== undefined) {
|
|
51667
52115
|
return this.jsonTextResponse({
|
|
51668
52116
|
pending: true,
|
|
@@ -51708,12 +52156,14 @@ Ran alongside ${ranWith.join(", ")}. ${ALONGSIDE_NOTE}`}${staleNote === undefine
|
|
|
51708
52156
|
|
|
51709
52157
|
${staleNote}`}`
|
|
51710
52158
|
},
|
|
51711
|
-
{ type: "image", data:
|
|
52159
|
+
{ type: "image", data: readFileSync27(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
51712
52160
|
]
|
|
51713
52161
|
};
|
|
51714
52162
|
} finally {
|
|
51715
52163
|
this.callsOnGames.delete(onGame);
|
|
51716
|
-
|
|
52164
|
+
if (!savedLater) {
|
|
52165
|
+
discard(screenshotDir);
|
|
52166
|
+
}
|
|
51717
52167
|
}
|
|
51718
52168
|
}
|
|
51719
52169
|
handleLateAnswer(args) {
|
|
@@ -51823,12 +52273,13 @@ async function runGodotServer() {
|
|
|
51823
52273
|
const server = new GodotServer;
|
|
51824
52274
|
await server.run();
|
|
51825
52275
|
}
|
|
51826
|
-
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, CLOSE_ASK_MS = 3000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, 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, OUTPUT_PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
52276
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, CLOSE_ASK_MS = 3000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", LOOPING_AFTER = 100, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, OUTPUT_PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
51827
52277
|
var init_server = __esm(() => {
|
|
51828
52278
|
init_dist();
|
|
51829
52279
|
init_stdio();
|
|
51830
52280
|
init_alive();
|
|
51831
52281
|
init_answer_json();
|
|
52282
|
+
init_autoload_failure();
|
|
51832
52283
|
init_boot_note();
|
|
51833
52284
|
init_breakpoint_note();
|
|
51834
52285
|
init_bridge_announce();
|
|
@@ -51839,6 +52290,7 @@ var init_server = __esm(() => {
|
|
|
51839
52290
|
init_cut_test_run();
|
|
51840
52291
|
init_dap_client();
|
|
51841
52292
|
init_desktop();
|
|
52293
|
+
init_doc_cache();
|
|
51842
52294
|
init_editor_log();
|
|
51843
52295
|
init_engine_run();
|
|
51844
52296
|
init_errors();
|
|
@@ -51881,7 +52333,7 @@ var init_server = __esm(() => {
|
|
|
51881
52333
|
init_tool_definitions();
|
|
51882
52334
|
init_tool_reference();
|
|
51883
52335
|
init_update_check();
|
|
51884
|
-
__dirname2 =
|
|
52336
|
+
__dirname2 = dirname23(fileURLToPath5(import.meta.url));
|
|
51885
52337
|
KEEPER_DEBUG_MS = PAUSE_MS + STEP_MS + 2 * READ_MS;
|
|
51886
52338
|
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
51887
52339
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
@@ -51941,8 +52393,8 @@ var init_server = __esm(() => {
|
|
|
51941
52393
|
init_bridge_announce();
|
|
51942
52394
|
init_errors();
|
|
51943
52395
|
init_godot_path();
|
|
51944
|
-
import { existsSync as
|
|
51945
|
-
import { join as
|
|
52396
|
+
import { existsSync as existsSync26 } from "node:fs";
|
|
52397
|
+
import { join as join33, resolve as resolve8 } from "node:path";
|
|
51946
52398
|
|
|
51947
52399
|
// src/harnesses.ts
|
|
51948
52400
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -52877,10 +53329,10 @@ init_issues();
|
|
|
52877
53329
|
init_paths();
|
|
52878
53330
|
|
|
52879
53331
|
// src/prompt.ts
|
|
52880
|
-
import
|
|
53332
|
+
import process9 from "node:process";
|
|
52881
53333
|
import { createInterface } from "node:readline/promises";
|
|
52882
53334
|
function interactive() {
|
|
52883
|
-
return
|
|
53335
|
+
return process9.stdin.isTTY && process9.stdout.isTTY;
|
|
52884
53336
|
}
|
|
52885
53337
|
|
|
52886
53338
|
class Ask {
|
|
@@ -52889,7 +53341,7 @@ class Ask {
|
|
|
52889
53341
|
if (!interactive()) {
|
|
52890
53342
|
return fallback;
|
|
52891
53343
|
}
|
|
52892
|
-
this.line ??= createInterface({ input:
|
|
53344
|
+
this.line ??= createInterface({ input: process9.stdin, output: process9.stdout });
|
|
52893
53345
|
const answer = (await this.line.question(`${question} ${fallback ? "[Y/n]" : "[y/N]"} `)).trim();
|
|
52894
53346
|
return answer === "" ? fallback : answer.toLowerCase().startsWith("y");
|
|
52895
53347
|
}
|
|
@@ -52907,10 +53359,10 @@ init_setup();
|
|
|
52907
53359
|
init_setup();
|
|
52908
53360
|
init_tool_definitions();
|
|
52909
53361
|
init_tool_reference();
|
|
52910
|
-
import { existsSync as
|
|
52911
|
-
import { basename as
|
|
53362
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync as readdirSync9, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
53363
|
+
import { basename as basename6, dirname as dirname8, join as join14 } from "node:path";
|
|
52912
53364
|
var SKILL_NAME = "gdharness";
|
|
52913
|
-
var SHARED_SKILLS =
|
|
53365
|
+
var SHARED_SKILLS = join14(".agents", "skills");
|
|
52914
53366
|
function skillMarkdown(version) {
|
|
52915
53367
|
return `---
|
|
52916
53368
|
name: ${SKILL_NAME}
|
|
@@ -52997,7 +53449,7 @@ that is running, and the project on disk. ${TOOL_SPECS.length} tools, named \`do
|
|
|
52997
53449
|
| What every label on a screen says | \`runtime_inspect tree\` with \`properties\` naming \`text\`, which reads it off every node that has one; \`includeProperties\` is every stored property of every node and ran to seventy-six thousand characters for one row |
|
|
52998
53450
|
| Whether this engine has a method or property, and what it takes | \`editor_classes info\` with \`member\`, which answers that one member and which class declares it rather than the whole class |
|
|
52999
53451
|
| Wait for a run to finish | \`editor_run wait\`, never a sleep or a shell loop on the pid: it answers as \`editor_output\` does, once the run is over |
|
|
53000
|
-
| Find which GDScript makes a run slow | \`editor_run start\` with \`profile\` set, then \`editor_run profile\`: the engine's own script profiler, per function with calls and self and total milliseconds, by self time, never \`Time.get_ticks_usec()\` laps edited into the game. A scene that does its work in \`_ready\` and quits is covered whole when the runtime autoload is registered, and a game the editor is already playing is profiled from the moment it is asked. \`callersOf\` names where the project's scripts call one function, each place with its function's numbers, since the engine records no callers |
|
|
53452
|
+
| Find which GDScript makes a run slow | \`editor_run start\` with \`profile\` set, then \`editor_run profile\`: the engine's own script profiler, per function with calls and self and total milliseconds, by self time, never \`Time.get_ticks_usec()\` laps edited into the game. A scene that does its work in \`_ready\` and quits is covered whole when the runtime autoload is registered, and a game the editor is already playing is profiled from the moment it is asked. For one action, a press or a commit, set \`mark\` on a profile before it and pass \`since: "mark"\` after it, which answers only what the action added. \`callersOf\` names where the project's scripts call one function, each place with its function's numbers, since the engine records no callers |
|
|
53001
53453
|
| End a bench and the workers it opened | \`editor_run stop\` with \`andChildren\`, never a process listing by scene name: it ends every process under the run's, to any depth, that is a game of the project, announced as one or the project's engine run with \`--path\` on it, names them under \`endedChildren\` with what identified each, and leaves and names any other |
|
|
53002
53454
|
| End a game of the project that no server is holding | \`editor_run stop\` with its \`pid\` from \`editor_status\` \`runtimes\`, never a process kill by hand: it is ended only when it announced itself for the project and its command line is the project's engine run |
|
|
53003
53455
|
| Play a game without touching the player's saves | \`editor_run start\` with \`savesIn\`, a directory for this run's \`user://\`: the editor plays with its own environment, so a run given one is started by the server, and the answer says under \`savesNote\` when the platform ignores it |
|
|
@@ -53135,27 +53587,27 @@ what they report while starting comes back under \`engine_messages_before_operat
|
|
|
53135
53587
|
function skillFiles(version) {
|
|
53136
53588
|
return new Map([
|
|
53137
53589
|
["SKILL.md", skillMarkdown(version)],
|
|
53138
|
-
[
|
|
53590
|
+
[join14("references", "tools.md"), renderToolsMarkdown()]
|
|
53139
53591
|
]);
|
|
53140
53592
|
}
|
|
53141
53593
|
function skillDirectories(harnesses, projectPath, exists) {
|
|
53142
|
-
const directories = [
|
|
53594
|
+
const directories = [join14(projectPath, SHARED_SKILLS, SKILL_NAME)];
|
|
53143
53595
|
for (const harness of harnesses) {
|
|
53144
53596
|
if (harness.skills === undefined) {
|
|
53145
53597
|
continue;
|
|
53146
53598
|
}
|
|
53147
|
-
const own =
|
|
53148
|
-
if ((!harness.skills.shared || exists(own)) && !directories.includes(
|
|
53149
|
-
directories.push(
|
|
53599
|
+
const own = join14(projectPath, harness.skills.dir);
|
|
53600
|
+
if ((!harness.skills.shared || exists(own)) && !directories.includes(join14(own, SKILL_NAME))) {
|
|
53601
|
+
directories.push(join14(own, SKILL_NAME));
|
|
53150
53602
|
}
|
|
53151
53603
|
}
|
|
53152
53604
|
return directories;
|
|
53153
53605
|
}
|
|
53154
53606
|
function everySkillDirectory(projectPath, harnesses) {
|
|
53155
|
-
const directories = new Set([
|
|
53607
|
+
const directories = new Set([join14(projectPath, SHARED_SKILLS, SKILL_NAME)]);
|
|
53156
53608
|
for (const harness of harnesses) {
|
|
53157
53609
|
if (harness.skills !== undefined) {
|
|
53158
|
-
directories.add(
|
|
53610
|
+
directories.add(join14(projectPath, harness.skills.dir, SKILL_NAME));
|
|
53159
53611
|
}
|
|
53160
53612
|
}
|
|
53161
53613
|
return [...directories];
|
|
@@ -53163,33 +53615,33 @@ function everySkillDirectory(projectPath, harnesses) {
|
|
|
53163
53615
|
function removeSkill(directories, projectPath) {
|
|
53164
53616
|
const removed = [];
|
|
53165
53617
|
for (const directory of directories) {
|
|
53166
|
-
if (
|
|
53618
|
+
if (existsSync14(directory)) {
|
|
53167
53619
|
rmSync6(directory, { recursive: true, force: true });
|
|
53168
|
-
pruneEmpty(
|
|
53620
|
+
pruneEmpty(dirname8(directory), projectPath);
|
|
53169
53621
|
removed.push(directory);
|
|
53170
53622
|
}
|
|
53171
53623
|
}
|
|
53172
53624
|
return removed;
|
|
53173
53625
|
}
|
|
53174
53626
|
function pruneEmpty(directory, projectPath) {
|
|
53175
|
-
const shared =
|
|
53627
|
+
const shared = join14(projectPath, ".agents");
|
|
53176
53628
|
let path = directory;
|
|
53177
|
-
while (path !== projectPath && (
|
|
53178
|
-
if (
|
|
53629
|
+
while (path !== projectPath && (basename6(path) === "skills" || path === shared)) {
|
|
53630
|
+
if (readdirSync9(path).length > 0) {
|
|
53179
53631
|
return;
|
|
53180
53632
|
}
|
|
53181
53633
|
rmSync6(path, { recursive: true, force: true });
|
|
53182
|
-
path =
|
|
53634
|
+
path = dirname8(path);
|
|
53183
53635
|
}
|
|
53184
53636
|
}
|
|
53185
53637
|
function writeSkill(directories, version) {
|
|
53186
53638
|
const files = skillFiles(version);
|
|
53187
53639
|
return directories.map((directory) => {
|
|
53188
|
-
const replaced =
|
|
53640
|
+
const replaced = existsSync14(directory);
|
|
53189
53641
|
rmSync6(directory, { recursive: true, force: true });
|
|
53190
53642
|
for (const [name, contents] of files) {
|
|
53191
|
-
const path =
|
|
53192
|
-
mkdirSync5(
|
|
53643
|
+
const path = join14(directory, name);
|
|
53644
|
+
mkdirSync5(dirname8(path), { recursive: true });
|
|
53193
53645
|
writeFileSync7(path, contents, "utf8");
|
|
53194
53646
|
}
|
|
53195
53647
|
return { path: directory, replaced };
|
|
@@ -53198,13 +53650,13 @@ function writeSkill(directories, version) {
|
|
|
53198
53650
|
|
|
53199
53651
|
// src/version.ts
|
|
53200
53652
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
53201
|
-
import { dirname as
|
|
53653
|
+
import { dirname as dirname9, join as join15 } from "node:path";
|
|
53202
53654
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
53203
53655
|
function getLocalVersion() {
|
|
53204
|
-
const currentDirectory =
|
|
53656
|
+
const currentDirectory = dirname9(fileURLToPath2(import.meta.url));
|
|
53205
53657
|
const packageCandidates = [
|
|
53206
|
-
|
|
53207
|
-
|
|
53658
|
+
join15(currentDirectory, "..", "package.json"),
|
|
53659
|
+
join15(currentDirectory, "..", "..", "package.json")
|
|
53208
53660
|
];
|
|
53209
53661
|
for (const packagePath of packageCandidates) {
|
|
53210
53662
|
try {
|
|
@@ -53226,7 +53678,7 @@ class UsageError extends Refusal {
|
|
|
53226
53678
|
function projectArgument(at) {
|
|
53227
53679
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
53228
53680
|
const projectPath = resolve8(named ?? ".");
|
|
53229
|
-
if (!
|
|
53681
|
+
if (!existsSync26(join33(projectPath, "project.godot"))) {
|
|
53230
53682
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
53231
53683
|
}
|
|
53232
53684
|
return projectPath;
|
|
@@ -53271,7 +53723,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
53271
53723
|
return;
|
|
53272
53724
|
}
|
|
53273
53725
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
53274
|
-
if (!
|
|
53726
|
+
if (!existsSync26(join33(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
53275
53727
|
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}`);
|
|
53276
53728
|
}
|
|
53277
53729
|
}
|
|
@@ -53403,7 +53855,7 @@ async function setup() {
|
|
|
53403
53855
|
console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
|
|
53404
53856
|
}
|
|
53405
53857
|
if (!args.includes("--no-skill")) {
|
|
53406
|
-
const directories = skillDirectories(chosen.harnesses, projectPath,
|
|
53858
|
+
const directories = skillDirectories(chosen.harnesses, projectPath, existsSync26);
|
|
53407
53859
|
for (const written of writeSkill(directories, getLocalVersion())) {
|
|
53408
53860
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
53409
53861
|
}
|
|
@@ -53517,7 +53969,7 @@ async function upgrade() {
|
|
|
53517
53969
|
moved.push(took.from);
|
|
53518
53970
|
}
|
|
53519
53971
|
}
|
|
53520
|
-
for (const written of writeSkill(skillDirectories(already, projectPath,
|
|
53972
|
+
for (const written of writeSkill(skillDirectories(already, projectPath, existsSync26), version)) {
|
|
53521
53973
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
53522
53974
|
}
|
|
53523
53975
|
console.log(`
|