gdharness 1.3.42 → 1.3.44
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 +383 -243
- package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
- package/build/godot/addons/gdharness_editor/animation_reads.gd +55 -33
- package/build/godot/addons/gdharness_editor/bridge_client.gd +53 -58
- package/build/godot/addons/gdharness_editor/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_editor/file_reads.gd +83 -42
- package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
- package/build/godot/addons/gdharness_editor/headless_check.gd +68 -44
- package/build/godot/addons/gdharness_editor/plugin.gd +2 -3
- package/build/godot/addons/gdharness_editor/property_paths.gd +22 -12
- package/build/godot/addons/gdharness_editor/property_values.gd +143 -78
- package/build/godot/addons/gdharness_editor/reading.gd +51 -17
- package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +127 -80
- package/build/godot/addons/gdharness_editor/scene_uids.gd +12 -6
- package/build/godot/addons/gdharness_editor/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_editor/setting_reads.gd +7 -6
- package/build/godot/addons/gdharness_editor/tool_executor.gd +3 -6
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +37 -27
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +79 -49
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +65 -42
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +24 -22
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +49 -29
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +26 -21
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +40 -40
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +166 -107
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +68 -41
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +35 -20
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +28 -13
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd +12 -5
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +113 -67
- package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
- package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
- package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
- package/build/godot/addons/gdharness_runtime/reading.gd +51 -17
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +149 -147
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
- package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +34 -17
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
- package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd +15 -15
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
- package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_tests/bench.gd +4 -5
- package/build/godot/addons/gdharness_tests/event.gd +10 -5
- package/build/godot/addons/gdharness_tests/share.gd +38 -18
- package/build/godot/addons/gdharness_tests/suites.gd +3 -3
- package/build/godot/addons/gdharness_tests/wait.gd +3 -2
- package/build/godot/loader/loader.gd +9 -9
- package/build/godot/operations/animation_reads.gd +55 -33
- package/build/godot/operations/audio_buses.gd +48 -19
- package/build/godot/operations/class_cache.gd +16 -11
- package/build/godot/operations/classdb_queries.gd +89 -44
- package/build/godot/operations/declared_values.gd +68 -32
- package/build/godot/operations/dependencies.gd +190 -109
- package/build/godot/operations/editor_tools.gd +10 -10
- package/build/godot/operations/export_presets.gd +100 -54
- package/build/godot/operations/file_reads.gd +83 -42
- package/build/godot/operations/file_walk.gd +12 -5
- package/build/godot/operations/gdscript_analysis.gd +86 -65
- package/build/godot/operations/gdscript_authoring.gd +83 -36
- package/build/godot/operations/godot_operations.gd +17 -11
- package/build/godot/operations/import_pipeline.gd +110 -59
- package/build/godot/operations/input_actions.gd +62 -39
- package/build/godot/operations/patterns.gd +3 -1
- package/build/godot/operations/plugins.gd +36 -16
- package/build/godot/operations/project_config.gd +61 -32
- package/build/godot/operations/project_diagnostics.gd +24 -11
- package/build/godot/operations/project_file.gd +16 -16
- package/build/godot/operations/project_validation.gd +71 -44
- package/build/godot/operations/property_paths.gd +22 -12
- package/build/godot/operations/property_values.gd +143 -78
- package/build/godot/operations/reading.gd +51 -17
- package/build/godot/operations/rename_checks.gd +2 -2
- package/build/godot/operations/resource_files.gd +11 -9
- package/build/godot/operations/same_values.gd +4 -4
- package/build/godot/operations/scene_connections.gd +27 -13
- package/build/godot/operations/script_check.gd +52 -49
- package/build/godot/operations/serialisation.gd +75 -51
- package/build/godot/operations/setting_reads.gd +7 -6
- package/build/godot/operations/value_shapes.gd +5 -6
- package/build/godot/tests/listed.gd +321 -268
- package/build/keeper.js +43 -43
- package/package.json +1 -1
|
@@ -124,7 +124,8 @@ static func json_or_null(text: String) -> Variant:
|
|
|
124
124
|
## [param said], the refusal for [param node_path] found nowhere in a scene whose root is called
|
|
125
125
|
## [param root_name], with how paths are read when the path starts with that name and the root has
|
|
126
126
|
## no child called that. Paths go from the root, so its own name there names a child of it, and a
|
|
127
|
-
## caller who read the root as Main in a tree wrote Main/Body and was told only that nothing is
|
|
127
|
+
## caller who read the root as Main in a tree wrote Main/Body and was told only that nothing is
|
|
128
|
+
## there.
|
|
128
129
|
static func from_the_root(said: String, node_path: String, root_name: String) -> String:
|
|
129
130
|
var first: String = node_path.get_slice("/", 0)
|
|
130
131
|
if root_name.is_empty() or first != root_name:
|
|
@@ -141,7 +142,11 @@ static func from_the_root(said: String, node_path: String, root_name: String) ->
|
|
|
141
142
|
## is empty; the name below it that is missing; and [param children], the names that node has, as
|
|
142
143
|
## after a node was moved or renamed. Names up to [constant NAMED_CHILDREN] of them.
|
|
143
144
|
static func missing_below(
|
|
144
|
-
said: String,
|
|
145
|
+
said: String,
|
|
146
|
+
root_name: String,
|
|
147
|
+
reached: PackedStringArray,
|
|
148
|
+
missing: String,
|
|
149
|
+
children: PackedStringArray
|
|
145
150
|
) -> String:
|
|
146
151
|
var shown: String = ", ".join(children.slice(0, NAMED_CHILDREN))
|
|
147
152
|
if children.size() > NAMED_CHILDREN:
|
|
@@ -167,9 +172,18 @@ static func not_of_kind(
|
|
|
167
172
|
) -> String:
|
|
168
173
|
var told: String = said + ("" if said.ends_with(".") else ".")
|
|
169
174
|
if not found.is_empty():
|
|
170
|
-
told +=
|
|
175
|
+
told += (
|
|
176
|
+
' The node at "%s" is %s, not %s.'
|
|
177
|
+
% [node_path, with_article(found), with_article(kind)]
|
|
178
|
+
)
|
|
171
179
|
if holders.is_empty():
|
|
172
|
-
return
|
|
180
|
+
return (
|
|
181
|
+
told
|
|
182
|
+
+ (
|
|
183
|
+
" The scene holds no %s; scene_node add with nodeType %s puts one in it."
|
|
184
|
+
% [kind, kind]
|
|
185
|
+
)
|
|
186
|
+
)
|
|
173
187
|
var quoted: PackedStringArray = []
|
|
174
188
|
for holder: String in holders.slice(0, NAMED_CHILDREN):
|
|
175
189
|
var _quoted: bool = quoted.append('"%s"' % holder)
|
|
@@ -209,7 +223,10 @@ static func offered(said: String, word: String, names: Array) -> String:
|
|
|
209
223
|
var close: PackedStringArray = nearest(word, every)
|
|
210
224
|
if close.is_empty():
|
|
211
225
|
return "%s; it has %s" % [said, ", ".join(every)]
|
|
212
|
-
return
|
|
226
|
+
return (
|
|
227
|
+
"%s; the nearest it has %s %s"
|
|
228
|
+
% [said, "is" if close.size() == 1 else "are", ", ".join(close)]
|
|
229
|
+
)
|
|
213
230
|
|
|
214
231
|
|
|
215
232
|
## [param said], the refusal for [param property], which [param target] does not declare, with the
|
|
@@ -233,14 +250,20 @@ static func near_property(said: String, target: Object, property: String) -> Str
|
|
|
233
250
|
scripted.append(str(declared.get("name", "")))
|
|
234
251
|
var close: PackedStringArray = nearest(property, PackedStringArray(names))
|
|
235
252
|
if not close.is_empty():
|
|
236
|
-
return
|
|
237
|
-
|
|
238
|
-
|
|
253
|
+
return (
|
|
254
|
+
"%s; the nearest it has %s %s"
|
|
255
|
+
% [said, "is" if close.size() == 1 else "are", ", ".join(close)]
|
|
256
|
+
)
|
|
257
|
+
# With a script that declares nothing readable, as one that does not compile, the name is
|
|
258
|
+
# likelier the script's than the class's, so the class's list would point the wrong way.
|
|
239
259
|
if scripted.is_empty() and target.get_script() != null:
|
|
240
260
|
return said
|
|
241
261
|
if scripted.is_empty():
|
|
242
262
|
return (
|
|
243
|
-
|
|
263
|
+
(
|
|
264
|
+
"%s; editor_classes info with className %s and includeInherited true lists the "
|
|
265
|
+
+ "ones it has"
|
|
266
|
+
)
|
|
244
267
|
% [said, target.get_class()]
|
|
245
268
|
)
|
|
246
269
|
scripted.sort()
|
|
@@ -264,11 +287,13 @@ static func _list_called(target: Object, count: Dictionary, property: String) ->
|
|
|
264
287
|
fields = _fields_at(fresh, first)
|
|
265
288
|
if not fresh is RefCounted:
|
|
266
289
|
fresh.free()
|
|
267
|
-
var each: String =
|
|
290
|
+
var each: String = (
|
|
291
|
+
"each one's fields" if fields.is_empty() else "each one's %s" % ", ".join(fields)
|
|
292
|
+
)
|
|
268
293
|
return (
|
|
269
294
|
(
|
|
270
|
-
"it keeps its %s as %s, how many there are, and %s under %s for the first, %s for the
|
|
271
|
-
+ "give %s before the fields of the entries it makes"
|
|
295
|
+
"it keeps its %s as %s, how many there are, and %s under %s for the first, %s for the "
|
|
296
|
+
+ "second and so on: give %s before the fields of the entries it makes"
|
|
272
297
|
)
|
|
273
298
|
% [parts[0].to_lower(), counted, each, first, parts[1] + "1/", counted]
|
|
274
299
|
)
|
|
@@ -287,16 +312,25 @@ static func _bare(word: String) -> String:
|
|
|
287
312
|
return word.to_lower().replace("_", "").replace(" ", "")
|
|
288
313
|
|
|
289
314
|
|
|
290
|
-
## The signals [param object] has that a caller can connect, its own class's first:
|
|
291
|
-
##
|
|
292
|
-
##
|
|
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.
|
|
293
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)
|
|
294
328
|
var names: Array = []
|
|
295
329
|
for entry: Dictionary in object.get_signal_list():
|
|
296
330
|
var name: String = str(entry["name"])
|
|
297
|
-
if not name.begins_with("_"):
|
|
331
|
+
if not name.begins_with("_") and not name in declared:
|
|
298
332
|
names.push_front(name)
|
|
299
|
-
return names
|
|
333
|
+
return declared + names
|
|
300
334
|
|
|
301
335
|
|
|
302
336
|
## [param word] after the article it takes, "an AnimationTree" or "a Node2D".
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
3
|
## Whether a value read back is the value that was given, as the tools that write properties judge
|
|
5
4
|
## what a write kept and what it changed besides.
|
|
6
5
|
|
|
7
6
|
const GdhRead = preload("reading.gd")
|
|
8
7
|
const GdhSerialisation = preload("serialisation.gd")
|
|
9
|
-
|
|
10
8
|
# The types made of floats, which a read back compares within float precision: a Vector2 property
|
|
11
9
|
# stores 32-bit components, so 0.1 comes back as 0.100000001.
|
|
12
10
|
const FLOAT_SHAPES: Array[int] = [
|
|
@@ -23,7 +21,6 @@ const FLOAT_SHAPES: Array[int] = [
|
|
|
23
21
|
TYPE_AABB,
|
|
24
22
|
TYPE_PROJECTION,
|
|
25
23
|
]
|
|
26
|
-
|
|
27
24
|
const PACKED: Array[int] = [
|
|
28
25
|
TYPE_PACKED_BYTE_ARRAY,
|
|
29
26
|
TYPE_PACKED_INT32_ARRAY,
|
|
@@ -63,7 +60,10 @@ static func same(held: Variant, given: Variant) -> bool:
|
|
|
63
60
|
return held_type == given_type and held == given
|
|
64
61
|
var held_number: float = GdhRead.as_float(held)
|
|
65
62
|
var given_number: float = GdhRead.as_float(given)
|
|
66
|
-
return (
|
|
63
|
+
return (
|
|
64
|
+
(is_nan(held_number) and is_nan(given_number))
|
|
65
|
+
or is_equal_approx(held_number, given_number)
|
|
66
|
+
)
|
|
67
67
|
if GdhSerialisation.TEXTS.has(held_type) and GdhSerialisation.TEXTS.has(given_type):
|
|
68
68
|
return str(held) == str(given)
|
|
69
69
|
if _listed(held_type) and _listed(given_type):
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
3
|
## One scene file opened for a tool, read past the resource cache and written back only once what
|
|
5
4
|
## would be saved has been checked.
|
|
6
5
|
##
|
|
@@ -26,7 +25,6 @@ extends RefCounted
|
|
|
26
25
|
## anything is saved, and a write is refused when that does not hold. See [GdhHeadlessCheck].
|
|
27
26
|
|
|
28
27
|
const GdhHeadlessCheck = preload("headless_check.gd")
|
|
29
|
-
|
|
30
28
|
## How many times what scripts changed is put back before a write is refused for it.
|
|
31
29
|
const PUTTING_BACK_PASSES: int = 4
|
|
32
30
|
const GdhRead = preload("reading.gd")
|
|
@@ -87,19 +85,19 @@ var _engine_kept: Dictionary = {}
|
|
|
87
85
|
func open(scene_path: String, for_writing: bool) -> Dictionary:
|
|
88
86
|
path = scene_path
|
|
89
87
|
if not FileAccess.file_exists(path):
|
|
90
|
-
return {"ok": false, "error": "Scene not found: " + path}
|
|
88
|
+
return { "ok": false, "error": "Scene not found: " + path }
|
|
91
89
|
if for_writing:
|
|
92
90
|
var held_back: String = unsaved_in_editor(path)
|
|
93
91
|
if not held_back.is_empty():
|
|
94
|
-
return {"ok": false, "error": held_back}
|
|
92
|
+
return { "ok": false, "error": held_back }
|
|
95
93
|
var packed: PackedScene = (
|
|
96
94
|
ResourceLoader.load(path, "PackedScene", ResourceLoader.CACHE_MODE_IGNORE) as PackedScene
|
|
97
95
|
)
|
|
98
96
|
if packed == null:
|
|
99
|
-
return {"ok": false, "error": "Failed to load %s as a scene" % path}
|
|
97
|
+
return { "ok": false, "error": "Failed to load %s as a scene" % path }
|
|
100
98
|
root = packed.instantiate(PackedScene.GEN_EDIT_STATE_MAIN)
|
|
101
99
|
if root == null:
|
|
102
|
-
return {"ok": false, "error": "Failed to instantiate " + path}
|
|
100
|
+
return { "ok": false, "error": "Failed to instantiate " + path }
|
|
103
101
|
_state = packed.get_state()
|
|
104
102
|
_base_paths = makers(_state)
|
|
105
103
|
# On a read as well, which would otherwise answer what the scripts set as what the file holds.
|
|
@@ -144,15 +142,16 @@ func asked(node: Node, names: Array) -> void:
|
|
|
144
142
|
GdhHeadlessCheck.spare(_reached, GdhHeadlessCheck.reached(node, property))
|
|
145
143
|
|
|
146
144
|
|
|
147
|
-
## Takes [param resource], built into the scene, as one the tool changed, which a write saves as it
|
|
145
|
+
## Takes [param resource], built into the scene, as one the tool changed, which a write saves as it
|
|
146
|
+
## is.
|
|
148
147
|
func reached_into(resource: Resource) -> void:
|
|
149
148
|
var id: String = resource.get_scene_unique_id()
|
|
150
149
|
if not id.is_empty():
|
|
151
150
|
_reached[id] = true
|
|
152
151
|
|
|
153
152
|
|
|
154
|
-
## Takes each copy in [param copies], keyed by the node it was made from, as holding what that
|
|
155
|
-
## file entry holds.
|
|
153
|
+
## Takes each copy in [param copies], keyed by the node it was made from, as holding what that
|
|
154
|
+
## node's file entry holds.
|
|
156
155
|
func copied(copies: Dictionary) -> void:
|
|
157
156
|
for source: Variant in copies:
|
|
158
157
|
var original: Node = source
|
|
@@ -166,8 +165,9 @@ static func unsaved_in_editor(scene_path: String) -> String:
|
|
|
166
165
|
return ""
|
|
167
166
|
return (
|
|
168
167
|
(
|
|
169
|
-
"%s has unsaved changes in the editor, and writing the file would discard them when
|
|
170
|
-
+ "editor reloads it. Save it first (scene_create op=save) or close it without
|
|
168
|
+
"%s has unsaved changes in the editor, and writing the file would discard them when "
|
|
169
|
+
+ "the editor reloads it. Save it first (scene_create op=save) or close it without "
|
|
170
|
+
+ "saving."
|
|
171
171
|
)
|
|
172
172
|
% scene_path
|
|
173
173
|
)
|
|
@@ -176,7 +176,7 @@ static func unsaved_in_editor(scene_path: String) -> String:
|
|
|
176
176
|
## Frees [member root] and answers [param error] as a refusal.
|
|
177
177
|
func refuse(error: String) -> Dictionary:
|
|
178
178
|
close()
|
|
179
|
-
return {"ok": false, "error": error}
|
|
179
|
+
return { "ok": false, "error": error }
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
## [param said], the refusal for [param node_path] found nowhere in this scene, with how a path is
|
|
@@ -213,10 +213,10 @@ func close() -> void:
|
|
|
213
213
|
|
|
214
214
|
|
|
215
215
|
## What the file holds under the instance at [param at] that goes once its children are not
|
|
216
|
-
## editable, as {values, nodes}: the paths of the instance's nodes it sets values on, which stay
|
|
217
|
-
## the instance's own values, and of the nodes the scene added below one of them, which are
|
|
218
|
-
## since the packer skips everything below a node of an instance that is not editable. A node
|
|
219
|
-
## straight under [param at] is the scene's own and stays.
|
|
216
|
+
## editable, as {values, nodes}: the paths of the instance's nodes it sets values on, which stay
|
|
217
|
+
## with the instance's own values, and of the nodes the scene added below one of them, which are
|
|
218
|
+
## gone, since the packer skips everything below a node of an instance that is not editable. A node
|
|
219
|
+
## added straight under [param at] is the scene's own and stays.
|
|
220
220
|
func held_under(at: String) -> Dictionary:
|
|
221
221
|
var values: Array[String] = []
|
|
222
222
|
var nodes: Array[String] = []
|
|
@@ -230,7 +230,7 @@ func held_under(at: String) -> Dictionary:
|
|
|
230
230
|
values.append(entry)
|
|
231
231
|
elif not values_only and parent.begins_with(at + "/"):
|
|
232
232
|
nodes.append(entry)
|
|
233
|
-
return {"values": values, "nodes": nodes}
|
|
233
|
+
return { "values": values, "nodes": nodes }
|
|
234
234
|
|
|
235
235
|
|
|
236
236
|
## The path of [param node] as a tool names it: "." for the root.
|
|
@@ -241,7 +241,7 @@ func path_of(node: Node) -> String:
|
|
|
241
241
|
## The class of [param node] as a read answers it: nodeType the engine class, and scriptClass the
|
|
242
242
|
## class its script declares, when it declares one.
|
|
243
243
|
static func class_of(node: Node) -> Dictionary:
|
|
244
|
-
var named: Dictionary = {"nodeType": node.get_class()}
|
|
244
|
+
var named: Dictionary = { "nodeType": node.get_class() }
|
|
245
245
|
var script: Script = node.get_script()
|
|
246
246
|
if script != null and not str(script.get_global_name()).is_empty():
|
|
247
247
|
named["scriptClass"] = str(script.get_global_name())
|
|
@@ -271,8 +271,8 @@ static func node_at(top: Node, at: String) -> Node:
|
|
|
271
271
|
## Why the property or signal [param missing] could not be found on [param node], adding the one
|
|
272
272
|
## cause the name alone does not show: a script that does not compile leaves the editor holding a
|
|
273
273
|
## placeholder with no properties or signals at all, and the refusal named the property instead.
|
|
274
|
-
## Such a script has no engine class beneath it. Compiling a copy of the source to find out failed
|
|
275
|
-
## every script with a class_name, since the copy hides the global class the original declares.
|
|
274
|
+
## Such a script has no engine class beneath it. Compiling a copy of the source to find out failed
|
|
275
|
+
## on every script with a class_name, since the copy hides the global class the original declares.
|
|
276
276
|
static func missing_on(node: Node, missing: String) -> String:
|
|
277
277
|
var script: Script = node.get_script()
|
|
278
278
|
if script == null or not str(script.get_instance_base_type()).is_empty():
|
|
@@ -290,15 +290,18 @@ func foreign(node: Node) -> String:
|
|
|
290
290
|
return ""
|
|
291
291
|
if node.owner == null:
|
|
292
292
|
return (
|
|
293
|
-
|
|
293
|
+
(
|
|
294
|
+
"%s is made by a script as the scene is instanced, and no scene file saves "
|
|
295
|
+
+ "anything about it."
|
|
296
|
+
)
|
|
294
297
|
% path_of(node)
|
|
295
298
|
)
|
|
296
299
|
if root.is_editable_instance(node.owner):
|
|
297
300
|
return ""
|
|
298
301
|
var said: String = (
|
|
299
302
|
(
|
|
300
|
-
"%s belongs to the scene instanced at %s (%s), and this scene saves nothing about it.
|
|
301
|
-
+ "that scene instead, which changes every instance of it"
|
|
303
|
+
"%s belongs to the scene instanced at %s (%s), and this scene saves nothing about it. "
|
|
304
|
+
+ "Edit that scene instead, which changes every instance of it"
|
|
302
305
|
)
|
|
303
306
|
% [path_of(node), path_of(node.owner), node.owner.scene_file_path]
|
|
304
307
|
)
|
|
@@ -349,19 +352,22 @@ func fixed(node: Node, deleting: bool = false) -> String:
|
|
|
349
352
|
"%s is inside the scene instanced at %s (%s), which creates it again on load."
|
|
350
353
|
% [path_of(node), path_of(node.owner), instanced]
|
|
351
354
|
)
|
|
352
|
-
return
|
|
355
|
+
return (
|
|
356
|
+
inside + (_deleted_elsewhere(node, maker) if deleting else " Edit %s instead." % maker)
|
|
357
|
+
)
|
|
353
358
|
var at: String = path_of(node)
|
|
354
359
|
if _base_paths.has(at):
|
|
355
360
|
var maker: String = str(_base_paths[at])
|
|
356
361
|
var made: String = (
|
|
357
|
-
"%s is made by %s, a scene %s inherits, which creates it again on load."
|
|
362
|
+
"%s is made by %s, a scene %s inherits, which creates it again on load."
|
|
363
|
+
% [at, maker, path]
|
|
358
364
|
)
|
|
359
365
|
return made + (_deleted_elsewhere(node, maker) if deleting else " Edit that scene instead.")
|
|
360
366
|
return ""
|
|
361
367
|
|
|
362
368
|
|
|
363
|
-
## Each node the scenes [param state] inherits give it, by path, with the deepest file making it
|
|
364
|
-
## than values; a node [param state] makes itself is not in it.
|
|
369
|
+
## Each node the scenes [param state] inherits give it, by path, with the deepest file making it
|
|
370
|
+
## more than values; a node [param state] makes itself is not in it.
|
|
365
371
|
static func makers(state: SceneState) -> Dictionary:
|
|
366
372
|
var made_by: Dictionary = {}
|
|
367
373
|
var at: SceneState = state.get_base_scene_state()
|
|
@@ -380,20 +386,22 @@ static func makers(state: SceneState) -> Dictionary:
|
|
|
380
386
|
## What a caller can do with [param node], made by [param maker], instead of deleting it here.
|
|
381
387
|
static func _deleted_elsewhere(node: Node, maker: String) -> String:
|
|
382
388
|
var hidden: String = (
|
|
383
|
-
"set its visible to false here to hide it in this scene alone, or "
|
|
389
|
+
"set its visible to false here to hide it in this scene alone, or "
|
|
390
|
+
if "visible" in node
|
|
391
|
+
else ""
|
|
384
392
|
)
|
|
385
393
|
return (
|
|
386
394
|
(
|
|
387
|
-
" No scene can delete a node another scene makes: %sdelete it in %s, which removes it
|
|
388
|
-
+ "every scene using that one."
|
|
395
|
+
" No scene can delete a node another scene makes: %sdelete it in %s, which removes it "
|
|
396
|
+
+ "from every scene using that one."
|
|
389
397
|
)
|
|
390
398
|
% [hidden, maker]
|
|
391
399
|
)
|
|
392
400
|
|
|
393
401
|
|
|
394
|
-
## Why the folder a new file at [param file_path] goes in cannot be made, or "" once it is there.
|
|
395
|
-
## engine makes no folder when it saves, so a create into one not yet made failed with no more
|
|
396
|
-
## the engine's error, while script_edit create made its folder and went ahead.
|
|
402
|
+
## Why the folder a new file at [param file_path] goes in cannot be made, or "" once it is there.
|
|
403
|
+
## The engine makes no folder when it saves, so a create into one not yet made failed with no more
|
|
404
|
+
## than the engine's error, while script_edit create made its folder and went ahead.
|
|
397
405
|
static func make_folder_for(file_path: String) -> String:
|
|
398
406
|
var folder: String = file_path.get_base_dir()
|
|
399
407
|
if DirAccess.dir_exists_absolute(folder):
|
|
@@ -402,10 +410,10 @@ static func make_folder_for(file_path: String) -> String:
|
|
|
402
410
|
return "" if made == OK else "Could not make the folder %s: %s" % [folder, error_string(made)]
|
|
403
411
|
|
|
404
412
|
|
|
405
|
-
## Why [param script], loaded from [param script_path], cannot be attached because the editor could
|
|
406
|
-
## compile it, or "". A script that did not compile has no base type, and the refusal built from
|
|
407
|
-
## said "start.gd extends , so it cannot be on a Control root" about a script that was fine,
|
|
408
|
-
## failure was a class it called being behind in the editor. A headless engine compiles every
|
|
413
|
+
## Why [param script], loaded from [param script_path], cannot be attached because the editor could
|
|
414
|
+
## not compile it, or "". A script that did not compile has no base type, and the refusal built from
|
|
415
|
+
## that said "start.gd extends , so it cannot be on a Control root" about a script that was fine,
|
|
416
|
+
## whose failure was a class it called being behind in the editor. A headless engine compiles every
|
|
409
417
|
## script from its file, so only an editor can hold a copy that is behind.
|
|
410
418
|
static func uncompiled(script: Script, script_path: String) -> String:
|
|
411
419
|
if not str(script.get_instance_base_type()).is_empty():
|
|
@@ -421,9 +429,9 @@ static func uncompiled(script: Script, script_path: String) -> String:
|
|
|
421
429
|
return (
|
|
422
430
|
(
|
|
423
431
|
"%s does not compile in the editor, so it cannot be attached. script_diagnostics on it "
|
|
424
|
-
+ "gives the parser's errors. If they deny a member that another class's file
|
|
425
|
-
+ "the editor's copy of that class is behind: editor_rescan with
|
|
426
|
-
+ "class, then ask again."
|
|
432
|
+
+ "gives the parser's errors. If they deny a member that another class's file "
|
|
433
|
+
+ "declares, the editor's copy of that class is behind: editor_rescan with "
|
|
434
|
+
+ "reloadScript on that class, then ask again."
|
|
427
435
|
)
|
|
428
436
|
% script_path
|
|
429
437
|
)
|
|
@@ -441,21 +449,29 @@ static func script_class(type_name: String, kind: String) -> Dictionary:
|
|
|
441
449
|
if entry.get("is_abstract", false) == true:
|
|
442
450
|
return {
|
|
443
451
|
"error":
|
|
444
|
-
|
|
452
|
+
(
|
|
453
|
+
"%s, declared in %s, is abstract, so nothing can be made of it."
|
|
454
|
+
% [type_name, declaring]
|
|
455
|
+
),
|
|
445
456
|
}
|
|
446
457
|
var script: Script = load(declaring) as Script
|
|
447
458
|
if script == null:
|
|
448
|
-
return {
|
|
459
|
+
return {
|
|
460
|
+
"error": "%s is declared in %s, which does not load." % [type_name, declaring],
|
|
461
|
+
}
|
|
449
462
|
var failed: String = uncompiled(script, declaring)
|
|
450
463
|
if not failed.is_empty():
|
|
451
|
-
return {"error": failed}
|
|
464
|
+
return { "error": failed }
|
|
452
465
|
var base: String = str(script.get_instance_base_type())
|
|
453
466
|
if not ClassDB.is_parent_class(base, kind):
|
|
454
467
|
return {
|
|
455
468
|
"error":
|
|
456
|
-
|
|
469
|
+
(
|
|
470
|
+
"%s, declared in %s, extends %s, which is not a %s."
|
|
471
|
+
% [type_name, declaring, base, kind]
|
|
472
|
+
),
|
|
457
473
|
}
|
|
458
|
-
return {"script": script, "path": declaring, "base": base}
|
|
474
|
+
return { "script": script, "path": declaring, "base": base }
|
|
459
475
|
return {}
|
|
460
476
|
|
|
461
477
|
|
|
@@ -465,12 +481,12 @@ static func script_class(type_name: String, kind: String) -> Dictionary:
|
|
|
465
481
|
## argument the type came in, for the refusal when the call also named another script.
|
|
466
482
|
static func made_as(type_name: String, given: String, kind: String, argument: String) -> Dictionary:
|
|
467
483
|
if ClassDB.class_exists(type_name):
|
|
468
|
-
return {"type": type_name, "script": given}
|
|
484
|
+
return { "type": type_name, "script": given }
|
|
469
485
|
var declared: Dictionary = script_class(type_name, kind)
|
|
470
486
|
if declared.has("error"):
|
|
471
487
|
return declared
|
|
472
488
|
if declared.is_empty():
|
|
473
|
-
return {"type": type_name, "script": given}
|
|
489
|
+
return { "type": type_name, "script": given }
|
|
474
490
|
var declaring: String = str(declared["path"])
|
|
475
491
|
if not given.is_empty() and given != declaring:
|
|
476
492
|
return {
|
|
@@ -478,9 +494,9 @@ static func made_as(type_name: String, given: String, kind: String, argument: St
|
|
|
478
494
|
(
|
|
479
495
|
"%s %s is the class %s declares, and script names %s: pass one of them."
|
|
480
496
|
% [argument, type_name, declaring, given]
|
|
481
|
-
)
|
|
497
|
+
),
|
|
482
498
|
}
|
|
483
|
-
return {"type": str(declared["base"]), "script": declaring}
|
|
499
|
+
return { "type": str(declared["base"]), "script": declaring }
|
|
484
500
|
|
|
485
501
|
|
|
486
502
|
## Why no node can be called [param node_name], wherever it sits, or "".
|
|
@@ -510,10 +526,27 @@ func unusable_name(parent: Node, child_name: String, moving: Node = null) -> Str
|
|
|
510
526
|
if taken != null and taken != moving and taken.get_parent() == parent:
|
|
511
527
|
# The root by its name, since its path is ".", and ". already has a child" read as a typo.
|
|
512
528
|
var holder: String = "The root, %s," % parent.name if parent == root else path_of(parent)
|
|
513
|
-
return
|
|
529
|
+
return (
|
|
530
|
+
"%s already has a child named %s; %s is free there."
|
|
531
|
+
% [holder, child_name, _free_name(parent, child_name)]
|
|
532
|
+
)
|
|
514
533
|
return ""
|
|
515
534
|
|
|
516
535
|
|
|
536
|
+
## The first name after [param taken] that no child of [param parent] has, numbered as Godot numbers
|
|
537
|
+
## a duplicate: Crate2 after Crate, Crate4 after Crate3 when that is taken too. The refusal alone
|
|
538
|
+
## left a caller guessing at a name that might be taken as well.
|
|
539
|
+
static func _free_name(parent: Node, taken: String) -> String:
|
|
540
|
+
var digits: int = 0
|
|
541
|
+
while digits < taken.length() and taken[taken.length() - 1 - digits] in "0123456789":
|
|
542
|
+
digits += 1
|
|
543
|
+
var base: String = taken.left(taken.length() - digits)
|
|
544
|
+
var at: int = 2 if digits == 0 else taken.right(digits).to_int() + 1
|
|
545
|
+
while parent.has_node(NodePath(base + str(at))):
|
|
546
|
+
at += 1
|
|
547
|
+
return base + str(at)
|
|
548
|
+
|
|
549
|
+
|
|
517
550
|
## The properties this scene's own file sets on [param node], each with the value the file gives it.
|
|
518
551
|
func set_here(node: Node) -> Dictionary:
|
|
519
552
|
return set_at(path_of(node))
|
|
@@ -550,7 +583,9 @@ func set_at(at: String) -> Dictionary:
|
|
|
550
583
|
if _relative(str(_state.get_node_path(index))) != at:
|
|
551
584
|
continue
|
|
552
585
|
for p: int in _state.get_node_property_count(index):
|
|
553
|
-
held[str(_state.get_node_property_name(index, p))] = _state.get_node_property_value(
|
|
586
|
+
held[str(_state.get_node_property_name(index, p))] = _state.get_node_property_value(
|
|
587
|
+
index, p
|
|
588
|
+
)
|
|
554
589
|
return held
|
|
555
590
|
|
|
556
591
|
|
|
@@ -607,15 +642,17 @@ func write(check: Callable = Callable()) -> Dictionary:
|
|
|
607
642
|
packed = settled[0]
|
|
608
643
|
if not path.ends_with(".tscn") and _holds_scripted_embedded():
|
|
609
644
|
var binary: String = (
|
|
610
|
-
"Nothing was written: %s is a binary scene holding an embedded resource with a
|
|
611
|
-
+ "that is not a tool, and what that script changed when a headless engine
|
|
612
|
-
+ "cannot be told apart from the file here. "
|
|
645
|
+
"Nothing was written: %s is a binary scene holding an embedded resource with a "
|
|
646
|
+
+ "script that is not a tool, and what that script changed when a headless engine "
|
|
647
|
+
+ "loaded it cannot be told apart from the file here. "
|
|
613
648
|
)
|
|
614
649
|
return refuse(binary % path + GdhHeadlessCheck.REMEDY)
|
|
615
650
|
if check.is_valid():
|
|
616
651
|
var saved: Node = packed.instantiate(PackedScene.GEN_EDIT_STATE_MAIN)
|
|
617
652
|
if saved == null:
|
|
618
|
-
return refuse(
|
|
653
|
+
return refuse(
|
|
654
|
+
"The packed copy of %s did not instantiate, so nothing was written." % path
|
|
655
|
+
)
|
|
619
656
|
_checked_pack = packed
|
|
620
657
|
var missing: String = str(check.call(saved))
|
|
621
658
|
saved.free()
|
|
@@ -637,30 +674,33 @@ func write(check: Callable = Callable()) -> Dictionary:
|
|
|
637
674
|
if written.is_empty():
|
|
638
675
|
also_saved.append_array(also)
|
|
639
676
|
return written
|
|
677
|
+
var just_saved: GdhHeadlessCheck.GdhSaved = GdhHeadlessCheck.GdhSaved.new(
|
|
678
|
+
path, _text, func() -> Error: return GdhSceneUids.save_keeping_uid(packed, path)
|
|
679
|
+
)
|
|
640
680
|
var unkept: String = GdhHeadlessCheck.settle_saved(
|
|
641
|
-
|
|
642
|
-
_text,
|
|
681
|
+
just_saved,
|
|
643
682
|
values,
|
|
644
683
|
null,
|
|
645
684
|
{},
|
|
646
685
|
_reached,
|
|
647
686
|
carried,
|
|
648
|
-
func() -> Error: return GdhSceneUids.save_keeping_uid(packed, path)
|
|
649
687
|
)
|
|
650
688
|
if unkept.is_empty():
|
|
651
689
|
also_saved.append_array(also)
|
|
652
690
|
return {}
|
|
653
691
|
return {
|
|
654
|
-
"ok": false,
|
|
692
|
+
"ok": false,
|
|
693
|
+
"error": "Nothing was written: " + unkept + GdhHeadlessCheck.put_back(path, previous),
|
|
655
694
|
}
|
|
656
695
|
|
|
657
696
|
|
|
658
697
|
## Each property [param state] stores differently from the file, on a node the file held, that the
|
|
659
|
-
## call neither asked for nor changed, as {node, property, was, saved}: was left out where the file
|
|
660
|
-
## not set it, saved where the write does not. The engine writes a node as it holds it once the
|
|
661
|
-
## has loaded, and one property can set others as it loads, as anchors_preset gives a centred
|
|
662
|
-
## both grow directions, so a file not written by the editor gains lines on any node, the
|
|
663
|
-
## names among them. What the call's own change moved on that node is its answer's
|
|
698
|
+
## call neither asked for nor changed, as {node, property, was, saved}: was left out where the file
|
|
699
|
+
## did not set it, saved where the write does not. The engine writes a node as it holds it once the
|
|
700
|
+
## file has loaded, and one property can set others as it loads, as anchors_preset gives a centred
|
|
701
|
+
## Control both grow directions, so a file not written by the editor gains lines on any node, the
|
|
702
|
+
## one a set names among them. What the call's own change moved on that node is its answer's
|
|
703
|
+
## alsoChanged.
|
|
664
704
|
func _also_saved(state: SceneState) -> Array[Dictionary]:
|
|
665
705
|
var noted: Array[Dictionary] = []
|
|
666
706
|
var values: GdhSerialisation = GdhSerialisation.new()
|
|
@@ -685,7 +725,7 @@ func _also_saved(state: SceneState) -> Array[Dictionary]:
|
|
|
685
725
|
var held: bool = filed.has(name)
|
|
686
726
|
if held == stored.has(name) and (not held or GdhSame.same(stored[name], filed[name])):
|
|
687
727
|
continue
|
|
688
|
-
var entry: Dictionary = {"node": path_of(node), "property": name}
|
|
728
|
+
var entry: Dictionary = { "node": path_of(node), "property": name }
|
|
689
729
|
if held:
|
|
690
730
|
entry["was"] = values.serialize_value(filed[name])
|
|
691
731
|
if stored.has(name):
|
|
@@ -712,10 +752,15 @@ static func save_packed(packed: PackedScene, scene_path: String) -> Dictionary:
|
|
|
712
752
|
if not DirAccess.dir_exists_absolute(directory):
|
|
713
753
|
var made: Error = DirAccess.make_dir_recursive_absolute(directory)
|
|
714
754
|
if made != OK:
|
|
715
|
-
return {
|
|
755
|
+
return {
|
|
756
|
+
"ok": false, "error": "Could not create %s: %s" % [directory, error_string(made)],
|
|
757
|
+
}
|
|
716
758
|
var saving: Error = GdhSceneUids.save_keeping_uid(packed, scene_path)
|
|
717
759
|
if saving != OK:
|
|
718
|
-
return {
|
|
760
|
+
return {
|
|
761
|
+
"ok": false,
|
|
762
|
+
"error": "Failed to save %s: %s" % [scene_path, error_string(saving)],
|
|
763
|
+
}
|
|
719
764
|
refresh(scene_path)
|
|
720
765
|
return {}
|
|
721
766
|
|
|
@@ -724,7 +769,9 @@ static func save_packed(packed: PackedScene, scene_path: String) -> Dictionary:
|
|
|
724
769
|
## the editor's file list, and the tab showing it, whether current or not.
|
|
725
770
|
static func refresh(scene_path: String) -> void:
|
|
726
771
|
if ResourceLoader.has_cached(scene_path):
|
|
727
|
-
var _replaced: Resource = ResourceLoader.load(
|
|
772
|
+
var _replaced: Resource = ResourceLoader.load(
|
|
773
|
+
scene_path, "", ResourceLoader.CACHE_MODE_REPLACE
|
|
774
|
+
)
|
|
728
775
|
if not Engine.is_editor_hint():
|
|
729
776
|
return
|
|
730
777
|
scan_later()
|
|
@@ -773,11 +820,11 @@ static func unsaved(scene_path: String) -> bool:
|
|
|
773
820
|
|
|
774
821
|
## [param packed] with what the scene's scripts changed put back, as [pack, ""], or [null, why not].
|
|
775
822
|
##
|
|
776
|
-
## On a node the tool did not ask to change, a difference is put back to what the file holds, or,
|
|
777
|
-
## a property the file does not set, to what the node would load with without it. On a node it
|
|
778
|
-
## ask to change, through a script that is not a tool, every property it did not name is put
|
|
779
|
-
## what it held before. Packed again afterwards and put back again until nothing differs,
|
|
780
|
-
## putting one property back can run a setter that changes another already put back (a script
|
|
823
|
+
## On a node the tool did not ask to change, a difference is put back to what the file holds, or,
|
|
824
|
+
## for a property the file does not set, to what the node would load with without it. On a node it
|
|
825
|
+
## did ask to change, through a script that is not a tool, every property it did not name is put
|
|
826
|
+
## back to what it held before. Packed again afterwards and put back again until nothing differs,
|
|
827
|
+
## since putting one property back can run a setter that changes another already put back (a script
|
|
781
828
|
## setting speed in _init, whose setter tints modulate), and refused if something still does.
|
|
782
829
|
func _settled(packed: PackedScene) -> Array:
|
|
783
830
|
var current: PackedScene = packed
|
|
@@ -819,8 +866,8 @@ func _settled(packed: PackedScene) -> Array:
|
|
|
819
866
|
return [null, _stray_text(stray_node, named)]
|
|
820
867
|
|
|
821
868
|
|
|
822
|
-
## Each node the tool asked to change through a script that is not a tool, with the properties it
|
|
823
|
-
## not name that no longer hold what they held before, as [node, names].
|
|
869
|
+
## Each node the tool asked to change through a script that is not a tool, with the properties it
|
|
870
|
+
## did not name that no longer hold what they held before, as [node, names].
|
|
824
871
|
func _moved_by_scripts() -> Array[Array]:
|
|
825
872
|
var moved: Array[Array] = []
|
|
826
873
|
for key: Variant in _asked:
|
|
@@ -882,8 +929,8 @@ func _stray_text(node: Node, name: String) -> String:
|
|
|
882
929
|
return (
|
|
883
930
|
(
|
|
884
931
|
(
|
|
885
|
-
"the script of %s did not start when a headless engine instanced %s, so the
|
|
886
|
-
+ "saved without it; the engine's messages give its errors. "
|
|
932
|
+
"the script of %s did not start when a headless engine instanced %s, so the "
|
|
933
|
+
+ "node would be saved without it; the engine's messages give its errors. "
|
|
887
934
|
)
|
|
888
935
|
% [at, path]
|
|
889
936
|
)
|
|
@@ -892,9 +939,9 @@ func _stray_text(node: Node, name: String) -> String:
|
|
|
892
939
|
return (
|
|
893
940
|
(
|
|
894
941
|
(
|
|
895
|
-
"%s.%s would be saved differently from what %s holds, and the call did not ask for
|
|
896
|
-
+ "scene's scripts ran when a headless engine instanced it, as they do in
|
|
897
|
-
+ "an editor. "
|
|
942
|
+
"%s.%s would be saved differently from what %s holds, and the call did not ask for "
|
|
943
|
+
+ "it: the scene's scripts ran when a headless engine instanced it, as they do in "
|
|
944
|
+
+ "the game and not in an editor. "
|
|
898
945
|
)
|
|
899
946
|
% [at, name, path]
|
|
900
947
|
)
|