gdharness 1.2.27 → 1.2.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/build/index.js
CHANGED
|
@@ -3363,17 +3363,17 @@ import {
|
|
|
3363
3363
|
closeSync as closeSync2,
|
|
3364
3364
|
constants,
|
|
3365
3365
|
copyFileSync,
|
|
3366
|
-
existsSync as
|
|
3366
|
+
existsSync as existsSync18,
|
|
3367
3367
|
fstatSync,
|
|
3368
3368
|
mkdirSync as mkdirSync14,
|
|
3369
3369
|
openSync as openSync3,
|
|
3370
|
-
readdirSync as
|
|
3371
|
-
readFileSync as
|
|
3370
|
+
readdirSync as readdirSync10,
|
|
3371
|
+
readFileSync as readFileSync22,
|
|
3372
3372
|
readSync as readSync2,
|
|
3373
3373
|
rmdirSync as rmdirSync2,
|
|
3374
3374
|
statSync as statSync9
|
|
3375
3375
|
} from "node:fs";
|
|
3376
|
-
import { basename as
|
|
3376
|
+
import { basename as basename6, dirname as dirname17, extname as extname3, isAbsolute as isAbsolute4, join as join24, normalize as normalize2, relative as relative4 } from "node:path";
|
|
3377
3377
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
3378
3378
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
3379
3379
|
import { promisify as promisify5 } from "node:util";
|
|
@@ -29517,33 +29517,114 @@ class GodotLocator {
|
|
|
29517
29517
|
}
|
|
29518
29518
|
|
|
29519
29519
|
// src/headless.ts
|
|
29520
|
-
import { existsSync as
|
|
29521
|
-
import { dirname as
|
|
29520
|
+
import { existsSync as existsSync12, readFileSync as readFileSync14, realpathSync as realpathSync2, writeFileSync as writeFileSync9 } from "node:fs";
|
|
29521
|
+
import { dirname as dirname9, join as join15 } from "node:path";
|
|
29522
|
+
|
|
29523
|
+
// src/library-copies.ts
|
|
29524
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync12, renameSync, unlinkSync as unlinkSync2 } from "node:fs";
|
|
29525
|
+
import { basename as basename2, dirname as dirname8, isAbsolute as isAbsolute3, join as join12 } from "node:path";
|
|
29526
|
+
function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
29527
|
+
const setAside = [];
|
|
29528
|
+
const swept = [];
|
|
29529
|
+
if (platform !== "win32") {
|
|
29530
|
+
return { setAside, swept };
|
|
29531
|
+
}
|
|
29532
|
+
for (const library of windowsLibraries(projectPath)) {
|
|
29533
|
+
const folder = dirname8(library);
|
|
29534
|
+
const copy = `~${basename2(library)}`;
|
|
29535
|
+
if (existsSync9(join12(folder, copy))) {
|
|
29536
|
+
const aside = join12(folder, `${copy}.aside-${process.pid}-${Date.now()}`);
|
|
29537
|
+
try {
|
|
29538
|
+
renameSync(join12(folder, copy), aside);
|
|
29539
|
+
setAside.push(aside);
|
|
29540
|
+
} catch {}
|
|
29541
|
+
}
|
|
29542
|
+
let names;
|
|
29543
|
+
try {
|
|
29544
|
+
names = readdirSync4(folder);
|
|
29545
|
+
} catch {
|
|
29546
|
+
continue;
|
|
29547
|
+
}
|
|
29548
|
+
for (const name of names) {
|
|
29549
|
+
if (name.length > copy.length && name.toLowerCase().startsWith(copy.toLowerCase())) {
|
|
29550
|
+
try {
|
|
29551
|
+
unlinkSync2(join12(folder, name));
|
|
29552
|
+
swept.push(join12(folder, name));
|
|
29553
|
+
} catch {}
|
|
29554
|
+
}
|
|
29555
|
+
}
|
|
29556
|
+
}
|
|
29557
|
+
return { setAside, swept };
|
|
29558
|
+
}
|
|
29559
|
+
function windowsLibraries(projectPath) {
|
|
29560
|
+
const listed = readOrNull(join12(projectPath, ".godot", "extension_list.cfg"));
|
|
29561
|
+
if (listed === null) {
|
|
29562
|
+
return [];
|
|
29563
|
+
}
|
|
29564
|
+
const libraries = new Set;
|
|
29565
|
+
for (const line of listed.split(/\r?\n/)) {
|
|
29566
|
+
const manifest = resolveResource(projectPath, projectPath, line.trim());
|
|
29567
|
+
const text = manifest === null ? null : readOrNull(manifest);
|
|
29568
|
+
if (manifest === null || text === null) {
|
|
29569
|
+
continue;
|
|
29570
|
+
}
|
|
29571
|
+
let inLibraries = false;
|
|
29572
|
+
for (const row of text.split(/\r?\n/)) {
|
|
29573
|
+
const section = /^\s*\[([^\]]+)\]/.exec(row)?.[1];
|
|
29574
|
+
if (section !== undefined) {
|
|
29575
|
+
inLibraries = section.trim() === "libraries";
|
|
29576
|
+
continue;
|
|
29577
|
+
}
|
|
29578
|
+
const named = inLibraries ? /^\s*windows[\w.]*\s*=\s*"([^"]+)"/.exec(row)?.[1] : undefined;
|
|
29579
|
+
const library = named === undefined ? null : resolveResource(projectPath, dirname8(manifest), named);
|
|
29580
|
+
if (library !== null) {
|
|
29581
|
+
libraries.add(library);
|
|
29582
|
+
}
|
|
29583
|
+
}
|
|
29584
|
+
}
|
|
29585
|
+
return [...libraries];
|
|
29586
|
+
}
|
|
29587
|
+
function resolveResource(projectPath, relativeTo, path) {
|
|
29588
|
+
if (path === "") {
|
|
29589
|
+
return null;
|
|
29590
|
+
}
|
|
29591
|
+
if (path.startsWith("res://")) {
|
|
29592
|
+
return join12(projectPath, path.slice("res://".length));
|
|
29593
|
+
}
|
|
29594
|
+
return isAbsolute3(path) ? path : join12(relativeTo, path);
|
|
29595
|
+
}
|
|
29596
|
+
function readOrNull(path) {
|
|
29597
|
+
try {
|
|
29598
|
+
return readFileSync12(path, "utf8");
|
|
29599
|
+
} catch {
|
|
29600
|
+
return null;
|
|
29601
|
+
}
|
|
29602
|
+
}
|
|
29522
29603
|
|
|
29523
29604
|
// src/reimport.ts
|
|
29524
29605
|
import { createHash as createHash2 } from "node:crypto";
|
|
29525
|
-
import { existsSync as
|
|
29526
|
-
import { basename as
|
|
29606
|
+
import { existsSync as existsSync10, readFileSync as readFileSync13, rmSync as rmSync5, statSync as statSync5, utimesSync, writeFileSync as writeFileSync8 } from "node:fs";
|
|
29607
|
+
import { basename as basename3, join as join13 } from "node:path";
|
|
29527
29608
|
function forceNextImport(projectPath, resourcePath) {
|
|
29528
29609
|
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
29529
|
-
const sidecar =
|
|
29530
|
-
if (!
|
|
29610
|
+
const sidecar = join13(projectPath, `${relative}.import`);
|
|
29611
|
+
if (!existsSync10(sidecar)) {
|
|
29531
29612
|
return;
|
|
29532
29613
|
}
|
|
29533
29614
|
const { atime, mtimeMs } = statSync5(sidecar);
|
|
29534
|
-
writeFileSync8(sidecar,
|
|
29615
|
+
writeFileSync8(sidecar, readFileSync13(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
29535
29616
|
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
29536
29617
|
rmSync5(importRecord(projectPath, resourcePath), { force: true });
|
|
29537
29618
|
}
|
|
29538
29619
|
function importRecord(projectPath, resourcePath) {
|
|
29539
29620
|
const digest = createHash2("md5").update(resourcePath).digest("hex");
|
|
29540
|
-
return
|
|
29621
|
+
return join13(projectPath, ".godot", "imported", `${basename3(resourcePath)}-${digest}.md5`);
|
|
29541
29622
|
}
|
|
29542
29623
|
|
|
29543
29624
|
// src/scratch.ts
|
|
29544
|
-
import { existsSync as
|
|
29625
|
+
import { existsSync as existsSync11, mkdtempSync, readdirSync as readdirSync5, rmSync as rmSync6 } from "node:fs";
|
|
29545
29626
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
29546
|
-
import { join as
|
|
29627
|
+
import { join as join14 } from "node:path";
|
|
29547
29628
|
import process7 from "node:process";
|
|
29548
29629
|
var SWEPT = { recursive: true, force: true };
|
|
29549
29630
|
var AGAIN_AFTER_MS = [250, 500, 1000, 2000, 4000, 8000, 16000];
|
|
@@ -29558,12 +29639,12 @@ var KINDS = [
|
|
|
29558
29639
|
"tests"
|
|
29559
29640
|
];
|
|
29560
29641
|
function scratchDirectory(kind) {
|
|
29561
|
-
return mkdtempSync(
|
|
29642
|
+
return mkdtempSync(join14(tmpdir4(), `gdharness-${kind}-${process7.pid}-`));
|
|
29562
29643
|
}
|
|
29563
29644
|
function sweepAbandonedScratch(directory = tmpdir4()) {
|
|
29564
29645
|
let entries;
|
|
29565
29646
|
try {
|
|
29566
|
-
entries =
|
|
29647
|
+
entries = readdirSync5(directory);
|
|
29567
29648
|
} catch {
|
|
29568
29649
|
return;
|
|
29569
29650
|
}
|
|
@@ -29574,7 +29655,7 @@ function sweepAbandonedScratch(directory = tmpdir4()) {
|
|
|
29574
29655
|
continue;
|
|
29575
29656
|
}
|
|
29576
29657
|
try {
|
|
29577
|
-
remove(
|
|
29658
|
+
remove(join14(directory, entry));
|
|
29578
29659
|
} catch {}
|
|
29579
29660
|
}
|
|
29580
29661
|
}
|
|
@@ -29612,7 +29693,7 @@ function tryAgain(path, attempt) {
|
|
|
29612
29693
|
lastTryOnExit();
|
|
29613
29694
|
}
|
|
29614
29695
|
const after = AGAIN_AFTER_MS[attempt];
|
|
29615
|
-
if (after === undefined || !
|
|
29696
|
+
if (after === undefined || !existsSync11(path)) {
|
|
29616
29697
|
waiting.delete(path);
|
|
29617
29698
|
if (after === undefined) {
|
|
29618
29699
|
atExit.add(path);
|
|
@@ -29703,7 +29784,7 @@ function refusalsIn(log) {
|
|
|
29703
29784
|
}
|
|
29704
29785
|
function scriptFault(problems, script) {
|
|
29705
29786
|
const spelled = (path) => `(${path.replaceAll("\\", "/").toLowerCase()}/`;
|
|
29706
|
-
const directories = [...new Set([spelled(
|
|
29787
|
+
const directories = [...new Set([spelled(dirname9(script)), spelled(resolvedOr(dirname9(script)))])];
|
|
29707
29788
|
return problems.find((entry) => {
|
|
29708
29789
|
const at = entry.detail.find((line) => line.startsWith("at: "))?.replaceAll("\\", "/").toLowerCase();
|
|
29709
29790
|
return entry.severity === "error" && at !== undefined && directories.some((directory) => at.includes(directory));
|
|
@@ -29728,7 +29809,7 @@ function librariesNotCopied(messages) {
|
|
|
29728
29809
|
}
|
|
29729
29810
|
function extensionRetryNote(libraries, stillNotLoaded = []) {
|
|
29730
29811
|
const second = stillNotLoaded.length === 0 ? "so the import was run again and loaded the extension, and the messages are from that second run." : `so the import was run again, and it still could not load ${stillNotLoaded.join(" and ")}: this answer is from an import without ${stillNotLoaded.length === 1 ? "it" : "them"}, and its messages say what the engine hit.`;
|
|
29731
|
-
return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + `editor's copy aside, ${second} The editor's copy is left beside the library as a ~RF….TMP file, which ` + "
|
|
29812
|
+
return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + `editor's copy aside, ${second} The editor's copy is left beside the library as a ~RF….TMP file, which ` + "the next import, export or editor this server starts on the project deletes once the editor has exited.";
|
|
29732
29813
|
}
|
|
29733
29814
|
async function runImport(godotPath, projectPath, options = {}) {
|
|
29734
29815
|
const once = options.once ?? importOnce;
|
|
@@ -29736,6 +29817,7 @@ async function runImport(godotPath, projectPath, options = {}) {
|
|
|
29736
29817
|
for (const target of options.forcing ?? []) {
|
|
29737
29818
|
forceNextImport(projectPath, target);
|
|
29738
29819
|
}
|
|
29820
|
+
freeLibraryCopies(projectPath);
|
|
29739
29821
|
return once(godotPath, projectPath);
|
|
29740
29822
|
};
|
|
29741
29823
|
const first = await pass();
|
|
@@ -29759,7 +29841,7 @@ async function importOnce(godotPath, projectPath) {
|
|
|
29759
29841
|
ran = await runEngine(godotPath, [
|
|
29760
29842
|
"--headless",
|
|
29761
29843
|
"--log-file",
|
|
29762
|
-
|
|
29844
|
+
join15(logDir, "engine.log"),
|
|
29763
29845
|
"--path",
|
|
29764
29846
|
projectPath,
|
|
29765
29847
|
"--import"
|
|
@@ -29779,13 +29861,13 @@ async function importOnce(godotPath, projectPath) {
|
|
|
29779
29861
|
}
|
|
29780
29862
|
async function runOperation(engine, operation, params, projectPath) {
|
|
29781
29863
|
const paramsDir = scratchDirectory("params");
|
|
29782
|
-
const paramsFile =
|
|
29783
|
-
const answerFile =
|
|
29864
|
+
const paramsFile = join15(paramsDir, `${operation}.json`);
|
|
29865
|
+
const answerFile = join15(paramsDir, "answer.json");
|
|
29784
29866
|
writeFileSync9(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
29785
29867
|
const args = [
|
|
29786
29868
|
"--headless",
|
|
29787
29869
|
"--log-file",
|
|
29788
|
-
|
|
29870
|
+
join15(paramsDir, "engine.log"),
|
|
29789
29871
|
"--path",
|
|
29790
29872
|
projectPath,
|
|
29791
29873
|
"--script",
|
|
@@ -29855,10 +29937,10 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
29855
29937
|
};
|
|
29856
29938
|
}
|
|
29857
29939
|
function answerIn(file) {
|
|
29858
|
-
if (!
|
|
29940
|
+
if (!existsSync12(file)) {
|
|
29859
29941
|
return { kind: "none" };
|
|
29860
29942
|
}
|
|
29861
|
-
const text =
|
|
29943
|
+
const text = readFileSync14(file, "utf8");
|
|
29862
29944
|
try {
|
|
29863
29945
|
const parsed = JSON.parse(text);
|
|
29864
29946
|
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { kind: "answer", payload: parsed } : { kind: "unreadable", why: `it is JSON but not an object: ${text.slice(0, 200)}` };
|
|
@@ -30431,20 +30513,20 @@ function askTheKeeper(address, line, withinMs) {
|
|
|
30431
30513
|
}
|
|
30432
30514
|
|
|
30433
30515
|
// src/lifecycle-log.ts
|
|
30434
|
-
import { appendFileSync, mkdirSync as mkdirSync8, renameSync, statSync as statSync6 } from "node:fs";
|
|
30435
|
-
import { dirname as
|
|
30516
|
+
import { appendFileSync, mkdirSync as mkdirSync8, renameSync as renameSync2, statSync as statSync6 } from "node:fs";
|
|
30517
|
+
import { dirname as dirname10, join as join16 } from "node:path";
|
|
30436
30518
|
import process8 from "node:process";
|
|
30437
30519
|
function lifecycleLogPath() {
|
|
30438
|
-
return
|
|
30520
|
+
return join16(runtimeDirectory(), "servers.log");
|
|
30439
30521
|
}
|
|
30440
30522
|
var LIFECYCLE_LOG_LIMIT = 1e6;
|
|
30441
30523
|
function writeLifecycle(event, fields = {}) {
|
|
30442
30524
|
const path = lifecycleLogPath();
|
|
30443
30525
|
try {
|
|
30444
|
-
mkdirSync8(
|
|
30526
|
+
mkdirSync8(dirname10(path), { recursive: true });
|
|
30445
30527
|
if ((statSync6(path, { throwIfNoEntry: false })?.size ?? 0) > LIFECYCLE_LOG_LIMIT) {
|
|
30446
30528
|
try {
|
|
30447
|
-
|
|
30529
|
+
renameSync2(path, `${path}.1`);
|
|
30448
30530
|
} catch {}
|
|
30449
30531
|
}
|
|
30450
30532
|
const line = {
|
|
@@ -30474,10 +30556,10 @@ function callsInFlight(calls, now = Date.now()) {
|
|
|
30474
30556
|
}
|
|
30475
30557
|
|
|
30476
30558
|
// src/lsp_client.ts
|
|
30477
|
-
import { existsSync as
|
|
30559
|
+
import { existsSync as existsSync13, realpathSync as realpathSync3 } from "node:fs";
|
|
30478
30560
|
import { readFile, realpath } from "node:fs/promises";
|
|
30479
30561
|
import { createConnection as createConnection3 } from "node:net";
|
|
30480
|
-
import { dirname as
|
|
30562
|
+
import { dirname as dirname11, join as join17, resolve as resolve3 } from "node:path";
|
|
30481
30563
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
30482
30564
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
30483
30565
|
var DEFAULT_LSP_PORT = 6005;
|
|
@@ -30675,7 +30757,7 @@ class GodotLSPClient {
|
|
|
30675
30757
|
if (this.initialized) {
|
|
30676
30758
|
return;
|
|
30677
30759
|
}
|
|
30678
|
-
const rootPath = this.rootPath ??
|
|
30760
|
+
const rootPath = this.rootPath ?? dirname11(resolve3(filePath));
|
|
30679
30761
|
await this.initialize(rootPath);
|
|
30680
30762
|
}
|
|
30681
30763
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -31233,7 +31315,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
31233
31315
|
} catch {
|
|
31234
31316
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
31235
31317
|
}
|
|
31236
|
-
if (!
|
|
31318
|
+
if (!existsSync13(join17(projectPath, "project.godot"))) {
|
|
31237
31319
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
31238
31320
|
}
|
|
31239
31321
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -31440,8 +31522,8 @@ function secondsFromClock(printed) {
|
|
|
31440
31522
|
}
|
|
31441
31523
|
|
|
31442
31524
|
// src/project-scan.ts
|
|
31443
|
-
import { existsSync as
|
|
31444
|
-
import { extname, join as
|
|
31525
|
+
import { existsSync as existsSync14, readdirSync as readdirSync6, readFileSync as readFileSync15 } from "node:fs";
|
|
31526
|
+
import { extname, join as join18 } from "node:path";
|
|
31445
31527
|
var SKIPPED = new Set(["node_modules"]);
|
|
31446
31528
|
function skipped(name) {
|
|
31447
31529
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
@@ -31451,10 +31533,10 @@ function steppedOver(directory) {
|
|
|
31451
31533
|
return gdignored(directory) || installedByUs(directory);
|
|
31452
31534
|
}
|
|
31453
31535
|
function gdignored(directory) {
|
|
31454
|
-
return
|
|
31536
|
+
return existsSync14(join18(directory, ".gdignore"));
|
|
31455
31537
|
}
|
|
31456
31538
|
function installedByUs(directory) {
|
|
31457
|
-
return
|
|
31539
|
+
return existsSync14(join18(directory, VERSION_MARKER));
|
|
31458
31540
|
}
|
|
31459
31541
|
var SCENE_EXTENSIONS = new Set(["tscn", "scn"]);
|
|
31460
31542
|
var ASSET_EXTENSIONS = new Set([
|
|
@@ -31493,12 +31575,12 @@ function projectStructure(projectPath) {
|
|
|
31493
31575
|
if (steppedOver(directory)) {
|
|
31494
31576
|
return;
|
|
31495
31577
|
}
|
|
31496
|
-
for (const entry of
|
|
31578
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
31497
31579
|
if (skipped(entry.name)) {
|
|
31498
31580
|
continue;
|
|
31499
31581
|
}
|
|
31500
31582
|
if (entry.isDirectory()) {
|
|
31501
|
-
visit(
|
|
31583
|
+
visit(join18(directory, entry.name));
|
|
31502
31584
|
} else if (entry.isFile()) {
|
|
31503
31585
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
31504
31586
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -31522,13 +31604,13 @@ function projectFiles(projectPath, extensions) {
|
|
|
31522
31604
|
if (steppedOver(directory)) {
|
|
31523
31605
|
return;
|
|
31524
31606
|
}
|
|
31525
|
-
for (const entry of
|
|
31607
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
31526
31608
|
if (skipped(entry.name)) {
|
|
31527
31609
|
continue;
|
|
31528
31610
|
}
|
|
31529
31611
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
31530
31612
|
if (entry.isDirectory()) {
|
|
31531
|
-
visit(
|
|
31613
|
+
visit(join18(directory, entry.name), spelled);
|
|
31532
31614
|
} else if (entry.isFile() && extensions.includes(extname(entry.name).toLowerCase())) {
|
|
31533
31615
|
found.push(spelled);
|
|
31534
31616
|
}
|
|
@@ -31544,20 +31626,20 @@ function scriptsWithoutUid(projectPath) {
|
|
|
31544
31626
|
if (steppedOver(directory)) {
|
|
31545
31627
|
return;
|
|
31546
31628
|
}
|
|
31547
|
-
for (const entry of
|
|
31629
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
31548
31630
|
if (skipped(entry.name)) {
|
|
31549
31631
|
continue;
|
|
31550
31632
|
}
|
|
31551
31633
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
31552
31634
|
if (entry.isDirectory()) {
|
|
31553
|
-
visit(
|
|
31635
|
+
visit(join18(directory, entry.name), spelled);
|
|
31554
31636
|
continue;
|
|
31555
31637
|
}
|
|
31556
31638
|
if (!entry.isFile()) {
|
|
31557
31639
|
continue;
|
|
31558
31640
|
}
|
|
31559
31641
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
31560
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
31642
|
+
if (CARRIES_A_UID.has(extension) && !existsSync14(join18(directory, `${entry.name}.uid`))) {
|
|
31561
31643
|
found.push(spelled);
|
|
31562
31644
|
}
|
|
31563
31645
|
}
|
|
@@ -31585,11 +31667,11 @@ function projectTexts(projectPath) {
|
|
|
31585
31667
|
if (steppedOver(directory)) {
|
|
31586
31668
|
return;
|
|
31587
31669
|
}
|
|
31588
|
-
for (const entry of
|
|
31670
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
31589
31671
|
if (skipped(entry.name)) {
|
|
31590
31672
|
continue;
|
|
31591
31673
|
}
|
|
31592
|
-
const absolute =
|
|
31674
|
+
const absolute = join18(directory, entry.name);
|
|
31593
31675
|
const spelled = `${prefix}${entry.name}`;
|
|
31594
31676
|
if (entry.isDirectory()) {
|
|
31595
31677
|
visit(absolute, `${spelled}/`);
|
|
@@ -31599,7 +31681,7 @@ function projectTexts(projectPath) {
|
|
|
31599
31681
|
if (!entry.isFile() || BINARY_EXTENSIONS.has(extension)) {
|
|
31600
31682
|
continue;
|
|
31601
31683
|
}
|
|
31602
|
-
const bytes =
|
|
31684
|
+
const bytes = readFileSync15(absolute);
|
|
31603
31685
|
if (!isBinary(bytes)) {
|
|
31604
31686
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
31605
31687
|
}
|
|
@@ -31615,11 +31697,11 @@ function ignoredTexts(projectPath) {
|
|
|
31615
31697
|
return;
|
|
31616
31698
|
}
|
|
31617
31699
|
const under = ignored || gdignored(directory);
|
|
31618
|
-
for (const entry of
|
|
31700
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
31619
31701
|
if (skipped(entry.name)) {
|
|
31620
31702
|
continue;
|
|
31621
31703
|
}
|
|
31622
|
-
const absolute =
|
|
31704
|
+
const absolute = join18(directory, entry.name);
|
|
31623
31705
|
const spelled = `${prefix}${entry.name}`;
|
|
31624
31706
|
if (entry.isDirectory()) {
|
|
31625
31707
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -31629,7 +31711,7 @@ function ignoredTexts(projectPath) {
|
|
|
31629
31711
|
if (!under || !entry.isFile() || BINARY_EXTENSIONS.has(extension) || entry.name === ".gdignore") {
|
|
31630
31712
|
continue;
|
|
31631
31713
|
}
|
|
31632
|
-
const bytes =
|
|
31714
|
+
const bytes = readFileSync15(absolute);
|
|
31633
31715
|
if (!isBinary(bytes)) {
|
|
31634
31716
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
31635
31717
|
}
|
|
@@ -31652,14 +31734,14 @@ function searchProject(projectPath, options) {
|
|
|
31652
31734
|
if (steppedOver(directory)) {
|
|
31653
31735
|
return;
|
|
31654
31736
|
}
|
|
31655
|
-
for (const entry of
|
|
31737
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
31656
31738
|
if (full()) {
|
|
31657
31739
|
return;
|
|
31658
31740
|
}
|
|
31659
31741
|
if (skipped(entry.name)) {
|
|
31660
31742
|
continue;
|
|
31661
31743
|
}
|
|
31662
|
-
const entryPath =
|
|
31744
|
+
const entryPath = join18(directory, entry.name);
|
|
31663
31745
|
if (entry.isDirectory()) {
|
|
31664
31746
|
visit(entryPath);
|
|
31665
31747
|
continue;
|
|
@@ -31668,7 +31750,7 @@ function searchProject(projectPath, options) {
|
|
|
31668
31750
|
if (!entry.isFile() || extensions !== null && !extensions.has(extension)) {
|
|
31669
31751
|
continue;
|
|
31670
31752
|
}
|
|
31671
|
-
const bytes =
|
|
31753
|
+
const bytes = readFileSync15(entryPath);
|
|
31672
31754
|
if (extensions === null && isBinary(bytes)) {
|
|
31673
31755
|
continue;
|
|
31674
31756
|
}
|
|
@@ -31830,8 +31912,8 @@ function setupPromptHandlers(server) {
|
|
|
31830
31912
|
}
|
|
31831
31913
|
|
|
31832
31914
|
// src/rename.ts
|
|
31833
|
-
import { existsSync as
|
|
31834
|
-
import { dirname as
|
|
31915
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync16, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync10 } from "node:fs";
|
|
31916
|
+
import { dirname as dirname12, posix as posix2 } from "node:path";
|
|
31835
31917
|
class Script {
|
|
31836
31918
|
path;
|
|
31837
31919
|
text;
|
|
@@ -32553,26 +32635,26 @@ function rewritesOf(plan, texts, move) {
|
|
|
32553
32635
|
}
|
|
32554
32636
|
function applyRewrites(rewrites) {
|
|
32555
32637
|
for (const rewrite of rewrites) {
|
|
32556
|
-
const now =
|
|
32638
|
+
const now = readFileSync16(rewrite.absolute, "utf8");
|
|
32557
32639
|
if (now !== rewrite.before) {
|
|
32558
32640
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
32559
32641
|
}
|
|
32560
|
-
if (rewrite.writeTo !== rewrite.absolute &&
|
|
32642
|
+
if (rewrite.writeTo !== rewrite.absolute && existsSync15(rewrite.writeTo)) {
|
|
32561
32643
|
throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
|
|
32562
32644
|
}
|
|
32563
32645
|
}
|
|
32564
32646
|
const done = [];
|
|
32565
32647
|
try {
|
|
32566
32648
|
for (const rewrite of rewrites) {
|
|
32567
|
-
mkdirSync9(
|
|
32649
|
+
mkdirSync9(dirname12(rewrite.writeTo), { recursive: true });
|
|
32568
32650
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
32569
32651
|
writeFileSync10(staging, rewrite.after, "utf8");
|
|
32570
|
-
|
|
32652
|
+
renameSync3(staging, rewrite.writeTo);
|
|
32571
32653
|
let movedUid = false;
|
|
32572
32654
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
32573
|
-
|
|
32574
|
-
if (
|
|
32575
|
-
|
|
32655
|
+
unlinkSync3(rewrite.absolute);
|
|
32656
|
+
if (existsSync15(`${rewrite.absolute}.uid`)) {
|
|
32657
|
+
renameSync3(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
|
|
32576
32658
|
movedUid = true;
|
|
32577
32659
|
}
|
|
32578
32660
|
}
|
|
@@ -32583,9 +32665,9 @@ function applyRewrites(rewrites) {
|
|
|
32583
32665
|
try {
|
|
32584
32666
|
writeFileSync10(rewrite.absolute, rewrite.before, "utf8");
|
|
32585
32667
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
32586
|
-
|
|
32668
|
+
unlinkSync3(rewrite.writeTo);
|
|
32587
32669
|
if (movedUid) {
|
|
32588
|
-
|
|
32670
|
+
renameSync3(`${rewrite.writeTo}.uid`, `${rewrite.absolute}.uid`);
|
|
32589
32671
|
}
|
|
32590
32672
|
}
|
|
32591
32673
|
} catch {}
|
|
@@ -32595,11 +32677,11 @@ function applyRewrites(rewrites) {
|
|
|
32595
32677
|
}
|
|
32596
32678
|
|
|
32597
32679
|
// src/rename-symbol.ts
|
|
32598
|
-
import { existsSync as
|
|
32599
|
-
import { basename as
|
|
32680
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync10, readdirSync as readdirSync7, realpathSync as realpathSync5, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
32681
|
+
import { basename as basename4, dirname as dirname13, join as join19, relative as relative3, resolve as resolve5 } from "node:path";
|
|
32600
32682
|
|
|
32601
32683
|
// src/resources.ts
|
|
32602
|
-
import { readFileSync as
|
|
32684
|
+
import { readFileSync as readFileSync17, realpathSync as realpathSync4 } from "node:fs";
|
|
32603
32685
|
import { extname as extname2, resolve as resolve4 } from "node:path";
|
|
32604
32686
|
var STATIC_RESOURCES = [
|
|
32605
32687
|
{
|
|
@@ -32830,7 +32912,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
32830
32912
|
const parsedUri = parseGodotUri(uri);
|
|
32831
32913
|
if (parsedUri.kind === "project-info") {
|
|
32832
32914
|
const projectFilePath = resolveProjectFile(projectPath, "project.godot");
|
|
32833
|
-
const rawProject =
|
|
32915
|
+
const rawProject = readFileSync17(projectFilePath, "utf-8");
|
|
32834
32916
|
const parsedProject = parseProjectGodot(rawProject);
|
|
32835
32917
|
return {
|
|
32836
32918
|
mimeType: "application/json",
|
|
@@ -32839,7 +32921,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
32839
32921
|
}
|
|
32840
32922
|
const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
|
|
32841
32923
|
ensureAllowedExtension(parsedUri.kind, filePath);
|
|
32842
|
-
const text =
|
|
32924
|
+
const text = readFileSync17(filePath, "utf-8");
|
|
32843
32925
|
return {
|
|
32844
32926
|
mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
|
|
32845
32927
|
text
|
|
@@ -32904,13 +32986,13 @@ function onDiskSpelling(path) {
|
|
|
32904
32986
|
let at = resolve5(path);
|
|
32905
32987
|
for (;; ) {
|
|
32906
32988
|
try {
|
|
32907
|
-
return
|
|
32989
|
+
return join19(realpathSync5.native(at), ...missing.reverse());
|
|
32908
32990
|
} catch {
|
|
32909
|
-
const parent =
|
|
32991
|
+
const parent = dirname13(at);
|
|
32910
32992
|
if (parent === at) {
|
|
32911
32993
|
return resolve5(path);
|
|
32912
32994
|
}
|
|
32913
|
-
missing.push(
|
|
32995
|
+
missing.push(basename4(at));
|
|
32914
32996
|
at = parent;
|
|
32915
32997
|
}
|
|
32916
32998
|
}
|
|
@@ -32957,7 +33039,7 @@ async function renameSymbol(request, services) {
|
|
|
32957
33039
|
reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
|
|
32958
33040
|
};
|
|
32959
33041
|
}
|
|
32960
|
-
if (
|
|
33042
|
+
if (existsSync16(request.newScriptPath)) {
|
|
32961
33043
|
return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
|
|
32962
33044
|
}
|
|
32963
33045
|
}
|
|
@@ -33133,7 +33215,7 @@ async function renameSymbol(request, services) {
|
|
|
33133
33215
|
scripts,
|
|
33134
33216
|
from: declaringPath,
|
|
33135
33217
|
to: moveTo,
|
|
33136
|
-
exists: (path) =>
|
|
33218
|
+
exists: (path) => existsSync16(`${projectPath}/${path.slice("res://".length)}`)
|
|
33137
33219
|
}, plan);
|
|
33138
33220
|
}
|
|
33139
33221
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? null : declaringPath);
|
|
@@ -33334,15 +33416,15 @@ ${mention.kind}`;
|
|
|
33334
33416
|
var LEFT_ALONE_ANSWER_LINES = 300;
|
|
33335
33417
|
var detailWrites = 0;
|
|
33336
33418
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
33337
|
-
const directory =
|
|
33419
|
+
const directory = join19(projectPath, ".godot", "gdharness-renames");
|
|
33338
33420
|
mkdirSync10(directory, { recursive: true });
|
|
33339
33421
|
detailWrites += 1;
|
|
33340
|
-
const path =
|
|
33422
|
+
const path = join19(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
33341
33423
|
writeFileSync11(path, `${JSON.stringify(detail, null, 2)}
|
|
33342
33424
|
`, "utf8");
|
|
33343
|
-
const kept =
|
|
33425
|
+
const kept = readdirSync7(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
33344
33426
|
for (const old of kept.slice(10)) {
|
|
33345
|
-
rmSync7(
|
|
33427
|
+
rmSync7(join19(directory, old), { force: true });
|
|
33346
33428
|
}
|
|
33347
33429
|
return path;
|
|
33348
33430
|
}
|
|
@@ -33365,15 +33447,15 @@ function summaryOf(edits, mentions, rewrites, left) {
|
|
|
33365
33447
|
}
|
|
33366
33448
|
|
|
33367
33449
|
// src/restart-note.ts
|
|
33368
|
-
import { mkdirSync as mkdirSync11, readFileSync as
|
|
33369
|
-
import { dirname as
|
|
33450
|
+
import { mkdirSync as mkdirSync11, readFileSync as readFileSync18, rmSync as rmSync8, writeFileSync as writeFileSync12 } from "node:fs";
|
|
33451
|
+
import { dirname as dirname14, join as join20 } from "node:path";
|
|
33370
33452
|
function restartNotePath(projectPath) {
|
|
33371
|
-
return
|
|
33453
|
+
return join20(projectPath, ".godot", "gdharness-restart.json");
|
|
33372
33454
|
}
|
|
33373
33455
|
function noteRestartBegun(note) {
|
|
33374
33456
|
const path = restartNotePath(note.projectPath);
|
|
33375
33457
|
try {
|
|
33376
|
-
mkdirSync11(
|
|
33458
|
+
mkdirSync11(dirname14(path), { recursive: true });
|
|
33377
33459
|
writeFileSync12(path, `${JSON.stringify(note, null, 2)}
|
|
33378
33460
|
`, "utf8");
|
|
33379
33461
|
} catch (error) {
|
|
@@ -33382,7 +33464,7 @@ function noteRestartBegun(note) {
|
|
|
33382
33464
|
}
|
|
33383
33465
|
function restartOwed(projectPath) {
|
|
33384
33466
|
try {
|
|
33385
|
-
const parsed = JSON.parse(
|
|
33467
|
+
const parsed = JSON.parse(readFileSync18(restartNotePath(projectPath), "utf8"));
|
|
33386
33468
|
if (typeof parsed !== "object" || parsed === null) {
|
|
33387
33469
|
return null;
|
|
33388
33470
|
}
|
|
@@ -33406,12 +33488,12 @@ function restartSettled(projectPath) {
|
|
|
33406
33488
|
// src/run-record.ts
|
|
33407
33489
|
import { execFile as execFile4 } from "node:child_process";
|
|
33408
33490
|
import { createHash as createHash3 } from "node:crypto";
|
|
33409
|
-
import { mkdirSync as mkdirSync12, openSync as openSync2, readdirSync as
|
|
33410
|
-
import { basename as
|
|
33491
|
+
import { mkdirSync as mkdirSync12, openSync as openSync2, readdirSync as readdirSync8, readFileSync as readFileSync19, realpathSync as realpathSync6, rmSync as rmSync10, statSync as statSync7 } from "node:fs";
|
|
33492
|
+
import { basename as basename5, dirname as dirname15, join as join21, resolve as resolve6 } from "node:path";
|
|
33411
33493
|
import { promisify as promisify4 } from "node:util";
|
|
33412
33494
|
|
|
33413
33495
|
// src/whole-file.ts
|
|
33414
|
-
import { renameSync as
|
|
33496
|
+
import { renameSync as renameSync4, rmSync as rmSync9, writeFileSync as writeFileSync13 } from "node:fs";
|
|
33415
33497
|
var HELD_FILE_MS = 1000;
|
|
33416
33498
|
var HELD_FILE_RETRY_MS = 10;
|
|
33417
33499
|
var HELD_BY_A_READER = new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
@@ -33425,7 +33507,7 @@ function writeWhole(path, text) {
|
|
|
33425
33507
|
const until = Date.now() + HELD_FILE_MS;
|
|
33426
33508
|
for (;; ) {
|
|
33427
33509
|
try {
|
|
33428
|
-
|
|
33510
|
+
renameSync4(partial, path);
|
|
33429
33511
|
return true;
|
|
33430
33512
|
} catch (error) {
|
|
33431
33513
|
const code = error.code ?? "";
|
|
@@ -33443,20 +33525,20 @@ function writeWhole(path, text) {
|
|
|
33443
33525
|
// src/run-record.ts
|
|
33444
33526
|
var execFileAsync = promisify4(execFile4);
|
|
33445
33527
|
function runsDirectory() {
|
|
33446
|
-
return
|
|
33528
|
+
return join21(runtimeDirectory(), "runs");
|
|
33447
33529
|
}
|
|
33448
33530
|
function realAsFarAsItExists(path) {
|
|
33449
33531
|
const missing = [];
|
|
33450
33532
|
let at = path;
|
|
33451
33533
|
for (;; ) {
|
|
33452
33534
|
try {
|
|
33453
|
-
return
|
|
33535
|
+
return join21(realpathSync6.native(at), ...missing);
|
|
33454
33536
|
} catch {
|
|
33455
|
-
const parent =
|
|
33537
|
+
const parent = dirname15(at);
|
|
33456
33538
|
if (parent === at) {
|
|
33457
33539
|
return path;
|
|
33458
33540
|
}
|
|
33459
|
-
missing.unshift(
|
|
33541
|
+
missing.unshift(basename5(at));
|
|
33460
33542
|
at = parent;
|
|
33461
33543
|
}
|
|
33462
33544
|
}
|
|
@@ -33467,10 +33549,10 @@ function projectKey(projectPath) {
|
|
|
33467
33549
|
return createHash3("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
33468
33550
|
}
|
|
33469
33551
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
33470
|
-
return
|
|
33552
|
+
return join21(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
33471
33553
|
}
|
|
33472
33554
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
33473
|
-
return
|
|
33555
|
+
return join21(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
33474
33556
|
}
|
|
33475
33557
|
function writeLatestRun(servedBy, latest) {
|
|
33476
33558
|
try {
|
|
@@ -33480,7 +33562,7 @@ function writeLatestRun(servedBy, latest) {
|
|
|
33480
33562
|
}
|
|
33481
33563
|
function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
33482
33564
|
try {
|
|
33483
|
-
const fields = JSON.parse(
|
|
33565
|
+
const fields = JSON.parse(readFileSync19(latestRunPath(servedBy, directory), "utf8"));
|
|
33484
33566
|
if (typeof fields !== "object" || fields === null) {
|
|
33485
33567
|
return null;
|
|
33486
33568
|
}
|
|
@@ -33496,17 +33578,17 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
33496
33578
|
var SHARED_NOTE = "run.json";
|
|
33497
33579
|
function openTranscript(startedAt) {
|
|
33498
33580
|
mkdirSync12(runsDirectory(), { recursive: true });
|
|
33499
|
-
const path =
|
|
33581
|
+
const path = join21(runsDirectory(), `run-${startedAt}.log`);
|
|
33500
33582
|
return { path, fd: openSync2(path, "a") };
|
|
33501
33583
|
}
|
|
33502
33584
|
function profilePath(startedAt) {
|
|
33503
|
-
return
|
|
33585
|
+
return join21(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
33504
33586
|
}
|
|
33505
33587
|
function noteIsAbout(note, playing) {
|
|
33506
33588
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
33507
33589
|
}
|
|
33508
33590
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
33509
|
-
return
|
|
33591
|
+
return join21(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
33510
33592
|
}
|
|
33511
33593
|
function writeEditorRunNote(note) {
|
|
33512
33594
|
mkdirSync12(runsDirectory(), { recursive: true });
|
|
@@ -33517,7 +33599,7 @@ function writeEditorRunNote(note) {
|
|
|
33517
33599
|
}
|
|
33518
33600
|
function readEditorRunNote(projectPath) {
|
|
33519
33601
|
for (const directory of runtimeDirectories()) {
|
|
33520
|
-
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(
|
|
33602
|
+
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join21(directory, "runs", "editor-run.json"));
|
|
33521
33603
|
if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
|
|
33522
33604
|
return note;
|
|
33523
33605
|
}
|
|
@@ -33527,7 +33609,7 @@ function readEditorRunNote(projectPath) {
|
|
|
33527
33609
|
function editorNoteAt(path) {
|
|
33528
33610
|
let parsed;
|
|
33529
33611
|
try {
|
|
33530
|
-
parsed = JSON.parse(
|
|
33612
|
+
parsed = JSON.parse(readFileSync19(path, "utf8"));
|
|
33531
33613
|
} catch {
|
|
33532
33614
|
return null;
|
|
33533
33615
|
}
|
|
@@ -33551,7 +33633,7 @@ function readRunRecord(projectPath) {
|
|
|
33551
33633
|
if (own !== null) {
|
|
33552
33634
|
return own;
|
|
33553
33635
|
}
|
|
33554
|
-
const shared = recordAt(
|
|
33636
|
+
const shared = recordAt(join21(directory, "runs", SHARED_NOTE));
|
|
33555
33637
|
if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
|
|
33556
33638
|
return shared;
|
|
33557
33639
|
}
|
|
@@ -33563,13 +33645,13 @@ function everyRunRecord() {
|
|
|
33563
33645
|
for (const directory of runtimeDirectories()) {
|
|
33564
33646
|
let entries;
|
|
33565
33647
|
try {
|
|
33566
|
-
entries =
|
|
33648
|
+
entries = readdirSync8(join21(directory, "runs"));
|
|
33567
33649
|
} catch {
|
|
33568
33650
|
continue;
|
|
33569
33651
|
}
|
|
33570
33652
|
for (const entry of entries) {
|
|
33571
33653
|
if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
33572
|
-
const record = recordAt(
|
|
33654
|
+
const record = recordAt(join21(directory, "runs", entry));
|
|
33573
33655
|
if (record !== null) {
|
|
33574
33656
|
found.push(record);
|
|
33575
33657
|
}
|
|
@@ -33581,7 +33663,7 @@ function everyRunRecord() {
|
|
|
33581
33663
|
function recordAt(path) {
|
|
33582
33664
|
let parsed;
|
|
33583
33665
|
try {
|
|
33584
|
-
parsed = JSON.parse(
|
|
33666
|
+
parsed = JSON.parse(readFileSync19(path, "utf8"));
|
|
33585
33667
|
} catch {
|
|
33586
33668
|
return null;
|
|
33587
33669
|
}
|
|
@@ -33634,7 +33716,7 @@ async function runningAs(pid) {
|
|
|
33634
33716
|
const began = await startedAt(pid, null);
|
|
33635
33717
|
const when = began === null ? {} : { startedAt: began };
|
|
33636
33718
|
if (process.platform === "linux") {
|
|
33637
|
-
const raw =
|
|
33719
|
+
const raw = readFileSync19(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
33638
33720
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
33639
33721
|
}
|
|
33640
33722
|
const args = (await execFileAsync("ps", ["-p", String(pid), "-o", "args="], { encoding: "utf8", timeout: 15000 })).stdout.trim();
|
|
@@ -33741,7 +33823,7 @@ function whyNotTheRun(record, running, needed) {
|
|
|
33741
33823
|
if (after > SAME_RUN_WINDOW_MS) {
|
|
33742
33824
|
return `that process started ${Math.round(after / 1000)} seconds after the run had`;
|
|
33743
33825
|
}
|
|
33744
|
-
const engine = record.command === undefined ? null :
|
|
33826
|
+
const engine = record.command === undefined ? null : basename5(record.command);
|
|
33745
33827
|
const said = process.platform === "win32" ? running.text.toLowerCase() : running.text;
|
|
33746
33828
|
const wanted = engine === null || process.platform !== "win32" ? engine : engine.toLowerCase();
|
|
33747
33829
|
if (wanted !== null && !said.includes(wanted)) {
|
|
@@ -33780,7 +33862,7 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
|
|
|
33780
33862
|
}
|
|
33781
33863
|
function clearRunRecord(projectPath) {
|
|
33782
33864
|
for (const directory of runtimeDirectories()) {
|
|
33783
|
-
const shared =
|
|
33865
|
+
const shared = join21(directory, "runs", SHARED_NOTE);
|
|
33784
33866
|
const sharedRecord = recordAt(shared);
|
|
33785
33867
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
33786
33868
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
@@ -33805,7 +33887,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
33805
33887
|
]));
|
|
33806
33888
|
let entries;
|
|
33807
33889
|
try {
|
|
33808
|
-
entries =
|
|
33890
|
+
entries = readdirSync8(directory);
|
|
33809
33891
|
} catch {
|
|
33810
33892
|
return;
|
|
33811
33893
|
}
|
|
@@ -33814,7 +33896,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
33814
33896
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
33815
33897
|
continue;
|
|
33816
33898
|
}
|
|
33817
|
-
const path =
|
|
33899
|
+
const path = join21(directory, entry);
|
|
33818
33900
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
33819
33901
|
continue;
|
|
33820
33902
|
}
|
|
@@ -33825,7 +33907,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
33825
33907
|
}
|
|
33826
33908
|
|
|
33827
33909
|
// src/script-origins.ts
|
|
33828
|
-
import { join as
|
|
33910
|
+
import { join as join22 } from "node:path";
|
|
33829
33911
|
var UNRESOLVED = /Could not resolve class "([A-Za-z_][A-Za-z0-9_]*)", because of a parser error/;
|
|
33830
33912
|
function unresolvedClassIn(message) {
|
|
33831
33913
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
@@ -33881,7 +33963,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
33881
33963
|
const ran = await runEngine(godotPath, [
|
|
33882
33964
|
"--headless",
|
|
33883
33965
|
"--log-file",
|
|
33884
|
-
|
|
33966
|
+
join22(logDir, "engine.log"),
|
|
33885
33967
|
"--path",
|
|
33886
33968
|
projectPath,
|
|
33887
33969
|
"--check-only",
|
|
@@ -33918,10 +34000,10 @@ function withoutListed(entries, errors) {
|
|
|
33918
34000
|
}
|
|
33919
34001
|
|
|
33920
34002
|
// src/script-profile.ts
|
|
33921
|
-
import { readFileSync as
|
|
34003
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
33922
34004
|
function readProfileFile(path) {
|
|
33923
34005
|
try {
|
|
33924
|
-
const parsed = JSON.parse(
|
|
34006
|
+
const parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
33925
34007
|
return typeof parsed === "object" && typeof parsed.connected === "boolean" ? parsed : null;
|
|
33926
34008
|
} catch {
|
|
33927
34009
|
return null;
|
|
@@ -35380,12 +35462,12 @@ var TOOL_SPECS = [
|
|
|
35380
35462
|
},
|
|
35381
35463
|
metrics: {
|
|
35382
35464
|
effect: "read",
|
|
35383
|
-
summary: "frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count",
|
|
35465
|
+
summary: "frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. physics_steps_per_second is the physics steps taken a second over those frames, fewer than physics_ticks_per_second when a frame needs more than max_physics_steps_per_frame or the editor's Game view slows it. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count",
|
|
35384
35466
|
requires: []
|
|
35385
35467
|
},
|
|
35386
35468
|
accessibility: {
|
|
35387
35469
|
effect: "read",
|
|
35388
|
-
summary:
|
|
35470
|
+
summary: "what the game tells a screen reader, read through UI Automation as a Windows screen reader reads it, under windows: one tree per window, each element with its role (text for static text, button, check_box, group for a control holding others), name, value, description, placeholder, bounds in the window's pixels, the actions a reader is offered (click, focus, toggle, set_value, select, expand_collapse, scroll_into_view), and focused on the one with the focus. node is the path of the node that made the element; an element a node made as a part of itself has part_of instead, and part: announcement marks the text a window speaks announcements through. An empty name is read as nothing: static text takes it from accessibility_update_set_value, and _note counts the text elements on screen with none. tooltip is the node's tooltip_text, which a Windows reader is not told. With no control focused, the engine reports the first node under the root, GdharnessRuntime, as focused. Needs a windowed run on Windows started with engineArgs [\"--accessibility\", \"always\"] or accessibility_support at Always Active, since the engine builds the tree only when asked; reading takes a second or two",
|
|
35389
35471
|
requires: []
|
|
35390
35472
|
}
|
|
35391
35473
|
},
|
|
@@ -35876,16 +35958,16 @@ import { spawnSync } from "node:child_process";
|
|
|
35876
35958
|
import { createHash as createHash4 } from "node:crypto";
|
|
35877
35959
|
import {
|
|
35878
35960
|
cpSync,
|
|
35879
|
-
existsSync as
|
|
35961
|
+
existsSync as existsSync17,
|
|
35880
35962
|
mkdirSync as mkdirSync13,
|
|
35881
|
-
readdirSync as
|
|
35882
|
-
readFileSync as
|
|
35963
|
+
readdirSync as readdirSync9,
|
|
35964
|
+
readFileSync as readFileSync21,
|
|
35883
35965
|
rmdirSync,
|
|
35884
35966
|
rmSync as rmSync11,
|
|
35885
35967
|
statSync as statSync8,
|
|
35886
35968
|
writeFileSync as writeFileSync14
|
|
35887
35969
|
} from "node:fs";
|
|
35888
|
-
import { dirname as
|
|
35970
|
+
import { dirname as dirname16, join as join23 } from "node:path";
|
|
35889
35971
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
35890
35972
|
var ADDONS = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
|
|
35891
35973
|
var EDITOR_PLUGINS = ["gdharness_editor", "auto_reload"];
|
|
@@ -35904,25 +35986,25 @@ function runtimeAddonDigest(from = shippedAddonsDirectory()) {
|
|
|
35904
35986
|
function addonsDigest(names, from) {
|
|
35905
35987
|
const hash = createHash4("sha256");
|
|
35906
35988
|
const walk = (directory, relative) => {
|
|
35907
|
-
for (const name of
|
|
35989
|
+
for (const name of readdirSync9(directory).sort()) {
|
|
35908
35990
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
35909
35991
|
continue;
|
|
35910
35992
|
}
|
|
35911
|
-
const path =
|
|
35993
|
+
const path = join23(directory, name);
|
|
35912
35994
|
const inside = `${relative}/${name}`;
|
|
35913
35995
|
if (statSync8(path).isDirectory()) {
|
|
35914
35996
|
walk(path, inside);
|
|
35915
35997
|
} else {
|
|
35916
35998
|
hash.update(`${inside}
|
|
35917
35999
|
`);
|
|
35918
|
-
hash.update(
|
|
36000
|
+
hash.update(readFileSync21(path));
|
|
35919
36001
|
hash.update(`
|
|
35920
36002
|
`);
|
|
35921
36003
|
}
|
|
35922
36004
|
}
|
|
35923
36005
|
};
|
|
35924
36006
|
for (const name of names) {
|
|
35925
|
-
walk(
|
|
36007
|
+
walk(join23(from, name), name);
|
|
35926
36008
|
}
|
|
35927
36009
|
return hash.digest("hex");
|
|
35928
36010
|
}
|
|
@@ -35955,22 +36037,22 @@ function installedRuntimeDigest(projectPath) {
|
|
|
35955
36037
|
return digestMarker(projectPath, RUNTIME_ADDON);
|
|
35956
36038
|
}
|
|
35957
36039
|
function digestMarker(projectPath, addon) {
|
|
35958
|
-
const marker =
|
|
35959
|
-
if (!
|
|
36040
|
+
const marker = join23(projectPath, "addons", addon, DIGEST_MARKER);
|
|
36041
|
+
if (!existsSync17(marker)) {
|
|
35960
36042
|
return null;
|
|
35961
36043
|
}
|
|
35962
|
-
const said =
|
|
36044
|
+
const said = readFileSync21(marker, "utf8").trim();
|
|
35963
36045
|
return said === "" ? null : said;
|
|
35964
36046
|
}
|
|
35965
36047
|
function shippedAddonsDirectory() {
|
|
35966
|
-
return
|
|
36048
|
+
return join23(dirname16(fileURLToPath3(import.meta.url)), "godot", "addons");
|
|
35967
36049
|
}
|
|
35968
36050
|
function installedAddonVersion(projectPath) {
|
|
35969
|
-
const marker =
|
|
35970
|
-
if (!
|
|
36051
|
+
const marker = join23(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
36052
|
+
if (!existsSync17(marker)) {
|
|
35971
36053
|
return null;
|
|
35972
36054
|
}
|
|
35973
|
-
const said =
|
|
36055
|
+
const said = readFileSync21(marker, "utf8").trim();
|
|
35974
36056
|
return said === "" ? null : said;
|
|
35975
36057
|
}
|
|
35976
36058
|
function loaderAmong(projectPath, named) {
|
|
@@ -35981,7 +36063,7 @@ function loaderAmong(projectPath, named) {
|
|
|
35981
36063
|
}
|
|
35982
36064
|
let source = null;
|
|
35983
36065
|
try {
|
|
35984
|
-
source =
|
|
36066
|
+
source = readFileSync21(join23(projectPath, path), "utf8");
|
|
35985
36067
|
} catch {}
|
|
35986
36068
|
if (source?.includes(RUNTIME_AUTOLOAD.path)) {
|
|
35987
36069
|
return { name: entry, path, exists: true };
|
|
@@ -36023,10 +36105,10 @@ function enabledPlugins(settings) {
|
|
|
36023
36105
|
function inspectProject(projectPath) {
|
|
36024
36106
|
const problems = [];
|
|
36025
36107
|
const addons = ADDONS.map((name) => {
|
|
36026
|
-
const directory =
|
|
36027
|
-
const marker =
|
|
36028
|
-
const installed =
|
|
36029
|
-
const version =
|
|
36108
|
+
const directory = join23(projectPath, "addons", name);
|
|
36109
|
+
const marker = join23(directory, VERSION_MARKER);
|
|
36110
|
+
const installed = existsSync17(directory);
|
|
36111
|
+
const version = existsSync17(marker) ? readFileSync21(marker, "utf8").trim() : null;
|
|
36030
36112
|
const current = version === SERVER_VERSION;
|
|
36031
36113
|
if (!installed) {
|
|
36032
36114
|
problems.push(`addons/${name} is not installed; run gdharness setup`);
|
|
@@ -36035,7 +36117,7 @@ function inspectProject(projectPath) {
|
|
|
36035
36117
|
}
|
|
36036
36118
|
return { name, installed, version, current };
|
|
36037
36119
|
});
|
|
36038
|
-
const settings = parseProjectGodot(
|
|
36120
|
+
const settings = parseProjectGodot(readFileSync21(join23(projectPath, "project.godot"), "utf8"));
|
|
36039
36121
|
const pluginsEnabled = enabledPlugins(settings);
|
|
36040
36122
|
for (const name of EDITOR_PLUGINS) {
|
|
36041
36123
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -36051,7 +36133,7 @@ function inspectProject(projectPath) {
|
|
|
36051
36133
|
const tracked = trackedByGit(projectPath, [...named.values()]);
|
|
36052
36134
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
36053
36135
|
for (const [name, path] of named) {
|
|
36054
|
-
if (!
|
|
36136
|
+
if (!existsSync17(join23(projectPath, path))) {
|
|
36055
36137
|
if (!addonMissing(path)) {
|
|
36056
36138
|
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`);
|
|
36057
36139
|
}
|
|
@@ -36102,7 +36184,7 @@ function argumentShape(schema) {
|
|
|
36102
36184
|
var UPDATE_NOTICE_EVERY = 500;
|
|
36103
36185
|
var FEEDBACK_NOTICE_EVERY = 250;
|
|
36104
36186
|
var run4 = promisify5(execFile5);
|
|
36105
|
-
var __dirname2 =
|
|
36187
|
+
var __dirname2 = dirname17(fileURLToPath4(import.meta.url));
|
|
36106
36188
|
var PARENT_ASKED_AFTER_MS = 5000;
|
|
36107
36189
|
function editorRestartTimeoutMs() {
|
|
36108
36190
|
return waitFromEnv("GDHARNESS_EDITOR_RESTART_TIMEOUT_MS", 90000);
|
|
@@ -36134,22 +36216,22 @@ function writtenFileRefusal(path, rule) {
|
|
|
36134
36216
|
if (/^(res|user):\/\//.test(path)) {
|
|
36135
36217
|
return `${named} ${path} is a Godot path; ${what} is saved outside the project, at an absolute path on this machine.`;
|
|
36136
36218
|
}
|
|
36137
|
-
if (!
|
|
36219
|
+
if (!isAbsolute4(path)) {
|
|
36138
36220
|
return `${named} ${path} is not absolute; name the whole path, so where it is written does not depend on this server's directory.`;
|
|
36139
36221
|
}
|
|
36140
36222
|
if (!rule.extensions.includes(extname3(path).toLowerCase())) {
|
|
36141
36223
|
return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
|
|
36142
36224
|
}
|
|
36143
|
-
const directory = realPathOr(
|
|
36144
|
-
if (!
|
|
36145
|
-
return `${named}'s directory ${
|
|
36225
|
+
const directory = realPathOr(dirname17(path));
|
|
36226
|
+
if (!existsSync18(directory)) {
|
|
36227
|
+
return `${named}'s directory ${dirname17(path)} does not exist; ${what} creates no directories.`;
|
|
36146
36228
|
}
|
|
36147
|
-
const where =
|
|
36229
|
+
const where = join24(directory, basename6(path));
|
|
36148
36230
|
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
36149
36231
|
if (inside !== undefined) {
|
|
36150
36232
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
36151
36233
|
}
|
|
36152
|
-
const taken = rule.writes(where).find((file) =>
|
|
36234
|
+
const taken = rule.writes(where).find((file) => existsSync18(file));
|
|
36153
36235
|
if (taken !== undefined) {
|
|
36154
36236
|
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.`;
|
|
36155
36237
|
}
|
|
@@ -36199,6 +36281,7 @@ function captureDetail(payload) {
|
|
|
36199
36281
|
function patienceForFrames(frames, atLeast) {
|
|
36200
36282
|
return Math.max(atLeast, frames / SLOWEST_FRAME_RATE * 1000 + atLeast);
|
|
36201
36283
|
}
|
|
36284
|
+
var 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.";
|
|
36202
36285
|
function longerWaitNote(tool, op, waitedMs) {
|
|
36203
36286
|
const spec = TOOL_SPECS.find((one) => one.name === tool);
|
|
36204
36287
|
if (spec !== undefined && argumentsOf(spec, op).includes("timeoutMs")) {
|
|
@@ -36669,6 +36752,7 @@ function notRunNote(notRun, failFast) {
|
|
|
36669
36752
|
function exportAnswer(ending, asked, before, after) {
|
|
36670
36753
|
const { log } = ending;
|
|
36671
36754
|
const written = after !== null && after !== before;
|
|
36755
|
+
const notCopied = librariesNotCopied(log.all);
|
|
36672
36756
|
const verdict = {
|
|
36673
36757
|
exported: ending.exitCode === 0 && ending.failure === null && log.count("error") === 0 && written,
|
|
36674
36758
|
...asked,
|
|
@@ -36677,6 +36761,10 @@ function exportAnswer(ending, asked, before, after) {
|
|
|
36677
36761
|
failure: ending.failure ?? undefined,
|
|
36678
36762
|
errors: log.count("error"),
|
|
36679
36763
|
warnings: log.count("warning"),
|
|
36764
|
+
...notCopied.length === 0 ? {} : {
|
|
36765
|
+
librariesNotLoaded: notCopied,
|
|
36766
|
+
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.`
|
|
36767
|
+
},
|
|
36680
36768
|
...foldedEntriesAnswer(log)
|
|
36681
36769
|
};
|
|
36682
36770
|
if (verdict.exported) {
|
|
@@ -36721,7 +36809,7 @@ var EDITOR_FREE_INSTEAD = {
|
|
|
36721
36809
|
scene_tree: "runtime_inspect tree reads the nodes of a game that is running, and needs no editor; scene_tree reads a scene file through the editor"
|
|
36722
36810
|
};
|
|
36723
36811
|
function editorAddonMissing(projectPath) {
|
|
36724
|
-
if (!
|
|
36812
|
+
if (!existsSync18(join24(projectPath, "project.godot"))) {
|
|
36725
36813
|
return null;
|
|
36726
36814
|
}
|
|
36727
36815
|
const report = inspectProject(projectPath);
|
|
@@ -37003,7 +37091,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
37003
37091
|
let projectGodot = null;
|
|
37004
37092
|
if (projectPath !== null) {
|
|
37005
37093
|
try {
|
|
37006
|
-
projectGodot =
|
|
37094
|
+
projectGodot = readFileSync22(join24(projectPath, "project.godot"), "utf8");
|
|
37007
37095
|
} catch {}
|
|
37008
37096
|
}
|
|
37009
37097
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -37029,7 +37117,7 @@ function projectSourceOf(projectPath) {
|
|
|
37029
37117
|
let source = null;
|
|
37030
37118
|
if (inside.ok) {
|
|
37031
37119
|
try {
|
|
37032
|
-
source =
|
|
37120
|
+
source = readFileSync22(inside.absolutePath, "utf8");
|
|
37033
37121
|
} catch {}
|
|
37034
37122
|
}
|
|
37035
37123
|
read.set(script, source);
|
|
@@ -37181,7 +37269,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
37181
37269
|
var ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd";
|
|
37182
37270
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
37183
37271
|
try {
|
|
37184
|
-
return settingKeys(
|
|
37272
|
+
return settingKeys(readFileSync22(join24(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
37185
37273
|
} catch {
|
|
37186
37274
|
return true;
|
|
37187
37275
|
}
|
|
@@ -37196,13 +37284,13 @@ function uncaughtNote(uncaught) {
|
|
|
37196
37284
|
}
|
|
37197
37285
|
function elsewhereIn(projectPath, asked) {
|
|
37198
37286
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
37199
|
-
if (wanted === "" ||
|
|
37287
|
+
if (wanted === "" || existsSync18(join24(projectPath, wanted))) {
|
|
37200
37288
|
return "";
|
|
37201
37289
|
}
|
|
37202
37290
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
37203
37291
|
let near = [];
|
|
37204
37292
|
try {
|
|
37205
|
-
near =
|
|
37293
|
+
near = readdirSync10(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()));
|
|
37206
37294
|
} catch {
|
|
37207
37295
|
return "";
|
|
37208
37296
|
}
|
|
@@ -37293,17 +37381,17 @@ function isDirectory(path) {
|
|
|
37293
37381
|
}
|
|
37294
37382
|
}
|
|
37295
37383
|
function scriptsUnder(directory, prefix) {
|
|
37296
|
-
if (
|
|
37384
|
+
if (existsSync18(join24(directory, ".gdignore"))) {
|
|
37297
37385
|
return [];
|
|
37298
37386
|
}
|
|
37299
37387
|
const found = [];
|
|
37300
|
-
for (const entry of
|
|
37388
|
+
for (const entry of readdirSync10(directory, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : 1)) {
|
|
37301
37389
|
if (entry.name.startsWith(".")) {
|
|
37302
37390
|
continue;
|
|
37303
37391
|
}
|
|
37304
37392
|
const path = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
37305
37393
|
if (entry.isDirectory()) {
|
|
37306
|
-
found.push(...scriptsUnder(
|
|
37394
|
+
found.push(...scriptsUnder(join24(directory, entry.name), path));
|
|
37307
37395
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
37308
37396
|
found.push(path);
|
|
37309
37397
|
}
|
|
@@ -37311,7 +37399,7 @@ function scriptsUnder(directory, prefix) {
|
|
|
37311
37399
|
return found;
|
|
37312
37400
|
}
|
|
37313
37401
|
function hasMainScene(projectFile) {
|
|
37314
|
-
const scene = parseProjectGodot(
|
|
37402
|
+
const scene = parseProjectGodot(readFileSync22(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
37315
37403
|
return typeof scene === "string" && scene !== "";
|
|
37316
37404
|
}
|
|
37317
37405
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -37319,13 +37407,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
37319
37407
|
return false;
|
|
37320
37408
|
}
|
|
37321
37409
|
try {
|
|
37322
|
-
return
|
|
37410
|
+
return readFileSync22(join24(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
37323
37411
|
} catch {
|
|
37324
37412
|
return false;
|
|
37325
37413
|
}
|
|
37326
37414
|
}
|
|
37327
37415
|
function editorPlaysHeadless(projectFile) {
|
|
37328
|
-
const runArgs = parseProjectGodot(
|
|
37416
|
+
const runArgs = parseProjectGodot(readFileSync22(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
37329
37417
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
37330
37418
|
}
|
|
37331
37419
|
function camelCased(params) {
|
|
@@ -37345,7 +37433,7 @@ var STATIC_LIST_HINT = { ttlMs: 5 * 60000, cacheScope: "private" };
|
|
|
37345
37433
|
class GodotServer {
|
|
37346
37434
|
stdio = null;
|
|
37347
37435
|
locator = new GodotLocator;
|
|
37348
|
-
operationsScript =
|
|
37436
|
+
operationsScript = join24(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
37349
37437
|
godotBridge;
|
|
37350
37438
|
tools = buildToolDefinitions();
|
|
37351
37439
|
activeProcess = null;
|
|
@@ -37370,6 +37458,7 @@ class GodotServer {
|
|
|
37370
37458
|
launchedDropped = null;
|
|
37371
37459
|
closedHere = null;
|
|
37372
37460
|
inFlight = new Map;
|
|
37461
|
+
callsOnGames = new Set;
|
|
37373
37462
|
callsTaken = 0;
|
|
37374
37463
|
clientNoted = null;
|
|
37375
37464
|
parentCommand = null;
|
|
@@ -37601,14 +37690,14 @@ class GodotServer {
|
|
|
37601
37690
|
}
|
|
37602
37691
|
noteTheStart() {
|
|
37603
37692
|
const cwd = process.cwd();
|
|
37604
|
-
const mcpJson =
|
|
37693
|
+
const mcpJson = join24(cwd, ".mcp.json");
|
|
37605
37694
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
37606
37695
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
37607
37696
|
writeLifecycle("started", {
|
|
37608
37697
|
argv: process.argv.slice(1),
|
|
37609
37698
|
cwd,
|
|
37610
37699
|
project: this.ownProject,
|
|
37611
|
-
mcpJson:
|
|
37700
|
+
mcpJson: existsSync18(mcpJson) ? mcpJson : null
|
|
37612
37701
|
});
|
|
37613
37702
|
if (predecessor !== null) {
|
|
37614
37703
|
writeLifecycle("replaced", { project: this.ownProject, predecessor });
|
|
@@ -37749,7 +37838,7 @@ class GodotServer {
|
|
|
37749
37838
|
return checked.response;
|
|
37750
37839
|
}
|
|
37751
37840
|
const named = args["projectPath"];
|
|
37752
|
-
if (typeof named === "string" &&
|
|
37841
|
+
if (typeof named === "string" && existsSync18(join24(named, "project.godot"))) {
|
|
37753
37842
|
this.lastProjectPath = named;
|
|
37754
37843
|
}
|
|
37755
37844
|
this.updates.refresh();
|
|
@@ -38307,8 +38396,8 @@ class GodotServer {
|
|
|
38307
38396
|
if (path === undefined) {
|
|
38308
38397
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
38309
38398
|
}
|
|
38310
|
-
const file =
|
|
38311
|
-
if (!
|
|
38399
|
+
const file = join24(path, "project.godot");
|
|
38400
|
+
if (!existsSync18(file)) {
|
|
38312
38401
|
return {
|
|
38313
38402
|
ok: false,
|
|
38314
38403
|
response: this.createErrorResponse(`Not a Godot project: ${path}`, [
|
|
@@ -38354,7 +38443,7 @@ class GodotServer {
|
|
|
38354
38443
|
}
|
|
38355
38444
|
runEnvironment(args) {
|
|
38356
38445
|
const savesIn = readNonEmptyString(args, "savesIn");
|
|
38357
|
-
if (savesIn !== undefined && !
|
|
38446
|
+
if (savesIn !== undefined && !isAbsolute4(savesIn)) {
|
|
38358
38447
|
return {
|
|
38359
38448
|
ok: false,
|
|
38360
38449
|
response: this.createErrorResponse(`savesIn must be an absolute path, not "${savesIn}".`, [
|
|
@@ -38938,11 +39027,11 @@ class GodotServer {
|
|
|
38938
39027
|
if (!engine.ok) {
|
|
38939
39028
|
return engine.response;
|
|
38940
39029
|
}
|
|
38941
|
-
const application = parseProjectGodot(
|
|
39030
|
+
const application = parseProjectGodot(readFileSync22(project.value.file, "utf8"))["application"] ?? {};
|
|
38942
39031
|
const name = application["config/name"];
|
|
38943
39032
|
const mainScene = application["run/main_scene"];
|
|
38944
39033
|
const info = {
|
|
38945
|
-
name: typeof name === "string" && name !== "" ? name :
|
|
39034
|
+
name: typeof name === "string" && name !== "" ? name : basename6(project.value.path),
|
|
38946
39035
|
path: project.value.path,
|
|
38947
39036
|
mainScene: typeof mainScene === "string" && mainScene !== "" ? mainScene : null,
|
|
38948
39037
|
godotVersion: await this.godotVersion(engine.value),
|
|
@@ -38994,18 +39083,18 @@ class GodotServer {
|
|
|
38994
39083
|
if (!engine.ok) {
|
|
38995
39084
|
return engine.response;
|
|
38996
39085
|
}
|
|
38997
|
-
const folder =
|
|
39086
|
+
const folder = dirname17(output.absolutePath);
|
|
38998
39087
|
try {
|
|
38999
39088
|
mkdirSync14(folder, { recursive: true });
|
|
39000
39089
|
} catch (error) {
|
|
39001
|
-
return this.createErrorResponse(`Cannot create ${
|
|
39090
|
+
return this.createErrorResponse(`Cannot create ${dirname17(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
39002
39091
|
}
|
|
39003
39092
|
const debug = readBoolean(args, "debug") ?? false;
|
|
39004
39093
|
const exportLogs = scratchDirectory("export");
|
|
39005
39094
|
const exportArgs = [
|
|
39006
39095
|
"--headless",
|
|
39007
39096
|
"--log-file",
|
|
39008
|
-
|
|
39097
|
+
join24(exportLogs, "engine.log"),
|
|
39009
39098
|
"--path",
|
|
39010
39099
|
project.value.path,
|
|
39011
39100
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -39013,8 +39102,12 @@ class GodotServer {
|
|
|
39013
39102
|
output.absolutePath
|
|
39014
39103
|
];
|
|
39015
39104
|
this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
|
|
39016
|
-
const writtenAt = () =>
|
|
39105
|
+
const writtenAt = () => existsSync18(output.absolutePath) ? statSync9(output.absolutePath).mtimeMs : null;
|
|
39017
39106
|
const before = writtenAt();
|
|
39107
|
+
const freed = freeLibraryCopies(project.value.path);
|
|
39108
|
+
if (freed.setAside.length > 0 || freed.swept.length > 0) {
|
|
39109
|
+
this.logDebug(`Before the export: set aside ${freed.setAside.join(", ")}; swept ${freed.swept.join(", ")}`);
|
|
39110
|
+
}
|
|
39018
39111
|
let ending;
|
|
39019
39112
|
try {
|
|
39020
39113
|
ending = await runEngine(engine.value, exportArgs, { timeout: { ms: 300000, said: "five minutes" } });
|
|
@@ -39077,10 +39170,10 @@ class GodotServer {
|
|
|
39077
39170
|
if (movie !== null) {
|
|
39078
39171
|
return this.createErrorResponse(movie);
|
|
39079
39172
|
}
|
|
39080
|
-
const missing = paths.value.filter((path) => !
|
|
39173
|
+
const missing = paths.value.filter((path) => !existsSync18(join24(project.value.path, path.slice("res://".length))));
|
|
39081
39174
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
39082
39175
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
39083
|
-
const noRunner = !
|
|
39176
|
+
const noRunner = !existsSync18(join24(project.value.path, runner));
|
|
39084
39177
|
if (asked.length === 0) {
|
|
39085
39178
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
39086
39179
|
const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
|
|
@@ -39171,21 +39264,21 @@ class GodotServer {
|
|
|
39171
39264
|
await untilGone([run.process.pid]);
|
|
39172
39265
|
}
|
|
39173
39266
|
await run.settled;
|
|
39174
|
-
const reportsDir =
|
|
39267
|
+
const reportsDir = join24(project.value.path, ".godot", ...ours.split("/"));
|
|
39175
39268
|
let report = null;
|
|
39176
39269
|
let reportProblem = null;
|
|
39177
39270
|
try {
|
|
39178
|
-
const written =
|
|
39271
|
+
const written = existsSync18(reportsDir) ? readdirSync10(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
39179
39272
|
const newest = written.at(-1);
|
|
39180
39273
|
if (newest !== undefined) {
|
|
39181
|
-
report = parseJUnit(
|
|
39274
|
+
report = parseJUnit(readFileSync22(join24(reportsDir, newest, "results.xml"), "utf8"));
|
|
39182
39275
|
}
|
|
39183
39276
|
} catch (error) {
|
|
39184
39277
|
reportProblem = errorMessage(error);
|
|
39185
39278
|
} finally {
|
|
39186
39279
|
discard(reportsDir, userData);
|
|
39187
39280
|
try {
|
|
39188
|
-
rmdirSync2(
|
|
39281
|
+
rmdirSync2(dirname17(reportsDir));
|
|
39189
39282
|
} catch {}
|
|
39190
39283
|
}
|
|
39191
39284
|
const engineEntries = foldedRepeats(run.log.select({
|
|
@@ -39445,11 +39538,11 @@ class GodotServer {
|
|
|
39445
39538
|
const cached = cachedClasses(projectPath);
|
|
39446
39539
|
const sourceOf = (resourcePath) => {
|
|
39447
39540
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
39448
|
-
if (!contained.ok || !
|
|
39541
|
+
if (!contained.ok || !existsSync18(contained.absolutePath)) {
|
|
39449
39542
|
return null;
|
|
39450
39543
|
}
|
|
39451
39544
|
try {
|
|
39452
|
-
return
|
|
39545
|
+
return readFileSync22(contained.absolutePath, "utf8");
|
|
39453
39546
|
} catch {
|
|
39454
39547
|
return null;
|
|
39455
39548
|
}
|
|
@@ -39514,11 +39607,11 @@ class GodotServer {
|
|
|
39514
39607
|
}
|
|
39515
39608
|
sourceOfClass(projectPath, resourcePath) {
|
|
39516
39609
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
39517
|
-
if (!contained.ok || !
|
|
39610
|
+
if (!contained.ok || !existsSync18(contained.absolutePath)) {
|
|
39518
39611
|
return null;
|
|
39519
39612
|
}
|
|
39520
39613
|
try {
|
|
39521
|
-
return
|
|
39614
|
+
return readFileSync22(contained.absolutePath, "utf8");
|
|
39522
39615
|
} catch {
|
|
39523
39616
|
return null;
|
|
39524
39617
|
}
|
|
@@ -39666,7 +39759,7 @@ class GodotServer {
|
|
|
39666
39759
|
...Array.isArray(stack) ? {
|
|
39667
39760
|
stack: stack.map((frame) => ({
|
|
39668
39761
|
...frame,
|
|
39669
|
-
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path:
|
|
39762
|
+
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join24(projectPath, frame["script"].slice("res://".length)) } : {}
|
|
39670
39763
|
}))
|
|
39671
39764
|
} : {}
|
|
39672
39765
|
});
|
|
@@ -39936,7 +40029,7 @@ class GodotServer {
|
|
|
39936
40029
|
return null;
|
|
39937
40030
|
}
|
|
39938
40031
|
try {
|
|
39939
|
-
return settingKeys(
|
|
40032
|
+
return settingKeys(readFileSync22(join24(projectPath, "project.godot"), "utf8"));
|
|
39940
40033
|
} catch {
|
|
39941
40034
|
return null;
|
|
39942
40035
|
}
|
|
@@ -40201,6 +40294,7 @@ class GodotServer {
|
|
|
40201
40294
|
}
|
|
40202
40295
|
async openAnEditor(engine, projectPath, ports, hidden) {
|
|
40203
40296
|
clearEditorLog(projectPath);
|
|
40297
|
+
freeLibraryCopies(projectPath);
|
|
40204
40298
|
const launched = await launchOutsideTheTree({
|
|
40205
40299
|
command: engine,
|
|
40206
40300
|
args: editorArguments(projectPath, ports, editorLogPath(projectPath), hidden),
|
|
@@ -40359,7 +40453,7 @@ class GodotServer {
|
|
|
40359
40453
|
}
|
|
40360
40454
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
40361
40455
|
const budgetMs = budget.ms;
|
|
40362
|
-
if (!
|
|
40456
|
+
if (!existsSync18(join24(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
40363
40457
|
return runtimeVerdict(null, {
|
|
40364
40458
|
addon: false,
|
|
40365
40459
|
budgetMs,
|
|
@@ -40638,7 +40732,7 @@ class GodotServer {
|
|
|
40638
40732
|
return 0;
|
|
40639
40733
|
}
|
|
40640
40734
|
try {
|
|
40641
|
-
return reportAlreadyInTranscript(
|
|
40735
|
+
return reportAlreadyInTranscript(readFileSync22(game.transcript), readFileSync22(report));
|
|
40642
40736
|
} catch (error) {
|
|
40643
40737
|
this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
|
|
40644
40738
|
return 0;
|
|
@@ -41318,7 +41412,7 @@ class GodotServer {
|
|
|
41318
41412
|
}
|
|
41319
41413
|
const going = await this.runStillGoing(run);
|
|
41320
41414
|
const projectPath = run.projectPath;
|
|
41321
|
-
const projectGone = projectPath !== null && !
|
|
41415
|
+
const projectGone = projectPath !== null && !existsSync18(join24(projectPath, "project.godot"));
|
|
41322
41416
|
if (projectGone && callersName !== undefined) {
|
|
41323
41417
|
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"]);
|
|
41324
41418
|
}
|
|
@@ -41606,7 +41700,7 @@ class GodotServer {
|
|
|
41606
41700
|
notes.push(endedWithoutACode({
|
|
41607
41701
|
throughEditor: run.throughEditor,
|
|
41608
41702
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
41609
|
-
couldAnnounce: run.projectPath !== null &&
|
|
41703
|
+
couldAnnounce: run.projectPath !== null && existsSync18(join24(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
41610
41704
|
endedHere: typeof run.endedHere === "string",
|
|
41611
41705
|
pickedUp: run.pickedUp === true
|
|
41612
41706
|
}));
|
|
@@ -42171,7 +42265,7 @@ class GodotServer {
|
|
|
42171
42265
|
if (!scriptPath.ok) {
|
|
42172
42266
|
return this.createErrorResponse(scriptPath.reason);
|
|
42173
42267
|
}
|
|
42174
|
-
if (!
|
|
42268
|
+
if (!existsSync18(scriptPath.absolutePath)) {
|
|
42175
42269
|
return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
|
|
42176
42270
|
}
|
|
42177
42271
|
const asked = readNonEmptyString(args, "newScriptPath");
|
|
@@ -42499,7 +42593,7 @@ class GodotServer {
|
|
|
42499
42593
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
42500
42594
|
const run = await this.currentRun();
|
|
42501
42595
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
42502
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !
|
|
42596
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync18(join24(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
42503
42597
|
return null;
|
|
42504
42598
|
}
|
|
42505
42599
|
const already = runtimesAnnounced().running;
|
|
@@ -42584,12 +42678,25 @@ class GodotServer {
|
|
|
42584
42678
|
}
|
|
42585
42679
|
}
|
|
42586
42680
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
42587
|
-
const screenshotPath = screenshotDir ?
|
|
42681
|
+
const screenshotPath = screenshotDir ? join24(screenshotDir, "capture.png") : null;
|
|
42588
42682
|
const staleNote = this.runtimeStaleNote(choice.endpoint);
|
|
42589
42683
|
const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
|
|
42590
42684
|
const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
|
|
42591
42685
|
|
|
42592
42686
|
${staleNote}`);
|
|
42687
|
+
const onGame = {
|
|
42688
|
+
pid: choice.endpoint.pid,
|
|
42689
|
+
what: `${asked.tool} ${asked.op}`,
|
|
42690
|
+
alongside: new Set
|
|
42691
|
+
};
|
|
42692
|
+
for (const other of this.callsOnGames) {
|
|
42693
|
+
if (other.pid === onGame.pid) {
|
|
42694
|
+
other.alongside.add(onGame.what);
|
|
42695
|
+
onGame.alongside.add(other.what);
|
|
42696
|
+
}
|
|
42697
|
+
}
|
|
42698
|
+
this.callsOnGames.add(onGame);
|
|
42699
|
+
const alongside = () => onGame.alongside.size === 0 ? {} : { ranAlongside: [...onGame.alongside], alongsideNote: ALONGSIDE_NOTE };
|
|
42593
42700
|
try {
|
|
42594
42701
|
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs, !expectsScreenshot);
|
|
42595
42702
|
if (!reply.ok && reply.requestId !== undefined) {
|
|
@@ -42600,7 +42707,8 @@ ${staleNote}`);
|
|
|
42600
42707
|
waitedMs: timeoutMs,
|
|
42601
42708
|
note: `${reply.message} ${longerWaitNote(asked.tool, asked.op, timeoutMs)}`,
|
|
42602
42709
|
...answeredBy,
|
|
42603
|
-
...stale
|
|
42710
|
+
...stale,
|
|
42711
|
+
...alongside()
|
|
42604
42712
|
});
|
|
42605
42713
|
}
|
|
42606
42714
|
if (!reply.ok) {
|
|
@@ -42608,7 +42716,7 @@ ${staleNote}`);
|
|
|
42608
42716
|
}
|
|
42609
42717
|
const { id: _id, ...payload } = reply.payload;
|
|
42610
42718
|
if (!expectsScreenshot) {
|
|
42611
|
-
return this.jsonTextResponse({ ...payload, ...answeredBy, ...stale });
|
|
42719
|
+
return this.jsonTextResponse({ ...payload, ...answeredBy, ...stale, ...alongside() });
|
|
42612
42720
|
}
|
|
42613
42721
|
const returnedPath = readString(payload, "path");
|
|
42614
42722
|
if (readString(payload, "type") !== "screenshot_file" || !returnedPath || !screenshotPath) {
|
|
@@ -42625,18 +42733,22 @@ ${staleNote}`);
|
|
|
42625
42733
|
return this.createErrorResponse(`The capture was taken and could not be saved at ${saveTo}: ${errorMessage(error)}`);
|
|
42626
42734
|
}
|
|
42627
42735
|
}
|
|
42736
|
+
const ranWith = [...onGame.alongside];
|
|
42628
42737
|
return {
|
|
42629
42738
|
content: [
|
|
42630
42739
|
{
|
|
42631
42740
|
type: "text",
|
|
42632
|
-
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}${captureDetail(payload)}${saveTo === null ? "" : `. Saved to ${saveTo}`}${
|
|
42741
|
+
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}${captureDetail(payload)}${saveTo === null ? "" : `. Saved to ${saveTo}`}${ranWith.length === 0 ? "" : `
|
|
42742
|
+
|
|
42743
|
+
Ran alongside ${ranWith.join(", ")}. ${ALONGSIDE_NOTE}`}${staleNote === undefined ? "" : `
|
|
42633
42744
|
|
|
42634
42745
|
${staleNote}`}`
|
|
42635
42746
|
},
|
|
42636
|
-
{ type: "image", data:
|
|
42747
|
+
{ type: "image", data: readFileSync22(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
42637
42748
|
]
|
|
42638
42749
|
};
|
|
42639
42750
|
} finally {
|
|
42751
|
+
this.callsOnGames.delete(onGame);
|
|
42640
42752
|
discard(screenshotDir);
|
|
42641
42753
|
}
|
|
42642
42754
|
}
|