gdharness 1.3.42 → 1.3.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +383 -243
- package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
- package/build/godot/addons/gdharness_editor/animation_reads.gd +55 -33
- package/build/godot/addons/gdharness_editor/bridge_client.gd +53 -58
- package/build/godot/addons/gdharness_editor/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_editor/file_reads.gd +83 -42
- package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
- package/build/godot/addons/gdharness_editor/headless_check.gd +68 -44
- package/build/godot/addons/gdharness_editor/plugin.gd +2 -3
- package/build/godot/addons/gdharness_editor/property_paths.gd +22 -12
- package/build/godot/addons/gdharness_editor/property_values.gd +143 -78
- package/build/godot/addons/gdharness_editor/reading.gd +51 -17
- package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +127 -80
- package/build/godot/addons/gdharness_editor/scene_uids.gd +12 -6
- package/build/godot/addons/gdharness_editor/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_editor/setting_reads.gd +7 -6
- package/build/godot/addons/gdharness_editor/tool_executor.gd +3 -6
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +37 -27
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +79 -49
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +65 -42
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +24 -22
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +49 -29
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +26 -21
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +40 -40
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +166 -107
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +68 -41
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +35 -20
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +28 -13
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd +12 -5
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +113 -67
- package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
- package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
- package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
- package/build/godot/addons/gdharness_runtime/reading.gd +51 -17
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +149 -147
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
- package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +34 -17
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
- package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd +15 -15
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
- package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_tests/bench.gd +4 -5
- package/build/godot/addons/gdharness_tests/event.gd +10 -5
- package/build/godot/addons/gdharness_tests/share.gd +38 -18
- package/build/godot/addons/gdharness_tests/suites.gd +3 -3
- package/build/godot/addons/gdharness_tests/wait.gd +3 -2
- package/build/godot/loader/loader.gd +9 -9
- package/build/godot/operations/animation_reads.gd +55 -33
- package/build/godot/operations/audio_buses.gd +48 -19
- package/build/godot/operations/class_cache.gd +16 -11
- package/build/godot/operations/classdb_queries.gd +89 -44
- package/build/godot/operations/declared_values.gd +68 -32
- package/build/godot/operations/dependencies.gd +190 -109
- package/build/godot/operations/editor_tools.gd +10 -10
- package/build/godot/operations/export_presets.gd +100 -54
- package/build/godot/operations/file_reads.gd +83 -42
- package/build/godot/operations/file_walk.gd +12 -5
- package/build/godot/operations/gdscript_analysis.gd +86 -65
- package/build/godot/operations/gdscript_authoring.gd +83 -36
- package/build/godot/operations/godot_operations.gd +17 -11
- package/build/godot/operations/import_pipeline.gd +110 -59
- package/build/godot/operations/input_actions.gd +62 -39
- package/build/godot/operations/patterns.gd +3 -1
- package/build/godot/operations/plugins.gd +36 -16
- package/build/godot/operations/project_config.gd +61 -32
- package/build/godot/operations/project_diagnostics.gd +24 -11
- package/build/godot/operations/project_file.gd +16 -16
- package/build/godot/operations/project_validation.gd +71 -44
- package/build/godot/operations/property_paths.gd +22 -12
- package/build/godot/operations/property_values.gd +143 -78
- package/build/godot/operations/reading.gd +51 -17
- package/build/godot/operations/rename_checks.gd +2 -2
- package/build/godot/operations/resource_files.gd +11 -9
- package/build/godot/operations/same_values.gd +4 -4
- package/build/godot/operations/scene_connections.gd +27 -13
- package/build/godot/operations/script_check.gd +52 -49
- package/build/godot/operations/serialisation.gd +75 -51
- package/build/godot/operations/setting_reads.gd +7 -6
- package/build/godot/operations/value_shapes.gd +5 -6
- package/build/godot/tests/listed.gd +321 -268
- package/build/keeper.js +43 -43
- package/package.json +1 -1
|
@@ -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.
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends Node
|
|
3
|
-
|
|
4
3
|
## The engine's script profiler on a game the editor plays, summed here for the server.
|
|
5
4
|
##
|
|
6
5
|
## The editor is that game's debugger, so the profiler is switched on through the editor's own
|
|
@@ -11,7 +10,6 @@ extends Node
|
|
|
11
10
|
## expose to scripts: found by its class in the editor's tree and connected to by name.
|
|
12
11
|
|
|
13
12
|
const GdhRead = preload("../reading.gd")
|
|
14
|
-
|
|
15
13
|
## The most functions a frame may carry when the server does not say. The engine names a function
|
|
16
14
|
## once, as it first appears, and the game's debugger queue drops those messages past its size, so
|
|
17
15
|
## the server keeps this below the queue the project sets; this is that for the engine's default.
|
|
@@ -26,37 +24,10 @@ const PROFILE_RECEIVED: String = "gdharness:profile_received"
|
|
|
26
24
|
## Kept in step with the runtime addon's runtime_autoload.gd.
|
|
27
25
|
const PROFILE_FROM_START: String = "GDHARNESS_PROFILE_FROM_START"
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
## The editor's debugger sessions, numbered as the editor numbers its debugger tabs.
|
|
31
|
-
class GdhSessions:
|
|
32
|
-
extends EditorDebuggerPlugin
|
|
33
|
-
|
|
34
|
-
signal session_started(session_id: int)
|
|
35
|
-
|
|
36
|
-
func _setup_session(session_id: int) -> void:
|
|
37
|
-
var session: EditorDebuggerSession = get_session(session_id)
|
|
38
|
-
# int rather than Error: Signal.connect answers with a plain int.
|
|
39
|
-
var joined: int = session.started.connect(func() -> void: session_started.emit(session_id))
|
|
40
|
-
if joined != OK:
|
|
41
|
-
push_error("gdharness could not watch debugger session %d start: %d" % [session_id, joined])
|
|
42
|
-
|
|
43
|
-
func _has_capture(capture: String) -> bool:
|
|
44
|
-
return capture == PROFILE_CAPTURE
|
|
45
|
-
|
|
46
|
-
## Answers a quitting game that its profile's totals arrived, which it waits for before it exits.
|
|
47
|
-
## The editor reads a session's messages in order, so the totals sent before this have been read.
|
|
48
|
-
func _capture(message: String, _data: Array, session_id: int) -> bool:
|
|
49
|
-
if message != PROFILE_SENT:
|
|
50
|
-
return false
|
|
51
|
-
get_session(session_id).send_message(PROFILE_RECEIVED, [])
|
|
52
|
-
return true
|
|
53
|
-
|
|
54
|
-
|
|
55
27
|
var _editor_plugin: EditorPlugin = null
|
|
56
28
|
var _sessions: GdhSessions = null
|
|
57
29
|
## The debugger tabs connected to, by instance id, so none is connected twice.
|
|
58
30
|
var _hooked: Dictionary[int, bool] = {}
|
|
59
|
-
|
|
60
31
|
## A play asked to be profiled before its session started.
|
|
61
32
|
var _armed: bool = false
|
|
62
33
|
var _session: int = -1
|
|
@@ -73,6 +44,12 @@ var _names: Dictionary[int, String] = {}
|
|
|
73
44
|
var _totals: Dictionary[String, PackedFloat64Array] = {}
|
|
74
45
|
|
|
75
46
|
|
|
47
|
+
func _exit_tree() -> void:
|
|
48
|
+
if _editor_plugin != null and _sessions != null:
|
|
49
|
+
_editor_plugin.remove_debugger_plugin(_sessions)
|
|
50
|
+
_sessions = null
|
|
51
|
+
|
|
52
|
+
|
|
76
53
|
func set_editor_plugin(plugin: EditorPlugin) -> void:
|
|
77
54
|
_editor_plugin = plugin
|
|
78
55
|
_sessions = GdhSessions.new()
|
|
@@ -82,12 +59,6 @@ func set_editor_plugin(plugin: EditorPlugin) -> void:
|
|
|
82
59
|
plugin.add_debugger_plugin(_sessions)
|
|
83
60
|
|
|
84
61
|
|
|
85
|
-
func _exit_tree() -> void:
|
|
86
|
-
if _editor_plugin != null and _sessions != null:
|
|
87
|
-
_editor_plugin.remove_debugger_plugin(_sessions)
|
|
88
|
-
_sessions = null
|
|
89
|
-
|
|
90
|
-
|
|
91
62
|
## Switches the profiler on for the game being played, or with [code]arm[/code] for the next one
|
|
92
63
|
## to start and never the one playing: a start ends the game before it, which can still be going.
|
|
93
64
|
## Either way what was summed before is dropped.
|
|
@@ -98,8 +69,9 @@ func profile_start(args: Dictionary) -> Dictionary:
|
|
|
98
69
|
"error":
|
|
99
70
|
(
|
|
100
71
|
(
|
|
101
|
-
"This editor's debugger has no %s node to read the profiler from, which is
|
|
102
|
-
+ " 4.7 keeps it: a Godot that moved it has to be met by a
|
|
72
|
+
"This editor's debugger has no %s node to read the profiler from, which is "
|
|
73
|
+
+ "where Godot 4.7 keeps it: a Godot that moved it has to be met by a "
|
|
74
|
+
+ "gdharness that knows where."
|
|
103
75
|
)
|
|
104
76
|
% DEBUGGER_CLASS
|
|
105
77
|
),
|
|
@@ -113,12 +85,12 @@ func profile_start(args: Dictionary) -> Dictionary:
|
|
|
113
85
|
# runtime addon to hold its start until the profiler is on, which the session's start
|
|
114
86
|
# switches on with a message the game reads only on a later poll.
|
|
115
87
|
OS.set_environment(PROFILE_FROM_START, "1")
|
|
116
|
-
return {"ok": true, "started": false, "armed": true, "session": -1}
|
|
88
|
+
return { "ok": true, "started": false, "armed": true, "session": -1 }
|
|
117
89
|
var active: int = _active_session()
|
|
118
90
|
if active < 0:
|
|
119
|
-
return {"ok": true, "started": false, "armed": false, "session": -1}
|
|
91
|
+
return { "ok": true, "started": false, "armed": false, "session": -1 }
|
|
120
92
|
_switch_on(active)
|
|
121
|
-
return {"ok": true, "started": true, "armed": false, "session": active}
|
|
93
|
+
return { "ok": true, "started": true, "armed": false, "session": active }
|
|
122
94
|
|
|
123
95
|
|
|
124
96
|
## What has been summed, for the server to put into an answer.
|
|
@@ -256,3 +228,31 @@ func _add(data: Array, into: Dictionary[String, PackedFloat64Array]) -> int:
|
|
|
256
228
|
into[signature] = sum
|
|
257
229
|
carried += 1
|
|
258
230
|
return carried
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
## The editor's debugger sessions, numbered as the editor numbers its debugger tabs.
|
|
234
|
+
class GdhSessions:
|
|
235
|
+
extends EditorDebuggerPlugin
|
|
236
|
+
|
|
237
|
+
signal session_started(session_id: int)
|
|
238
|
+
|
|
239
|
+
func _setup_session(session_id: int) -> void:
|
|
240
|
+
var session: EditorDebuggerSession = get_session(session_id)
|
|
241
|
+
# int rather than Error: Signal.connect answers with a plain int.
|
|
242
|
+
var joined: int = session.started.connect(func() -> void: session_started.emit(session_id))
|
|
243
|
+
if joined != OK:
|
|
244
|
+
push_error(
|
|
245
|
+
"gdharness could not watch debugger session %d start: %d" % [session_id, joined]
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
func _has_capture(capture: String) -> bool:
|
|
249
|
+
return capture == PROFILE_CAPTURE
|
|
250
|
+
|
|
251
|
+
## Answers a quitting game that its profile's totals arrived, which it waits for before it
|
|
252
|
+
## exits. The editor reads a session's messages in order, so the totals sent before this have
|
|
253
|
+
## been read.
|
|
254
|
+
func _capture(message: String, _data: Array, session_id: int) -> bool:
|
|
255
|
+
if message != PROFILE_SENT:
|
|
256
|
+
return false
|
|
257
|
+
get_session(session_id).send_message(PROFILE_RECEIVED, [])
|
|
258
|
+
return true
|