gdharness 1.3.41 → 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 +131 -47
- package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
- package/build/godot/addons/gdharness_editor/animation_reads.gd +84 -35
- 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 +114 -39
- package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
- package/build/godot/addons/gdharness_editor/headless_check.gd +94 -41
- 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 +136 -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 +41 -28
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +84 -50
- 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 +181 -107
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +75 -43
- 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 +66 -0
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +171 -62
- 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 +153 -148
- 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 +38 -20
- 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 +211 -0
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd.uid +1 -0
- 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 +84 -35
- 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 +114 -39
- package/build/godot/operations/file_walk.gd +12 -5
- package/build/godot/operations/gdscript_analysis.gd +105 -66
- package/build/godot/operations/gdscript_authoring.gd +138 -39
- 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 +63 -39
- package/build/godot/operations/patterns.gd +3 -1
- package/build/godot/operations/plugins.gd +36 -16
- package/build/godot/operations/project_config.gd +144 -27
- 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,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends Node
|
|
3
|
-
|
|
4
3
|
## The global classes the running editor is holding.
|
|
5
4
|
##
|
|
6
5
|
## The editor fixes this list when it starts and adds to it only from a filesystem scan, and
|
|
@@ -51,10 +50,10 @@ func set_editor_plugin(plugin: EditorPlugin) -> void:
|
|
|
51
50
|
func reload_script(args: Dictionary) -> Dictionary:
|
|
52
51
|
var asked: String = str(args.get("scriptPath", ""))
|
|
53
52
|
if asked.is_empty():
|
|
54
|
-
return {"ok": false, "error": "scriptPath is required"}
|
|
53
|
+
return { "ok": false, "error": "scriptPath is required" }
|
|
55
54
|
var path: String = _spelled_as_on_disk(asked)
|
|
56
55
|
if not ResourceLoader.exists(path):
|
|
57
|
-
return {"ok": false, "error": "No script at " + path}
|
|
56
|
+
return { "ok": false, "error": "No script at " + path }
|
|
58
57
|
|
|
59
58
|
# The copy the editor holds, when it holds one, and otherwise a load that reads the file, rather
|
|
60
59
|
# than load(): a load recompiles a script whose base has been reloaded from the text it holds,
|
|
@@ -63,11 +62,13 @@ func reload_script(args: Dictionary) -> Dictionary:
|
|
|
63
62
|
# the reload below compiled it clean.
|
|
64
63
|
var cached: Resource = ResourceLoader.get_cached_ref(path)
|
|
65
64
|
var loaded: Resource = (
|
|
66
|
-
cached
|
|
65
|
+
cached
|
|
66
|
+
if cached != null
|
|
67
|
+
else ResourceLoader.load(path, "", ResourceLoader.CACHE_MODE_IGNORE)
|
|
67
68
|
)
|
|
68
69
|
var script: GDScript = loaded as GDScript
|
|
69
70
|
if script == null:
|
|
70
|
-
return {"ok": false, "error": path + " is not a GDScript"}
|
|
71
|
+
return { "ok": false, "error": path + " is not a GDScript" }
|
|
71
72
|
|
|
72
73
|
# What the copy had before anything touched it, which is the reading that shows the fault rather
|
|
73
74
|
# than the one that mends it. Without it a caller cannot tell a copy that was already current
|
|
@@ -82,7 +83,10 @@ func reload_script(args: Dictionary) -> Dictionary:
|
|
|
82
83
|
# method added since is still absent from the method list.
|
|
83
84
|
var reader: FileAccess = FileAccess.open(path, FileAccess.READ)
|
|
84
85
|
if reader == null:
|
|
85
|
-
return {
|
|
86
|
+
return {
|
|
87
|
+
"ok": false,
|
|
88
|
+
"error": "Could not read " + path + ": " + str(FileAccess.get_open_error()),
|
|
89
|
+
}
|
|
86
90
|
var previous: String = script.source_code
|
|
87
91
|
var text: String = reader.get_as_text()
|
|
88
92
|
reader.close()
|
|
@@ -90,11 +94,11 @@ func reload_script(args: Dictionary) -> Dictionary:
|
|
|
90
94
|
# Godot keeps one parse of each script for other scripts to resolve its class through, and a
|
|
91
95
|
# reload drops that parse only when the text it compiles differs from the text that was parsed.
|
|
92
96
|
# A parse that failed because a class it names was not yet known has the file's own text, so it
|
|
93
|
-
# outlived every reload: the class answered with all its methods while each script naming it
|
|
94
|
-
# "because of a parser error" until the editor restarted, for as long as anything held the
|
|
95
|
-
# such as a file open in an editor's language server. Compiling once with a line more
|
|
96
|
-
# texts differ, which drops that parse and those of the scripts that resolved through
|
|
97
|
-
# file's own text is compiled below.
|
|
97
|
+
# outlived every reload: the class answered with all its methods while each script naming it
|
|
98
|
+
# read "because of a parser error" until the editor restarted, for as long as anything held the
|
|
99
|
+
# parse, such as a file open in an editor's language server. Compiling once with a line more
|
|
100
|
+
# makes the texts differ, which drops that parse and those of the scripts that resolved through
|
|
101
|
+
# it; the file's own text is compiled below.
|
|
98
102
|
script.source_code = text + "\n"
|
|
99
103
|
var _dropped: Error = script.reload(true)
|
|
100
104
|
script.source_code = text
|
|
@@ -103,11 +107,11 @@ func reload_script(args: Dictionary) -> Dictionary:
|
|
|
103
107
|
# caller needs then: a failure travels back as its message alone, and a project met one and
|
|
104
108
|
# could not tell whether the copy had been damaged by it or before it.
|
|
105
109
|
#
|
|
106
|
-
# And the old text is compiled again. A failed reload leaves the object unusable while its
|
|
107
|
-
# lists read as before, measured on 4.7.2: it can make no instance, and an instance it
|
|
108
|
-
# has lost its methods, and the old text put back uncompiled leaves it that way.
|
|
109
|
-
# it was built from puts it back, and whether that worked is said, since the
|
|
110
|
-
# compile against what it names.
|
|
110
|
+
# And the old text is compiled again. A failed reload leaves the object unusable while its
|
|
111
|
+
# member lists read as before, measured on 4.7.2: it can make no instance, and an instance it
|
|
112
|
+
# already made has lost its methods, and the old text put back uncompiled leaves it that way.
|
|
113
|
+
# Compiling the text it was built from puts it back, and whether that worked is said, since the
|
|
114
|
+
# text may no longer compile against what it names.
|
|
111
115
|
var failed: Error = script.reload(true)
|
|
112
116
|
var answer: Dictionary = {
|
|
113
117
|
"ok": true,
|
|
@@ -129,8 +133,8 @@ func reload_script(args: Dictionary) -> Dictionary:
|
|
|
129
133
|
return answer
|
|
130
134
|
|
|
131
135
|
|
|
132
|
-
## Which of [code]scriptPaths[/code] the editor holds a compiled copy of whose text is not the
|
|
133
|
-
## without loading or reloading anything. A script compiled against such a copy fails on the
|
|
136
|
+
## Which of [code]scriptPaths[/code] the editor holds a compiled copy of whose text is not the
|
|
137
|
+
## file's, without loading or reloading anything. A script compiled against such a copy fails on the
|
|
134
138
|
## members the file has gained, and a reload of it fails the same way until the copy is reloaded
|
|
135
139
|
## first; the rescan that met this blamed the new classes the script also named, which were fine.
|
|
136
140
|
##
|
|
@@ -140,7 +144,7 @@ func reload_script(args: Dictionary) -> Dictionary:
|
|
|
140
144
|
func scripts_behind(args: Dictionary) -> Dictionary:
|
|
141
145
|
var given: Variant = args.get("scriptPaths", [])
|
|
142
146
|
if not given is Array:
|
|
143
|
-
return {"ok": false, "error": "scriptPaths must be a list of res:// paths"}
|
|
147
|
+
return { "ok": false, "error": "scriptPaths must be a list of res:// paths" }
|
|
144
148
|
var asked: Array = given
|
|
145
149
|
var unsaved: PackedStringArray = []
|
|
146
150
|
if Engine.is_editor_hint():
|
|
@@ -163,13 +167,13 @@ func scripts_behind(args: Dictionary) -> Dictionary:
|
|
|
163
167
|
edited.append(path)
|
|
164
168
|
else:
|
|
165
169
|
behind.append(path)
|
|
166
|
-
return {"ok": true, "behind": behind, "unsavedInEditor": edited}
|
|
170
|
+
return { "ok": true, "behind": behind, "unsavedInEditor": edited }
|
|
167
171
|
|
|
168
172
|
|
|
169
|
-
## Has the script editor's tabs reread what changed on disk, as the editor does when its window
|
|
170
|
-
## the focus, which a hidden editor's never does. A tab with unsaved edits is left alone and
|
|
171
|
-
## saving it writes its text over the file, and rereading would ask a question nobody can
|
|
172
|
-
## hidden editor's dialog.
|
|
173
|
+
## Has the script editor's tabs reread what changed on disk, as the editor does when its window
|
|
174
|
+
## takes the focus, which a hidden editor's never does. A tab with unsaved edits is left alone and
|
|
175
|
+
## named: saving it writes its text over the file, and rereading would ask a question nobody can
|
|
176
|
+
## answer in a hidden editor's dialog.
|
|
173
177
|
func _reread_open_tabs(path: String, answer: Dictionary) -> void:
|
|
174
178
|
# The engine fixtures reload from a script run outside any editor, where there are no tabs and
|
|
175
179
|
# EditorInterface has none of its methods.
|
|
@@ -223,11 +227,11 @@ func _method_names(script: GDScript) -> Array[String]:
|
|
|
223
227
|
|
|
224
228
|
|
|
225
229
|
## The script's constants, those of the scripts it extends included as its methods include theirs,
|
|
226
|
-
## and the keys of each one that is a Dictionary as `Kind.SHORT`, which is how the copy holds a
|
|
227
|
-
## enum: a stale enum member is denied as a member of the enum rather than of the class. A
|
|
228
|
-
## Dictionary that is not an enum is listed the same way, since the map does not tell the
|
|
229
|
-
## The engine's map holds only the script's own, so a script extending a class_name
|
|
230
|
-
## method it inherited and none of the constants.
|
|
230
|
+
## and the keys of each one that is a Dictionary as `Kind.SHORT`, which is how the copy holds a
|
|
231
|
+
## named enum: a stale enum member is denied as a member of the enum rather than of the class. A
|
|
232
|
+
## constant Dictionary that is not an enum is listed the same way, since the map does not tell the
|
|
233
|
+
## two apart. The engine's map holds only the script's own, so a script extending a class_name
|
|
234
|
+
## listed every method it inherited and none of the constants.
|
|
231
235
|
func _constant_names(script: GDScript) -> Array[String]:
|
|
232
236
|
var names: Array[String] = []
|
|
233
237
|
var holder: Script = script
|
|
@@ -250,7 +254,7 @@ func _constant_names(script: GDScript) -> Array[String]:
|
|
|
250
254
|
|
|
251
255
|
func global_classes(_args: Dictionary) -> Dictionary:
|
|
252
256
|
if not _editor_plugin:
|
|
253
|
-
return {"ok": false, "error": "Editor plugin unavailable"}
|
|
257
|
+
return { "ok": false, "error": "Editor plugin unavailable" }
|
|
254
258
|
|
|
255
259
|
var filesystem: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
|
256
260
|
var names: Array[String] = []
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends Node
|
|
3
|
-
|
|
4
3
|
## The editor's view of the files on disk: scans, the reimports asked for, where each stands, and
|
|
5
4
|
## files moved under it.
|
|
6
5
|
|
|
@@ -8,20 +7,17 @@ const GdhRead = preload("../reading.gd")
|
|
|
8
7
|
const GdhSceneFile = preload("../scene_file.gd")
|
|
9
8
|
|
|
10
9
|
var _editor_plugin: EditorPlugin = null
|
|
11
|
-
|
|
12
10
|
## How many scans the editor has completed, counted off `sources_changed`, which it emits at the end
|
|
13
11
|
## of every scan once the reimport the scan found is done: the one moment a scan is over. Neither
|
|
14
12
|
## flag it offers says so. A threaded scan stops reporting itself when its thread finishes, and its
|
|
15
13
|
## import runs a frame or more later when the thread is joined; an editor in the background took
|
|
16
14
|
## long enough over that frame that a rescan answered before the import of 211 changed scenes began.
|
|
17
15
|
var _scans_completed: int = 0
|
|
18
|
-
|
|
19
16
|
## When the last scan completed, on the engine's clock, or -1 before the first. The editor writes
|
|
20
17
|
## the class cache a frame after it stops reporting a scan, measured on 4.7.2, so a game started
|
|
21
18
|
## in between reads a file being rewritten; how long ago the scan ended is what tells a caller
|
|
22
19
|
## whether that write can still be coming.
|
|
23
20
|
var _scan_finished_at_msec: int = -1
|
|
24
|
-
|
|
25
21
|
## How many reimports asked for through [method reimport_files] have finished. Counted here rather
|
|
26
22
|
## than off `resources_reimported`, which a scan's own import emits as well.
|
|
27
23
|
var _reimports_completed: int = 0
|
|
@@ -53,7 +49,7 @@ func _scan_completed(_sources_exist: bool) -> void:
|
|
|
53
49
|
## another scan.
|
|
54
50
|
func rescan_filesystem(args: Dictionary) -> Dictionary:
|
|
55
51
|
if not _editor_plugin:
|
|
56
|
-
return {"ok": false, "error": "Editor plugin unavailable"}
|
|
52
|
+
return { "ok": false, "error": "Editor plugin unavailable" }
|
|
57
53
|
|
|
58
54
|
var filesystem: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
|
59
55
|
# Not over a scan or an import the editor is already running: a scan asked for then starts a
|
|
@@ -62,13 +58,13 @@ func rescan_filesystem(args: Dictionary) -> Dictionary:
|
|
|
62
58
|
var busy: bool = filesystem.is_scanning() or filesystem.is_importing()
|
|
63
59
|
var started: bool = false
|
|
64
60
|
var completed_before: int = _scans_completed
|
|
65
|
-
var uids: Dictionary = {"reread": [], "duplicated": []}
|
|
61
|
+
var uids: Dictionary = { "reread": [], "duplicated": [] }
|
|
66
62
|
if not GdhRead.as_bool(args.get("statusOnly", false)) and not busy:
|
|
67
63
|
uids = _reread_changed_uids(filesystem)
|
|
68
|
-
# Files the caller has just written, moved or deleted, read again one by one. The scan finds
|
|
69
|
-
# script that appeared and leaves the global class of one that has gone in the editor's
|
|
70
|
-
# which it then writes back into the class cache at a path that is not there; reading
|
|
71
|
-
# path again is what drops it.
|
|
64
|
+
# Files the caller has just written, moved or deleted, read again one by one. The scan finds
|
|
65
|
+
# a script that appeared and leaves the global class of one that has gone in the editor's
|
|
66
|
+
# list, which it then writes back into the class cache at a path that is not there; reading
|
|
67
|
+
# the gone path again is what drops it.
|
|
72
68
|
var named: Variant = args.get("updateFiles", [])
|
|
73
69
|
if named is Array:
|
|
74
70
|
var paths: Array = named
|
|
@@ -137,14 +133,14 @@ func _reread_changed_uids(filesystem: EditorFileSystem) -> Dictionary:
|
|
|
137
133
|
reread.append(path)
|
|
138
134
|
reread.sort()
|
|
139
135
|
duplicated.sort()
|
|
140
|
-
return {"reread": reread, "duplicated": duplicated}
|
|
136
|
+
return { "reread": reread, "duplicated": duplicated }
|
|
141
137
|
|
|
142
138
|
|
|
143
139
|
## Every `.uid` under [param directory], skipping what the editor skips: hidden directories, any
|
|
144
140
|
## holding a `.gdignore`, and any below the project's own that holds a `project.godot` of its own,
|
|
145
|
-
## which the editor passes over as another project. A nested project's UIDs are never in the
|
|
146
|
-
## table, so without that last one its files would be read again on every rescan, and one
|
|
147
|
-
## copy of an outer file's `.uid` named as a duplicate on disk.
|
|
141
|
+
## which the editor passes over as another project. A nested project's UIDs are never in the
|
|
142
|
+
## editor's table, so without that last one its files would be read again on every rescan, and one
|
|
143
|
+
## holding a copy of an outer file's `.uid` named as a duplicate on disk.
|
|
148
144
|
func _uid_files(directory: String, found: Array[String]) -> void:
|
|
149
145
|
var listing: DirAccess = DirAccess.open(directory)
|
|
150
146
|
if listing == null or FileAccess.file_exists(directory.path_join(".gdignore")):
|
|
@@ -166,12 +162,13 @@ func _uid_files(directory: String, found: Array[String]) -> void:
|
|
|
166
162
|
## Apart from `rescan_filesystem` because asking must not start or count anything.
|
|
167
163
|
func scan_status(_args: Dictionary) -> Dictionary:
|
|
168
164
|
if not _editor_plugin:
|
|
169
|
-
return {"ok": false, "error": "Editor plugin unavailable"}
|
|
165
|
+
return { "ok": false, "error": "Editor plugin unavailable" }
|
|
170
166
|
var filesystem: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
|
171
|
-
# The scenes open in tabs, the one shown and the ones holding unsaved edits: a scene edit on
|
|
172
|
-
# meets what the editor holds of it, and nothing said which scenes those were. A tab of a
|
|
173
|
-
# never saved has no path, and a new project's editor opens with an empty one, so it is
|
|
174
|
-
# of the open; one with edits is kept among the unsaved, as the edits a close would
|
|
167
|
+
# The scenes open in tabs, the one shown and the ones holding unsaved edits: a scene edit on
|
|
168
|
+
# disk meets what the editor holds of it, and nothing said which scenes those were. A tab of a
|
|
169
|
+
# scene never saved has no path, and a new project's editor opens with an empty one, so it is
|
|
170
|
+
# left out of the open; one with edits is kept among the unsaved, as the edits a close would
|
|
171
|
+
# lose.
|
|
175
172
|
var shown: Node = EditorInterface.get_edited_scene_root()
|
|
176
173
|
var open: Array[String] = []
|
|
177
174
|
open.assign(Array(EditorInterface.get_open_scenes()).filter(_has_path))
|
|
@@ -204,14 +201,14 @@ func _has_path(path: String) -> bool:
|
|
|
204
201
|
## while the editor scans or imports, since `reimport_files` refuses to run inside an import.
|
|
205
202
|
func reimport_files(args: Dictionary) -> Dictionary:
|
|
206
203
|
if not _editor_plugin:
|
|
207
|
-
return {"ok": false, "error": "Editor plugin unavailable"}
|
|
204
|
+
return { "ok": false, "error": "Editor plugin unavailable" }
|
|
208
205
|
var paths: Array[String] = []
|
|
209
206
|
var given: Variant = args.get("paths", [])
|
|
210
207
|
if given is Array:
|
|
211
208
|
for path: Variant in given:
|
|
212
209
|
paths.append(str(path))
|
|
213
210
|
if paths.is_empty():
|
|
214
|
-
return {"ok": false, "error": "reimport_files needs paths"}
|
|
211
|
+
return { "ok": false, "error": "reimport_files needs paths" }
|
|
215
212
|
var filesystem: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
|
216
213
|
var busy: bool = filesystem.is_scanning() or filesystem.is_importing()
|
|
217
214
|
if not busy:
|
|
@@ -282,7 +279,12 @@ func files_moved(args: Dictionary) -> Dictionary:
|
|
|
282
279
|
reloaded.append(scene)
|
|
283
280
|
reloaded.sort()
|
|
284
281
|
unsaved.sort()
|
|
285
|
-
return {
|
|
282
|
+
return {
|
|
283
|
+
"ok": true,
|
|
284
|
+
"retargetedTabs": retargeted,
|
|
285
|
+
"reloadedScenes": reloaded,
|
|
286
|
+
"unsavedScenes": unsaved,
|
|
287
|
+
}
|
|
286
288
|
|
|
287
289
|
|
|
288
290
|
func _reimport(paths: PackedStringArray) -> void:
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
3
|
## Bakes what a NavigationRegion2D or NavigationRegion3D in a scene file navigates by, as the
|
|
5
4
|
## editor's Bake button does, and saves it where the region keeps it.
|
|
6
5
|
##
|
|
@@ -13,10 +12,11 @@ extends RefCounted
|
|
|
13
12
|
|
|
14
13
|
const GdhSceneFile = preload("../scene_file.gd")
|
|
15
14
|
const GdhSceneUids = preload("../scene_uids.gd")
|
|
16
|
-
|
|
17
15
|
## The names NavigationPolygon.SourceGeometryMode and NavigationMesh.SourceGeometryMode give each
|
|
18
16
|
## mode, which share their values.
|
|
19
|
-
const SOURCE_MODES: Array[String] = [
|
|
17
|
+
const SOURCE_MODES: Array[String] = [
|
|
18
|
+
"root_node_children", "groups_with_children", "groups_explicit",
|
|
19
|
+
]
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
## Bakes the region [param region] of [param scene], answering what it now holds or why not.
|
|
@@ -55,13 +55,16 @@ static func bake(scene: GdhSceneFile, region: Node) -> Dictionary:
|
|
|
55
55
|
var file: String = held.resource_path.get_slice("::", 0)
|
|
56
56
|
var embedded: bool = file.is_empty() or held.resource_path.contains("::")
|
|
57
57
|
if embedded and not file.is_empty() and file != scene.path:
|
|
58
|
-
return
|
|
59
|
-
|
|
58
|
+
return (
|
|
59
|
+
scene
|
|
60
|
+
. refuse(
|
|
60
61
|
(
|
|
61
|
-
|
|
62
|
-
|
|
62
|
+
(
|
|
63
|
+
"The %s %s holds is built into %s, and a bake here cannot save that file. "
|
|
64
|
+
+ "Bake the region there, or save the %s to a file of its own."
|
|
65
|
+
)
|
|
66
|
+
% [held.get_class(), at, file, held.get_class()]
|
|
63
67
|
)
|
|
64
|
-
% [held.get_class(), at, file, held.get_class()]
|
|
65
68
|
)
|
|
66
69
|
)
|
|
67
70
|
|
|
@@ -78,7 +81,8 @@ static func bake(scene: GdhSceneFile, region: Node) -> Dictionary:
|
|
|
78
81
|
}
|
|
79
82
|
if made:
|
|
80
83
|
answer["made"] = (
|
|
81
|
-
"%s held no %s, so one was made and built into the scene, with the defaults."
|
|
84
|
+
"%s held no %s, so one was made and built into the scene, with the defaults."
|
|
85
|
+
% [at, property]
|
|
82
86
|
)
|
|
83
87
|
if polygons == 0:
|
|
84
88
|
answer["note"] = _why_empty(held, flat, parsed)
|
|
@@ -86,17 +90,17 @@ static func bake(scene: GdhSceneFile, region: Node) -> Dictionary:
|
|
|
86
90
|
answer["note"] = _nothing_read(scene, held)
|
|
87
91
|
if not flat and not Engine.is_editor_hint() and _reads_meshes(region, held):
|
|
88
92
|
answer["meshesNote"] = (
|
|
89
|
-
"The engine's warning that it had to parse RenderingServer meshes at runtime is about
|
|
90
|
-
+ "game rebaking as it runs, where reading a mesh back from the GPU stalls a frame.
|
|
91
|
-
+ "headless engine gives it for any bake read from MeshInstance3D nodes; this one
|
|
92
|
-
+ "and is saved."
|
|
93
|
+
"The engine's warning that it had to parse RenderingServer meshes at runtime is about "
|
|
94
|
+
+ "a game rebaking as it runs, where reading a mesh back from the GPU stalls a frame. "
|
|
95
|
+
+ "A headless engine gives it for any bake read from MeshInstance3D nodes; this one "
|
|
96
|
+
+ "ran once and is saved."
|
|
93
97
|
)
|
|
94
98
|
|
|
95
99
|
if not embedded:
|
|
96
100
|
scene.close()
|
|
97
101
|
var saving: Error = GdhSceneUids.save_keeping_uid(held, file)
|
|
98
102
|
if saving != OK:
|
|
99
|
-
return {"ok": false, "error": "Failed to save %s: %s" % [file, error_string(saving)]}
|
|
103
|
+
return { "ok": false, "error": "Failed to save %s: %s" % [file, error_string(saving)] }
|
|
100
104
|
if Engine.is_editor_hint():
|
|
101
105
|
EditorInterface.get_resource_filesystem().update_file(file)
|
|
102
106
|
answer["savedTo"] = file
|
|
@@ -121,7 +125,9 @@ static func bake(scene: GdhSceneFile, region: Node) -> Dictionary:
|
|
|
121
125
|
static func _parse_and_bake(scene: GdhSceneFile, at: String, held: Resource, flat: bool) -> bool:
|
|
122
126
|
var copy: Node = scene.root.duplicate(Node.DUPLICATE_GROUPS)
|
|
123
127
|
var reading: Resource = held.duplicate()
|
|
124
|
-
var group_property: String =
|
|
128
|
+
var group_property: String = (
|
|
129
|
+
"source_geometry_group_name" if flat else "geometry_source_group_name"
|
|
130
|
+
)
|
|
125
131
|
var group: String = str(held.get(group_property))
|
|
126
132
|
var own_group: String = "_gdh_bake_%d" % copy.get_instance_id()
|
|
127
133
|
reading.set(group_property, own_group)
|
|
@@ -136,8 +142,8 @@ static func _parse_and_bake(scene: GdhSceneFile, at: String, held: Resource, fla
|
|
|
136
142
|
var outline: NavigationPolygon = reading
|
|
137
143
|
var geometry: NavigationMeshSourceGeometryData2D = NavigationMeshSourceGeometryData2D.new()
|
|
138
144
|
NavigationServer2D.parse_source_geometry_data(outline, geometry, region)
|
|
139
|
-
# has_data() counts only traversable outlines, so a bake carving every obstacle it read
|
|
140
|
-
# still say it read nothing.
|
|
145
|
+
# has_data() counts only traversable outlines, so a bake carving every obstacle it read
|
|
146
|
+
# would still say it read nothing.
|
|
141
147
|
found = (
|
|
142
148
|
not geometry.get_obstruction_outlines().is_empty()
|
|
143
149
|
or not geometry.get_projected_obstructions().is_empty()
|
|
@@ -211,12 +217,15 @@ static func _why_empty(held: Resource, flat: bool, parsed: bool) -> String:
|
|
|
211
217
|
return (
|
|
212
218
|
"The NavigationPolygon has no outlines, and a 2D bake fills only the area its "
|
|
213
219
|
+ "outlines enclose, less what the obstacles inside take. Give it outlines with "
|
|
214
|
-
+ "scene_node set, such as navigation_polygon/outlines [[[0, 0], [512, 0], [512,
|
|
215
|
-
+ "[0, 512]]], then bake again."
|
|
220
|
+
+ "scene_node set, such as navigation_polygon/outlines [[[0, 0], [512, 0], [512, "
|
|
221
|
+
+ "512], [0, 512]]], then bake again."
|
|
216
222
|
)
|
|
217
223
|
return (
|
|
218
224
|
(
|
|
219
|
-
|
|
225
|
+
(
|
|
226
|
+
"Nothing was left to walk inside the outlines once the obstacles read from %s, "
|
|
227
|
+
+ "and the "
|
|
228
|
+
)
|
|
220
229
|
% _source_said(polygon.source_geometry_mode, polygon.source_geometry_group_name)
|
|
221
230
|
)
|
|
222
231
|
+ "agent_radius %s kept from their edges, were taken out." % polygon.agent_radius
|
|
@@ -225,17 +234,27 @@ static func _why_empty(held: Resource, flat: bool, parsed: bool) -> String:
|
|
|
225
234
|
if not parsed:
|
|
226
235
|
return (
|
|
227
236
|
(
|
|
228
|
-
|
|
237
|
+
(
|
|
238
|
+
"No geometry was read from %s with geometry_parsed_geometry_type %s, so there "
|
|
239
|
+
+ "was nothing to "
|
|
240
|
+
)
|
|
229
241
|
% [
|
|
230
|
-
_source_said(
|
|
231
|
-
|
|
242
|
+
_source_said(
|
|
243
|
+
mesh.geometry_source_geometry_mode, mesh.geometry_source_group_name
|
|
244
|
+
),
|
|
245
|
+
["mesh_instances", "static_colliders", "both"][
|
|
246
|
+
mesh.geometry_parsed_geometry_type
|
|
247
|
+
],
|
|
232
248
|
]
|
|
233
249
|
)
|
|
234
|
-
+ "bake: mesh_instances reads MeshInstance3D nodes, static_colliders the shapes of
|
|
235
|
-
+ "bodies on geometry_collision_mask, and both reads both."
|
|
250
|
+
+ "bake: mesh_instances reads MeshInstance3D nodes, static_colliders the shapes of "
|
|
251
|
+
+ "physics bodies on geometry_collision_mask, and both reads both."
|
|
236
252
|
)
|
|
237
253
|
return (
|
|
238
|
-
|
|
254
|
+
(
|
|
255
|
+
"The geometry read gave no floor an agent of agent_height %s and agent_radius %s can "
|
|
256
|
+
+ "stand on."
|
|
257
|
+
)
|
|
239
258
|
% [mesh.agent_height, mesh.agent_radius]
|
|
240
259
|
)
|
|
241
260
|
|
|
@@ -252,7 +271,7 @@ static func _nothing_read(scene: GdhSceneFile, held: Resource) -> String:
|
|
|
252
271
|
)
|
|
253
272
|
% [
|
|
254
273
|
_source_said(polygon.source_geometry_mode, polygon.source_geometry_group_name),
|
|
255
|
-
polygon.agent_radius
|
|
274
|
+
polygon.agent_radius,
|
|
256
275
|
]
|
|
257
276
|
)
|
|
258
277
|
var outside: PackedStringArray = []
|
|
@@ -270,8 +289,9 @@ static func _nothing_read(scene: GdhSceneFile, held: Resource) -> String:
|
|
|
270
289
|
return said
|
|
271
290
|
return (
|
|
272
291
|
(
|
|
273
|
-
"%s %s %s outside what was read; the bake reads a node elsewhere once it is in the
|
|
274
|
-
+ "source_geometry_mode is groups_with_children, or once it is moved
|
|
292
|
+
"%s %s %s outside what was read; the bake reads a node elsewhere once it is in the "
|
|
293
|
+
+ "group %s and source_geometry_mode is groups_with_children, or once it is moved "
|
|
294
|
+
+ "under the region."
|
|
275
295
|
)
|
|
276
296
|
% [
|
|
277
297
|
said,
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends Node
|
|
3
|
-
|
|
4
3
|
## Running the game from the editor, so the editor is the one debugging it.
|
|
5
4
|
##
|
|
6
5
|
## A game started as its own process is a game nothing is attached to: the editor's debug adapter
|
|
@@ -10,7 +9,6 @@ extends Node
|
|
|
10
9
|
|
|
11
10
|
## What the display server calls itself when the engine was started with no display at all.
|
|
12
11
|
const GdhRead = preload("../reading.gd")
|
|
13
|
-
|
|
14
12
|
const HEADLESS_DISPLAY: String = "headless"
|
|
15
13
|
## Where Godot keeps the port its own debugger listens on for a game it is playing. One setting
|
|
16
14
|
## for every editor on the machine, and bound only while a game runs.
|
|
@@ -18,7 +16,6 @@ const DEBUGGER_SETTING: String = "network/debug/remote_port"
|
|
|
18
16
|
const LOOPBACK: String = "127.0.0.1"
|
|
19
17
|
|
|
20
18
|
var _editor_plugin: EditorPlugin = null
|
|
21
|
-
|
|
22
19
|
## Which play this is, counted up for every one the editor starts, whoever starts it. What a server
|
|
23
20
|
## writes about a play is kept on disk after it, and the editor saying it is playing does not say
|
|
24
21
|
## which play: a server picking a play back up read the console of an earlier one as this one's.
|
|
@@ -29,14 +26,14 @@ var _was_playing: bool = false
|
|
|
29
26
|
var _awaited: bool = false
|
|
30
27
|
|
|
31
28
|
|
|
32
|
-
func set_editor_plugin(plugin: EditorPlugin) -> void:
|
|
33
|
-
_editor_plugin = plugin
|
|
34
|
-
|
|
35
|
-
|
|
36
29
|
func _process(_delta: float) -> void:
|
|
37
30
|
_count_a_play_begun()
|
|
38
31
|
|
|
39
32
|
|
|
33
|
+
func set_editor_plugin(plugin: EditorPlugin) -> void:
|
|
34
|
+
_editor_plugin = plugin
|
|
35
|
+
|
|
36
|
+
|
|
40
37
|
## Counts a play the editor has begun since the last look, whoever began it. Looked at every frame
|
|
41
38
|
## and before the count is read: read alone, a play begun in this frame went uncounted until this
|
|
42
39
|
## node's turn came round, and a server asking before then was told the earlier play's number with
|
|
@@ -60,20 +57,23 @@ func play_scene(args: Dictionary) -> Dictionary:
|
|
|
60
57
|
# play was answered as started.
|
|
61
58
|
var main: String = str(ProjectSettings.get_setting("application/run/main_scene", ""))
|
|
62
59
|
if main.is_empty():
|
|
63
|
-
return {"ok": false, "error": "The project has no main scene to play."}
|
|
60
|
+
return { "ok": false, "error": "The project has no main scene to play." }
|
|
64
61
|
if not ResourceLoader.exists(main, "PackedScene"):
|
|
65
62
|
return {
|
|
66
63
|
"ok": false,
|
|
67
64
|
"error":
|
|
68
65
|
(
|
|
69
|
-
|
|
66
|
+
(
|
|
67
|
+
"The main scene, %s, is not there, so the editor would ask for another "
|
|
68
|
+
+ "rather than play."
|
|
69
|
+
)
|
|
70
70
|
% main
|
|
71
|
-
)
|
|
71
|
+
),
|
|
72
72
|
}
|
|
73
73
|
else:
|
|
74
74
|
full_path = scene_path if scene_path.begins_with("res://") else "res://" + scene_path
|
|
75
75
|
if not ResourceLoader.exists(full_path, "PackedScene"):
|
|
76
|
-
return {"ok": false, "error": "No scene at " + full_path}
|
|
76
|
+
return { "ok": false, "error": "No scene at " + full_path }
|
|
77
77
|
|
|
78
78
|
if EditorInterface.is_playing_scene():
|
|
79
79
|
EditorInterface.stop_playing_scene()
|
|
@@ -138,7 +138,7 @@ func stop_playing(_args: Dictionary) -> Dictionary:
|
|
|
138
138
|
# Known over now rather than at the next frame's look: a play somebody else starts before that
|
|
139
139
|
# look would otherwise be read as this one still going, and given its number and its console.
|
|
140
140
|
_was_playing = EditorInterface.is_playing_scene()
|
|
141
|
-
return {"ok": true, "wasPlaying": was_playing, "playing": _was_playing}
|
|
141
|
+
return { "ok": true, "wasPlaying": was_playing, "playing": _was_playing }
|
|
142
142
|
|
|
143
143
|
|
|
144
144
|
## Restarts the editor, which is how a replaced addon is picked up.
|
|
@@ -167,7 +167,7 @@ func restart_editor(_args: Dictionary) -> Dictionary:
|
|
|
167
167
|
"This editor is headless, and the engine does not hand back the arguments it was "
|
|
168
168
|
+ "started with, so nothing can bring it back as the editor it is. Start it again "
|
|
169
169
|
+ "yourself."
|
|
170
|
-
)
|
|
170
|
+
),
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
var saving: Dictionary = _save_everything()
|
|
@@ -177,7 +177,7 @@ func restart_editor(_args: Dictionary) -> Dictionary:
|
|
|
177
177
|
# Deferred so this answer is on its way out before the editor goes.
|
|
178
178
|
EditorInterface.restart_editor.call_deferred(true)
|
|
179
179
|
|
|
180
|
-
return {"ok": true, "restarting": true, "saved": saving["saved"]}
|
|
180
|
+
return { "ok": true, "restarting": true, "saved": saving["saved"] }
|
|
181
181
|
|
|
182
182
|
|
|
183
183
|
## Saves every open scene, answering a refusal naming what is still unsaved, or the scenes that had
|
|
@@ -193,7 +193,7 @@ static func _save_everything() -> Dictionary:
|
|
|
193
193
|
EditorInterface.save_all_scenes()
|
|
194
194
|
var left: PackedStringArray = EditorInterface.get_unsaved_scenes()
|
|
195
195
|
if left.is_empty():
|
|
196
|
-
return {"ok": true, "saved": changed}
|
|
196
|
+
return { "ok": true, "saved": changed }
|
|
197
197
|
var named: Array[String] = []
|
|
198
198
|
for scene: String in left:
|
|
199
199
|
named.append(scene if not scene.is_empty() else "a scene that has never been saved")
|
|
@@ -202,15 +202,15 @@ static func _save_everything() -> Dictionary:
|
|
|
202
202
|
"error":
|
|
203
203
|
(
|
|
204
204
|
(
|
|
205
|
-
"%s still %s unsaved changes after saving, and ending the editor would lose them.
|
|
206
|
-
+ "or close %s in the editor first."
|
|
205
|
+
"%s still %s unsaved changes after saving, and ending the editor would lose them. "
|
|
206
|
+
+ "Save or close %s in the editor first."
|
|
207
207
|
)
|
|
208
208
|
% [
|
|
209
209
|
", ".join(PackedStringArray(named)),
|
|
210
210
|
"has" if named.size() == 1 else "have",
|
|
211
|
-
"it" if named.size() == 1 else "them"
|
|
211
|
+
"it" if named.size() == 1 else "them",
|
|
212
212
|
]
|
|
213
|
-
)
|
|
213
|
+
),
|
|
214
214
|
}
|
|
215
215
|
|
|
216
216
|
|
|
@@ -230,7 +230,7 @@ func quit_editor(_args: Dictionary) -> Dictionary:
|
|
|
230
230
|
# Deferred so this answer is on its way out before the editor goes.
|
|
231
231
|
_leave.call_deferred()
|
|
232
232
|
|
|
233
|
-
return {"ok": true, "quitting": true, "saved": saving["saved"]}
|
|
233
|
+
return { "ok": true, "quitting": true, "saved": saving["saved"] }
|
|
234
234
|
|
|
235
235
|
|
|
236
236
|
## What the editor said as it started each of its servers, and the debug adapter started again when
|
|
@@ -258,7 +258,12 @@ func editor_servers(args: Dictionary) -> Dictionary:
|
|
|
258
258
|
parent.add_child(adapter)
|
|
259
259
|
parent.move_child(adapter, at)
|
|
260
260
|
rebound = true
|
|
261
|
-
return {
|
|
261
|
+
return {
|
|
262
|
+
"ok": true,
|
|
263
|
+
"said": said,
|
|
264
|
+
"rebound": rebound,
|
|
265
|
+
"saidSince": _servers_said().slice(said.size()),
|
|
266
|
+
}
|
|
262
267
|
|
|
263
268
|
|
|
264
269
|
## The lines the editor's log panel holds about its language server and debug adapter, in order.
|