gdharness 1.3.58 → 1.3.60
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 +351 -206
- package/build/godot/addons/gdharness_editor/property_values.gd +8 -1
- package/build/godot/addons/gdharness_editor/setting_reads.gd +58 -0
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +5 -2
- package/build/godot/operations/project_config.gd +21 -1
- package/build/godot/operations/property_values.gd +8 -1
- package/build/godot/operations/setting_reads.gd +58 -0
- package/build/godot/tests/listed.gd +19 -1
- package/build/keeper.js +5 -4
- package/package.json +1 -1
|
@@ -40,6 +40,7 @@ const HEADINGS: int = PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_
|
|
|
40
40
|
## Control's layout_mode values, which the engine does not expose as constants.
|
|
41
41
|
const LAYOUT_MODE_POSITION: int = 0
|
|
42
42
|
const LAYOUT_MODE_ANCHORS: int = 1
|
|
43
|
+
const LAYOUT_MODE_UNCONTROLLED: int = 3
|
|
43
44
|
## What a container sets on each child it lays out, beside the anchors and offsets.
|
|
44
45
|
const PLACED_BY_A_CONTAINER: Array[String] = ["position", "size", "global_position"]
|
|
45
46
|
|
|
@@ -366,8 +367,14 @@ func _laid_out(control: Control, name: String) -> String:
|
|
|
366
367
|
)
|
|
367
368
|
% [control.name, parent.name, parent.get_class(), base]
|
|
368
369
|
)
|
|
369
|
-
|
|
370
|
+
var mode: int = GdhRead.as_int(control.get("layout_mode"))
|
|
371
|
+
if anchoring and mode == LAYOUT_MODE_POSITION:
|
|
370
372
|
control.set("layout_mode", LAYOUT_MODE_ANCHORS)
|
|
373
|
+
# A scene's root reads as Uncontrolled but keeps the Position mode it was made with until it
|
|
374
|
+
# enters a tree, and the engine ignores a preset in that stored mode, so full rect read back as
|
|
375
|
+
# top left on every root a headless edit wrote.
|
|
376
|
+
elif anchoring and mode == LAYOUT_MODE_UNCONTROLLED:
|
|
377
|
+
control.set("layout_mode", LAYOUT_MODE_UNCONTROLLED)
|
|
371
378
|
return ""
|
|
372
379
|
|
|
373
380
|
|
|
@@ -16,6 +16,13 @@ static func one(setting_path: String, values: GdhSerialisation) -> Dictionary:
|
|
|
16
16
|
|
|
17
17
|
if result["exists"]:
|
|
18
18
|
var value: Variant = ProjectSettings.get_setting(setting_path)
|
|
19
|
+
var laid: Dictionary = laid_over(setting_path, override_files())
|
|
20
|
+
if not laid.is_empty():
|
|
21
|
+
if not Engine.is_editor_hint():
|
|
22
|
+
value = laid["project"]
|
|
23
|
+
result["overridden_in"] = {
|
|
24
|
+
"file": laid["file"], "value": values.serialize_value(laid["value"]),
|
|
25
|
+
}
|
|
19
26
|
result["value"] = values.serialize_value(value)
|
|
20
27
|
# A setting the engine registers exists whether or not the project sets it, so exists did
|
|
21
28
|
# not say whether project.godot has a line for it. At its default it has none: the engine
|
|
@@ -54,17 +61,25 @@ static func one(setting_path: String, values: GdhSerialisation) -> Dictionary:
|
|
|
54
61
|
## wrong: 2 is as true as true is.
|
|
55
62
|
static func under(prefix: String, values: GdhSerialisation) -> Dictionary:
|
|
56
63
|
var found: Array[Dictionary] = []
|
|
64
|
+
var overrides: Array[Dictionary] = override_files()
|
|
57
65
|
for property: Dictionary in ProjectSettings.get_property_list():
|
|
58
66
|
var name: String = str(property.get("name", ""))
|
|
59
67
|
if not name.begins_with(prefix):
|
|
60
68
|
continue
|
|
61
69
|
var kind: int = GdhRead.as_int(property.get("type", TYPE_NIL), TYPE_NIL)
|
|
62
70
|
var value: Variant = ProjectSettings.get_setting(name)
|
|
71
|
+
var laid: Dictionary = laid_over(name, overrides)
|
|
72
|
+
if not laid.is_empty() and not Engine.is_editor_hint():
|
|
73
|
+
value = laid["project"]
|
|
63
74
|
var described: Dictionary = {
|
|
64
75
|
"setting": name,
|
|
65
76
|
"type": type_named(kind, value),
|
|
66
77
|
"value": values.serialize_value(value),
|
|
67
78
|
}
|
|
79
|
+
if not laid.is_empty():
|
|
80
|
+
described["overridden_in"] = {
|
|
81
|
+
"file": laid["file"], "value": values.serialize_value(laid["value"]),
|
|
82
|
+
}
|
|
68
83
|
# As a single get says it, so a family read can be told apart into what the project sets and
|
|
69
84
|
# the engine's defaults: "input/" is ninety actions the engine ships and the project's few.
|
|
70
85
|
var default_value: Variant = ProjectSettings.property_get_revert(name)
|
|
@@ -81,6 +96,49 @@ static func under(prefix: String, values: GdhSerialisation) -> Dictionary:
|
|
|
81
96
|
return { "prefix": prefix, "count": found.size(), "settings": found }
|
|
82
97
|
|
|
83
98
|
|
|
99
|
+
## The files a run lays over project.godot, loaded, in the order the engine lays them, each as
|
|
100
|
+
## {"file", "config"}: res://override.cfg, then the file
|
|
101
|
+
## application/config/project_settings_override
|
|
102
|
+
## names. The editor lays neither, so a game keeping a player's window size in user:// had that size
|
|
103
|
+
## answered headless as the project's own setting, while the editor answered the project's.
|
|
104
|
+
static func override_files() -> Array[Dictionary]:
|
|
105
|
+
var files: Array[Dictionary] = []
|
|
106
|
+
var named: String = str(
|
|
107
|
+
ProjectSettings.get_setting("application/config/project_settings_override", "")
|
|
108
|
+
)
|
|
109
|
+
for file: String in ["res://override.cfg", named]:
|
|
110
|
+
var config: ConfigFile = ConfigFile.new()
|
|
111
|
+
if not file.is_empty() and config.load(file) == OK:
|
|
112
|
+
files.append({ "file": file, "config": config })
|
|
113
|
+
return files
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
## What the last of [param overrides], from [method override_files], to set [param setting_path]
|
|
117
|
+
## sets it to, as {"file", "value", "project"}, where project is what project.godot holds, or the
|
|
118
|
+
## engine's default where it has no line: the value the editor holds, unsaved changes aside. Empty
|
|
119
|
+
## when none sets it.
|
|
120
|
+
static func laid_over(setting_path: String, overrides: Array[Dictionary]) -> Dictionary:
|
|
121
|
+
var at: int = setting_path.find("/")
|
|
122
|
+
if at <= 0:
|
|
123
|
+
return {}
|
|
124
|
+
var section: String = setting_path.left(at)
|
|
125
|
+
var key: String = setting_path.substr(at + 1)
|
|
126
|
+
for index: int in range(overrides.size() - 1, -1, -1):
|
|
127
|
+
var config: ConfigFile = overrides[index]["config"]
|
|
128
|
+
if not config.has_section_key(section, key):
|
|
129
|
+
continue
|
|
130
|
+
var project: ConfigFile = ConfigFile.new()
|
|
131
|
+
var held: Variant = ProjectSettings.property_get_revert(setting_path)
|
|
132
|
+
if project.load("res://project.godot") == OK and project.has_section_key(section, key):
|
|
133
|
+
held = project.get_value(section, key)
|
|
134
|
+
return {
|
|
135
|
+
"file": overrides[index]["file"],
|
|
136
|
+
"value": config.get_value(section, key),
|
|
137
|
+
"project": held,
|
|
138
|
+
}
|
|
139
|
+
return {}
|
|
140
|
+
|
|
141
|
+
|
|
84
142
|
## The type [param kind] names, with what [param value] holds when it is a typed array or
|
|
85
143
|
## dictionary, as project.godot writes it: a `Dictionary[String, bool]` was answered as Dictionary,
|
|
86
144
|
## which hides what its keys and values must be.
|
|
@@ -804,12 +804,15 @@ static func with_methods_offered(said: String, object: Object, method: String) -
|
|
|
804
804
|
# to a refusal.
|
|
805
805
|
var flags: int = entry.get("flags", 0)
|
|
806
806
|
var callable: bool = not object is Script or flags & METHOD_FLAG_STATIC
|
|
807
|
-
|
|
807
|
+
# A property's setter and getter are named by the engine, @asked_setter, and offered
|
|
808
|
+
# they
|
|
809
|
+
# crowded out the script's own methods with names nobody means.
|
|
810
|
+
if callable and not name.begins_with("@") and not name.is_empty() and not own.has(name):
|
|
808
811
|
own.append(name)
|
|
809
812
|
var every: Array[String] = own.duplicate()
|
|
810
813
|
for entry: Dictionary in GdhValues.methods_of(object):
|
|
811
814
|
var name: String = str(entry.get("name", ""))
|
|
812
|
-
if not every.has(name):
|
|
815
|
+
if not name.begins_with("@") and not every.has(name):
|
|
813
816
|
every.append(name)
|
|
814
817
|
var close: PackedStringArray = GdhRead.nearest(method, PackedStringArray(every))
|
|
815
818
|
if not close.is_empty():
|
|
@@ -89,8 +89,13 @@ func set_project_setting(params: Dictionary) -> Dictionary:
|
|
|
89
89
|
|
|
90
90
|
var old_value: Variant = null
|
|
91
91
|
var had_value: bool = ProjectSettings.has_setting(setting_path)
|
|
92
|
+
var laid: Dictionary = GdhSettingReads.laid_over(
|
|
93
|
+
setting_path, GdhSettingReads.override_files()
|
|
94
|
+
)
|
|
92
95
|
if had_value:
|
|
93
|
-
old_value =
|
|
96
|
+
old_value = (
|
|
97
|
+
ProjectSettings.get_setting(setting_path) if laid.is_empty() else laid["project"]
|
|
98
|
+
)
|
|
94
99
|
# A setting the engine registers is there with its default whether or not project.godot sets it,
|
|
95
100
|
# so has_setting alone called the description of a project that had never set one an old value.
|
|
96
101
|
# Whether it is new is the file's to say: one at its default has a line only where the project
|
|
@@ -131,6 +136,16 @@ func set_project_setting(params: Dictionary) -> Dictionary:
|
|
|
131
136
|
"was_default": was_default,
|
|
132
137
|
"saved": true,
|
|
133
138
|
}
|
|
139
|
+
if not laid.is_empty():
|
|
140
|
+
var read: Variant = _values.serialize_value(laid["value"])
|
|
141
|
+
answer["overridden_in"] = { "file": laid["file"], "value": read }
|
|
142
|
+
answer["overriddenNote"] = (
|
|
143
|
+
(
|
|
144
|
+
"A run on this machine still reads %s for it from %s, which a run lays over "
|
|
145
|
+
+ "project.godot and the editor does not."
|
|
146
|
+
)
|
|
147
|
+
% [JSON.stringify(read), laid["file"]]
|
|
148
|
+
)
|
|
134
149
|
# The same value in the same type is no change, and the file is left alone: a save rewrites it
|
|
135
150
|
# whole. The type is compared as well, so an int setting the file holds as 50.0 is still put
|
|
136
151
|
# right.
|
|
@@ -254,6 +269,11 @@ func unset_project_setting(params: Dictionary) -> Dictionary:
|
|
|
254
269
|
var default_value: Variant = ProjectSettings.property_get_revert(setting_path)
|
|
255
270
|
var held: bool = ProjectSettings.has_setting(setting_path)
|
|
256
271
|
var value: Variant = ProjectSettings.get_setting(setting_path) if held else null
|
|
272
|
+
var laid: Dictionary = GdhSettingReads.laid_over(
|
|
273
|
+
setting_path, GdhSettingReads.override_files()
|
|
274
|
+
)
|
|
275
|
+
if held and not laid.is_empty():
|
|
276
|
+
value = laid["project"]
|
|
257
277
|
var answer: Dictionary = {
|
|
258
278
|
"setting_path": setting_path,
|
|
259
279
|
"default_value": _values.serialize_value(default_value) if default_value != null else null,
|
|
@@ -40,6 +40,7 @@ const HEADINGS: int = PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_
|
|
|
40
40
|
## Control's layout_mode values, which the engine does not expose as constants.
|
|
41
41
|
const LAYOUT_MODE_POSITION: int = 0
|
|
42
42
|
const LAYOUT_MODE_ANCHORS: int = 1
|
|
43
|
+
const LAYOUT_MODE_UNCONTROLLED: int = 3
|
|
43
44
|
## What a container sets on each child it lays out, beside the anchors and offsets.
|
|
44
45
|
const PLACED_BY_A_CONTAINER: Array[String] = ["position", "size", "global_position"]
|
|
45
46
|
|
|
@@ -366,8 +367,14 @@ func _laid_out(control: Control, name: String) -> String:
|
|
|
366
367
|
)
|
|
367
368
|
% [control.name, parent.name, parent.get_class(), base]
|
|
368
369
|
)
|
|
369
|
-
|
|
370
|
+
var mode: int = GdhRead.as_int(control.get("layout_mode"))
|
|
371
|
+
if anchoring and mode == LAYOUT_MODE_POSITION:
|
|
370
372
|
control.set("layout_mode", LAYOUT_MODE_ANCHORS)
|
|
373
|
+
# A scene's root reads as Uncontrolled but keeps the Position mode it was made with until it
|
|
374
|
+
# enters a tree, and the engine ignores a preset in that stored mode, so full rect read back as
|
|
375
|
+
# top left on every root a headless edit wrote.
|
|
376
|
+
elif anchoring and mode == LAYOUT_MODE_UNCONTROLLED:
|
|
377
|
+
control.set("layout_mode", LAYOUT_MODE_UNCONTROLLED)
|
|
371
378
|
return ""
|
|
372
379
|
|
|
373
380
|
|
|
@@ -16,6 +16,13 @@ static func one(setting_path: String, values: GdhSerialisation) -> Dictionary:
|
|
|
16
16
|
|
|
17
17
|
if result["exists"]:
|
|
18
18
|
var value: Variant = ProjectSettings.get_setting(setting_path)
|
|
19
|
+
var laid: Dictionary = laid_over(setting_path, override_files())
|
|
20
|
+
if not laid.is_empty():
|
|
21
|
+
if not Engine.is_editor_hint():
|
|
22
|
+
value = laid["project"]
|
|
23
|
+
result["overridden_in"] = {
|
|
24
|
+
"file": laid["file"], "value": values.serialize_value(laid["value"]),
|
|
25
|
+
}
|
|
19
26
|
result["value"] = values.serialize_value(value)
|
|
20
27
|
# A setting the engine registers exists whether or not the project sets it, so exists did
|
|
21
28
|
# not say whether project.godot has a line for it. At its default it has none: the engine
|
|
@@ -54,17 +61,25 @@ static func one(setting_path: String, values: GdhSerialisation) -> Dictionary:
|
|
|
54
61
|
## wrong: 2 is as true as true is.
|
|
55
62
|
static func under(prefix: String, values: GdhSerialisation) -> Dictionary:
|
|
56
63
|
var found: Array[Dictionary] = []
|
|
64
|
+
var overrides: Array[Dictionary] = override_files()
|
|
57
65
|
for property: Dictionary in ProjectSettings.get_property_list():
|
|
58
66
|
var name: String = str(property.get("name", ""))
|
|
59
67
|
if not name.begins_with(prefix):
|
|
60
68
|
continue
|
|
61
69
|
var kind: int = GdhRead.as_int(property.get("type", TYPE_NIL), TYPE_NIL)
|
|
62
70
|
var value: Variant = ProjectSettings.get_setting(name)
|
|
71
|
+
var laid: Dictionary = laid_over(name, overrides)
|
|
72
|
+
if not laid.is_empty() and not Engine.is_editor_hint():
|
|
73
|
+
value = laid["project"]
|
|
63
74
|
var described: Dictionary = {
|
|
64
75
|
"setting": name,
|
|
65
76
|
"type": type_named(kind, value),
|
|
66
77
|
"value": values.serialize_value(value),
|
|
67
78
|
}
|
|
79
|
+
if not laid.is_empty():
|
|
80
|
+
described["overridden_in"] = {
|
|
81
|
+
"file": laid["file"], "value": values.serialize_value(laid["value"]),
|
|
82
|
+
}
|
|
68
83
|
# As a single get says it, so a family read can be told apart into what the project sets and
|
|
69
84
|
# the engine's defaults: "input/" is ninety actions the engine ships and the project's few.
|
|
70
85
|
var default_value: Variant = ProjectSettings.property_get_revert(name)
|
|
@@ -81,6 +96,49 @@ static func under(prefix: String, values: GdhSerialisation) -> Dictionary:
|
|
|
81
96
|
return { "prefix": prefix, "count": found.size(), "settings": found }
|
|
82
97
|
|
|
83
98
|
|
|
99
|
+
## The files a run lays over project.godot, loaded, in the order the engine lays them, each as
|
|
100
|
+
## {"file", "config"}: res://override.cfg, then the file
|
|
101
|
+
## application/config/project_settings_override
|
|
102
|
+
## names. The editor lays neither, so a game keeping a player's window size in user:// had that size
|
|
103
|
+
## answered headless as the project's own setting, while the editor answered the project's.
|
|
104
|
+
static func override_files() -> Array[Dictionary]:
|
|
105
|
+
var files: Array[Dictionary] = []
|
|
106
|
+
var named: String = str(
|
|
107
|
+
ProjectSettings.get_setting("application/config/project_settings_override", "")
|
|
108
|
+
)
|
|
109
|
+
for file: String in ["res://override.cfg", named]:
|
|
110
|
+
var config: ConfigFile = ConfigFile.new()
|
|
111
|
+
if not file.is_empty() and config.load(file) == OK:
|
|
112
|
+
files.append({ "file": file, "config": config })
|
|
113
|
+
return files
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
## What the last of [param overrides], from [method override_files], to set [param setting_path]
|
|
117
|
+
## sets it to, as {"file", "value", "project"}, where project is what project.godot holds, or the
|
|
118
|
+
## engine's default where it has no line: the value the editor holds, unsaved changes aside. Empty
|
|
119
|
+
## when none sets it.
|
|
120
|
+
static func laid_over(setting_path: String, overrides: Array[Dictionary]) -> Dictionary:
|
|
121
|
+
var at: int = setting_path.find("/")
|
|
122
|
+
if at <= 0:
|
|
123
|
+
return {}
|
|
124
|
+
var section: String = setting_path.left(at)
|
|
125
|
+
var key: String = setting_path.substr(at + 1)
|
|
126
|
+
for index: int in range(overrides.size() - 1, -1, -1):
|
|
127
|
+
var config: ConfigFile = overrides[index]["config"]
|
|
128
|
+
if not config.has_section_key(section, key):
|
|
129
|
+
continue
|
|
130
|
+
var project: ConfigFile = ConfigFile.new()
|
|
131
|
+
var held: Variant = ProjectSettings.property_get_revert(setting_path)
|
|
132
|
+
if project.load("res://project.godot") == OK and project.has_section_key(section, key):
|
|
133
|
+
held = project.get_value(section, key)
|
|
134
|
+
return {
|
|
135
|
+
"file": overrides[index]["file"],
|
|
136
|
+
"value": config.get_value(section, key),
|
|
137
|
+
"project": held,
|
|
138
|
+
}
|
|
139
|
+
return {}
|
|
140
|
+
|
|
141
|
+
|
|
84
142
|
## The type [param kind] names, with what [param value] holds when it is a typed array or
|
|
85
143
|
## dictionary, as project.godot writes it: a `Dictionary[String, bool]` was answered as Dictionary,
|
|
86
144
|
## which hides what its keys and values must be.
|
|
@@ -549,7 +549,25 @@ class GdhRunner:
|
|
|
549
549
|
return
|
|
550
550
|
if Engine.is_embedded_in_editor():
|
|
551
551
|
return
|
|
552
|
-
|
|
552
|
+
var mode: DisplayServer.WindowMode = DisplayServer.window_get_mode()
|
|
553
|
+
if mode == DisplayServer.WINDOW_MODE_MAXIMIZED and not DisplayServer.window_can_draw():
|
|
554
|
+
# On macOS Godot reads a window whose frame fills the usable screen as maximised before
|
|
555
|
+
# asking whether it is minimised, and changing the mode from that reading unzooms it, so
|
|
556
|
+
# a minimised window that size, as a small screen makes the default one, stayed
|
|
557
|
+
# minimised. A point off it reads as what it is, and goes back once restored. Godot
|
|
558
|
+
# divides pixels by the scale into whole points, so a pixel moves nothing on a scaled
|
|
559
|
+
# screen.
|
|
560
|
+
var at: Vector2i = DisplayServer.window_get_position()
|
|
561
|
+
DisplayServer.window_set_position(
|
|
562
|
+
at + Vector2i(ceili(DisplayServer.screen_get_max_scale()), 0)
|
|
563
|
+
)
|
|
564
|
+
mode = DisplayServer.window_get_mode()
|
|
565
|
+
if mode == DisplayServer.WINDOW_MODE_MINIMIZED:
|
|
566
|
+
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
|
567
|
+
minimise_pending = false
|
|
568
|
+
DisplayServer.window_set_position(at)
|
|
569
|
+
return
|
|
570
|
+
if mode == DisplayServer.WINDOW_MODE_MINIMIZED:
|
|
553
571
|
DisplayServer.window_set_mode(DisplayServer.WINDOW_MODE_WINDOWED)
|
|
554
572
|
minimise_pending = false
|
|
555
573
|
|
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 closeSync3, existsSync as
|
|
6949
|
+
import { closeSync as closeSync3, existsSync as existsSync4, mkdtempSync as mkdtempSync4, openSync as openSync4, readFileSync as readFileSync9, rmSync as rmSync7, writeFileSync as writeFileSync4 } from "node:fs";
|
|
6950
6950
|
import { tmpdir as tmpdir8 } from "node:os";
|
|
6951
6951
|
import { join as join10 } from "node:path";
|
|
6952
6952
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
@@ -7435,6 +7435,7 @@ function throughHelper(how, command, args, files, assembly = helperAssembly()) {
|
|
|
7435
7435
|
// src/engine-slots.ts
|
|
7436
7436
|
import {
|
|
7437
7437
|
closeSync as closeSync2,
|
|
7438
|
+
existsSync as existsSync2,
|
|
7438
7439
|
mkdirSync,
|
|
7439
7440
|
openSync as openSync2,
|
|
7440
7441
|
readdirSync,
|
|
@@ -9338,7 +9339,7 @@ var IMPORTABLE = new Set(IMPORTABLE_EXTENSIONS);
|
|
|
9338
9339
|
var hashes = new Map;
|
|
9339
9340
|
|
|
9340
9341
|
// src/scratch.ts
|
|
9341
|
-
import { existsSync as
|
|
9342
|
+
import { existsSync as existsSync3, mkdtempSync as mkdtempSync2, readdirSync as readdirSync2, rmSync as rmSync4 } from "node:fs";
|
|
9342
9343
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
9343
9344
|
import { join as join4 } from "node:path";
|
|
9344
9345
|
import process6 from "node:process";
|
|
@@ -9383,7 +9384,7 @@ function tryAgain(path, attempt) {
|
|
|
9383
9384
|
lastTryOnExit();
|
|
9384
9385
|
}
|
|
9385
9386
|
const after = AGAIN_AFTER_MS[attempt];
|
|
9386
|
-
if (after === undefined || !
|
|
9387
|
+
if (after === undefined || !existsSync3(path)) {
|
|
9387
9388
|
waiting.delete(path);
|
|
9388
9389
|
if (after === undefined) {
|
|
9389
9390
|
atExit.add(path);
|
|
@@ -10888,7 +10889,7 @@ async function startThroughHelper(spec, output, stopFile) {
|
|
|
10888
10889
|
exited.now = true;
|
|
10889
10890
|
});
|
|
10890
10891
|
for (let waited = 0;waited < DESKTOP_START_MS; waited += 50) {
|
|
10891
|
-
const pid =
|
|
10892
|
+
const pid = existsSync4(pidFile) ? Number(readFileSync9(pidFile, "utf8").trim()) : Number.NaN;
|
|
10892
10893
|
if (Number.isInteger(pid) && pid > 0) {
|
|
10893
10894
|
return { child: started.child, pid };
|
|
10894
10895
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.60",
|
|
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",
|