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.
- package/build/cli.js +322 -96
- package/build/godot/addons/gdharness_editor/animation_reads.gd +7 -19
- package/build/godot/addons/gdharness_editor/bridge_client.gd +8 -8
- package/build/godot/addons/gdharness_editor/declared_values.gd +116 -0
- package/build/godot/addons/gdharness_editor/declared_values.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd +65 -25
- package/build/godot/addons/gdharness_editor/headless_check.gd +7 -7
- package/build/godot/addons/gdharness_editor/plugin.gd +6 -6
- package/build/godot/addons/gdharness_editor/property_values.gd +222 -59
- package/build/godot/addons/gdharness_editor/reading.gd +65 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +28 -23
- package/build/godot/addons/gdharness_editor/serialisation.gd +49 -94
- package/build/godot/addons/gdharness_editor/tool_executor.gd +24 -24
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +52 -41
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +4 -4
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +2 -2
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +14 -14
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +175 -74
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +17 -17
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +14 -12
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +7 -7
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +88 -84
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +4 -4
- package/build/godot/addons/gdharness_editor/value_shapes.gd +118 -0
- package/build/godot/addons/gdharness_editor/value_shapes.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +65 -0
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +18 -18
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +29 -29
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +21 -18
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +86 -80
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +65 -64
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +6 -6
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +8 -8
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +6 -6
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +10 -10
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +7 -7
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +41 -41
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +3 -3
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +33 -33
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +20 -20
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +52 -25
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +56 -50
- package/build/godot/addons/gdharness_runtime/serialisation.gd +49 -94
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +118 -0
- package/build/godot/addons/gdharness_runtime/value_shapes.gd.uid +1 -0
- package/build/godot/operations/animation_reads.gd +7 -19
- package/build/godot/operations/audio_buses.gd +10 -10
- package/build/godot/operations/class_cache.gd +16 -16
- package/build/godot/operations/classdb_queries.gd +25 -15
- package/build/godot/operations/declared_values.gd +116 -0
- package/build/godot/operations/dependencies.gd +141 -33
- package/build/godot/operations/editor_tools.gd +12 -12
- package/build/godot/operations/export_presets.gd +14 -14
- package/build/godot/operations/file_reads.gd +65 -25
- package/build/godot/operations/file_walk.gd +34 -0
- package/build/godot/operations/gdscript_analysis.gd +27 -22
- package/build/godot/operations/gdscript_authoring.gd +122 -17
- package/build/godot/operations/godot_operations.gd +66 -63
- package/build/godot/operations/import_pipeline.gd +59 -20
- package/build/godot/operations/input_actions.gd +90 -18
- package/build/godot/operations/logger.gd +1 -1
- package/build/godot/operations/plugins.gd +8 -8
- package/build/godot/operations/project_config.gd +83 -20
- package/build/godot/operations/project_diagnostics.gd +36 -11
- package/build/godot/operations/property_values.gd +222 -59
- package/build/godot/operations/reading.gd +65 -0
- package/build/godot/operations/rename_checks.gd +3 -3
- package/build/godot/operations/resource_files.gd +9 -6
- package/build/godot/operations/script_check.gd +23 -7
- package/build/godot/operations/serialisation.gd +49 -94
- package/build/godot/operations/value_shapes.gd +118 -0
- 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
|
|
8
|
-
const
|
|
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 =
|
|
60
|
-
if not read.
|
|
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
|
|
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:
|
|
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:
|
|
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
|
|
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 =
|
|
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
|
|
265
|
+
if GdhRead.as_int(announcement.get("protocol", 0)) != ANNOUNCE_PROTOCOL:
|
|
266
266
|
return ""
|
|
267
|
-
var port: int =
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
14
|
-
const
|
|
15
|
-
const
|
|
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
|
-
##
|
|
46
|
-
##
|
|
47
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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:
|
|
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 (
|
|
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
|
|
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
|
|
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
|
|
15
|
-
const
|
|
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 (
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
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
|
|
9
|
-
const
|
|
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:
|
|
23
|
-
var _tool_executor:
|
|
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 =
|
|
28
|
+
_client = GdhBridgeClient.new()
|
|
29
29
|
_client.name = "GdharnessBridgeClient"
|
|
30
30
|
add_child(_client)
|
|
31
31
|
|
|
32
|
-
_tool_executor =
|
|
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)
|