gdharness 1.3.0 → 1.3.2

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.
Files changed (72) hide show
  1. package/build/cli.js +322 -96
  2. package/build/godot/addons/gdharness_editor/animation_reads.gd +7 -19
  3. package/build/godot/addons/gdharness_editor/bridge_client.gd +8 -8
  4. package/build/godot/addons/gdharness_editor/declared_values.gd +116 -0
  5. package/build/godot/addons/gdharness_editor/declared_values.gd.uid +1 -0
  6. package/build/godot/addons/gdharness_editor/file_reads.gd +65 -25
  7. package/build/godot/addons/gdharness_editor/headless_check.gd +7 -7
  8. package/build/godot/addons/gdharness_editor/plugin.gd +6 -6
  9. package/build/godot/addons/gdharness_editor/property_values.gd +222 -59
  10. package/build/godot/addons/gdharness_editor/reading.gd +65 -0
  11. package/build/godot/addons/gdharness_editor/scene_file.gd +28 -23
  12. package/build/godot/addons/gdharness_editor/serialisation.gd +49 -94
  13. package/build/godot/addons/gdharness_editor/tool_executor.gd +24 -24
  14. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +52 -41
  15. package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +4 -4
  16. package/build/godot/addons/gdharness_editor/tools/play_tools.gd +2 -2
  17. package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +14 -14
  18. package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +175 -74
  19. package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +17 -17
  20. package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +14 -12
  21. package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +7 -7
  22. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +88 -84
  23. package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +4 -4
  24. package/build/godot/addons/gdharness_editor/value_shapes.gd +118 -0
  25. package/build/godot/addons/gdharness_editor/value_shapes.gd.uid +1 -0
  26. package/build/godot/addons/gdharness_runtime/reading.gd +65 -0
  27. package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +18 -18
  28. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +29 -29
  29. package/build/godot/addons/gdharness_runtime/runtime_capture.gd +21 -18
  30. package/build/godot/addons/gdharness_runtime/runtime_changes.gd +86 -80
  31. package/build/godot/addons/gdharness_runtime/runtime_input.gd +65 -64
  32. package/build/godot/addons/gdharness_runtime/runtime_items.gd +6 -6
  33. package/build/godot/addons/gdharness_runtime/runtime_menus.gd +8 -8
  34. package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +6 -6
  35. package/build/godot/addons/gdharness_runtime/runtime_pad.gd +10 -10
  36. package/build/godot/addons/gdharness_runtime/runtime_paths.gd +7 -7
  37. package/build/godot/addons/gdharness_runtime/runtime_queries.gd +41 -41
  38. package/build/godot/addons/gdharness_runtime/runtime_says.gd +3 -3
  39. package/build/godot/addons/gdharness_runtime/runtime_targets.gd +33 -33
  40. package/build/godot/addons/gdharness_runtime/runtime_typing.gd +20 -20
  41. package/build/godot/addons/gdharness_runtime/runtime_values.gd +52 -25
  42. package/build/godot/addons/gdharness_runtime/runtime_waits.gd +56 -50
  43. package/build/godot/addons/gdharness_runtime/serialisation.gd +49 -94
  44. package/build/godot/addons/gdharness_runtime/value_shapes.gd +118 -0
  45. package/build/godot/addons/gdharness_runtime/value_shapes.gd.uid +1 -0
  46. package/build/godot/operations/animation_reads.gd +7 -19
  47. package/build/godot/operations/audio_buses.gd +10 -10
  48. package/build/godot/operations/class_cache.gd +16 -16
  49. package/build/godot/operations/classdb_queries.gd +25 -15
  50. package/build/godot/operations/declared_values.gd +116 -0
  51. package/build/godot/operations/dependencies.gd +141 -33
  52. package/build/godot/operations/editor_tools.gd +12 -12
  53. package/build/godot/operations/export_presets.gd +14 -14
  54. package/build/godot/operations/file_reads.gd +65 -25
  55. package/build/godot/operations/file_walk.gd +34 -0
  56. package/build/godot/operations/gdscript_analysis.gd +27 -22
  57. package/build/godot/operations/gdscript_authoring.gd +122 -17
  58. package/build/godot/operations/godot_operations.gd +66 -63
  59. package/build/godot/operations/import_pipeline.gd +59 -20
  60. package/build/godot/operations/input_actions.gd +90 -18
  61. package/build/godot/operations/logger.gd +1 -1
  62. package/build/godot/operations/plugins.gd +8 -8
  63. package/build/godot/operations/project_config.gd +83 -20
  64. package/build/godot/operations/project_diagnostics.gd +36 -11
  65. package/build/godot/operations/property_values.gd +222 -59
  66. package/build/godot/operations/reading.gd +65 -0
  67. package/build/godot/operations/rename_checks.gd +3 -3
  68. package/build/godot/operations/resource_files.gd +9 -6
  69. package/build/godot/operations/script_check.gd +23 -7
  70. package/build/godot/operations/serialisation.gd +49 -94
  71. package/build/godot/operations/value_shapes.gd +118 -0
  72. package/package.json +1 -1
@@ -4,9 +4,8 @@ extends RefCounted
4
4
  ## and by a headless engine with none connected from a bare node of the class given what the file
5
5
  ## stores, which runs no script of the scene's, so a read answers the same either way.
6
6
 
7
- const FileReads = preload("file_reads.gd")
8
- const Read = preload("reading.gd")
9
- const Serialisation = preload("serialisation.gd")
7
+ const GdhFileReads = preload("file_reads.gd")
8
+ const GdhSerialisation = preload("serialisation.gd")
10
9
 
11
10
  const LOOP_MODE_NAMES: Dictionary[int, String] = {
12
11
  Animation.LOOP_NONE: "none",
@@ -56,21 +55,10 @@ static func scene_states(scene_path: String, tree_path: String, state_machine_pa
56
55
  ## A node of the class the file gives the node at [param node_path], holding every property the
57
56
  ## files set but its script, as [node, null] or [null, refusal]. The caller frees it.
58
57
  static func _bare(scene_path: String, node_path: String, kind: String) -> Array:
59
- var read: Dictionary = FileReads.scene_node(scene_path, node_path)
60
- if not read.is_empty() and not read.get("ok", false):
58
+ var read: Dictionary = GdhFileReads.scene_node_of_kind(scene_path, node_path, kind)
59
+ if not read.get("ok", false):
61
60
  return [null, read]
62
- var type: String = str(read.get("type", ""))
63
- if not ClassDB.class_exists(type) or not ClassDB.is_parent_class(type, kind):
64
- var said: String = "%s not found at: %s" % [kind, node_path]
65
- if read.is_empty():
66
- var hinted: String = said
67
- if FileReads.scene_node(scene_path, node_path.get_slice("/", 0)).is_empty():
68
- hinted = Read.from_the_root(
69
- said, node_path, str(FileReads.scene_node(scene_path, ".").get("name", ""))
70
- )
71
- said = hinted if hinted != said else FileReads.missing_from(said, scene_path, node_path)
72
- return [null, {"ok": false, "error": said}]
73
- var bare: Node = ClassDB.instantiate(type)
61
+ var bare: Node = ClassDB.instantiate(str(read["type"]))
74
62
  bare.name = str(read["name"])
75
63
  var given: Dictionary = read["properties"]
76
64
  for property: Variant in given:
@@ -107,7 +95,7 @@ static func animations(player: AnimationPlayer, animation_name: String) -> Dicti
107
95
  answer["ok"] = true
108
96
  answer["animationName"] = animation_name
109
97
  var tracks: Array[Dictionary] = []
110
- var values: Serialisation = Serialisation.new()
98
+ var values: GdhSerialisation = GdhSerialisation.new()
111
99
  for track: int in anim.get_track_count():
112
100
  tracks.append(_track(anim, track, values))
113
101
  answer["tracks"] = tracks
@@ -122,7 +110,7 @@ static func _summary(anim: Animation) -> Dictionary:
122
110
  }
123
111
 
124
112
 
125
- static func _track(anim: Animation, track: int, values: Serialisation) -> Dictionary:
113
+ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dictionary:
126
114
  var kind: int = anim.track_get_type(track)
127
115
  var path: NodePath = anim.track_get_path(track)
128
116
  var shaped: Dictionary = {
@@ -8,7 +8,7 @@ signal connected
8
8
  signal disconnected
9
9
  signal tool_requested(request_id: String, tool_name: String, args: Dictionary)
10
10
 
11
- const Read = preload("reading.gd")
11
+ const GdhRead = preload("reading.gd")
12
12
 
13
13
  const DEFAULT_URL: String = "ws://127.0.0.1:6505/godot"
14
14
  ## Written beside the addon by the install, so it names the version this copy came from.
@@ -256,15 +256,15 @@ func announced_url() -> String:
256
256
  var file: FileAccess = FileAccess.open(ANNOUNCEMENT, FileAccess.READ)
257
257
  if file == null:
258
258
  return ""
259
- var said: Variant = Read.json_or_null(file.get_as_text())
259
+ var said: Variant = GdhRead.json_or_null(file.get_as_text())
260
260
  file.close()
261
261
  if not said is Dictionary:
262
262
  return ""
263
263
 
264
264
  var announcement: Dictionary = said
265
- if Read.as_int(announcement.get("protocol", 0)) != ANNOUNCE_PROTOCOL:
265
+ if GdhRead.as_int(announcement.get("protocol", 0)) != ANNOUNCE_PROTOCOL:
266
266
  return ""
267
- var port: int = Read.as_int(announcement.get("port", 0))
267
+ var port: int = GdhRead.as_int(announcement.get("port", 0))
268
268
  if port < 1 or port > 65535:
269
269
  return ""
270
270
  var host: String = str(announcement.get("host", "127.0.0.1"))
@@ -382,7 +382,7 @@ func _keep_breakpoints_through_sessions() -> void:
382
382
  var settings: EditorSettings = EditorInterface.get_editor_settings()
383
383
  if settings == null or not settings.has_setting(SYNC_BREAKPOINTS_SETTING):
384
384
  return
385
- if Read.as_bool(settings.get_setting(SYNC_BREAKPOINTS_SETTING)):
385
+ if GdhRead.as_bool(settings.get_setting(SYNC_BREAKPOINTS_SETTING)):
386
386
  return
387
387
  settings.set_setting(SYNC_BREAKPOINTS_SETTING, true)
388
388
  # Stored is not the same as noticed. The adapter reads this setting again on the editor-wide
@@ -406,7 +406,7 @@ func _syncs_breakpoints() -> bool:
406
406
  var settings: EditorSettings = EditorInterface.get_editor_settings()
407
407
  if settings == null or not settings.has_setting(SYNC_BREAKPOINTS_SETTING):
408
408
  return false
409
- return Read.as_bool(settings.get_setting(SYNC_BREAKPOINTS_SETTING))
409
+ return GdhRead.as_bool(settings.get_setting(SYNC_BREAKPOINTS_SETTING))
410
410
 
411
411
 
412
412
  ## What the settings say this editor serves on, or 0 when there is nothing to ask.
@@ -416,7 +416,7 @@ func _serving(setting: String) -> int:
416
416
  var settings: EditorSettings = EditorInterface.get_editor_settings()
417
417
  if settings == null or not settings.has_setting(setting):
418
418
  return 0
419
- return Read.as_int(settings.get_setting(setting))
419
+ return GdhRead.as_int(settings.get_setting(setting))
420
420
 
421
421
 
422
422
  ## The version marker beside this addon, or "" when the copy was not installed by gdharness.
@@ -497,7 +497,7 @@ func _on_reconnect_timer() -> void:
497
497
 
498
498
 
499
499
  func _handle_message(json_string: String) -> void:
500
- var parsed: Variant = Read.json_or_null(json_string)
500
+ var parsed: Variant = GdhRead.json_or_null(json_string)
501
501
  if not parsed is Dictionary:
502
502
  push_error("[gdharness] The server sent something that is not a message: %s" % json_string)
503
503
  return
@@ -0,0 +1,116 @@
1
+ @tool
2
+ extends RefCounted
3
+
4
+ ## What a property declares beyond its type: the values of its enum and the range it is exported
5
+ ## with, for taking a value by name and for saying when a value written lies outside them.
6
+
7
+ const GdhRead = preload("reading.gd")
8
+
9
+
10
+ ## The values an enum hint names, by name, from its hint string: "SWORD:0,SHIELD:1" as a script
11
+ ## declares one, or "Add,Subtract" as an engine property does, each name then taking the number
12
+ ## after the one before it.
13
+ static func enum_values(hint_string: String) -> Dictionary:
14
+ var values: Dictionary = {}
15
+ var next: int = 0
16
+ for entry: String in hint_string.split(",", false):
17
+ var name: String = entry.get_slice(":", 0).strip_edges()
18
+ var number: String = entry.get_slice(":", 1).strip_edges() if ":" in entry else ""
19
+ var value: int = number.to_int() if number.is_valid_int() else next
20
+ values[name] = value
21
+ next = value + 1
22
+ return values
23
+
24
+
25
+ ## The values of the enum [param declared] declares, by name, spelt as the script of [param target]
26
+ ## spells them when it declares that enum. The engine's hint capitalises each name, "SWORD" as
27
+ ## "Sword", and a refusal listing those named values the script never spells.
28
+ static func enum_of(target: Object, declared: Dictionary) -> Dictionary:
29
+ var hinted: Dictionary = enum_values(str(declared.get("hint_string", "")))
30
+ var attached: Variant = target.get_script()
31
+ if not attached is Script:
32
+ return hinted
33
+ var script: Script = attached
34
+ for constant: Variant in script.get_script_constant_map().values():
35
+ if not constant is Dictionary:
36
+ continue
37
+ var values: Dictionary = constant
38
+ if values.size() != hinted.size():
39
+ continue
40
+ var spelt: Dictionary = {}
41
+ for name: Variant in values:
42
+ if hinted.get(str(name).capitalize()) == values[name]:
43
+ spelt[str(name)] = values[name]
44
+ if spelt.size() == hinted.size():
45
+ return spelt
46
+ return hinted
47
+
48
+
49
+ ## The number [param word] names among [param choices], matched as written and then ignoring case,
50
+ ## or null when it names none.
51
+ static func enum_named(choices: Dictionary, word: String) -> Variant:
52
+ var wanted: String = word.strip_edges()
53
+ if choices.has(wanted):
54
+ return choices[wanted]
55
+ for name: String in choices:
56
+ if name.to_lower() == wanted.to_lower():
57
+ return choices[name]
58
+ return null
59
+
60
+
61
+ static func enum_refusal(holder: String, property: String, word: String, choices: Dictionary) -> String:
62
+ var said: String = (
63
+ "%s.%s is an enum, and %s is none of its values: send %s by name or by number"
64
+ % [holder, property, word, _listed(choices)]
65
+ )
66
+ var close: PackedStringArray = GdhRead.nearest(word, PackedStringArray(choices.keys()))
67
+ if close.is_empty():
68
+ return said + "."
69
+ return said + ". The nearest %s %s." % ["is" if close.size() == 1 else "are", ", ".join(close)]
70
+
71
+
72
+ ## Why [param value], shown as [param shown], lies outside what [param declared] allows, an export
73
+ ## range or an enum's values, or "" when it does not. A range marked or_greater or or_less is open
74
+ ## on that side.
75
+ static func outside(target: Object, declared: Dictionary, value: Variant, shown: String) -> String:
76
+ var hint: int = GdhRead.as_int(declared.get("hint", PROPERTY_HINT_NONE))
77
+ var hint_string: String = str(declared.get("hint_string", ""))
78
+ var property: String = str(declared.get("name", ""))
79
+ if typeof(value) != TYPE_INT and typeof(value) != TYPE_FLOAT:
80
+ return ""
81
+ var number: float = GdhRead.as_float(value)
82
+ if hint == PROPERTY_HINT_ENUM and typeof(value) == TYPE_INT:
83
+ var choices: Dictionary = enum_of(target, declared)
84
+ if choices.is_empty() or choices.values().has(value):
85
+ return ""
86
+ return "%d is none of the values %s declares: %s." % [value, property, _listed(choices)]
87
+ if hint != PROPERTY_HINT_RANGE:
88
+ return ""
89
+ var parts: PackedStringArray = []
90
+ for part: String in hint_string.split(","):
91
+ var _added: bool = parts.append(part.strip_edges())
92
+ if parts.size() < 2 or not parts[0].is_valid_float() or not parts[1].is_valid_float():
93
+ return ""
94
+ var low: float = parts[0].to_float()
95
+ var high: float = parts[1].to_float()
96
+ if (number <= high or parts.has("or_greater")) and (number >= low or parts.has("or_less")):
97
+ return ""
98
+ return (
99
+ (
100
+ "%s is outside the range %s declares, %s to %s, which the inspector holds it to; the "
101
+ + "engine stores it as given."
102
+ )
103
+ % [shown, property, _bound(low), _bound(high)]
104
+ )
105
+
106
+
107
+ static func _listed(choices: Dictionary) -> String:
108
+ var listed: PackedStringArray = []
109
+ for name: String in choices:
110
+ var _added: bool = listed.append("%s (%d)" % [name, choices[name]])
111
+ return ", ".join(listed)
112
+
113
+
114
+ ## A range's end as the script wrote it: the engine's hint gives @export_range(0, 10) as 0.0 and 10.0.
115
+ static func _bound(end: float) -> String:
116
+ return str(int(end)) if end == floorf(end) and absf(end) < 1e15 else str(end)
@@ -0,0 +1 @@
1
+ uid://c2bcafdf9rzgo
@@ -10,9 +10,9 @@ extends RefCounted
10
10
  ## without making a node. Loading the file still does what loading does in the game: static
11
11
  ## initialisers run, and a resource with a script is made with it.
12
12
 
13
- const PropertyValues = preload("property_values.gd")
14
- const Read = preload("reading.gd")
15
- const Serialisation = preload("serialisation.gd")
13
+ const GdhPropertyValues = preload("property_values.gd")
14
+ const GdhRead = preload("reading.gd")
15
+ const GdhSerialisation = preload("serialisation.gd")
16
16
 
17
17
 
18
18
  ## The nodes of [param scene_path] as {name, type, path, children}, with instanceOf and
@@ -42,13 +42,43 @@ static func scene_node(scene_path: String, node_path: String) -> Dictionary:
42
42
  return {"ok": true, "name": found["name"], "type": found["type"], "properties": found["properties"]}
43
43
 
44
44
 
45
- ## [param said], the refusal for [param node_path], which [param scene_path] does not have, with
46
- ## where the path leaves the scene as [method Read.missing_below] says it.
47
- static func missing_from(said: String, scene_path: String, node_path: String) -> String:
45
+ ## The node at [param node_path] in [param scene_path] as [method scene_node] answers it, when it is
46
+ ## a [param kind]; otherwise a refusal saying what the scene has there and where its nodes of that
47
+ ## kind are, so the caller has the path to pass. The file is read once, because every load prints
48
+ ## the engine's warnings about it again.
49
+ static func scene_node_of_kind(scene_path: String, node_path: String, kind: String) -> Dictionary:
48
50
  var laid: Dictionary = _laid(scene_path)
49
51
  if not laid.get("ok", false):
50
- return said
52
+ return laid
51
53
  var nodes: Dictionary = laid["nodes"]
54
+ var path: String = _joined(".", node_path)
55
+ var said: String = "%s not found at: %s" % [kind, node_path]
56
+ var found: String = ""
57
+ if nodes.has(path):
58
+ found = str(nodes[path]["type"])
59
+ if ClassDB.class_exists(found) and ClassDB.is_parent_class(found, kind):
60
+ return {
61
+ "ok": true,
62
+ "name": nodes[path]["name"],
63
+ "type": found,
64
+ "properties": nodes[path]["properties"]
65
+ }
66
+ else:
67
+ var hinted: String = said
68
+ if not nodes.has(_joined(".", node_path.get_slice("/", 0))):
69
+ hinted = GdhRead.from_the_root(said, node_path, str(nodes["."]["name"]))
70
+ said = hinted if hinted != said else _missing_in(nodes, said, node_path)
71
+ var held: PackedStringArray = []
72
+ for at: Variant in nodes:
73
+ var type: String = str(nodes[at]["type"])
74
+ if ClassDB.class_exists(type) and ClassDB.is_parent_class(type, kind):
75
+ var _held: bool = held.append(str(at))
76
+ return {"ok": false, "error": GdhRead.not_of_kind(said, node_path, found, kind, held)}
77
+
78
+
79
+ ## [param said], the refusal for [param node_path], which the scene laid as [param nodes] does not
80
+ ## have, with where the path leaves the scene as [method GdhRead.missing_below] says it.
81
+ static func _missing_in(nodes: Dictionary, said: String, node_path: String) -> String:
52
82
  var parts: PackedStringArray = node_path.trim_prefix("./").split("/", false)
53
83
  var at: String = "."
54
84
  var reached: PackedStringArray = []
@@ -64,7 +94,7 @@ static func missing_from(said: String, scene_path: String, node_path: String) ->
64
94
  var children: Array[String] = nodes[at]["children"]
65
95
  for child: String in children:
66
96
  var _named: bool = names.append(str(nodes[child]["name"]))
67
- return Read.missing_below(said, str(nodes["."]["name"]), reached, parts[reached.size()], names)
97
+ return GdhRead.missing_below(said, str(nodes["."]["name"]), reached, parts[reached.size()], names)
68
98
 
69
99
 
70
100
  ## Every node of [param scene_path] as {ok, nodes}, keyed by path, or a refusal.
@@ -77,15 +107,14 @@ static func _laid(scene_path: String) -> Dictionary:
77
107
  if loaded == null:
78
108
  return {"ok": false, "error": "Failed to load %s as a scene" % scene_path}
79
109
  if not loaded is PackedScene:
80
- var kind: String = loaded.get_class()
81
- var sort: String = ("an " if kind.left(1).to_upper() in ["A", "E", "I", "O", "U"] else "a ") + kind
110
+ var sort: String = GdhRead.with_article(loaded.get_class())
82
111
  return {
83
112
  "ok": false,
84
113
  "error": "%s is %s, not a scene: resource_edit read reads its properties." % [scene_path, sort]
85
114
  }
86
115
  var packed: PackedScene = loaded
87
116
  var nodes: Dictionary = {}
88
- var refused: String = _lay(packed.get_state(), ".", nodes, [scene_path])
117
+ var refused: String = _lay(packed.get_state(), ".", nodes, [scene_path], packed)
89
118
  if not refused.is_empty():
90
119
  return {"ok": false, "error": refused}
91
120
  if not nodes.has("."):
@@ -99,8 +128,11 @@ static func _laid(scene_path: String) -> Dictionary:
99
128
  ##
100
129
  ## [param within] is the files being laid, outermost first, so a scene that instances one of them
101
130
  ## is refused rather than laid forever; the engine refuses to load such a scene, so this is only
102
- ## reached through a state built some other way.
103
- static func _lay(state: SceneState, prefix: String, nodes: Dictionary, within: Array) -> String:
131
+ ## reached through a state built some other way. [param loaded] is the scene [param state] is read
132
+ ## from, when the caller has it loaded already.
133
+ static func _lay(
134
+ state: SceneState, prefix: String, nodes: Dictionary, within: Array, loaded: PackedScene = null
135
+ ) -> String:
104
136
  var base: SceneState = state.get_base_scene_state()
105
137
  if base != null and not nodes.has(prefix):
106
138
  var refused: String = _lay(base, prefix, nodes, within)
@@ -164,7 +196,7 @@ static func _lay(state: SceneState, prefix: String, nodes: Dictionary, within: A
164
196
  # Only the scene being read and the scenes it inherits share its root, the owner whose editable
165
197
  # instances a write here sets; a file instanced below makes its own owner's editable.
166
198
  if prefix == ".":
167
- for editable: String in _editable_in(state.get_path()):
199
+ for editable: String in _editable_in(state.get_path(), loaded):
168
200
  var at: String = _joined(prefix, editable)
169
201
  var marked: Dictionary = nodes.get(at, {})
170
202
  if marked.has("editableChildren"):
@@ -173,10 +205,13 @@ static func _lay(state: SceneState, prefix: String, nodes: Dictionary, within: A
173
205
 
174
206
 
175
207
  ## The paths of the instances [param scene_path] gives editable children, from its root. SceneState
176
- ## does not give them to a script, so they are read off the scene's bundle.
177
- static func _editable_in(scene_path: String) -> PackedStringArray:
208
+ ## does not give them to a script, so they are read off the scene's bundle: [param given_scene]'s,
209
+ ## when the caller has the file loaded, since loading it again prints its warnings again.
210
+ static func _editable_in(scene_path: String, given_scene: PackedScene = null) -> PackedStringArray:
178
211
  var found: PackedStringArray = []
179
- var loaded: Resource = ResourceLoader.load(scene_path, "", ResourceLoader.CACHE_MODE_IGNORE)
212
+ var loaded: Resource = given_scene
213
+ if loaded == null:
214
+ loaded = ResourceLoader.load(scene_path, "", ResourceLoader.CACHE_MODE_IGNORE)
180
215
  if not loaded is PackedScene:
181
216
  return found
182
217
  var given: Variant = loaded.get("_bundled")
@@ -236,7 +271,7 @@ static func _by_id(nodes: Dictionary, owner: String, id: int) -> String:
236
271
  if path == owner or not (owner == "." or path.begins_with(owner + "/")):
237
272
  continue
238
273
  var entry: Dictionary = nodes[path]
239
- if Read.as_int(entry.get("uniqueId", -1), -1) != id:
274
+ if GdhRead.as_int(entry.get("uniqueId", -1), -1) != id:
240
275
  continue
241
276
  var above: String = "." if not path.contains("/") else path.get_base_dir()
242
277
  if _owner_of(nodes, above, owner) == owner:
@@ -311,7 +346,7 @@ static func _stored(type: String, given: Dictionary) -> Dictionary:
311
346
  if script != null:
312
347
  for declared: Dictionary in script.get_script_property_list():
313
348
  var property: String = str(declared.get("name", ""))
314
- if not property.is_empty() and Read.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE:
349
+ if not property.is_empty() and GdhRead.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE:
315
350
  scripted[property] = script.get_property_default_value(property)
316
351
  var values: Dictionary = {}
317
352
  if ClassDB.class_exists(type) and ClassDB.can_instantiate(type):
@@ -321,7 +356,7 @@ static func _stored(type: String, given: Dictionary) -> Dictionary:
321
356
  bare.set(str(property), given[property])
322
357
  for declared: Dictionary in bare.get_property_list():
323
358
  var property: String = str(declared.get("name", ""))
324
- if not property.is_empty() and Read.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE:
359
+ if not property.is_empty() and GdhRead.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE:
325
360
  values[property] = bare.get(property)
326
361
  if bare is Node:
327
362
  bare.free()
@@ -330,7 +365,7 @@ static func _stored(type: String, given: Dictionary) -> Dictionary:
330
365
  # Given last, which keeps what a bare node cannot hold: the script itself, and a path to a node
331
366
  # for a property that holds the node once it is in a tree.
332
367
  values.merge(given, true)
333
- var serialiser: Serialisation = Serialisation.new()
368
+ var serialiser: GdhSerialisation = GdhSerialisation.new()
334
369
  var answered: Dictionary = {}
335
370
  for property: Variant in values:
336
371
  answered[property] = serialiser.serialize_value(values[property])
@@ -369,13 +404,13 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
369
404
  if script != null:
370
405
  for declared: Dictionary in script.get_script_property_list():
371
406
  scripted[str(declared.get("name", ""))] = true
372
- var serialiser: Serialisation = Serialisation.new()
407
+ var serialiser: GdhSerialisation = GdhSerialisation.new()
373
408
  var properties: Dictionary = {}
374
409
  for p: Dictionary in loaded.get_property_list():
375
410
  var property: String = str(p.get("name", ""))
376
411
  if property.is_empty() or property == "script":
377
412
  continue
378
- if not (Read.as_int(p.get("usage", 0)) & PROPERTY_USAGE_STORAGE):
413
+ if not (GdhRead.as_int(p.get("usage", 0)) & PROPERTY_USAGE_STORAGE):
379
414
  continue
380
415
  var value: Variant = loaded.get(property)
381
416
  if not include_defaults:
@@ -384,7 +419,7 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
384
419
  if scripted.has(property)
385
420
  else ClassDB.class_get_property_default_value(loaded.get_class(), property)
386
421
  )
387
- if PropertyValues.same(value, default):
422
+ if GdhPropertyValues.same(value, default):
388
423
  continue
389
424
  properties[property] = serialiser.serialize_value(value)
390
425
  var answer: Dictionary = {
@@ -392,6 +427,11 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
392
427
  }
393
428
  if script != null:
394
429
  answer["script"] = script.resource_path
430
+ # resourceType is the engine class under the script, which for a class the project declares
431
+ # is Resource whatever create was given.
432
+ var declared_class: String = str(script.get_global_name())
433
+ if not declared_class.is_empty():
434
+ answer["scriptClass"] = declared_class
395
435
  return answer
396
436
 
397
437
 
@@ -412,7 +452,7 @@ static func _kept_in(value: Variant, file: String, id: String, seen: Dictionary)
412
452
  if held.resource_scene_unique_id == id:
413
453
  return held
414
454
  for p: Dictionary in held.get_property_list():
415
- if Read.as_int(p.get("usage", 0)) & PROPERTY_USAGE_STORAGE:
455
+ if GdhRead.as_int(p.get("usage", 0)) & PROPERTY_USAGE_STORAGE:
416
456
  var found: Resource = _kept_in(held.get(str(p.get("name", ""))), file, id, seen)
417
457
  if found != null:
418
458
  return found
@@ -11,8 +11,8 @@ extends RefCounted
11
11
  ## both, so a property a built-in setter derives (a Control's offsets from its size) is left alone
12
12
  ## unless a script that is not a tool is involved.
13
13
 
14
- const PropertyValues = preload("property_values.gd")
15
- const Read = preload("reading.gd")
14
+ const GdhPropertyValues = preload("property_values.gd")
15
+ const GdhRead = preload("reading.gd")
16
16
 
17
17
  const REMEDY: String = (
18
18
  "Open the project's editor (editor_launch with hidden: true) and ask again: the write then goes "
@@ -25,7 +25,7 @@ const REMEDY: String = (
25
25
  static func stored(object: Object) -> Dictionary:
26
26
  var values: Dictionary = {}
27
27
  for declared: Dictionary in object.get_property_list():
28
- if not (Read.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE):
28
+ if not (GdhRead.as_int(declared.get("usage", 0)) & PROPERTY_USAGE_STORAGE):
29
29
  continue
30
30
  var name: String = str(declared.get("name", ""))
31
31
  if name.is_empty():
@@ -78,7 +78,7 @@ static func differing(object: Object, before: Dictionary, names: Dictionary) ->
78
78
  if names.has(name):
79
79
  continue
80
80
  var was: Variant = before[name] if before.has(name) else default_of(object, name)
81
- if not PropertyValues.same(now[name], was):
81
+ if not GdhPropertyValues.same(now[name], was):
82
82
  changed.append(name)
83
83
  return changed
84
84
 
@@ -93,7 +93,7 @@ static func default_of(object: Object, name: String) -> Variant:
93
93
  var given: Variant = script.get_property_default_value(name)
94
94
  if given != null:
95
95
  return given
96
- return _initialised(script, name, Read.as_int(declared.get("type", TYPE_NIL)))
96
+ return _initialised(script, name, GdhRead.as_int(declared.get("type", TYPE_NIL)))
97
97
  return ClassDB.class_get_property_default_value(object.get_class(), name)
98
98
 
99
99
 
@@ -122,7 +122,7 @@ static func _initialised(script: Script, name: String, type: int) -> Variant:
122
122
  ## nothing a script did: the saver writes a property whose default the engine cannot report whatever
123
123
  ## it holds, the editor's save as well.
124
124
  static func at_default(object: Object, name: String) -> bool:
125
- return PropertyValues.same(object.get(name), default_of(object, name))
125
+ return GdhPropertyValues.same(object.get(name), default_of(object, name))
126
126
 
127
127
 
128
128
  ## The ids, in the file, of the embedded resources [param property] reaches into on [param object],
@@ -276,7 +276,7 @@ static func reads_the_same(was: Variant, now: Variant, default: Variant) -> bool
276
276
  return false
277
277
  else:
278
278
  values.append(str_to_var(str(text)))
279
- return PropertyValues.same(values[0], values[1])
279
+ return GdhPropertyValues.same(values[0], values[1])
280
280
 
281
281
 
282
282
  static func _main_entries(text: String) -> Dictionary:
@@ -5,8 +5,8 @@ extends EditorPlugin
5
5
  ## that does what the server asks, with a label in the toolbar saying whether the two are
6
6
  ## connected.
7
7
 
8
- const BridgeClient = preload("bridge_client.gd")
9
- const ToolExecutor = preload("tool_executor.gd")
8
+ const GdhBridgeClient = preload("bridge_client.gd")
9
+ const GdhToolExecutor = preload("tool_executor.gd")
10
10
 
11
11
  ## The tools answered while the editor scans or imports, because none of them saves a file: the
12
12
  ## ones a caller waits on a scan with, and the ones that only read or stop a played game.
@@ -19,17 +19,17 @@ const ANSWERED_WHILE_SCANNING: Array[String] = [
19
19
  "profile_read",
20
20
  ]
21
21
 
22
- var _client: BridgeClient
23
- var _tool_executor: ToolExecutor
22
+ var _client: GdhBridgeClient
23
+ var _tool_executor: GdhToolExecutor
24
24
  var _status_label: Label
25
25
 
26
26
 
27
27
  func _enter_tree() -> void:
28
- _client = BridgeClient.new()
28
+ _client = GdhBridgeClient.new()
29
29
  _client.name = "GdharnessBridgeClient"
30
30
  add_child(_client)
31
31
 
32
- _tool_executor = ToolExecutor.new()
32
+ _tool_executor = GdhToolExecutor.new()
33
33
  _tool_executor.name = "GdharnessToolExecutor"
34
34
  add_child(_tool_executor)
35
35
  _tool_executor.set_editor_plugin(self)