gdharness 1.3.42 → 1.3.43
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 +62 -9
- 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 +37 -12
- package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +109 -79
- 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 +34 -30
- 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 +37 -12
- 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 +139 -93
- package/build/godot/operations/editor_tools.gd +9 -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 +53 -29
- 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 +37 -12
- 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/package.json +1 -1
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
3
|
## What a property declares beyond its type: the values of its enum and the range it is exported
|
|
5
4
|
## with, for taking a value by name and for saying when a value written lies outside them.
|
|
6
5
|
|
|
7
6
|
const GdhRead = preload("reading.gd")
|
|
8
|
-
|
|
9
7
|
## The section of layer_names in project.godot naming the layers of each layers hint.
|
|
10
8
|
const LAYER_NAMES: Dictionary[int, String] = {
|
|
11
9
|
PROPERTY_HINT_LAYERS_2D_PHYSICS: "2d_physics",
|
|
@@ -27,7 +25,7 @@ static func layers_in(hint: int, mask: int) -> Array[Dictionary]:
|
|
|
27
25
|
for layer: int in range(1, MASK_LAYERS + 1):
|
|
28
26
|
if mask & (1 << (layer - 1)) == 0:
|
|
29
27
|
continue
|
|
30
|
-
var entry: Dictionary = {"layer": layer}
|
|
28
|
+
var entry: Dictionary = { "layer": layer }
|
|
31
29
|
var name: String = str(
|
|
32
30
|
ProjectSettings.get_setting("layer_names/%s/layer_%d" % [LAYER_NAMES[hint], layer], "")
|
|
33
31
|
)
|
|
@@ -119,10 +117,12 @@ static func engine_constants(target: Object, property: String) -> Dictionary:
|
|
|
119
117
|
## names by it, as [true, number], or [false, why] naming the values, with [param holder] naming the
|
|
120
118
|
## target in the refusal. [false, ""] for a word that is a number, which converts as one.
|
|
121
119
|
##
|
|
122
|
-
## An enum is written by the name of a value as well as its number, since the name is what the
|
|
123
|
-
## and the inspector show and the number is only where the value happens to sit. Tried as the
|
|
124
|
-
## spells it, then as the hint does, then as the engine's constant.
|
|
125
|
-
static func enum_by_name(
|
|
120
|
+
## An enum is written by the name of a value as well as its number, since the name is what the
|
|
121
|
+
## script and the inspector show and the number is only where the value happens to sit. Tried as the
|
|
122
|
+
## script spells it, then as the hint does, then as the engine's constant.
|
|
123
|
+
static func enum_by_name(
|
|
124
|
+
target: Object, declared: Dictionary, word: String, holder: String
|
|
125
|
+
) -> Array:
|
|
126
126
|
var property: String = str(declared.get("name", ""))
|
|
127
127
|
var choices: Dictionary = enum_of(target, declared)
|
|
128
128
|
var chosen: Variant = enum_named(choices, word)
|
|
@@ -144,7 +144,9 @@ static func enum_refusal(
|
|
|
144
144
|
) -> String:
|
|
145
145
|
var ways: String = "by name or by number"
|
|
146
146
|
if not constants.is_empty():
|
|
147
|
-
ways =
|
|
147
|
+
ways = (
|
|
148
|
+
"by name, by number or by the engine's constant, such as %s" % str(constants.keys()[0])
|
|
149
|
+
)
|
|
148
150
|
var said: String = (
|
|
149
151
|
"%s.%s is an enum, and %s is none of its values: send %s %s"
|
|
150
152
|
% [holder, property, word, _listed(choices), ways]
|
|
@@ -163,7 +165,10 @@ static func enum_argument_refusal(
|
|
|
163
165
|
called: String, index: int, named: String, word: String, choices: Dictionary
|
|
164
166
|
) -> String:
|
|
165
167
|
var said: String = (
|
|
166
|
-
|
|
168
|
+
(
|
|
169
|
+
"%s takes the enum %s as argument %d, and %s is none of its values: send %s by name or "
|
|
170
|
+
+ "by number"
|
|
171
|
+
)
|
|
167
172
|
% [called, named, index + 1, word, _listed(choices)]
|
|
168
173
|
)
|
|
169
174
|
var close: PackedStringArray = GdhRead.nearest(word, PackedStringArray(choices.keys()))
|
|
@@ -174,7 +179,9 @@ static func enum_argument_refusal(
|
|
|
174
179
|
|
|
175
180
|
## Why [param value], given as argument [param index] for the enum [param named], is none of its
|
|
176
181
|
## [param choices], or "" when it is one. The engine passes any number on.
|
|
177
|
-
static func outside_enum_argument(
|
|
182
|
+
static func outside_enum_argument(
|
|
183
|
+
index: int, named: String, value: Variant, choices: Dictionary
|
|
184
|
+
) -> String:
|
|
178
185
|
if typeof(value) != TYPE_INT or choices.values().has(value):
|
|
179
186
|
return ""
|
|
180
187
|
return (
|
|
@@ -235,7 +242,10 @@ static func outside(target: Object, declared: Dictionary, value: Variant, shown:
|
|
|
235
242
|
var hint: int = GdhRead.as_int(declared.get("hint", PROPERTY_HINT_NONE))
|
|
236
243
|
var hint_string: String = str(declared.get("hint_string", ""))
|
|
237
244
|
var property: String = str(declared.get("name", ""))
|
|
238
|
-
if
|
|
245
|
+
if (
|
|
246
|
+
hint == PROPERTY_HINT_ENUM
|
|
247
|
+
and (typeof(value) == TYPE_STRING or typeof(value) == TYPE_STRING_NAME)
|
|
248
|
+
):
|
|
239
249
|
var names: PackedStringArray = PackedStringArray(enum_values(hint_string).keys())
|
|
240
250
|
if names.is_empty() or names.has(str(value)):
|
|
241
251
|
return ""
|
|
@@ -308,15 +318,17 @@ static func _listed(choices: Dictionary) -> String:
|
|
|
308
318
|
return ", ".join(listed)
|
|
309
319
|
|
|
310
320
|
|
|
311
|
-
## A range's end as the script wrote it: the engine's hint gives @export_range(0, 10) as 0.0 and
|
|
321
|
+
## A range's end as the script wrote it: the engine's hint gives @export_range(0, 10) as 0.0 and
|
|
322
|
+
## 10.0.
|
|
312
323
|
static func _bound(end: float) -> String:
|
|
313
324
|
return str(int(end)) if end == floorf(end) and absf(end) < 1e15 else str(end)
|
|
314
325
|
|
|
315
326
|
|
|
316
327
|
## Puts back on [param built] what its script set as it was attached and was not among
|
|
317
|
-
## [param wanted], to the defaults it declares, outside the editor. Attaching runs the script's
|
|
318
|
-
## there, which the editor never runs for a script that is not a tool, so a resource_name
|
|
319
|
-
## was saved as if asked for. A property whose default the engine cannot report is left
|
|
328
|
+
## [param wanted], to the defaults it declares, outside the editor. Attaching runs the script's
|
|
329
|
+
## _init there, which the editor never runs for a script that is not a tool, so a resource_name
|
|
330
|
+
## _init chose was saved as if asked for. A property whose default the engine cannot report is left
|
|
331
|
+
## as made.
|
|
320
332
|
static func unmade(built: Resource, wanted: Dictionary) -> void:
|
|
321
333
|
var attached: Variant = built.get_script()
|
|
322
334
|
if Engine.is_editor_hint() or not attached is Script:
|
|
@@ -445,17 +457,25 @@ static func runs_script_code(target: Object, name: String) -> bool:
|
|
|
445
457
|
## the buses are read from the AudioServer, which holds the project's layout; "the engine changed or
|
|
446
458
|
## refused the value" left a caller to guess the bus did not exist.
|
|
447
459
|
static func not_held(
|
|
448
|
-
holder: String,
|
|
460
|
+
holder: String,
|
|
461
|
+
engine_class: String,
|
|
462
|
+
property: String,
|
|
463
|
+
declared: Dictionary,
|
|
464
|
+
given: Array,
|
|
465
|
+
held: String
|
|
449
466
|
) -> String:
|
|
450
467
|
var value: Variant = given[0]
|
|
451
|
-
var said: String =
|
|
468
|
+
var said: String = (
|
|
469
|
+
"%s.%s was given %s and holds %s after it was set" % [holder, property, given[1], held]
|
|
470
|
+
)
|
|
452
471
|
var type: int = GdhRead.as_int(declared.get("type", TYPE_NIL))
|
|
453
472
|
var hint: int = GdhRead.as_int(declared.get("hint", PROPERTY_HINT_NONE))
|
|
454
473
|
if hint in LAYER_NAMES and typeof(value) == TYPE_INT:
|
|
455
474
|
return (
|
|
456
475
|
(
|
|
457
|
-
"%s: it is a mask of the %s layers, one bit each from layer 1 (1) to layer %d
|
|
458
|
-
+ "takes 0 to %d and the engine keeps only those bits. Nothing was
|
|
476
|
+
"%s: it is a mask of the %s layers, one bit each from layer 1 (1) to layer %d "
|
|
477
|
+
+ "(%d), so it takes 0 to %d and the engine keeps only those bits. Nothing was "
|
|
478
|
+
+ "written."
|
|
459
479
|
)
|
|
460
480
|
% [
|
|
461
481
|
said,
|
|
@@ -477,22 +497,28 @@ static func not_held(
|
|
|
477
497
|
return said + ": the engine changed or refused the value, so nothing was written."
|
|
478
498
|
var close: PackedStringArray = GdhRead.nearest(str(value), names)
|
|
479
499
|
var nearest: String = (
|
|
480
|
-
"%s %s" % ["is" if close.size() == 1 else "are", ", ".join(close)]
|
|
500
|
+
"%s %s" % ["is" if close.size() == 1 else "are", ", ".join(close)]
|
|
501
|
+
if not close.is_empty()
|
|
502
|
+
else ""
|
|
481
503
|
)
|
|
482
504
|
if bus:
|
|
483
505
|
# Not "holds Master after it was set": the getter answers the first bus for any name the
|
|
484
506
|
# layout lacks, and nothing was kept.
|
|
485
507
|
return (
|
|
486
508
|
(
|
|
487
|
-
"%s.bus takes the name of one of the project's audio buses, and %s is none of
|
|
488
|
-
+ "would play on %s; %s. project_info include audio_buses lists them,
|
|
489
|
-
+ "project_settings add_audio_bus adds one. Nothing was written."
|
|
509
|
+
"%s.bus takes the name of one of the project's audio buses, and %s is none of "
|
|
510
|
+
+ "them, so it would play on %s; %s. project_info include audio_buses lists them, "
|
|
511
|
+
+ "and project_settings add_audio_bus adds one. Nothing was written."
|
|
490
512
|
)
|
|
491
513
|
% [
|
|
492
514
|
holder,
|
|
493
515
|
given[1],
|
|
494
516
|
AudioServer.get_bus_name(0),
|
|
495
|
-
|
|
517
|
+
(
|
|
518
|
+
"the buses are " + ", ".join(names)
|
|
519
|
+
if nearest.is_empty()
|
|
520
|
+
else "the nearest " + nearest
|
|
521
|
+
),
|
|
496
522
|
]
|
|
497
523
|
)
|
|
498
524
|
return (
|
|
@@ -500,7 +526,11 @@ static func not_held(
|
|
|
500
526
|
% [
|
|
501
527
|
said,
|
|
502
528
|
given[1],
|
|
503
|
-
|
|
529
|
+
(
|
|
530
|
+
"it lists " + ", ".join(names)
|
|
531
|
+
if nearest.is_empty()
|
|
532
|
+
else "the nearest it lists " + nearest
|
|
533
|
+
),
|
|
504
534
|
]
|
|
505
535
|
)
|
|
506
536
|
|
|
@@ -513,7 +543,9 @@ static func held_by_children(node: Node, property: String) -> String:
|
|
|
513
543
|
var holders: PackedStringArray = []
|
|
514
544
|
for child: Node in node.get_children():
|
|
515
545
|
if property in child:
|
|
516
|
-
var _added: bool = holders.append(
|
|
546
|
+
var _added: bool = holders.append(
|
|
547
|
+
"%s (a %s)" % [root.get_path_to(child), child.get_class()]
|
|
548
|
+
)
|
|
517
549
|
if holders.is_empty():
|
|
518
550
|
return ""
|
|
519
551
|
if holders.size() == 1:
|
|
@@ -523,14 +555,15 @@ static func held_by_children(node: Node, property: String) -> String:
|
|
|
523
555
|
|
|
524
556
|
|
|
525
557
|
## A sentence for each node path in [param given], by property, that names no node from
|
|
526
|
-
## [param node]. A path is written whatever it names, as the inspector writes one, since the node
|
|
527
|
-
## be added next; a joint whose node_a named nothing then joined nothing, and no answer said so.
|
|
558
|
+
## [param node]. A path is written whatever it names, as the inspector writes one, since the node
|
|
559
|
+
## may be added next; a joint whose node_a named nothing then joined nothing, and no answer said so.
|
|
528
560
|
static func naming_nothing(node: Node, given: Dictionary) -> Dictionary:
|
|
529
561
|
var said: Dictionary = {}
|
|
530
562
|
for property: String in given:
|
|
531
563
|
if "/" in property or ":" in property:
|
|
532
564
|
continue
|
|
533
|
-
# What the node holds, since a path given as text is held as text until the engine stores
|
|
565
|
+
# What the node holds, since a path given as text is held as text until the engine stores
|
|
566
|
+
# it.
|
|
534
567
|
var path: Variant = node.get(property)
|
|
535
568
|
if not path is NodePath:
|
|
536
569
|
continue
|
|
@@ -539,8 +572,8 @@ static func naming_nothing(node: Node, given: Dictionary) -> Dictionary:
|
|
|
539
572
|
continue
|
|
540
573
|
said[property] = (
|
|
541
574
|
(
|
|
542
|
-
"%s names no node from %s. It is written, as the editor writes one, since that
|
|
543
|
-
+ "be added next; until it is, %s finds nothing."
|
|
575
|
+
"%s names no node from %s. It is written, as the editor writes one, since that "
|
|
576
|
+
+ "node may be added next; until it is, %s finds nothing."
|
|
544
577
|
)
|
|
545
578
|
% [str(named), node.name, property]
|
|
546
579
|
)
|
|
@@ -553,7 +586,10 @@ static func naming_nothing(node: Node, given: Dictionary) -> Dictionary:
|
|
|
553
586
|
static func no_uniform(material: ShaderMaterial, property: String) -> String:
|
|
554
587
|
var uniform: String = property.trim_prefix("shader_parameter/")
|
|
555
588
|
if material.shader == null:
|
|
556
|
-
return
|
|
589
|
+
return (
|
|
590
|
+
"The ShaderMaterial has no shader, so it has no parameter %s yet; set shader first."
|
|
591
|
+
% uniform
|
|
592
|
+
)
|
|
557
593
|
var shader: String = material.shader.resource_path
|
|
558
594
|
var named: String = shader if not shader.is_empty() else "Its shader"
|
|
559
595
|
var uniforms: Array = []
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## Scenes and resources read from their files, by the editor's tools and by a headless engine with
|
|
4
3
|
## none connected, so a read answers the same either way.
|
|
5
4
|
##
|
|
@@ -30,7 +29,10 @@ static func scene_tree(
|
|
|
30
29
|
if not laid.get("ok", false):
|
|
31
30
|
return laid
|
|
32
31
|
var nodes: Dictionary = laid["nodes"]
|
|
33
|
-
return {
|
|
32
|
+
return {
|
|
33
|
+
"ok": true,
|
|
34
|
+
"tree": _entry(nodes, ".", include_properties, include_defaults, depth, 0),
|
|
35
|
+
}
|
|
34
36
|
|
|
35
37
|
|
|
36
38
|
## The node at [param node_path] in [param scene_path] as {ok, name, type, properties}, laid as
|
|
@@ -45,7 +47,9 @@ static func scene_node(scene_path: String, node_path: String) -> Dictionary:
|
|
|
45
47
|
if not nodes.has(path):
|
|
46
48
|
return {}
|
|
47
49
|
var found: Dictionary = nodes[path]
|
|
48
|
-
return {
|
|
50
|
+
return {
|
|
51
|
+
"ok": true, "name": found["name"], "type": found["type"], "properties": found["properties"],
|
|
52
|
+
}
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
## The node at [param node_path] in [param scene_path] as [method scene_node] answers it, when it is
|
|
@@ -67,7 +71,7 @@ static func scene_node_of_kind(scene_path: String, node_path: String, kind: Stri
|
|
|
67
71
|
"ok": true,
|
|
68
72
|
"name": nodes[path]["name"],
|
|
69
73
|
"type": found,
|
|
70
|
-
"properties": nodes[path]["properties"]
|
|
74
|
+
"properties": nodes[path]["properties"],
|
|
71
75
|
}
|
|
72
76
|
else:
|
|
73
77
|
var hinted: String = said
|
|
@@ -79,7 +83,7 @@ static func scene_node_of_kind(scene_path: String, node_path: String, kind: Stri
|
|
|
79
83
|
var type: String = str(nodes[at]["type"])
|
|
80
84
|
if ClassDB.class_exists(type) and ClassDB.is_parent_class(type, kind):
|
|
81
85
|
var _held: bool = held.append(str(at))
|
|
82
|
-
return {"ok": false, "error": GdhRead.not_of_kind(said, node_path, found, kind, held)}
|
|
86
|
+
return { "ok": false, "error": GdhRead.not_of_kind(said, node_path, found, kind, held) }
|
|
83
87
|
|
|
84
88
|
|
|
85
89
|
## [param said], the refusal for [param node_path], which the scene laid as [param nodes] does not
|
|
@@ -100,35 +104,38 @@ static func _missing_in(nodes: Dictionary, said: String, node_path: String) -> S
|
|
|
100
104
|
var children: Array[String] = nodes[at]["children"]
|
|
101
105
|
for child: String in children:
|
|
102
106
|
var _named: bool = names.append(str(nodes[child]["name"]))
|
|
103
|
-
return GdhRead.missing_below(
|
|
107
|
+
return GdhRead.missing_below(
|
|
108
|
+
said, str(nodes["."]["name"]), reached, parts[reached.size()], names
|
|
109
|
+
)
|
|
104
110
|
|
|
105
111
|
|
|
106
112
|
## Every node of [param scene_path] as {ok, nodes}, keyed by path, or a refusal.
|
|
107
113
|
static func _laid(scene_path: String) -> Dictionary:
|
|
108
114
|
if not FileAccess.file_exists(scene_path):
|
|
109
|
-
return {"ok": false, "error": "Scene not found: " + scene_path}
|
|
115
|
+
return { "ok": false, "error": "Scene not found: " + scene_path }
|
|
110
116
|
# Loaded without naming the type, so a resource handed in by mistake loads as what it is and the
|
|
111
117
|
# refusal can say so, where asking for a PackedScene printed the loader's error and a backtrace.
|
|
112
118
|
var loaded: Resource = ResourceLoader.load(scene_path, "", ResourceLoader.CACHE_MODE_IGNORE)
|
|
113
119
|
if loaded == null:
|
|
114
|
-
return {"ok": false, "error": "Failed to load %s as a scene" % scene_path}
|
|
120
|
+
return { "ok": false, "error": "Failed to load %s as a scene" % scene_path }
|
|
115
121
|
if not loaded is PackedScene:
|
|
116
122
|
var sort: String = GdhRead.with_article(loaded.get_class())
|
|
117
123
|
return {
|
|
118
124
|
"ok": false,
|
|
119
|
-
"error":
|
|
125
|
+
"error":
|
|
126
|
+
"%s is %s, not a scene: resource_edit read reads its properties." % [scene_path, sort],
|
|
120
127
|
}
|
|
121
128
|
var packed: PackedScene = loaded
|
|
122
129
|
var nodes: Dictionary = {}
|
|
123
130
|
var refused: String = _lay(packed.get_state(), ".", nodes, [scene_path], packed)
|
|
124
131
|
if not refused.is_empty():
|
|
125
|
-
return {"ok": false, "error": refused}
|
|
132
|
+
return { "ok": false, "error": refused }
|
|
126
133
|
if not nodes.has("."):
|
|
127
|
-
return {"ok": false, "error": "%s holds no nodes" % scene_path}
|
|
134
|
+
return { "ok": false, "error": "%s holds no nodes" % scene_path }
|
|
128
135
|
var base: SceneState = packed.get_state().get_base_scene_state()
|
|
129
136
|
if base != null:
|
|
130
137
|
nodes["."]["inherits"] = base.get_path()
|
|
131
|
-
return {"ok": true, "nodes": nodes}
|
|
138
|
+
return { "ok": true, "nodes": nodes }
|
|
132
139
|
|
|
133
140
|
|
|
134
141
|
## Lays the nodes of [param state] into [param nodes] under [param prefix], keyed by their path
|
|
@@ -152,8 +159,8 @@ static func _lay(
|
|
|
152
159
|
var path: String = _joined(prefix, str(state.get_node_path(index)))
|
|
153
160
|
var recovered: bool = false
|
|
154
161
|
if not nodes.has(path) and index > 0 and _overrides(state, index) and ids[index] >= 0:
|
|
155
|
-
# The engine finds an entry whose node was renamed or moved in the scene it comes from
|
|
156
|
-
# the node's unique_id, and keeps the node's new name.
|
|
162
|
+
# The engine finds an entry whose node was renamed or moved in the scene it comes from
|
|
163
|
+
# by the node's unique_id, and keeps the node's new name.
|
|
157
164
|
var parent: String = _joined(prefix, str(state.get_node_path(index, true)))
|
|
158
165
|
var found: String = _by_id(nodes, _owner_of(nodes, parent, prefix), ids[index])
|
|
159
166
|
recovered = not found.is_empty()
|
|
@@ -164,7 +171,9 @@ static func _lay(
|
|
|
164
171
|
var instance: PackedScene = state.get_node_instance(index)
|
|
165
172
|
if instance != null:
|
|
166
173
|
if within.has(instance.resource_path):
|
|
167
|
-
return
|
|
174
|
+
return (
|
|
175
|
+
"%s instances %s, which holds it" % [within.back(), instance.resource_path]
|
|
176
|
+
)
|
|
168
177
|
var refused: String = _lay(
|
|
169
178
|
instance.get_state(), path, nodes, within + [instance.resource_path]
|
|
170
179
|
)
|
|
@@ -178,15 +187,16 @@ static func _lay(
|
|
|
178
187
|
nodes[path] = _made("InstancePlaceholder")
|
|
179
188
|
nodes[path]["instanceOf"] = state.get_node_instance_placeholder(index)
|
|
180
189
|
elif str(state.get_node_type(index)).is_empty():
|
|
181
|
-
# An entry for a node of an instance that no longer has it, which the engine skips
|
|
190
|
+
# An entry for a node of an instance that no longer has it, which the engine skips
|
|
191
|
+
# too.
|
|
182
192
|
continue
|
|
183
193
|
else:
|
|
184
194
|
nodes[path] = _made(str(state.get_node_type(index)))
|
|
185
195
|
nodes[path]["madeHere"] = loaded != null
|
|
186
196
|
if ids[index] >= 0:
|
|
187
197
|
nodes[path]["uniqueId"] = ids[index]
|
|
188
|
-
# Laid at the root with no scene handed in is a scene the one being read inherits, and
|
|
189
|
-
# deepest is laid first, so this is the file that makes the node.
|
|
198
|
+
# Laid at the root with no scene handed in is a scene the one being read inherits, and
|
|
199
|
+
# the deepest is laid first, so this is the file that makes the node.
|
|
190
200
|
if prefix == "." and loaded == null:
|
|
191
201
|
nodes[path]["inheritedFrom"] = state.get_path()
|
|
192
202
|
# The name is the state's even for an entry laid already, since an inherited scene names its
|
|
@@ -251,8 +261,8 @@ static func _editable_in(scene_path: String, given_scene: PackedScene = null) ->
|
|
|
251
261
|
|
|
252
262
|
|
|
253
263
|
## The unique_id of each node entry in the text scene at [param scene_path], in the order its state
|
|
254
|
-
## holds them, -1 where an entry has none. All -1 for a binary scene, and for a file whose entries
|
|
255
|
-
## not number [param count], since SceneState does not give the ids to a script.
|
|
264
|
+
## holds them, -1 where an entry has none. All -1 for a binary scene, and for a file whose entries
|
|
265
|
+
## do not number [param count], since SceneState does not give the ids to a script.
|
|
256
266
|
static func _unique_ids(scene_path: String, count: int) -> Array[int]:
|
|
257
267
|
var ids: Array[int] = []
|
|
258
268
|
var _sized: int = ids.resize(count)
|
|
@@ -289,8 +299,9 @@ static func _owner_of(nodes: Dictionary, parent: String, prefix: String) -> Stri
|
|
|
289
299
|
return at if nodes.has(at) else prefix
|
|
290
300
|
|
|
291
301
|
|
|
292
|
-
## The path of the node [param owner] owns whose unique_id is [param id], or "": one under an
|
|
293
|
-
## nested below [param owner] belongs to that instance's file, whose ids are numbered
|
|
302
|
+
## The path of the node [param owner] owns whose unique_id is [param id], or "": one under an
|
|
303
|
+
## instance nested below [param owner] belongs to that instance's file, whose ids are numbered
|
|
304
|
+
## apart.
|
|
294
305
|
static func _by_id(nodes: Dictionary, owner: String, id: int) -> String:
|
|
295
306
|
for path: String in nodes:
|
|
296
307
|
if path == owner or not (owner == "." or path.begins_with(owner + "/")):
|
|
@@ -307,7 +318,7 @@ static func _by_id(nodes: Dictionary, owner: String, id: int) -> String:
|
|
|
307
318
|
static func _made(type: String) -> Dictionary:
|
|
308
319
|
var children: Array[String] = []
|
|
309
320
|
var groups: Array[String] = []
|
|
310
|
-
return {"name": "", "type": type, "properties": {}, "children": children, "groups": groups}
|
|
321
|
+
return { "name": "", "type": type, "properties": {}, "children": children, "groups": groups }
|
|
311
322
|
|
|
312
323
|
|
|
313
324
|
## Puts [param path] among [param parent]'s children, at [param at] when the file says where (a node
|
|
@@ -342,7 +353,9 @@ static func _entry(
|
|
|
342
353
|
) -> Dictionary:
|
|
343
354
|
var node: Dictionary = nodes[path]
|
|
344
355
|
var children: Array[Dictionary] = []
|
|
345
|
-
var data: Dictionary = {
|
|
356
|
+
var data: Dictionary = {
|
|
357
|
+
"name": node["name"], "type": node["type"], "path": path, "children": children,
|
|
358
|
+
}
|
|
346
359
|
# The root's file is the scene being read, so only another node's names a second scene.
|
|
347
360
|
if path != "." and node.has("instanceOf"):
|
|
348
361
|
data["instanceOf"] = node["instanceOf"]
|
|
@@ -369,7 +382,8 @@ static func _entry(
|
|
|
369
382
|
if cells >= 0:
|
|
370
383
|
data["cellCount"] = cells
|
|
371
384
|
data["cellsListedBy"] = (
|
|
372
|
-
"scene_node get with nodePath %s, which lists them as set_tilemap_cells takes them"
|
|
385
|
+
"scene_node get with nodePath %s, which lists them as set_tilemap_cells takes them"
|
|
386
|
+
% path
|
|
373
387
|
)
|
|
374
388
|
if not node.get("madeHere", false):
|
|
375
389
|
var kept: Array = node.get("setHere", [])
|
|
@@ -383,14 +397,16 @@ static func _entry(
|
|
|
383
397
|
data["childrenNotListed"] = below.size()
|
|
384
398
|
return data
|
|
385
399
|
for child: String in below:
|
|
386
|
-
children.append(
|
|
400
|
+
children.append(
|
|
401
|
+
_entry(nodes, child, include_properties, include_defaults, depth, current_depth + 1)
|
|
402
|
+
)
|
|
387
403
|
return data
|
|
388
404
|
|
|
389
405
|
|
|
390
406
|
## How many cells a TileMapLayer or TileMap of [param type] holds under the values [param given], or
|
|
391
|
-
## -1 when it is another node or the file sets none of its cells. The file keeps them as packed
|
|
392
|
-
## which a read of the tree answered with alone, so a caller decoded the base64 to learn what
|
|
393
|
-
## painted. Only cells the file sets are counted: an instance's come from its own scene.
|
|
407
|
+
## -1 when it is another node or the file sets none of its cells. The file keeps them as packed
|
|
408
|
+
## bytes, which a read of the tree answered with alone, so a caller decoded the base64 to learn what
|
|
409
|
+
## was painted. Only cells the file sets are counted: an instance's come from its own scene.
|
|
394
410
|
static func _cell_count(type: String, given: Dictionary) -> int:
|
|
395
411
|
if not ClassDB.class_exists(type) or not ClassDB.can_instantiate(type):
|
|
396
412
|
return -1
|
|
@@ -400,7 +416,10 @@ static func _cell_count(type: String, given: Dictionary) -> int:
|
|
|
400
416
|
var painted: bool = false
|
|
401
417
|
for property: Variant in given:
|
|
402
418
|
var named: String = str(property)
|
|
403
|
-
if
|
|
419
|
+
if (
|
|
420
|
+
named == "tile_map_data"
|
|
421
|
+
or (named.begins_with("layer_") and named.ends_with("/tile_data"))
|
|
422
|
+
):
|
|
404
423
|
painted = true
|
|
405
424
|
if not painted:
|
|
406
425
|
return -1
|
|
@@ -439,7 +458,10 @@ static func _stored(type: String, given: Dictionary, include_defaults: bool) ->
|
|
|
439
458
|
if script != null:
|
|
440
459
|
for declared: Dictionary in script.get_script_property_list():
|
|
441
460
|
var property: String = str(declared.get("name", ""))
|
|
442
|
-
if
|
|
461
|
+
if (
|
|
462
|
+
not property.is_empty()
|
|
463
|
+
and GdhRead.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE
|
|
464
|
+
):
|
|
443
465
|
scripted[property] = script.get_property_default_value(property)
|
|
444
466
|
var values: Dictionary = {}
|
|
445
467
|
var defaults: Dictionary = {}
|
|
@@ -451,7 +473,10 @@ static func _stored(type: String, given: Dictionary, include_defaults: bool) ->
|
|
|
451
473
|
bare.set(str(property), given[property])
|
|
452
474
|
for declared: Dictionary in bare.get_property_list():
|
|
453
475
|
var property: String = str(declared.get("name", ""))
|
|
454
|
-
if
|
|
476
|
+
if (
|
|
477
|
+
not property.is_empty()
|
|
478
|
+
and GdhRead.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE
|
|
479
|
+
):
|
|
455
480
|
values[property] = bare.get(property)
|
|
456
481
|
defaults[property] = plain.get(property)
|
|
457
482
|
for made: Object in [bare, plain]:
|
|
@@ -476,7 +501,7 @@ static func _stored(type: String, given: Dictionary, include_defaults: bool) ->
|
|
|
476
501
|
left_out += 1
|
|
477
502
|
continue
|
|
478
503
|
answered[property] = serialiser.serialize_value(values[property])
|
|
479
|
-
return {"properties": answered, "left_out": left_out}
|
|
504
|
+
return { "properties": answered, "left_out": left_out }
|
|
480
505
|
|
|
481
506
|
|
|
482
507
|
## What a resource file holds, in the form modify takes, so a value read here can be written back.
|
|
@@ -488,10 +513,13 @@ static func _stored(type: String, given: Dictionary, include_defaults: bool) ->
|
|
|
488
513
|
static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
489
514
|
var file: String = res_path.get_slice("::", 0)
|
|
490
515
|
if not FileAccess.file_exists(file):
|
|
491
|
-
return {
|
|
516
|
+
return {
|
|
517
|
+
"ok": false,
|
|
518
|
+
"error": GdhFileWalk.new().with_nearest_files("No resource at " + file, file),
|
|
519
|
+
}
|
|
492
520
|
var whole: Resource = ResourceLoader.load(file, "", ResourceLoader.CACHE_MODE_IGNORE)
|
|
493
521
|
if whole == null:
|
|
494
|
-
return {"ok": false, "error": "%s did not load as a resource." % file}
|
|
522
|
+
return { "ok": false, "error": "%s did not load as a resource." % file }
|
|
495
523
|
# The file's resource is held until the read is done, since what it keeps can depend on it: a
|
|
496
524
|
# TileSet that goes unlinks its sources, and an atlas source with no TileSet lists none of its
|
|
497
525
|
# tiles' physics, navigation or custom data.
|
|
@@ -500,14 +528,20 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
|
500
528
|
var id: String = res_path.trim_prefix(file + "::")
|
|
501
529
|
var inner: Resource = _kept_in(whole, file, id, {})
|
|
502
530
|
if inner == null:
|
|
503
|
-
return {"ok": false, "error": "%s keeps no resource with the id %s." % [file, id]}
|
|
531
|
+
return { "ok": false, "error": "%s keeps no resource with the id %s." % [file, id] }
|
|
504
532
|
loaded = inner
|
|
505
533
|
# A scene's own properties are its packed bundle, which says nothing a caller can use.
|
|
506
534
|
if loaded is PackedScene:
|
|
507
535
|
return {
|
|
508
536
|
"ok": false,
|
|
509
537
|
"error":
|
|
510
|
-
|
|
538
|
+
(
|
|
539
|
+
(
|
|
540
|
+
"%s is a scene: scene_tree reads its nodes, and scene_node get one node's "
|
|
541
|
+
+ "properties."
|
|
542
|
+
)
|
|
543
|
+
% res_path
|
|
544
|
+
),
|
|
511
545
|
}
|
|
512
546
|
|
|
513
547
|
var script: Script = loaded.get_script()
|
|
@@ -525,13 +559,17 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
|
525
559
|
untooled and file.ends_with(".tres") and not GdhShapes.STORED_APART.has(loaded.get_class())
|
|
526
560
|
)
|
|
527
561
|
var set_in_file: Dictionary = (
|
|
528
|
-
_names_set_in(
|
|
562
|
+
_names_set_in(
|
|
563
|
+
FileAccess.get_file_as_string(file), res_path.trim_prefix(file).trim_prefix("::")
|
|
564
|
+
)
|
|
529
565
|
if from_file
|
|
530
566
|
else {}
|
|
531
567
|
)
|
|
532
568
|
# A Curve's points are read off one with none, since the engine has no default for point_0.
|
|
533
569
|
var fresh: Object = (
|
|
534
|
-
ClassDB.instantiate(loaded.get_class())
|
|
570
|
+
ClassDB.instantiate(loaded.get_class())
|
|
571
|
+
if GdhShapes.STORED_APART.has(loaded.get_class())
|
|
572
|
+
else null
|
|
535
573
|
)
|
|
536
574
|
for p: Dictionary in GdhShapes.read_properties(loaded):
|
|
537
575
|
var property: String = str(p.get("name", ""))
|
|
@@ -554,7 +592,10 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
|
554
592
|
continue
|
|
555
593
|
properties[property] = serialiser.serialize_value(value)
|
|
556
594
|
var answer: Dictionary = {
|
|
557
|
-
"ok": true,
|
|
595
|
+
"ok": true,
|
|
596
|
+
"resourcePath": res_path,
|
|
597
|
+
"resourceType": loaded.get_class(),
|
|
598
|
+
"properties": properties,
|
|
558
599
|
}
|
|
559
600
|
if script != null:
|
|
560
601
|
answer["script"] = script.resource_path
|
|
@@ -566,8 +607,8 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
|
566
607
|
return answer
|
|
567
608
|
|
|
568
609
|
|
|
569
|
-
## The names the [code][resource][/code] section of [param text], a .tres file, sets, or those of
|
|
570
|
-
## embedded resource with the id [param id] when one is given, as a set.
|
|
610
|
+
## The names the [code][resource][/code] section of [param text], a .tres file, sets, or those of
|
|
611
|
+
## the embedded resource with the id [param id] when one is given, as a set.
|
|
571
612
|
static func _names_set_in(text: String, id: String) -> Dictionary:
|
|
572
613
|
var names: Dictionary = {}
|
|
573
614
|
var inside: bool = false
|
|
@@ -23,12 +23,15 @@ static func missing_reason(path: String) -> String:
|
|
|
23
23
|
if id == ResourceUID.INVALID_ID or not ResourceUID.has_id(id):
|
|
24
24
|
return (
|
|
25
25
|
path
|
|
26
|
-
+ " does not resolve: no file in the project's uid cache has it, so the file has
|
|
27
|
-
+ " project has not been imported yet (project_import refresh_uids
|
|
26
|
+
+ " does not resolve: no file in the project's uid cache has it, so the file has "
|
|
27
|
+
+ "gone or the project has not been imported yet (project_import refresh_uids "
|
|
28
|
+
+ "imports it)"
|
|
28
29
|
)
|
|
29
30
|
var resolved: String = ResourceUID.get_id_path(id)
|
|
30
31
|
return (
|
|
31
|
-
""
|
|
32
|
+
""
|
|
33
|
+
if FileAccess.file_exists(resolved)
|
|
34
|
+
else path + " names " + resolved + ", which does not exist"
|
|
32
35
|
)
|
|
33
36
|
return "" if FileAccess.file_exists(path) else path + " does not exist"
|
|
34
37
|
|
|
@@ -41,7 +44,9 @@ func with_nearest_files(said: String, path: String) -> String:
|
|
|
41
44
|
# One the refusal names is the file it refuses, as a script given for a scene is. Word by word,
|
|
42
45
|
# since res://brain.gd is inside res://brain.gdd.
|
|
43
46
|
var named: Array[String] = []
|
|
44
|
-
for word: String in said.replace("\n", " ").replace(",", " ").replace(";", " ").split(
|
|
47
|
+
for word: String in said.replace("\n", " ").replace(",", " ").replace(";", " ").split(
|
|
48
|
+
" ", false
|
|
49
|
+
):
|
|
45
50
|
named.append(word.rstrip(".:"))
|
|
46
51
|
var namesakes: Array[String] = []
|
|
47
52
|
for file: String in _namesakes(path):
|
|
@@ -78,7 +83,9 @@ func with_nearest_files(said: String, path: String) -> String:
|
|
|
78
83
|
)
|
|
79
84
|
if files.size() <= GdhRead.NAMED_CHILDREN:
|
|
80
85
|
return "%s. The project's .%s files are %s." % [said, extension, ", ".join(files)]
|
|
81
|
-
return
|
|
86
|
+
return (
|
|
87
|
+
"%s. None of the project's %d .%s files is named like it." % [said, files.size(), extension]
|
|
88
|
+
)
|
|
82
89
|
|
|
83
90
|
|
|
84
91
|
# The files in [param path]'s folder named as it is with another extension, leaving out the import
|