gdharness 1.3.43 → 1.3.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +607 -511
- package/build/godot/addons/gdharness_editor/reading.gd +14 -5
- package/build/godot/addons/gdharness_editor/scene_file.gd +18 -1
- package/build/godot/addons/gdharness_editor/setting_reads.gd +42 -1
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +31 -12
- package/build/godot/addons/gdharness_runtime/reading.gd +14 -5
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +13 -0
- package/build/godot/operations/dependencies.gd +154 -85
- package/build/godot/operations/editor_tools.gd +5 -4
- package/build/godot/operations/gdscript_analysis.gd +45 -8
- package/build/godot/operations/patterns.gd +6 -0
- package/build/godot/operations/project_config.gd +8 -3
- package/build/godot/operations/project_validation.gd +3 -4
- package/build/godot/operations/reading.gd +14 -5
- package/build/godot/operations/setting_reads.gd +42 -1
- package/build/keeper.js +43 -43
- package/package.json +1 -1
|
@@ -56,6 +56,25 @@ func get_gdscript_info(params: Dictionary) -> Dictionary:
|
|
|
56
56
|
# its first function would otherwise be judged against the default.
|
|
57
57
|
_mark_virtuals(functions, extends_name, full_script_path)
|
|
58
58
|
|
|
59
|
+
# The engine loads a base named by its path and a constant handed to load or preload as surely
|
|
60
|
+
# as a path written in the call, and both were left out while project_dependencies named them: a
|
|
61
|
+
# script extending "frame.gd" did not depend on it, nor one calling load(TYPE_CASE).
|
|
62
|
+
for written: String in _based_on_paths(extends_name, own.inner_classes):
|
|
63
|
+
if not written in dependencies:
|
|
64
|
+
dependencies.append(written)
|
|
65
|
+
var held: Dictionary = {}
|
|
66
|
+
for constant: Dictionary in own.constants:
|
|
67
|
+
held[str(constant.get("name", ""))] = _unquoted(str(constant.get("value", "")))
|
|
68
|
+
for code: String in GdhDependencies.code_by_line(content):
|
|
69
|
+
for loading: RegExMatch in (
|
|
70
|
+
GdhPatterns
|
|
71
|
+
. compiled("(?<![\\w.])(?:preload|load)\\s*\\(\\s*([A-Za-z_]\\w*)\\s*\\)")
|
|
72
|
+
. search_all(code)
|
|
73
|
+
):
|
|
74
|
+
var written: String = str(held.get(loading.get_string(1), ""))
|
|
75
|
+
if not written.is_empty() and not written in dependencies:
|
|
76
|
+
dependencies.append(written)
|
|
77
|
+
|
|
59
78
|
# A script typed by its class_name is depended on as surely as one preloaded, and the answer
|
|
60
79
|
# listed only the preloads: a `var stats: Stats` left stats.gd out, while project_dependencies
|
|
61
80
|
# named it. The same reading as that tool's, so the two agree.
|
|
@@ -131,7 +150,7 @@ func _members(
|
|
|
131
150
|
# Inside a string the raw line is counted, because a `#` in it is text and taking it for a
|
|
132
151
|
# comment would cut off the quotes that close the string.
|
|
133
152
|
if in_multiline_string:
|
|
134
|
-
if
|
|
153
|
+
if GdhPatterns.triple_quotes(raw) % 2 == 1:
|
|
135
154
|
in_multiline_string = false
|
|
136
155
|
continue
|
|
137
156
|
# A `##` block documents what it sits directly above: the class when that is its class_name
|
|
@@ -165,7 +184,7 @@ func _members(
|
|
|
165
184
|
# it opened on. Toggling on any count lost `const HELP := """Usage"""`, and everything after
|
|
166
185
|
# it up to the next triple quote, with the state inverted from there to the end of the file.
|
|
167
186
|
# The line that opens one is still read: it can be the declaration the string belongs to.
|
|
168
|
-
if
|
|
187
|
+
if GdhPatterns.triple_quotes(stripped) % 2 == 1:
|
|
169
188
|
in_multiline_string = true
|
|
170
189
|
|
|
171
190
|
# Only a line at this body's own margin declares one of its members. Deeper, it is a
|
|
@@ -207,7 +226,7 @@ func _members(
|
|
|
207
226
|
while i + 1 < lines.size():
|
|
208
227
|
i += 1
|
|
209
228
|
stripped += "\n" + lines[i]
|
|
210
|
-
if
|
|
229
|
+
if GdhPatterns.triple_quotes(lines[i]) % 2 == 1:
|
|
211
230
|
break
|
|
212
231
|
in_multiline_string = false
|
|
213
232
|
header = GdhPatterns.without_annotations(stripped)
|
|
@@ -821,13 +840,31 @@ func _collect_dependencies(line: String, into: Array[String]) -> void:
|
|
|
821
840
|
into.append(dep)
|
|
822
841
|
|
|
823
842
|
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
843
|
+
## The paths a script and its inner classes extend by, as written: [param extends_name] the
|
|
844
|
+
## script's own extends, and [param inner_classes] its inner classes as answered.
|
|
845
|
+
static func _based_on_paths(extends_name: String, inner_classes: Array) -> Array[String]:
|
|
846
|
+
var paths: Array[String] = []
|
|
847
|
+
var written: String = _unquoted(extends_name)
|
|
848
|
+
if not written.is_empty():
|
|
849
|
+
paths.append(written)
|
|
850
|
+
for inner: Dictionary in inner_classes:
|
|
851
|
+
var deeper: Array = inner.get("inner_classes", [])
|
|
852
|
+
for path: String in _based_on_paths(str(inner.get("extends", "")), deeper):
|
|
853
|
+
if not path in paths:
|
|
854
|
+
paths.append(path)
|
|
855
|
+
return paths
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
## The text of [param literal], a string literal as written; empty for anything else.
|
|
859
|
+
static func _unquoted(literal: String) -> String:
|
|
860
|
+
var text: String = literal.strip_edges()
|
|
861
|
+
if text.length() >= 2 and text[0] in ["\"", "'"] and text[-1] == text[0]:
|
|
862
|
+
return text.substr(1, text.length() - 2)
|
|
863
|
+
return ""
|
|
827
864
|
|
|
828
865
|
|
|
829
|
-
static func
|
|
830
|
-
return
|
|
866
|
+
static func _declares_variable(header: String) -> bool:
|
|
867
|
+
return header.begins_with("var ") or header.begins_with("static var ")
|
|
831
868
|
|
|
832
869
|
|
|
833
870
|
func _extract_dependencies(line: String) -> Array[String]:
|
|
@@ -108,3 +108,9 @@ static func without_comment(line: String) -> String:
|
|
|
108
108
|
if ch == '"' or ch == "'":
|
|
109
109
|
quote = ch
|
|
110
110
|
return line.strip_edges()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
## How many triple quotes of either kind [param line] holds. An odd count opens or closes a string
|
|
114
|
+
## that runs across lines.
|
|
115
|
+
static func triple_quotes(line: String) -> int:
|
|
116
|
+
return line.count('"""') + line.count("'''")
|
|
@@ -387,8 +387,7 @@ func add_autoload(params: Dictionary) -> Dictionary:
|
|
|
387
387
|
var global: bool = GdhRead.as_bool(params.get("global", true), true)
|
|
388
388
|
|
|
389
389
|
# There is no disabled autoload in Godot 4: every entry in the list is loaded under the root at
|
|
390
|
-
# every start.
|
|
391
|
-
# was disabled, and the autoload ran all the same.
|
|
390
|
+
# every start. An entry without its asterisk is one whose name is not global, and it still runs.
|
|
392
391
|
if not GdhRead.as_bool(params.get("enabled", true), true):
|
|
393
392
|
return (
|
|
394
393
|
_log
|
|
@@ -433,12 +432,15 @@ func add_autoload(params: Dictionary) -> Dictionary:
|
|
|
433
432
|
"action": "unchanged",
|
|
434
433
|
"saved": false,
|
|
435
434
|
}
|
|
435
|
+
# The editor's Add replaces an entry of the same name too; said here, since the replaced script
|
|
436
|
+
# is no longer loaded and nothing else in the answer names it.
|
|
437
|
+
var held: String = str(ProjectSettings.get_setting(setting)) if was_updated else ""
|
|
436
438
|
ProjectSettings.set_setting(setting, entry)
|
|
437
439
|
var err: Error = GdhProjectFile.save([setting])
|
|
438
440
|
if err != OK:
|
|
439
441
|
return _log.failure("Failed to save project.godot: " + error_string(err))
|
|
440
442
|
|
|
441
|
-
|
|
443
|
+
var answer: Dictionary = {
|
|
442
444
|
"name": name,
|
|
443
445
|
"path": path,
|
|
444
446
|
"enabled": true,
|
|
@@ -446,6 +448,9 @@ func add_autoload(params: Dictionary) -> Dictionary:
|
|
|
446
448
|
"action": "updated" if was_updated else "added",
|
|
447
449
|
"saved": true,
|
|
448
450
|
}
|
|
451
|
+
if was_updated:
|
|
452
|
+
answer["old_path"] = held.trim_prefix("*")
|
|
453
|
+
return answer
|
|
449
454
|
|
|
450
455
|
|
|
451
456
|
## Why [param name] cannot name an autoload, or empty when it can. The editor's Autoload tab refuses
|
|
@@ -196,9 +196,9 @@ func validate_project(params: Dictionary) -> Dictionary:
|
|
|
196
196
|
var scripts_checked: int = 0
|
|
197
197
|
var script_issues: Array[Dictionary] = []
|
|
198
198
|
|
|
199
|
-
#
|
|
200
|
-
#
|
|
201
|
-
for script_path: String in script_files
|
|
199
|
+
# Every script, as health reads them: cut at the first hundred in walk order, the count sat
|
|
200
|
+
# beside a compile check of all 803 scripts of a game and read as how many were compiled.
|
|
201
|
+
for script_path: String in script_files:
|
|
202
202
|
scripts_checked += 1
|
|
203
203
|
var file: FileAccess = FileAccess.open(script_path, FileAccess.READ)
|
|
204
204
|
if file:
|
|
@@ -292,7 +292,6 @@ func validate_project(params: Dictionary) -> Dictionary:
|
|
|
292
292
|
"warnings": warnings,
|
|
293
293
|
"checks_performed": checks_performed,
|
|
294
294
|
"scripts_checked": scripts_checked,
|
|
295
|
-
# Said beside the count, so a project of four hundred scripts is not read as checked whole.
|
|
296
295
|
"scripts_total": script_files.size(),
|
|
297
296
|
"issue_count": issues.size(),
|
|
298
297
|
"warning_count": warnings.size(),
|
|
@@ -312,16 +312,25 @@ static func _bare(word: String) -> String:
|
|
|
312
312
|
return word.to_lower().replace("_", "").replace(" ", "")
|
|
313
313
|
|
|
314
314
|
|
|
315
|
-
## The signals [param object] has that a caller can connect, its own class's first:
|
|
316
|
-
##
|
|
317
|
-
##
|
|
315
|
+
## The signals [param object] has that a caller can connect, its own class's first: its script's,
|
|
316
|
+
## as declared, then the engine's from the class down to Object. The engine lists its own from
|
|
317
|
+
## Object down, which puts a Button's pressed after thirty a Control has, and its script's ahead of
|
|
318
|
+
## those, so reversing the whole list put a game's own two signals after twenty-four of the
|
|
319
|
+
## engine's. Names starting with an underscore are the engine's own.
|
|
318
320
|
static func signals_of(object: Object) -> Array:
|
|
321
|
+
var declared: Array = []
|
|
322
|
+
var script: Script = object.get_script()
|
|
323
|
+
if script != null:
|
|
324
|
+
for entry: Dictionary in script.get_script_signal_list():
|
|
325
|
+
var name: String = str(entry["name"])
|
|
326
|
+
if not name.begins_with("_") and not name in declared:
|
|
327
|
+
declared.append(name)
|
|
319
328
|
var names: Array = []
|
|
320
329
|
for entry: Dictionary in object.get_signal_list():
|
|
321
330
|
var name: String = str(entry["name"])
|
|
322
|
-
if not name.begins_with("_"):
|
|
331
|
+
if not name.begins_with("_") and not name in declared:
|
|
323
332
|
names.push_front(name)
|
|
324
|
-
return names
|
|
333
|
+
return declared + names
|
|
325
334
|
|
|
326
335
|
|
|
327
336
|
## [param word] after the article it takes, "an AnimationTree" or "a Node2D".
|
|
@@ -62,7 +62,7 @@ static func under(prefix: String, values: GdhSerialisation) -> Dictionary:
|
|
|
62
62
|
var value: Variant = ProjectSettings.get_setting(name)
|
|
63
63
|
var described: Dictionary = {
|
|
64
64
|
"setting": name,
|
|
65
|
-
"type":
|
|
65
|
+
"type": type_named(kind, value),
|
|
66
66
|
"value": values.serialize_value(value),
|
|
67
67
|
}
|
|
68
68
|
# As a single get says it, so a family read can be told apart into what the project sets and
|
|
@@ -81,6 +81,47 @@ static func under(prefix: String, values: GdhSerialisation) -> Dictionary:
|
|
|
81
81
|
return { "prefix": prefix, "count": found.size(), "settings": found }
|
|
82
82
|
|
|
83
83
|
|
|
84
|
+
## The type [param kind] names, with what [param value] holds when it is a typed array or
|
|
85
|
+
## dictionary, as project.godot writes it: a `Dictionary[String, bool]` was answered as Dictionary,
|
|
86
|
+
## which hides what its keys and values must be.
|
|
87
|
+
static func type_named(kind: int, value: Variant) -> String:
|
|
88
|
+
if kind == TYPE_ARRAY and value is Array:
|
|
89
|
+
var list: Array = value
|
|
90
|
+
if list.is_typed():
|
|
91
|
+
var element: String = _held(
|
|
92
|
+
list.get_typed_builtin(), list.get_typed_class_name(), list.get_typed_script()
|
|
93
|
+
)
|
|
94
|
+
return "Array[%s]" % element
|
|
95
|
+
elif kind == TYPE_DICTIONARY and value is Dictionary:
|
|
96
|
+
var table: Dictionary = value
|
|
97
|
+
if table.is_typed():
|
|
98
|
+
var key: String = _held(
|
|
99
|
+
table.get_typed_key_builtin(),
|
|
100
|
+
table.get_typed_key_class_name(),
|
|
101
|
+
table.get_typed_key_script()
|
|
102
|
+
)
|
|
103
|
+
var held: String = _held(
|
|
104
|
+
table.get_typed_value_builtin(),
|
|
105
|
+
table.get_typed_value_class_name(),
|
|
106
|
+
table.get_typed_value_script()
|
|
107
|
+
)
|
|
108
|
+
return "Dictionary[%s, %s]" % [key, held]
|
|
109
|
+
return type_string(kind)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
## One side of a typed collection: a built-in type, a class, or a script by its class_name.
|
|
113
|
+
static func _held(builtin: int, named: StringName, script: Variant) -> String:
|
|
114
|
+
if builtin == TYPE_NIL:
|
|
115
|
+
return "Variant"
|
|
116
|
+
if builtin != TYPE_OBJECT:
|
|
117
|
+
return type_string(builtin)
|
|
118
|
+
if script is Script:
|
|
119
|
+
var declared: Script = script
|
|
120
|
+
if not declared.get_global_name().is_empty():
|
|
121
|
+
return declared.get_global_name()
|
|
122
|
+
return str(named)
|
|
123
|
+
|
|
124
|
+
|
|
84
125
|
## The setting [param setting_path] overrides for builds with one feature, as
|
|
85
126
|
## `application/run/max_fps.mobile` does `application/run/max_fps`, or "" for a plain setting.
|
|
86
127
|
static func override_base(setting_path: String) -> String:
|
package/build/keeper.js
CHANGED
|
@@ -6946,7 +6946,7 @@ var require_public_api = __commonJS(function(exports) {
|
|
|
6946
6946
|
});
|
|
6947
6947
|
|
|
6948
6948
|
// src/keeper.ts
|
|
6949
|
-
import { closeSync as closeSync2, existsSync as existsSync3, mkdtempSync as mkdtempSync4, openSync as openSync3, readFileSync as
|
|
6949
|
+
import { closeSync as closeSync2, existsSync as existsSync3, mkdtempSync as mkdtempSync4, openSync as openSync3, readFileSync as readFileSync7, rmSync as rmSync6, writeFileSync as writeFileSync3 } from "node:fs";
|
|
6950
6950
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
6951
6951
|
import { join as join10 } from "node:path";
|
|
6952
6952
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
@@ -7435,7 +7435,7 @@ function crashesReported() {
|
|
|
7435
7435
|
}
|
|
7436
7436
|
|
|
7437
7437
|
// src/game-debugger.ts
|
|
7438
|
-
import { readFileSync as
|
|
7438
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
7439
7439
|
import { createServer } from "node:net";
|
|
7440
7440
|
import { join as join6 } from "node:path";
|
|
7441
7441
|
|
|
@@ -7802,6 +7802,36 @@ var KEYWORDS = new Set([
|
|
|
7802
7802
|
"TAU"
|
|
7803
7803
|
]);
|
|
7804
7804
|
|
|
7805
|
+
// src/whole-file.ts
|
|
7806
|
+
import { readFileSync as readFileSync2, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
7807
|
+
var HELD_FILE_MS = 1000;
|
|
7808
|
+
var HELD_FILE_RETRY_MS = 10;
|
|
7809
|
+
var HELD_BY_A_READER = new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
7810
|
+
function writeWhole(path, text) {
|
|
7811
|
+
const partial = `${path}.${process.pid}.partial`;
|
|
7812
|
+
try {
|
|
7813
|
+
writeFileSync2(partial, text, "utf8");
|
|
7814
|
+
} catch {
|
|
7815
|
+
return false;
|
|
7816
|
+
}
|
|
7817
|
+
const until = Date.now() + HELD_FILE_MS;
|
|
7818
|
+
for (;; ) {
|
|
7819
|
+
try {
|
|
7820
|
+
renameSync(partial, path);
|
|
7821
|
+
return true;
|
|
7822
|
+
} catch (error) {
|
|
7823
|
+
const code = error.code ?? "";
|
|
7824
|
+
if (!HELD_BY_A_READER.has(code) || Date.now() >= until) {
|
|
7825
|
+
try {
|
|
7826
|
+
rmSync2(partial, { force: true });
|
|
7827
|
+
} catch {}
|
|
7828
|
+
return false;
|
|
7829
|
+
}
|
|
7830
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, HELD_FILE_RETRY_MS);
|
|
7831
|
+
}
|
|
7832
|
+
}
|
|
7833
|
+
}
|
|
7834
|
+
|
|
7805
7835
|
// src/dictionary.ts
|
|
7806
7836
|
function emptyRecord() {
|
|
7807
7837
|
return Object.create(null);
|
|
@@ -8399,14 +8429,14 @@ var MEMORY_KEPT = 4 * 1024 ** 3;
|
|
|
8399
8429
|
var MEMORY_PER_ENGINE = 1.5 * 1024 ** 3;
|
|
8400
8430
|
|
|
8401
8431
|
// src/process-children.ts
|
|
8402
|
-
import { readFileSync as
|
|
8432
|
+
import { readFileSync as readFileSync3 } from "node:fs";
|
|
8403
8433
|
import { basename as basename3 } from "node:path";
|
|
8404
8434
|
async function linuxStartTimes(pids) {
|
|
8405
8435
|
const began = new Map;
|
|
8406
8436
|
const hertz = await ticksPerSecond();
|
|
8407
8437
|
let booted;
|
|
8408
8438
|
try {
|
|
8409
|
-
booted = Number(/^btime\s+(\d+)$/m.exec(
|
|
8439
|
+
booted = Number(/^btime\s+(\d+)$/m.exec(readFileSync3("/proc/stat", "utf8"))?.[1] ?? Number.NaN) * 1000;
|
|
8410
8440
|
} catch {
|
|
8411
8441
|
return began;
|
|
8412
8442
|
}
|
|
@@ -8416,7 +8446,7 @@ async function linuxStartTimes(pids) {
|
|
|
8416
8446
|
for (const pid of pids) {
|
|
8417
8447
|
let stat;
|
|
8418
8448
|
try {
|
|
8419
|
-
stat =
|
|
8449
|
+
stat = readFileSync3(`/proc/${String(pid)}/stat`, "utf8");
|
|
8420
8450
|
} catch {
|
|
8421
8451
|
continue;
|
|
8422
8452
|
}
|
|
@@ -8516,36 +8546,6 @@ function commandWords(command) {
|
|
|
8516
8546
|
return words;
|
|
8517
8547
|
}
|
|
8518
8548
|
|
|
8519
|
-
// src/whole-file.ts
|
|
8520
|
-
import { renameSync, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8521
|
-
var HELD_FILE_MS = 1000;
|
|
8522
|
-
var HELD_FILE_RETRY_MS = 10;
|
|
8523
|
-
var HELD_BY_A_READER = new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
8524
|
-
function writeWhole(path, text) {
|
|
8525
|
-
const partial = `${path}.${process.pid}.partial`;
|
|
8526
|
-
try {
|
|
8527
|
-
writeFileSync2(partial, text, "utf8");
|
|
8528
|
-
} catch {
|
|
8529
|
-
return false;
|
|
8530
|
-
}
|
|
8531
|
-
const until = Date.now() + HELD_FILE_MS;
|
|
8532
|
-
for (;; ) {
|
|
8533
|
-
try {
|
|
8534
|
-
renameSync(partial, path);
|
|
8535
|
-
return true;
|
|
8536
|
-
} catch (error) {
|
|
8537
|
-
const code = error.code ?? "";
|
|
8538
|
-
if (!HELD_BY_A_READER.has(code) || Date.now() >= until) {
|
|
8539
|
-
try {
|
|
8540
|
-
rmSync2(partial, { force: true });
|
|
8541
|
-
} catch {}
|
|
8542
|
-
return false;
|
|
8543
|
-
}
|
|
8544
|
-
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, HELD_FILE_RETRY_MS);
|
|
8545
|
-
}
|
|
8546
|
-
}
|
|
8547
|
-
}
|
|
8548
|
-
|
|
8549
8549
|
// src/game-log.ts
|
|
8550
8550
|
var COLOUR_CODE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
8551
8551
|
var ADMITTED = {
|
|
@@ -8847,7 +8847,7 @@ function parseIniLikeValue(value) {
|
|
|
8847
8847
|
}
|
|
8848
8848
|
|
|
8849
8849
|
// src/server-version.ts
|
|
8850
|
-
import { readFileSync as
|
|
8850
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
8851
8851
|
|
|
8852
8852
|
// src/update-check.ts
|
|
8853
8853
|
var CACHE_MS = 10 * 60 * 1000;
|
|
@@ -8858,7 +8858,7 @@ var MAX_RETRY_MS = 60 * 60 * 1000;
|
|
|
8858
8858
|
var DEBUG_MODE = process.env["GDHARNESS_DEBUG"] === "1";
|
|
8859
8859
|
var SERVER_VERSION = (() => {
|
|
8860
8860
|
try {
|
|
8861
|
-
const pkg = JSON.parse(
|
|
8861
|
+
const pkg = JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8"));
|
|
8862
8862
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
8863
8863
|
} catch {
|
|
8864
8864
|
return "0.0.0";
|
|
@@ -8884,7 +8884,7 @@ var RUNTIME_AUTOLOAD2 = `${RUNTIME_ADDON}runtime_autoload.gd`;
|
|
|
8884
8884
|
var frameEnd = null;
|
|
8885
8885
|
function frameEndLine() {
|
|
8886
8886
|
if (frameEnd === null) {
|
|
8887
|
-
const lines =
|
|
8887
|
+
const lines = readFileSync5(join6(shippedAddonsDirectory(), "gdharness_runtime", "runtime_autoload.gd"), "utf8").split(`
|
|
8888
8888
|
`);
|
|
8889
8889
|
const declared = lines.findIndex((line) => line.startsWith("func _notification("));
|
|
8890
8890
|
if (declared === -1) {
|
|
@@ -9642,7 +9642,7 @@ function readLaunched(printed) {
|
|
|
9642
9642
|
|
|
9643
9643
|
// src/run-record.ts
|
|
9644
9644
|
import { createHash as createHash2 } from "node:crypto";
|
|
9645
|
-
import { mkdirSync, openSync as openSync2, readdirSync as readdirSync2, readFileSync as
|
|
9645
|
+
import { mkdirSync, openSync as openSync2, readdirSync as readdirSync2, readFileSync as readFileSync6, rmSync as rmSync5, statSync as statSync3 } from "node:fs";
|
|
9646
9646
|
import { basename as basename4, join as join9, resolve as resolve4 } from "node:path";
|
|
9647
9647
|
|
|
9648
9648
|
// src/runtime-client.ts
|
|
@@ -9738,7 +9738,7 @@ function recordRunEnded(projectPath, pid, ending) {
|
|
|
9738
9738
|
function recordAt(path) {
|
|
9739
9739
|
let parsed;
|
|
9740
9740
|
try {
|
|
9741
|
-
parsed = JSON.parse(
|
|
9741
|
+
parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
9742
9742
|
} catch {
|
|
9743
9743
|
return null;
|
|
9744
9744
|
}
|
|
@@ -10186,7 +10186,7 @@ async function startThroughHelper(spec, output, stopFile) {
|
|
|
10186
10186
|
exited.now = true;
|
|
10187
10187
|
});
|
|
10188
10188
|
for (let waited = 0;waited < DESKTOP_START_MS; waited += 50) {
|
|
10189
|
-
const pid = existsSync3(pidFile) ? Number(
|
|
10189
|
+
const pid = existsSync3(pidFile) ? Number(readFileSync7(pidFile, "utf8").trim()) : Number.NaN;
|
|
10190
10190
|
if (Number.isInteger(pid) && pid > 0) {
|
|
10191
10191
|
return { child: started.child, pid };
|
|
10192
10192
|
}
|
|
@@ -10217,7 +10217,7 @@ function profiledBy(path, projectPath) {
|
|
|
10217
10217
|
};
|
|
10218
10218
|
let projectGodot = null;
|
|
10219
10219
|
try {
|
|
10220
|
-
projectGodot =
|
|
10220
|
+
projectGodot = readFileSync7(join10(projectPath, "project.godot"), "utf8");
|
|
10221
10221
|
} catch {}
|
|
10222
10222
|
const observer = new ProfileObserver((totals) => {
|
|
10223
10223
|
const first = latest === null;
|
|
@@ -10456,7 +10456,7 @@ function restartSaid(notes, pid) {
|
|
|
10456
10456
|
return null;
|
|
10457
10457
|
}
|
|
10458
10458
|
try {
|
|
10459
|
-
const said = JSON.parse(
|
|
10459
|
+
const said = JSON.parse(readFileSync7(join10(notes, `exit-${String(pid)}.json`), "utf8"));
|
|
10460
10460
|
return typeof said === "object" && said !== null && "restart" in said && typeof said.restart === "boolean" ? said.restart : null;
|
|
10461
10461
|
} catch {
|
|
10462
10462
|
return null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.45",
|
|
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",
|