gdharness 1.1.20 → 1.1.21
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.
|
@@ -734,12 +734,17 @@ func _reread_changed_uids(filesystem: EditorFileSystem) -> Dictionary:
|
|
|
734
734
|
return {"reread": reread, "duplicated": duplicated}
|
|
735
735
|
|
|
736
736
|
|
|
737
|
-
## Every `.uid` under [param directory], skipping what the editor skips: hidden directories
|
|
738
|
-
## holding a `.gdignore
|
|
737
|
+
## Every `.uid` under [param directory], skipping what the editor skips: hidden directories, any
|
|
738
|
+
## holding a `.gdignore`, and any below the project's own that holds a `project.godot` of its own,
|
|
739
|
+
## which the editor passes over as another project. A nested project's UIDs are never in the editor's
|
|
740
|
+
## table, so without that last one its files would be read again on every rescan, and one holding a
|
|
741
|
+
## copy of an outer file's `.uid` named as a duplicate on disk.
|
|
739
742
|
func _uid_files(directory: String, found: Array[String]) -> void:
|
|
740
743
|
var listing: DirAccess = DirAccess.open(directory)
|
|
741
744
|
if listing == null or FileAccess.file_exists(directory.path_join(".gdignore")):
|
|
742
745
|
return
|
|
746
|
+
if directory != "res://" and FileAccess.file_exists(directory.path_join("project.godot")):
|
|
747
|
+
return
|
|
743
748
|
for child: String in listing.get_directories():
|
|
744
749
|
if not child.begins_with("."):
|
|
745
750
|
_uid_files(directory.path_join(child), found)
|
|
@@ -684,9 +684,11 @@ func set_property(params: Dictionary) -> Dictionary:
|
|
|
684
684
|
## A float arrives as 64 bits and most engine properties keep 32: Camera3D.h_offset given -1.1
|
|
685
685
|
## reads back as -1.10000002384186, so comparing the two exactly took a write of the value already
|
|
686
686
|
## held for one the engine refused (#780). Rounding both to 32 bits is exact for a 32-bit property.
|
|
687
|
-
## A 64-bit one can take a write this calls the same
|
|
688
|
-
##
|
|
689
|
-
##
|
|
687
|
+
## A 64-bit one can take a write this calls the same and read back changed, and the caller calls a
|
|
688
|
+
## write unchanged only when it reads back as it was. What that cannot tell apart is a 64-bit
|
|
689
|
+
## property whose setter ignored a value differing from the held one below 32 bits: it is answered
|
|
690
|
+
## unchanged rather than refused. Vectors and colours hold 32-bit parts in the value itself, so they
|
|
691
|
+
## already compare at the property's precision.
|
|
690
692
|
static func _held_already(given: Variant, held: Variant) -> bool:
|
|
691
693
|
if not Values.comparable(given, held):
|
|
692
694
|
return false
|
|
@@ -145,7 +145,11 @@ func wait_until(params: Dictionary) -> Dictionary:
|
|
|
145
145
|
# with null until the time ran out. #772 waited a minute for "null" that way and answered met
|
|
146
146
|
# false with value null beside it.
|
|
147
147
|
var declared: int = watched["declared"]
|
|
148
|
-
|
|
148
|
+
# An untyped slot holding null or an object is read the same way, as set reads it: otherwise the
|
|
149
|
+
# two ops would disagree about one property, and the wait would run out on the text null.
|
|
150
|
+
var untyped_object: bool = declared == TYPE_NIL and (current == null or typeof(current) == TYPE_OBJECT)
|
|
151
|
+
var holds_objects: bool = declared == TYPE_OBJECT or untyped_object
|
|
152
|
+
if holds_objects and wanted is String and str(wanted).strip_edges() == "null":
|
|
149
153
|
wanted = null
|
|
150
154
|
var refused: String = _not_comparable(current, wanted, node_path, property)
|
|
151
155
|
var never_held: bool = typeof(wanted) != TYPE_NIL and typeof(wanted) != TYPE_OBJECT
|
package/build/index.js
CHANGED
|
@@ -26153,32 +26153,33 @@ function missingMemberIn(message) {
|
|
|
26153
26153
|
if (called !== null) {
|
|
26154
26154
|
return { kind: "static method", member: called[1] ?? "", type: called[2] ?? "" };
|
|
26155
26155
|
}
|
|
26156
|
-
const inBase = /Cannot find member "([A-Za-z_]\w*)" in base "([A-Za-z_]\w*)((
|
|
26156
|
+
const inBase = /Cannot find member "([A-Za-z_]\w*)" in base "([A-Za-z_]\w*)(?:\.([A-Za-z_]\w*))?"/.exec(message);
|
|
26157
26157
|
if (inBase === null) {
|
|
26158
26158
|
return null;
|
|
26159
26159
|
}
|
|
26160
26160
|
const member = inBase[1] ?? "";
|
|
26161
26161
|
const type = inBase[2] ?? "";
|
|
26162
|
-
const
|
|
26163
|
-
const enumName = within.at(-1);
|
|
26162
|
+
const enumName = inBase[3];
|
|
26164
26163
|
return enumName === undefined ? { kind: "member", member, type } : { kind: "enum member", member, type, enum: enumName };
|
|
26165
26164
|
}
|
|
26166
26165
|
function declaresMember(source, missing) {
|
|
26167
26166
|
const name = missing.member.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
26167
|
+
const top = String.raw`^(?:@\w+(?:\([^)]*\))?\s+)*`;
|
|
26168
|
+
const declared = (kinds) => new RegExp(String.raw`${top}(?:${kinds})\s+${name}\b`, "m").test(source);
|
|
26168
26169
|
if (missing.kind === "method") {
|
|
26169
|
-
return new RegExp(String.raw
|
|
26170
|
+
return new RegExp(String.raw`${top}(?:static\s+)?func\s+${name}\s*\(`, "m").test(source);
|
|
26170
26171
|
}
|
|
26171
26172
|
if (missing.kind === "static method") {
|
|
26172
|
-
return new RegExp(String.raw
|
|
26173
|
+
return new RegExp(String.raw`${top}static\s+func\s+${name}\s*\(`, "m").test(source);
|
|
26173
26174
|
}
|
|
26174
26175
|
if (missing.kind === "enum member") {
|
|
26175
26176
|
return enumValues(source, missing.enum ?? "").includes(missing.member);
|
|
26176
26177
|
}
|
|
26177
|
-
const kinds = missing.kind === "property" ?
|
|
26178
|
-
return
|
|
26178
|
+
const kinds = missing.kind === "property" ? String.raw`(?:static\s+)?(?:var|func)|const|enum|class|signal` : String.raw`static\s+(?:var|func)|const|enum|class`;
|
|
26179
|
+
return declared(kinds) || enumValues(source, null).includes(missing.member);
|
|
26179
26180
|
}
|
|
26180
26181
|
function enumValues(source, named) {
|
|
26181
|
-
const opening = named === null ?
|
|
26182
|
+
const opening = named === null ? /^enum\s*\{([^}]*)\}/gm : new RegExp(String.raw`^enum\s+${named.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\s*\{([^}]*)\}`, "gm");
|
|
26182
26183
|
const values = [];
|
|
26183
26184
|
for (const [, body] of source.matchAll(opening)) {
|
|
26184
26185
|
const uncommented = (body ?? "").split(`
|
|
@@ -26221,21 +26222,22 @@ function classesNamedIn(source, known) {
|
|
|
26221
26222
|
return [...named].sort();
|
|
26222
26223
|
}
|
|
26223
26224
|
function contradictedDiagnostics(messages, classes, sourceOf) {
|
|
26225
|
+
const keyOf = (missing) => [missing.type, missing.enum ?? "", missing.member].join(".");
|
|
26226
|
+
const denials = messages.map((message) => missingMemberIn(message)).filter((missing) => missing !== null);
|
|
26227
|
+
const calledOnTheClass = new Set(denials.filter((missing) => missing.kind === "static method").map((missing) => keyOf(missing)));
|
|
26224
26228
|
const read = new Map;
|
|
26225
26229
|
const found = new Map;
|
|
26226
|
-
for (const
|
|
26227
|
-
const
|
|
26228
|
-
const
|
|
26229
|
-
if (
|
|
26230
|
+
for (const missing of denials) {
|
|
26231
|
+
const declaredIn = classes.get(missing.type);
|
|
26232
|
+
const key = keyOf(missing);
|
|
26233
|
+
if (declaredIn === undefined || missing.kind === "method" && calledOnTheClass.has(key)) {
|
|
26230
26234
|
continue;
|
|
26231
26235
|
}
|
|
26232
26236
|
if (!read.has(declaredIn)) {
|
|
26233
26237
|
read.set(declaredIn, sourceOf(declaredIn));
|
|
26234
26238
|
}
|
|
26235
26239
|
const source = read.get(declaredIn) ?? null;
|
|
26236
|
-
|
|
26237
|
-
const already = found.get(key);
|
|
26238
|
-
if (source !== null && declaresMember(source, missing) && already?.kind !== "static method") {
|
|
26240
|
+
if (source !== null && declaresMember(source, missing)) {
|
|
26239
26241
|
found.set(key, { ...missing, declaredIn });
|
|
26240
26242
|
}
|
|
26241
26243
|
}
|
|
@@ -26285,9 +26287,9 @@ function failedReloadNote(failed) {
|
|
|
26285
26287
|
];
|
|
26286
26288
|
const unchanged = lost.length === 0 && missing(failed.heldAfter, failed.heldBefore).length === 0 && missing(failed.constantsAfter, failed.constantsBefore).length === 0;
|
|
26287
26289
|
const answered = failed.codeName === "" ? `error ${failed.code}` : `error ${failed.code}, ${failed.codeName}`;
|
|
26288
|
-
const copy = unchanged ? "
|
|
26290
|
+
const copy = unchanged ? "Nothing was reloaded: the copy the editor holds is as it was, with the members under heldBeforeReload and heldConstantsBeforeReload." : `The copy the editor holds changed even so${lost.length === 0 ? "" : `, and has lost ${lost.join(", ")}`}: anything diagnosed against it now is answered from what is left, and reloading it once it compiles rebuilds it.`;
|
|
26289
26291
|
const order = failed.names.length === 0 ? "" : ` If they deny a member of ${failed.names.join(" or ")} that its file declares, the editor's copy of that class is behind: reload that class first with reloadScript and then this script, the order that cleared it in the one project that has met this.`;
|
|
26290
|
-
return `${failed.script} did not compile
|
|
26292
|
+
return `${failed.script} did not compile: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
|
|
26291
26293
|
}
|
|
26292
26294
|
function uncachedClassNote(types) {
|
|
26293
26295
|
if (types.length === 0) {
|
|
@@ -28147,14 +28149,34 @@ class GodotLocator {
|
|
|
28147
28149
|
|
|
28148
28150
|
// src/headless.ts
|
|
28149
28151
|
import { execFile as execFile3 } from "node:child_process";
|
|
28150
|
-
import { writeFileSync as
|
|
28151
|
-
import { join as
|
|
28152
|
+
import { writeFileSync as writeFileSync8 } from "node:fs";
|
|
28153
|
+
import { join as join13 } from "node:path";
|
|
28152
28154
|
import { promisify as promisify3 } from "node:util";
|
|
28153
28155
|
|
|
28156
|
+
// src/reimport.ts
|
|
28157
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
28158
|
+
import { existsSync as existsSync8, readFileSync as readFileSync11, rmSync as rmSync4, statSync as statSync5, utimesSync, writeFileSync as writeFileSync7 } from "node:fs";
|
|
28159
|
+
import { basename as basename2, join as join11 } from "node:path";
|
|
28160
|
+
function forceNextImport(projectPath, resourcePath) {
|
|
28161
|
+
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
28162
|
+
const sidecar = join11(projectPath, `${relative}.import`);
|
|
28163
|
+
if (!existsSync8(sidecar)) {
|
|
28164
|
+
return;
|
|
28165
|
+
}
|
|
28166
|
+
const { atime, mtimeMs } = statSync5(sidecar);
|
|
28167
|
+
writeFileSync7(sidecar, readFileSync11(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
28168
|
+
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
28169
|
+
rmSync4(importRecord(projectPath, resourcePath), { force: true });
|
|
28170
|
+
}
|
|
28171
|
+
function importRecord(projectPath, resourcePath) {
|
|
28172
|
+
const digest = createHash2("md5").update(resourcePath).digest("hex");
|
|
28173
|
+
return join11(projectPath, ".godot", "imported", `${basename2(resourcePath)}-${digest}.md5`);
|
|
28174
|
+
}
|
|
28175
|
+
|
|
28154
28176
|
// src/scratch.ts
|
|
28155
|
-
import { existsSync as
|
|
28177
|
+
import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync5 } from "node:fs";
|
|
28156
28178
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
28157
|
-
import { join as
|
|
28179
|
+
import { join as join12 } from "node:path";
|
|
28158
28180
|
import process7 from "node:process";
|
|
28159
28181
|
var SWEPT = { recursive: true, force: true };
|
|
28160
28182
|
var AGAIN_AFTER_MS = [250, 500, 1000, 2000, 4000, 8000, 16000];
|
|
@@ -28169,7 +28191,7 @@ var KINDS = [
|
|
|
28169
28191
|
"tests"
|
|
28170
28192
|
];
|
|
28171
28193
|
function scratchDirectory(kind) {
|
|
28172
|
-
return mkdtempSync(
|
|
28194
|
+
return mkdtempSync(join12(tmpdir3(), `gdharness-${kind}-${process7.pid}-`));
|
|
28173
28195
|
}
|
|
28174
28196
|
function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
28175
28197
|
let entries;
|
|
@@ -28185,7 +28207,7 @@ function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
|
28185
28207
|
continue;
|
|
28186
28208
|
}
|
|
28187
28209
|
try {
|
|
28188
|
-
remove(
|
|
28210
|
+
remove(join12(directory, entry));
|
|
28189
28211
|
} catch {}
|
|
28190
28212
|
}
|
|
28191
28213
|
}
|
|
@@ -28211,7 +28233,7 @@ function discard(...paths) {
|
|
|
28211
28233
|
return left;
|
|
28212
28234
|
}
|
|
28213
28235
|
function remove(path) {
|
|
28214
|
-
|
|
28236
|
+
rmSync5(path, SWEPT);
|
|
28215
28237
|
}
|
|
28216
28238
|
function tryAgain(path, attempt) {
|
|
28217
28239
|
if (attempt === 0) {
|
|
@@ -28223,7 +28245,7 @@ function tryAgain(path, attempt) {
|
|
|
28223
28245
|
lastTryOnExit();
|
|
28224
28246
|
}
|
|
28225
28247
|
const after = AGAIN_AFTER_MS[attempt];
|
|
28226
|
-
if (after === undefined || !
|
|
28248
|
+
if (after === undefined || !existsSync9(path)) {
|
|
28227
28249
|
waiting.delete(path);
|
|
28228
28250
|
if (after === undefined) {
|
|
28229
28251
|
atExit.add(path);
|
|
@@ -28303,21 +28325,36 @@ function librariesNotCopied(messages) {
|
|
|
28303
28325
|
}
|
|
28304
28326
|
return [...found];
|
|
28305
28327
|
}
|
|
28306
|
-
function extensionRetryNote(libraries) {
|
|
28307
|
-
|
|
28328
|
+
function extensionRetryNote(libraries, stillNotLoaded = []) {
|
|
28329
|
+
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.`;
|
|
28330
|
+
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 ` + "can be deleted once the editor has exited.";
|
|
28308
28331
|
}
|
|
28309
|
-
async function runImport(godotPath, projectPath,
|
|
28310
|
-
const
|
|
28332
|
+
async function runImport(godotPath, projectPath, options = {}) {
|
|
28333
|
+
const once = options.once ?? importOnce;
|
|
28334
|
+
const pass = () => {
|
|
28335
|
+
for (const target of options.forcing ?? []) {
|
|
28336
|
+
forceNextImport(projectPath, target);
|
|
28337
|
+
}
|
|
28338
|
+
return once(godotPath, projectPath);
|
|
28339
|
+
};
|
|
28340
|
+
const first = await pass();
|
|
28311
28341
|
const collided = librariesNotCopied(first.messages);
|
|
28312
28342
|
if (collided.length === 0) {
|
|
28313
28343
|
return first;
|
|
28314
28344
|
}
|
|
28315
|
-
|
|
28345
|
+
const second = await pass();
|
|
28346
|
+
const still = librariesNotCopied(second.messages);
|
|
28347
|
+
return {
|
|
28348
|
+
...second,
|
|
28349
|
+
librariesRetried: collided,
|
|
28350
|
+
...still.length === 0 ? {} : { librariesNotLoaded: still },
|
|
28351
|
+
extensionNote: extensionRetryNote(collided, still)
|
|
28352
|
+
};
|
|
28316
28353
|
}
|
|
28317
28354
|
async function importOnce(godotPath, projectPath) {
|
|
28318
28355
|
const logDir = scratchDirectory("import");
|
|
28319
28356
|
try {
|
|
28320
|
-
const { stderr } = await run3(godotPath, ["--headless", "--log-file",
|
|
28357
|
+
const { stderr } = await run3(godotPath, ["--headless", "--log-file", join13(logDir, "engine.log"), "--path", projectPath, "--import"], { signal: callSignal() });
|
|
28321
28358
|
return { ok: true, messages: problems(stderr) };
|
|
28322
28359
|
} catch (error) {
|
|
28323
28360
|
if (error instanceof Error && "stdout" in error && "stderr" in error) {
|
|
@@ -28339,12 +28376,12 @@ async function importOnce(godotPath, projectPath) {
|
|
|
28339
28376
|
}
|
|
28340
28377
|
async function runOperation(engine, operation, params, projectPath) {
|
|
28341
28378
|
const paramsDir = scratchDirectory("params");
|
|
28342
|
-
const paramsFile =
|
|
28343
|
-
|
|
28379
|
+
const paramsFile = join13(paramsDir, `${operation}.json`);
|
|
28380
|
+
writeFileSync8(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
28344
28381
|
const args = [
|
|
28345
28382
|
"--headless",
|
|
28346
28383
|
"--log-file",
|
|
28347
|
-
|
|
28384
|
+
join13(paramsDir, "engine.log"),
|
|
28348
28385
|
"--path",
|
|
28349
28386
|
projectPath,
|
|
28350
28387
|
"--script",
|
|
@@ -29400,14 +29437,14 @@ function secondsFromClock(printed) {
|
|
|
29400
29437
|
}
|
|
29401
29438
|
|
|
29402
29439
|
// src/project-scan.ts
|
|
29403
|
-
import { existsSync as
|
|
29404
|
-
import { join as
|
|
29440
|
+
import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync12 } from "node:fs";
|
|
29441
|
+
import { join as join14 } from "node:path";
|
|
29405
29442
|
var SKIPPED = new Set(["node_modules"]);
|
|
29406
29443
|
function skipped(name) {
|
|
29407
29444
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
29408
29445
|
}
|
|
29409
29446
|
function steppedOver(directory) {
|
|
29410
|
-
return
|
|
29447
|
+
return existsSync10(join14(directory, ".gdignore"));
|
|
29411
29448
|
}
|
|
29412
29449
|
var ASSET_EXTENSIONS = new Set(["png", "jpg", "jpeg", "webp", "svg", "ttf", "otf", "wav", "mp3", "ogg"]);
|
|
29413
29450
|
function projectStructure(projectPath) {
|
|
@@ -29421,7 +29458,7 @@ function projectStructure(projectPath) {
|
|
|
29421
29458
|
continue;
|
|
29422
29459
|
}
|
|
29423
29460
|
if (entry.isDirectory()) {
|
|
29424
|
-
visit(
|
|
29461
|
+
visit(join14(directory, entry.name));
|
|
29425
29462
|
} else if (entry.isFile()) {
|
|
29426
29463
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
29427
29464
|
if (extension === "tscn") {
|
|
@@ -29452,14 +29489,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
29452
29489
|
}
|
|
29453
29490
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
29454
29491
|
if (entry.isDirectory()) {
|
|
29455
|
-
visit(
|
|
29492
|
+
visit(join14(directory, entry.name), spelled);
|
|
29456
29493
|
continue;
|
|
29457
29494
|
}
|
|
29458
29495
|
if (!entry.isFile()) {
|
|
29459
29496
|
continue;
|
|
29460
29497
|
}
|
|
29461
29498
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
29462
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
29499
|
+
if (CARRIES_A_UID.has(extension) && !existsSync10(join14(directory, `${entry.name}.uid`))) {
|
|
29463
29500
|
found.push(spelled);
|
|
29464
29501
|
}
|
|
29465
29502
|
}
|
|
@@ -29494,7 +29531,7 @@ function searchProject(projectPath, options) {
|
|
|
29494
29531
|
if (skipped(entry.name)) {
|
|
29495
29532
|
continue;
|
|
29496
29533
|
}
|
|
29497
|
-
const entryPath =
|
|
29534
|
+
const entryPath = join14(directory, entry.name);
|
|
29498
29535
|
if (entry.isDirectory()) {
|
|
29499
29536
|
visit(entryPath);
|
|
29500
29537
|
continue;
|
|
@@ -29505,7 +29542,7 @@ function searchProject(projectPath, options) {
|
|
|
29505
29542
|
}
|
|
29506
29543
|
result.summary.files_searched += 1;
|
|
29507
29544
|
const matches = [];
|
|
29508
|
-
for (const [index, line] of
|
|
29545
|
+
for (const [index, line] of readFileSync12(entryPath, "utf8").split(`
|
|
29509
29546
|
`).entries()) {
|
|
29510
29547
|
if (full()) {
|
|
29511
29548
|
break;
|
|
@@ -29527,26 +29564,6 @@ function searchProject(projectPath, options) {
|
|
|
29527
29564
|
return result;
|
|
29528
29565
|
}
|
|
29529
29566
|
|
|
29530
|
-
// src/reimport.ts
|
|
29531
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
29532
|
-
import { existsSync as existsSync10, readFileSync as readFileSync12, rmSync as rmSync5, statSync as statSync5, utimesSync, writeFileSync as writeFileSync8 } from "node:fs";
|
|
29533
|
-
import { basename as basename2, join as join14 } from "node:path";
|
|
29534
|
-
function forceNextImport(projectPath, resourcePath) {
|
|
29535
|
-
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
29536
|
-
const sidecar = join14(projectPath, `${relative}.import`);
|
|
29537
|
-
if (!existsSync10(sidecar)) {
|
|
29538
|
-
return;
|
|
29539
|
-
}
|
|
29540
|
-
const { atime, mtimeMs } = statSync5(sidecar);
|
|
29541
|
-
writeFileSync8(sidecar, readFileSync12(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
29542
|
-
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
29543
|
-
rmSync5(importRecord(projectPath, resourcePath), { force: true });
|
|
29544
|
-
}
|
|
29545
|
-
function importRecord(projectPath, resourcePath) {
|
|
29546
|
-
const digest = createHash2("md5").update(resourcePath).digest("hex");
|
|
29547
|
-
return join14(projectPath, ".godot", "imported", `${basename2(resourcePath)}-${digest}.md5`);
|
|
29548
|
-
}
|
|
29549
|
-
|
|
29550
29567
|
// src/resources.ts
|
|
29551
29568
|
import { readFileSync as readFileSync13 } from "node:fs";
|
|
29552
29569
|
import { extname, resolve as resolve4 } from "node:path";
|
|
@@ -31412,7 +31429,7 @@ var TOOL_SPECS = [
|
|
|
31412
31429
|
},
|
|
31413
31430
|
value: {
|
|
31414
31431
|
ops: ["until"],
|
|
31415
|
-
description: "until: the value to wait for, fitted to the property's type. On a property declared to
|
|
31432
|
+
description: "until: the value to wait for, fitted to the property's type. On a property holding an object, whether declared to or untyped and holding one or null, the text null means null. One declared to hold an object refuses any value other than null, since it can never hold it."
|
|
31416
31433
|
},
|
|
31417
31434
|
says: {
|
|
31418
31435
|
type: "string",
|
|
@@ -33111,7 +33128,7 @@ class GodotServer {
|
|
|
33111
33128
|
uidsCreated: given,
|
|
33112
33129
|
stillWithoutUid: after,
|
|
33113
33130
|
note: uidsLeftNote(after.length),
|
|
33114
|
-
...imported.
|
|
33131
|
+
...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote }
|
|
33115
33132
|
}
|
|
33116
33133
|
}, scanned));
|
|
33117
33134
|
}
|
|
@@ -33256,19 +33273,16 @@ class GodotServer {
|
|
|
33256
33273
|
if (!engine.ok) {
|
|
33257
33274
|
return { ok: false, response: engine.response };
|
|
33258
33275
|
}
|
|
33259
|
-
for (const target of targets) {
|
|
33260
|
-
forceNextImport(projectPath, target);
|
|
33261
|
-
}
|
|
33262
33276
|
const scanned = await this.untilTheEditorsScanIsWritten(projectPath);
|
|
33263
|
-
const imported = await runImport(engine.value, projectPath);
|
|
33277
|
+
const imported = await runImport(engine.value, projectPath, { forcing: targets });
|
|
33264
33278
|
if (!imported.ok) {
|
|
33265
33279
|
return { ok: false, response: this.answer(withScanWait(imported, scanned)) };
|
|
33266
33280
|
}
|
|
33267
33281
|
if (imported.messages.length > 0) {
|
|
33268
33282
|
extra["engine_messages"] = imported.messages;
|
|
33269
33283
|
}
|
|
33270
|
-
if (imported.
|
|
33271
|
-
extra["extensionNote"] =
|
|
33284
|
+
if (imported.extensionNote !== undefined) {
|
|
33285
|
+
extra["extensionNote"] = imported.extensionNote;
|
|
33272
33286
|
}
|
|
33273
33287
|
}
|
|
33274
33288
|
const after = await this.operation("get_import_status", resourcePath === undefined ? { includeUpToDate: true } : { resourcePath }, projectPath);
|
|
@@ -33759,7 +33773,7 @@ class GodotServer {
|
|
|
33759
33773
|
const notes = [];
|
|
33760
33774
|
if (contradicted.length > 0) {
|
|
33761
33775
|
const dependsOn = new Set;
|
|
33762
|
-
for (const one of contradicted) {
|
|
33776
|
+
for (const one of contradicted.filter((entry) => entry.kind === "method" || entry.kind === "property")) {
|
|
33763
33777
|
const source = cached === null ? null : this.sourceOfClass(projectPath, one.declaredIn);
|
|
33764
33778
|
if (source === null || cached === null) {
|
|
33765
33779
|
continue;
|
|
@@ -33799,9 +33813,10 @@ class GodotServer {
|
|
|
33799
33813
|
});
|
|
33800
33814
|
}
|
|
33801
33815
|
classesAScriptNames(projectPath, scriptPath) {
|
|
33802
|
-
const
|
|
33816
|
+
const resourcePath = scriptPath.startsWith("res://") ? scriptPath : `res://${scriptPath.replace(/^\/+/, "")}`;
|
|
33817
|
+
const source = this.sourceOfClass(projectPath, resourcePath);
|
|
33803
33818
|
const known = cachedClasses(projectPath) ?? declaredClasses(projectPath);
|
|
33804
|
-
return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !==
|
|
33819
|
+
return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== resourcePath);
|
|
33805
33820
|
}
|
|
33806
33821
|
sourceOfClass(projectPath, resourcePath) {
|
|
33807
33822
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.21",
|
|
4
4
|
"mcpName": "io.github.Aureliolo/gdharness",
|
|
5
5
|
"description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
|
|
6
6
|
"type": "module",
|