gdharness 1.1.19 → 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
|
}
|
|
@@ -26274,7 +26276,8 @@ function classRemedy(entries) {
|
|
|
26274
26276
|
}
|
|
26275
26277
|
const declaring = declaringScripts(entries);
|
|
26276
26278
|
const reload = declaring.length === 1 ? `editor_rescan with reloadScript set to ${declaring[0]}` : `editor_rescan with reloadScript, a call each for ${declaring.join(" and ")} since it takes one script,`;
|
|
26277
|
-
|
|
26279
|
+
const oneAtATime = declaring.length === 1 ? "" : " Take them one at a time: a rescan that brings in a class also reloads every script naming it and lists them under dependentsReloaded, so a script listed there needs no call of its own.";
|
|
26280
|
+
return `For ${entries.map(written).join(", ")}, run ${reload} first.${oneAtATime} In the one project ` + "that has reproduced a stale enum member, three times, the reload cleared it at once each time, " + "and the one plain editor_rescan tried left the diagnostic standing.";
|
|
26278
26281
|
}
|
|
26279
26282
|
function failedReloadNote(failed) {
|
|
26280
26283
|
const missing = (before, after) => before.filter((name) => !after.includes(name));
|
|
@@ -26284,9 +26287,9 @@ function failedReloadNote(failed) {
|
|
|
26284
26287
|
];
|
|
26285
26288
|
const unchanged = lost.length === 0 && missing(failed.heldAfter, failed.heldBefore).length === 0 && missing(failed.constantsAfter, failed.constantsBefore).length === 0;
|
|
26286
26289
|
const answered = failed.codeName === "" ? `error ${failed.code}` : `error ${failed.code}, ${failed.codeName}`;
|
|
26287
|
-
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.`;
|
|
26288
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.`;
|
|
26289
|
-
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}`;
|
|
26290
26293
|
}
|
|
26291
26294
|
function uncachedClassNote(types) {
|
|
26292
26295
|
if (types.length === 0) {
|
|
@@ -28146,14 +28149,34 @@ class GodotLocator {
|
|
|
28146
28149
|
|
|
28147
28150
|
// src/headless.ts
|
|
28148
28151
|
import { execFile as execFile3 } from "node:child_process";
|
|
28149
|
-
import { writeFileSync as
|
|
28150
|
-
import { join as
|
|
28152
|
+
import { writeFileSync as writeFileSync8 } from "node:fs";
|
|
28153
|
+
import { join as join13 } from "node:path";
|
|
28151
28154
|
import { promisify as promisify3 } from "node:util";
|
|
28152
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
|
+
|
|
28153
28176
|
// src/scratch.ts
|
|
28154
|
-
import { existsSync as
|
|
28177
|
+
import { existsSync as existsSync9, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync5 } from "node:fs";
|
|
28155
28178
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
28156
|
-
import { join as
|
|
28179
|
+
import { join as join12 } from "node:path";
|
|
28157
28180
|
import process7 from "node:process";
|
|
28158
28181
|
var SWEPT = { recursive: true, force: true };
|
|
28159
28182
|
var AGAIN_AFTER_MS = [250, 500, 1000, 2000, 4000, 8000, 16000];
|
|
@@ -28168,7 +28191,7 @@ var KINDS = [
|
|
|
28168
28191
|
"tests"
|
|
28169
28192
|
];
|
|
28170
28193
|
function scratchDirectory(kind) {
|
|
28171
|
-
return mkdtempSync(
|
|
28194
|
+
return mkdtempSync(join12(tmpdir3(), `gdharness-${kind}-${process7.pid}-`));
|
|
28172
28195
|
}
|
|
28173
28196
|
function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
28174
28197
|
let entries;
|
|
@@ -28184,7 +28207,7 @@ function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
|
28184
28207
|
continue;
|
|
28185
28208
|
}
|
|
28186
28209
|
try {
|
|
28187
|
-
remove(
|
|
28210
|
+
remove(join12(directory, entry));
|
|
28188
28211
|
} catch {}
|
|
28189
28212
|
}
|
|
28190
28213
|
}
|
|
@@ -28210,7 +28233,7 @@ function discard(...paths) {
|
|
|
28210
28233
|
return left;
|
|
28211
28234
|
}
|
|
28212
28235
|
function remove(path) {
|
|
28213
|
-
|
|
28236
|
+
rmSync5(path, SWEPT);
|
|
28214
28237
|
}
|
|
28215
28238
|
function tryAgain(path, attempt) {
|
|
28216
28239
|
if (attempt === 0) {
|
|
@@ -28222,7 +28245,7 @@ function tryAgain(path, attempt) {
|
|
|
28222
28245
|
lastTryOnExit();
|
|
28223
28246
|
}
|
|
28224
28247
|
const after = AGAIN_AFTER_MS[attempt];
|
|
28225
|
-
if (after === undefined || !
|
|
28248
|
+
if (after === undefined || !existsSync9(path)) {
|
|
28226
28249
|
waiting.delete(path);
|
|
28227
28250
|
if (after === undefined) {
|
|
28228
28251
|
atExit.add(path);
|
|
@@ -28302,21 +28325,36 @@ function librariesNotCopied(messages) {
|
|
|
28302
28325
|
}
|
|
28303
28326
|
return [...found];
|
|
28304
28327
|
}
|
|
28305
|
-
function extensionRetryNote(libraries) {
|
|
28306
|
-
|
|
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.";
|
|
28307
28331
|
}
|
|
28308
|
-
async function runImport(godotPath, projectPath,
|
|
28309
|
-
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();
|
|
28310
28341
|
const collided = librariesNotCopied(first.messages);
|
|
28311
28342
|
if (collided.length === 0) {
|
|
28312
28343
|
return first;
|
|
28313
28344
|
}
|
|
28314
|
-
|
|
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
|
+
};
|
|
28315
28353
|
}
|
|
28316
28354
|
async function importOnce(godotPath, projectPath) {
|
|
28317
28355
|
const logDir = scratchDirectory("import");
|
|
28318
28356
|
try {
|
|
28319
|
-
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() });
|
|
28320
28358
|
return { ok: true, messages: problems(stderr) };
|
|
28321
28359
|
} catch (error) {
|
|
28322
28360
|
if (error instanceof Error && "stdout" in error && "stderr" in error) {
|
|
@@ -28338,12 +28376,12 @@ async function importOnce(godotPath, projectPath) {
|
|
|
28338
28376
|
}
|
|
28339
28377
|
async function runOperation(engine, operation, params, projectPath) {
|
|
28340
28378
|
const paramsDir = scratchDirectory("params");
|
|
28341
|
-
const paramsFile =
|
|
28342
|
-
|
|
28379
|
+
const paramsFile = join13(paramsDir, `${operation}.json`);
|
|
28380
|
+
writeFileSync8(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
28343
28381
|
const args = [
|
|
28344
28382
|
"--headless",
|
|
28345
28383
|
"--log-file",
|
|
28346
|
-
|
|
28384
|
+
join13(paramsDir, "engine.log"),
|
|
28347
28385
|
"--path",
|
|
28348
28386
|
projectPath,
|
|
28349
28387
|
"--script",
|
|
@@ -29399,14 +29437,14 @@ function secondsFromClock(printed) {
|
|
|
29399
29437
|
}
|
|
29400
29438
|
|
|
29401
29439
|
// src/project-scan.ts
|
|
29402
|
-
import { existsSync as
|
|
29403
|
-
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";
|
|
29404
29442
|
var SKIPPED = new Set(["node_modules"]);
|
|
29405
29443
|
function skipped(name) {
|
|
29406
29444
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
29407
29445
|
}
|
|
29408
29446
|
function steppedOver(directory) {
|
|
29409
|
-
return
|
|
29447
|
+
return existsSync10(join14(directory, ".gdignore"));
|
|
29410
29448
|
}
|
|
29411
29449
|
var ASSET_EXTENSIONS = new Set(["png", "jpg", "jpeg", "webp", "svg", "ttf", "otf", "wav", "mp3", "ogg"]);
|
|
29412
29450
|
function projectStructure(projectPath) {
|
|
@@ -29420,7 +29458,7 @@ function projectStructure(projectPath) {
|
|
|
29420
29458
|
continue;
|
|
29421
29459
|
}
|
|
29422
29460
|
if (entry.isDirectory()) {
|
|
29423
|
-
visit(
|
|
29461
|
+
visit(join14(directory, entry.name));
|
|
29424
29462
|
} else if (entry.isFile()) {
|
|
29425
29463
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
29426
29464
|
if (extension === "tscn") {
|
|
@@ -29451,14 +29489,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
29451
29489
|
}
|
|
29452
29490
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
29453
29491
|
if (entry.isDirectory()) {
|
|
29454
|
-
visit(
|
|
29492
|
+
visit(join14(directory, entry.name), spelled);
|
|
29455
29493
|
continue;
|
|
29456
29494
|
}
|
|
29457
29495
|
if (!entry.isFile()) {
|
|
29458
29496
|
continue;
|
|
29459
29497
|
}
|
|
29460
29498
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
29461
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
29499
|
+
if (CARRIES_A_UID.has(extension) && !existsSync10(join14(directory, `${entry.name}.uid`))) {
|
|
29462
29500
|
found.push(spelled);
|
|
29463
29501
|
}
|
|
29464
29502
|
}
|
|
@@ -29493,7 +29531,7 @@ function searchProject(projectPath, options) {
|
|
|
29493
29531
|
if (skipped(entry.name)) {
|
|
29494
29532
|
continue;
|
|
29495
29533
|
}
|
|
29496
|
-
const entryPath =
|
|
29534
|
+
const entryPath = join14(directory, entry.name);
|
|
29497
29535
|
if (entry.isDirectory()) {
|
|
29498
29536
|
visit(entryPath);
|
|
29499
29537
|
continue;
|
|
@@ -29504,7 +29542,7 @@ function searchProject(projectPath, options) {
|
|
|
29504
29542
|
}
|
|
29505
29543
|
result.summary.files_searched += 1;
|
|
29506
29544
|
const matches = [];
|
|
29507
|
-
for (const [index, line] of
|
|
29545
|
+
for (const [index, line] of readFileSync12(entryPath, "utf8").split(`
|
|
29508
29546
|
`).entries()) {
|
|
29509
29547
|
if (full()) {
|
|
29510
29548
|
break;
|
|
@@ -29526,26 +29564,6 @@ function searchProject(projectPath, options) {
|
|
|
29526
29564
|
return result;
|
|
29527
29565
|
}
|
|
29528
29566
|
|
|
29529
|
-
// src/reimport.ts
|
|
29530
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
29531
|
-
import { existsSync as existsSync10, readFileSync as readFileSync12, rmSync as rmSync5, statSync as statSync5, utimesSync, writeFileSync as writeFileSync8 } from "node:fs";
|
|
29532
|
-
import { basename as basename2, join as join14 } from "node:path";
|
|
29533
|
-
function forceNextImport(projectPath, resourcePath) {
|
|
29534
|
-
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
29535
|
-
const sidecar = join14(projectPath, `${relative}.import`);
|
|
29536
|
-
if (!existsSync10(sidecar)) {
|
|
29537
|
-
return;
|
|
29538
|
-
}
|
|
29539
|
-
const { atime, mtimeMs } = statSync5(sidecar);
|
|
29540
|
-
writeFileSync8(sidecar, readFileSync12(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
29541
|
-
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
29542
|
-
rmSync5(importRecord(projectPath, resourcePath), { force: true });
|
|
29543
|
-
}
|
|
29544
|
-
function importRecord(projectPath, resourcePath) {
|
|
29545
|
-
const digest = createHash2("md5").update(resourcePath).digest("hex");
|
|
29546
|
-
return join14(projectPath, ".godot", "imported", `${basename2(resourcePath)}-${digest}.md5`);
|
|
29547
|
-
}
|
|
29548
|
-
|
|
29549
29567
|
// src/resources.ts
|
|
29550
29568
|
import { readFileSync as readFileSync13 } from "node:fs";
|
|
29551
29569
|
import { extname, resolve as resolve4 } from "node:path";
|
|
@@ -31411,7 +31429,7 @@ var TOOL_SPECS = [
|
|
|
31411
31429
|
},
|
|
31412
31430
|
value: {
|
|
31413
31431
|
ops: ["until"],
|
|
31414
|
-
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."
|
|
31415
31433
|
},
|
|
31416
31434
|
says: {
|
|
31417
31435
|
type: "string",
|
|
@@ -33110,7 +33128,7 @@ class GodotServer {
|
|
|
33110
33128
|
uidsCreated: given,
|
|
33111
33129
|
stillWithoutUid: after,
|
|
33112
33130
|
note: uidsLeftNote(after.length),
|
|
33113
|
-
...imported.
|
|
33131
|
+
...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote }
|
|
33114
33132
|
}
|
|
33115
33133
|
}, scanned));
|
|
33116
33134
|
}
|
|
@@ -33255,19 +33273,16 @@ class GodotServer {
|
|
|
33255
33273
|
if (!engine.ok) {
|
|
33256
33274
|
return { ok: false, response: engine.response };
|
|
33257
33275
|
}
|
|
33258
|
-
for (const target of targets) {
|
|
33259
|
-
forceNextImport(projectPath, target);
|
|
33260
|
-
}
|
|
33261
33276
|
const scanned = await this.untilTheEditorsScanIsWritten(projectPath);
|
|
33262
|
-
const imported = await runImport(engine.value, projectPath);
|
|
33277
|
+
const imported = await runImport(engine.value, projectPath, { forcing: targets });
|
|
33263
33278
|
if (!imported.ok) {
|
|
33264
33279
|
return { ok: false, response: this.answer(withScanWait(imported, scanned)) };
|
|
33265
33280
|
}
|
|
33266
33281
|
if (imported.messages.length > 0) {
|
|
33267
33282
|
extra["engine_messages"] = imported.messages;
|
|
33268
33283
|
}
|
|
33269
|
-
if (imported.
|
|
33270
|
-
extra["extensionNote"] =
|
|
33284
|
+
if (imported.extensionNote !== undefined) {
|
|
33285
|
+
extra["extensionNote"] = imported.extensionNote;
|
|
33271
33286
|
}
|
|
33272
33287
|
}
|
|
33273
33288
|
const after = await this.operation("get_import_status", resourcePath === undefined ? { includeUpToDate: true } : { resourcePath }, projectPath);
|
|
@@ -33758,7 +33773,7 @@ class GodotServer {
|
|
|
33758
33773
|
const notes = [];
|
|
33759
33774
|
if (contradicted.length > 0) {
|
|
33760
33775
|
const dependsOn = new Set;
|
|
33761
|
-
for (const one of contradicted) {
|
|
33776
|
+
for (const one of contradicted.filter((entry) => entry.kind === "method" || entry.kind === "property")) {
|
|
33762
33777
|
const source = cached === null ? null : this.sourceOfClass(projectPath, one.declaredIn);
|
|
33763
33778
|
if (source === null || cached === null) {
|
|
33764
33779
|
continue;
|
|
@@ -33798,9 +33813,10 @@ class GodotServer {
|
|
|
33798
33813
|
});
|
|
33799
33814
|
}
|
|
33800
33815
|
classesAScriptNames(projectPath, scriptPath) {
|
|
33801
|
-
const
|
|
33816
|
+
const resourcePath = scriptPath.startsWith("res://") ? scriptPath : `res://${scriptPath.replace(/^\/+/, "")}`;
|
|
33817
|
+
const source = this.sourceOfClass(projectPath, resourcePath);
|
|
33802
33818
|
const known = cachedClasses(projectPath) ?? declaredClasses(projectPath);
|
|
33803
|
-
return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !==
|
|
33819
|
+
return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== resourcePath);
|
|
33804
33820
|
}
|
|
33805
33821
|
sourceOfClass(projectPath, resourcePath) {
|
|
33806
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",
|