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
|
@@ -14,25 +14,25 @@ extends SceneTree
|
|
|
14
14
|
# the operations directory ships inside the server package and is handed to the engine as an
|
|
15
15
|
# absolute path outside the project. A res:// path would only resolve in the test fixture.
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
const
|
|
26
|
-
const
|
|
27
|
-
const
|
|
28
|
-
const
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
const
|
|
17
|
+
const GdhAnimationReads = preload("animation_reads.gd")
|
|
18
|
+
const GdhAudioBuses = preload("audio_buses.gd")
|
|
19
|
+
const GdhClassCache = preload("class_cache.gd")
|
|
20
|
+
const GdhClassDbQueries = preload("classdb_queries.gd")
|
|
21
|
+
const GdhDependencies = preload("dependencies.gd")
|
|
22
|
+
const GdhExportPresets = preload("export_presets.gd")
|
|
23
|
+
const GdhFileReads = preload("file_reads.gd")
|
|
24
|
+
const GdhGdscriptAnalysis = preload("gdscript_analysis.gd")
|
|
25
|
+
const GdhGdscriptAuthoring = preload("gdscript_authoring.gd")
|
|
26
|
+
const GdhImportPipeline = preload("import_pipeline.gd")
|
|
27
|
+
const GdhInputActions = preload("input_actions.gd")
|
|
28
|
+
const GdhLog = preload("logger.gd")
|
|
29
|
+
const GdhPlugins = preload("plugins.gd")
|
|
30
|
+
const GdhProjectConfig = preload("project_config.gd")
|
|
31
|
+
const GdhProjectDiagnostics = preload("project_diagnostics.gd")
|
|
32
|
+
const GdhRead = preload("reading.gd")
|
|
33
|
+
const GdhRenameChecks = preload("rename_checks.gd")
|
|
34
|
+
const GdhResourceFiles = preload("resource_files.gd")
|
|
35
|
+
const GdhScriptCheck = preload("script_check.gd")
|
|
36
36
|
|
|
37
37
|
## Printed once the answer is written, after which the engine runs the project's autoloads. Kept in
|
|
38
38
|
## step with `ANSWERED` in src/headless.ts.
|
|
@@ -59,12 +59,12 @@ const AFTER_THE_AUTOLOADS: Array[String] = [
|
|
|
59
59
|
"run_editor_tool",
|
|
60
60
|
]
|
|
61
61
|
|
|
62
|
-
var _log:
|
|
62
|
+
var _log: GdhLog
|
|
63
63
|
|
|
64
64
|
|
|
65
65
|
func _init() -> void:
|
|
66
66
|
var args: PackedStringArray = OS.get_cmdline_args()
|
|
67
|
-
_log =
|
|
67
|
+
_log = GdhLog.new("--debug-godot" in args)
|
|
68
68
|
|
|
69
69
|
# The script path is the argument after --script, so the operation and its parameters are
|
|
70
70
|
# the two that follow it.
|
|
@@ -180,26 +180,27 @@ func _run(operation: String, params: Dictionary) -> Dictionary:
|
|
|
180
180
|
match operation:
|
|
181
181
|
# Resource files
|
|
182
182
|
"get_uid":
|
|
183
|
-
payload =
|
|
183
|
+
payload = GdhResourceFiles.new(_log).get_uid(params)
|
|
184
184
|
"refresh_class_cache":
|
|
185
|
-
payload =
|
|
185
|
+
payload = GdhClassCache.new(_log).refresh_class_cache(params)
|
|
186
186
|
"read_scene_tree":
|
|
187
187
|
payload = _read(
|
|
188
|
-
|
|
188
|
+
GdhFileReads.scene_tree(
|
|
189
189
|
str(params.get("scene_path", "")),
|
|
190
|
-
|
|
191
|
-
|
|
190
|
+
GdhRead.as_int(params.get("depth", -1), -1),
|
|
191
|
+
GdhRead.as_bool(params.get("include_properties", false))
|
|
192
192
|
)
|
|
193
193
|
)
|
|
194
194
|
"read_resource":
|
|
195
195
|
payload = _read(
|
|
196
|
-
|
|
197
|
-
str(params.get("resource_path", "")),
|
|
196
|
+
GdhFileReads.resource(
|
|
197
|
+
str(params.get("resource_path", "")),
|
|
198
|
+
GdhRead.as_bool(params.get("include_defaults", false))
|
|
198
199
|
)
|
|
199
200
|
)
|
|
200
201
|
"read_animations":
|
|
201
202
|
payload = _read(
|
|
202
|
-
|
|
203
|
+
GdhAnimationReads.scene_animations(
|
|
203
204
|
str(params.get("scene_path", "")),
|
|
204
205
|
str(params.get("player_node_path", ".")),
|
|
205
206
|
str(params.get("animation_name", ""))
|
|
@@ -207,7 +208,7 @@ func _run(operation: String, params: Dictionary) -> Dictionary:
|
|
|
207
208
|
)
|
|
208
209
|
"read_animation_states":
|
|
209
210
|
payload = _read(
|
|
210
|
-
|
|
211
|
+
GdhAnimationReads.scene_states(
|
|
211
212
|
str(params.get("scene_path", "")),
|
|
212
213
|
str(params.get("anim_tree_path", "")),
|
|
213
214
|
str(params.get("state_machine_path", ""))
|
|
@@ -219,83 +220,85 @@ func _run(operation: String, params: Dictionary) -> Dictionary:
|
|
|
219
220
|
|
|
220
221
|
# Import and export pipeline
|
|
221
222
|
"get_import_status":
|
|
222
|
-
payload =
|
|
223
|
+
payload = GdhImportPipeline.new(_log).get_import_status(params)
|
|
223
224
|
"get_import_options":
|
|
224
|
-
payload =
|
|
225
|
+
payload = GdhImportPipeline.new(_log).get_import_options(params)
|
|
225
226
|
"set_import_options":
|
|
226
|
-
payload =
|
|
227
|
+
payload = GdhImportPipeline.new(_log).set_import_options(params)
|
|
227
228
|
"list_export_presets":
|
|
228
|
-
payload =
|
|
229
|
+
payload = GdhExportPresets.new(_log).list_export_presets(params)
|
|
229
230
|
"add_export_preset":
|
|
230
|
-
payload =
|
|
231
|
+
payload = GdhExportPresets.new(_log).add_export_preset(params)
|
|
231
232
|
"validate_project":
|
|
232
|
-
payload =
|
|
233
|
+
payload = GdhImportPipeline.new(_log).validate_project(params)
|
|
233
234
|
|
|
234
235
|
# Dependencies and project inspection
|
|
235
236
|
"get_dependencies":
|
|
236
|
-
payload =
|
|
237
|
+
payload = GdhDependencies.new(_log).get_dependencies(params)
|
|
237
238
|
"find_resource_usages":
|
|
238
|
-
payload =
|
|
239
|
+
payload = GdhDependencies.new(_log).find_resource_usages(params)
|
|
239
240
|
"get_project_health":
|
|
240
|
-
payload =
|
|
241
|
+
payload = GdhProjectDiagnostics.new(_log).get_project_health(params)
|
|
241
242
|
|
|
242
243
|
# project.godot
|
|
243
244
|
"get_project_setting":
|
|
244
|
-
payload =
|
|
245
|
+
payload = GdhProjectConfig.new(_log).get_project_setting(params)
|
|
245
246
|
"set_project_setting":
|
|
246
|
-
payload =
|
|
247
|
+
payload = GdhProjectConfig.new(_log).set_project_setting(params)
|
|
247
248
|
"add_autoload":
|
|
248
|
-
payload =
|
|
249
|
+
payload = GdhProjectConfig.new(_log).add_autoload(params)
|
|
249
250
|
"remove_autoload":
|
|
250
|
-
payload =
|
|
251
|
+
payload = GdhProjectConfig.new(_log).remove_autoload(params)
|
|
251
252
|
"list_autoloads":
|
|
252
|
-
payload =
|
|
253
|
+
payload = GdhProjectConfig.new(_log).list_autoloads(params)
|
|
253
254
|
"set_main_scene":
|
|
254
|
-
payload =
|
|
255
|
+
payload = GdhProjectConfig.new(_log).set_main_scene(params)
|
|
255
256
|
|
|
256
257
|
# GDScript files
|
|
257
258
|
"create_script":
|
|
258
|
-
payload =
|
|
259
|
+
payload = GdhGdscriptAuthoring.new(_log).create_gdscript(params)
|
|
259
260
|
"modify_script":
|
|
260
|
-
payload =
|
|
261
|
+
payload = GdhGdscriptAuthoring.new(_log).modify_gdscript(params)
|
|
261
262
|
"get_script_info":
|
|
262
|
-
payload =
|
|
263
|
+
payload = GdhGdscriptAnalysis.new(_log).get_gdscript_info(params)
|
|
263
264
|
"names_taken":
|
|
264
|
-
payload =
|
|
265
|
+
payload = GdhRenameChecks.new(_log).names_taken(params)
|
|
265
266
|
"check_scripts":
|
|
266
|
-
payload =
|
|
267
|
+
payload = GdhScriptCheck.new(_log).check_scripts(params)
|
|
267
268
|
|
|
268
269
|
# Plugins and input
|
|
269
270
|
"list_plugins":
|
|
270
|
-
payload =
|
|
271
|
+
payload = GdhPlugins.new(_log).list_plugins(params)
|
|
271
272
|
"enable_plugin":
|
|
272
|
-
payload =
|
|
273
|
+
payload = GdhPlugins.new(_log).enable_plugin(params)
|
|
273
274
|
"disable_plugin":
|
|
274
|
-
payload =
|
|
275
|
+
payload = GdhPlugins.new(_log).disable_plugin(params)
|
|
275
276
|
"add_input_action":
|
|
276
|
-
payload =
|
|
277
|
+
payload = GdhInputActions.new(_log).add_input_action(params)
|
|
277
278
|
"remove_input_action":
|
|
278
|
-
payload =
|
|
279
|
+
payload = GdhInputActions.new(_log).remove_input_action(params)
|
|
280
|
+
"list_input_actions":
|
|
281
|
+
payload = GdhInputActions.new(_log).list_input_actions(params)
|
|
279
282
|
|
|
280
283
|
# Audio
|
|
281
284
|
"create_audio_bus":
|
|
282
|
-
payload =
|
|
285
|
+
payload = GdhAudioBuses.new(_log).create_audio_bus(params)
|
|
283
286
|
"remove_audio_bus":
|
|
284
|
-
payload =
|
|
287
|
+
payload = GdhAudioBuses.new(_log).remove_audio_bus(params)
|
|
285
288
|
"get_audio_buses":
|
|
286
|
-
payload =
|
|
289
|
+
payload = GdhAudioBuses.new(_log).get_audio_buses(params)
|
|
287
290
|
"set_audio_bus_effect":
|
|
288
|
-
payload =
|
|
291
|
+
payload = GdhAudioBuses.new(_log).set_audio_bus_effect(params)
|
|
289
292
|
"set_audio_bus_volume":
|
|
290
|
-
payload =
|
|
293
|
+
payload = GdhAudioBuses.new(_log).set_audio_bus_volume(params)
|
|
291
294
|
|
|
292
295
|
# ClassDB
|
|
293
296
|
"query_classes":
|
|
294
|
-
payload =
|
|
297
|
+
payload = GdhClassDbQueries.new(_log).query_classes(params)
|
|
295
298
|
"query_class_info":
|
|
296
|
-
payload =
|
|
299
|
+
payload = GdhClassDbQueries.new(_log).query_class_info(params)
|
|
297
300
|
"inspect_inheritance":
|
|
298
|
-
payload =
|
|
301
|
+
payload = GdhClassDbQueries.new(_log).inspect_inheritance(params)
|
|
299
302
|
|
|
300
303
|
_:
|
|
301
304
|
_log.error("Unknown operation: " + operation)
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
const
|
|
3
|
+
const GdhDependencies = preload("dependencies.gd")
|
|
4
|
+
const GdhExportPresets = preload("export_presets.gd")
|
|
5
|
+
const GdhScriptCheck = preload("script_check.gd")
|
|
6
|
+
const GdhRead = preload("reading.gd")
|
|
7
|
+
const GdhSerialisation = preload("serialisation.gd")
|
|
8
|
+
const GdhFileWalk = preload("file_walk.gd")
|
|
9
|
+
const GdhLog = preload("logger.gd")
|
|
8
10
|
|
|
9
11
|
## What the engine imports with no tool outside it, for finding what has not been imported yet;
|
|
10
12
|
## everything imported already is found by its sidecar whatever its extension.
|
|
@@ -53,20 +55,20 @@ const OPENTYPE_OPTIONS: Dictionary[String, Array] = {"font_data_dynamic": ["open
|
|
|
53
55
|
const EDITOR_DIRECTORY: String = "res://.godot/editor"
|
|
54
56
|
const EDITOR_CACHE_PREFIX: String = "filesystem_cache"
|
|
55
57
|
|
|
56
|
-
var _log:
|
|
57
|
-
var _files:
|
|
58
|
-
var _values:
|
|
58
|
+
var _log: GdhLog
|
|
59
|
+
var _files: GdhFileWalk = GdhFileWalk.new()
|
|
60
|
+
var _values: GdhSerialisation = GdhSerialisation.new()
|
|
59
61
|
var _sidecar_md5s: Variant = null
|
|
60
62
|
|
|
61
63
|
|
|
62
|
-
func _init(p_log:
|
|
64
|
+
func _init(p_log: GdhLog) -> void:
|
|
63
65
|
_log = p_log
|
|
64
66
|
|
|
65
67
|
|
|
66
68
|
# Get import status for resources
|
|
67
69
|
func get_import_status(params: Dictionary) -> Dictionary:
|
|
68
70
|
var resource_path: String = str(params.get("resource_path", ""))
|
|
69
|
-
var include_up_to_date: bool =
|
|
71
|
+
var include_up_to_date: bool = GdhRead.as_bool(params.get("include_up_to_date", false))
|
|
70
72
|
|
|
71
73
|
_log.info(
|
|
72
74
|
(
|
|
@@ -85,6 +87,14 @@ func get_import_status(params: Dictionary) -> Dictionary:
|
|
|
85
87
|
if not full_path.begins_with("res://"):
|
|
86
88
|
full_path = "res://" + full_path
|
|
87
89
|
|
|
90
|
+
# missing_source is a sidecar whose source has gone; with neither, the path names nothing the
|
|
91
|
+
# engine ever imported, and answering it as a lost source sent a caller to restore a file.
|
|
92
|
+
if not FileAccess.file_exists(full_path) and not FileAccess.file_exists(full_path + ".import"):
|
|
93
|
+
return _log.failure(
|
|
94
|
+
_files.with_nearest_files(
|
|
95
|
+
"Nothing is at %s, and no import sidecar names it" % full_path, full_path
|
|
96
|
+
)
|
|
97
|
+
)
|
|
88
98
|
var status: Dictionary = _import_status_of(full_path, full_path + ".import")
|
|
89
99
|
resources.append(status)
|
|
90
100
|
_tally(summary, status)
|
|
@@ -203,7 +213,7 @@ func set_import_options(params: Dictionary) -> Dictionary:
|
|
|
203
213
|
and (value is int or value is float)
|
|
204
214
|
and type_convert(value, typeof(given)) != given
|
|
205
215
|
)
|
|
206
|
-
if held != null and (lossy or not
|
|
216
|
+
if held != null and (lossy or not GdhSerialisation.acceptable(value, typeof(held))):
|
|
207
217
|
var held_type: String = type_string(typeof(held))
|
|
208
218
|
var article: String = "an" if held_type.to_lower()[0] in "aeiou" else "a"
|
|
209
219
|
refused.append(
|
|
@@ -323,7 +333,7 @@ static func _all_ints(held: Variant, keys: bool) -> bool:
|
|
|
323
333
|
# Validate project for export
|
|
324
334
|
func validate_project(params: Dictionary) -> Dictionary:
|
|
325
335
|
var preset_name: String = str(params.get("preset", ""))
|
|
326
|
-
var include_suggestions: bool =
|
|
336
|
+
var include_suggestions: bool = GdhRead.as_bool(params.get("include_suggestions", true), true)
|
|
327
337
|
|
|
328
338
|
_log.info("Validating project" + (" for preset: " + preset_name if not preset_name.is_empty() else ""))
|
|
329
339
|
|
|
@@ -355,12 +365,12 @@ func validate_project(params: Dictionary) -> Dictionary:
|
|
|
355
365
|
include_suggestions
|
|
356
366
|
)
|
|
357
367
|
)
|
|
358
|
-
elif not
|
|
368
|
+
elif not GdhFileWalk.missing_reason(main_scene).is_empty():
|
|
359
369
|
issues.append(
|
|
360
370
|
_finding(
|
|
361
371
|
"error",
|
|
362
372
|
"main_scene",
|
|
363
|
-
"Main scene " +
|
|
373
|
+
"Main scene " + GdhFileWalk.missing_reason(main_scene),
|
|
364
374
|
"Update the main scene setting or create the missing scene file",
|
|
365
375
|
include_suggestions
|
|
366
376
|
)
|
|
@@ -382,7 +392,7 @@ func validate_project(params: Dictionary) -> Dictionary:
|
|
|
382
392
|
# does not exist answered valid, as though it had been checked.
|
|
383
393
|
if not preset_name.is_empty():
|
|
384
394
|
checks_performed.append("export_preset")
|
|
385
|
-
var named: Array[String] =
|
|
395
|
+
var named: Array[String] = GdhExportPresets.names()
|
|
386
396
|
if preset_name not in named:
|
|
387
397
|
issues.append(
|
|
388
398
|
_finding(
|
|
@@ -457,6 +467,35 @@ func validate_project(params: Dictionary) -> Dictionary:
|
|
|
457
467
|
if "pass # TODO" in content:
|
|
458
468
|
script_issues.append({"path": script_path, "issue": "Contains unimplemented functions"})
|
|
459
469
|
|
|
470
|
+
# Compiled by the server and handed in, as for the health check: a script the engine refuses
|
|
471
|
+
# stops the export's game at the first scene using it, which the comment check above never saw.
|
|
472
|
+
var compiled: Dictionary = params.get("compiled", {})
|
|
473
|
+
if compiled.has("checked"):
|
|
474
|
+
checks_performed.append("compiles")
|
|
475
|
+
var failures: Array = compiled.get("failures", [])
|
|
476
|
+
for failure: Dictionary in failures:
|
|
477
|
+
issues.append(
|
|
478
|
+
_finding(
|
|
479
|
+
"error",
|
|
480
|
+
"compiles",
|
|
481
|
+
GdhScriptCheck.failure_line(failure),
|
|
482
|
+
"script_diagnostics gives every error in it with its line",
|
|
483
|
+
include_suggestions
|
|
484
|
+
)
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
checks_performed.append("references")
|
|
488
|
+
for entry: Dictionary in GdhDependencies.new(_log).missing_references():
|
|
489
|
+
issues.append(
|
|
490
|
+
_finding(
|
|
491
|
+
"error",
|
|
492
|
+
"references",
|
|
493
|
+
GdhDependencies.missing_line(entry),
|
|
494
|
+
"Restore the file, or point the reference at the file that replaced it",
|
|
495
|
+
include_suggestions
|
|
496
|
+
)
|
|
497
|
+
)
|
|
498
|
+
|
|
460
499
|
if script_issues.size() > 0:
|
|
461
500
|
var warning: Dictionary = _finding(
|
|
462
501
|
"warning",
|
|
@@ -611,8 +650,8 @@ static func _import_staleness(resource_path: String, sidecar: ConfigFile) -> Dic
|
|
|
611
650
|
# imported them again: every scene after an engine upgrade raised the importer's version, and
|
|
612
651
|
# every VRAM texture once the project asked for another compression format.
|
|
613
652
|
var importer: String = str(sidecar.get_value("remap", "importer", ""))
|
|
614
|
-
var version: int =
|
|
615
|
-
var current: int =
|
|
653
|
+
var version: int = GdhRead.as_int(sidecar.get_value("remap", "importer_version", 0))
|
|
654
|
+
var current: int = GdhRead.as_int(IMPORTER_VERSIONS.get(importer, 0))
|
|
616
655
|
if current > version:
|
|
617
656
|
return _stale(
|
|
618
657
|
(
|
|
@@ -697,12 +736,12 @@ static func _vram_formats_lacking(metadata: Variant) -> Array[String]:
|
|
|
697
736
|
if not metadata is Dictionary:
|
|
698
737
|
return lacking
|
|
699
738
|
var meta: Dictionary = metadata
|
|
700
|
-
if not
|
|
739
|
+
if not GdhRead.as_bool(meta.get("vram_texture", false)):
|
|
701
740
|
return lacking
|
|
702
741
|
var written: Variant = meta.get("imported_formats", [])
|
|
703
742
|
var formats: Array = written if written is Array else []
|
|
704
743
|
for format: String in VRAM_FORMATS:
|
|
705
|
-
var asked: bool =
|
|
744
|
+
var asked: bool = GdhRead.as_bool(
|
|
706
745
|
ProjectSettings.get_setting("rendering/textures/vram_compression/import_" + format, false)
|
|
707
746
|
)
|
|
708
747
|
if asked and format not in formats:
|
|
@@ -850,7 +889,7 @@ static func _images_of(scene_path: String) -> Array[String]:
|
|
|
850
889
|
text = bytes.slice(20, mini(20 + length, bytes.size())).get_string_from_utf8()
|
|
851
890
|
if text.is_empty():
|
|
852
891
|
return images
|
|
853
|
-
var parsed: Variant =
|
|
892
|
+
var parsed: Variant = GdhRead.json_or_null(text)
|
|
854
893
|
if not parsed is Dictionary:
|
|
855
894
|
return images
|
|
856
895
|
var document: Dictionary = parsed
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
3
|
+
const GdhRead = preload("reading.gd")
|
|
4
|
+
const GdhLog = preload("logger.gd")
|
|
5
|
+
const GdhProjectFile = preload("project_file.gd")
|
|
6
6
|
|
|
7
7
|
# The key names a caller may write, and what the engine calls them.
|
|
8
8
|
const KEY_NAMES: Dictionary = {
|
|
@@ -97,10 +97,10 @@ const KEY_NAMES: Dictionary = {
|
|
|
97
97
|
"Equal": KEY_EQUAL,
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
var _log:
|
|
100
|
+
var _log: GdhLog
|
|
101
101
|
|
|
102
102
|
|
|
103
|
-
func _init(p_log:
|
|
103
|
+
func _init(p_log: GdhLog) -> void:
|
|
104
104
|
_log = p_log
|
|
105
105
|
|
|
106
106
|
|
|
@@ -108,9 +108,27 @@ func _init(p_log: Log) -> void:
|
|
|
108
108
|
func add_input_action(params: Dictionary) -> Dictionary:
|
|
109
109
|
var action_name: String = str(params.get("action_name", ""))
|
|
110
110
|
var events: Array = params.get("events", [])
|
|
111
|
-
var deadzone: float =
|
|
111
|
+
var deadzone: float = GdhRead.as_float(params.get("deadzone", 0.5), 0.5)
|
|
112
112
|
|
|
113
113
|
_log.info("Adding input action: " + action_name)
|
|
114
|
+
# The editor's Input Map refuses these, since project.godot keys an action by its name and
|
|
115
|
+
# the characters below are its own syntax there; a name holding one is written escaped and
|
|
116
|
+
# reads back as a name nobody typed.
|
|
117
|
+
for index: int in action_name.length():
|
|
118
|
+
var code: int = action_name.unicode_at(index)
|
|
119
|
+
if code < 32 or action_name[index] in ["/", ":", "=", "\\", '"']:
|
|
120
|
+
return (
|
|
121
|
+
_log
|
|
122
|
+
. failure(
|
|
123
|
+
(
|
|
124
|
+
(
|
|
125
|
+
"%s is not an action name the engine's Input Map takes: a name cannot hold / : = \\ "
|
|
126
|
+
+ "or a double quote, or a control character. jump, move_left or ui_menu are names it takes."
|
|
127
|
+
)
|
|
128
|
+
% JSON.stringify(action_name)
|
|
129
|
+
)
|
|
130
|
+
)
|
|
131
|
+
)
|
|
114
132
|
_log.debug("Events: " + JSON.stringify(events))
|
|
115
133
|
_log.debug("Deadzone: " + str(deadzone))
|
|
116
134
|
|
|
@@ -130,7 +148,7 @@ func add_input_action(params: Dictionary) -> Dictionary:
|
|
|
130
148
|
return _log.failure("A key event needs keycode")
|
|
131
149
|
var key_value: int = _keycode_value(keycode)
|
|
132
150
|
if key_value == KEY_NONE:
|
|
133
|
-
return _log.failure(
|
|
151
|
+
return _log.failure(_unknown_key(keycode))
|
|
134
152
|
|
|
135
153
|
var event_config: Dictionary = {"class_name": "InputEventKey", "keycode": key_value}
|
|
136
154
|
if fields.get("ctrl", false):
|
|
@@ -192,12 +210,12 @@ func add_input_action(params: Dictionary) -> Dictionary:
|
|
|
192
210
|
var bound: InputEvent = event
|
|
193
211
|
old_events.append(_shaped(bound))
|
|
194
212
|
answer["old_events"] = old_events
|
|
195
|
-
answer["old_deadzone"] =
|
|
213
|
+
answer["old_deadzone"] = GdhRead.as_float(old.get("deadzone", 0.5), 0.5)
|
|
196
214
|
|
|
197
215
|
# Through ProjectSettings so the file is saved the way the editor saves it, header and
|
|
198
216
|
# every other line kept; a ConfigFile of project.godot drops the comments on the way out.
|
|
199
217
|
ProjectSettings.set_setting(setting, action)
|
|
200
|
-
var err: Error =
|
|
218
|
+
var err: Error = GdhProjectFile.save()
|
|
201
219
|
if err != OK:
|
|
202
220
|
return _log.failure("Failed to save project.godot: " + error_string(err))
|
|
203
221
|
|
|
@@ -229,6 +247,42 @@ static func _shaped(event: InputEvent) -> Dictionary:
|
|
|
229
247
|
return shaped
|
|
230
248
|
|
|
231
249
|
|
|
250
|
+
## The input actions project.godot holds, each with its deadzone and events in the form
|
|
251
|
+
## add_input_action answers with. An engine ui_ action is listed only when the project rebinds it,
|
|
252
|
+
## as engineAction, since the engine's own eighty are there in every project.
|
|
253
|
+
func list_input_actions(_params: Dictionary) -> Dictionary:
|
|
254
|
+
var actions: Array[Dictionary] = []
|
|
255
|
+
for info: Dictionary in ProjectSettings.get_property_list():
|
|
256
|
+
var setting: String = str(info.get("name", ""))
|
|
257
|
+
if not setting.begins_with("input/"):
|
|
258
|
+
continue
|
|
259
|
+
var engine_default: Variant = ProjectSettings.property_get_revert(setting)
|
|
260
|
+
var held: Variant = ProjectSettings.get_setting(setting)
|
|
261
|
+
if engine_default != null and held == engine_default:
|
|
262
|
+
continue
|
|
263
|
+
var action: Dictionary = held if held is Dictionary else {}
|
|
264
|
+
var events: Array[Dictionary] = []
|
|
265
|
+
var listed: Variant = action.get("events", [])
|
|
266
|
+
if listed is Array:
|
|
267
|
+
var bound: Array = listed
|
|
268
|
+
for event: Variant in bound:
|
|
269
|
+
if event is InputEvent:
|
|
270
|
+
var shaped: InputEvent = event
|
|
271
|
+
events.append(_shaped(shaped))
|
|
272
|
+
(
|
|
273
|
+
actions
|
|
274
|
+
. append(
|
|
275
|
+
{
|
|
276
|
+
"name": setting.trim_prefix("input/"),
|
|
277
|
+
"deadzone": GdhRead.as_float(action.get("deadzone", 0.5), 0.5),
|
|
278
|
+
"events": events,
|
|
279
|
+
"engineAction": engine_default != null,
|
|
280
|
+
}
|
|
281
|
+
)
|
|
282
|
+
)
|
|
283
|
+
return {"actions": actions, "count": actions.size()}
|
|
284
|
+
|
|
285
|
+
|
|
232
286
|
# Remove an input action the project defines, which add_input_action had no way back from.
|
|
233
287
|
func remove_input_action(params: Dictionary) -> Dictionary:
|
|
234
288
|
var action_name: String = str(params.get("action_name", ""))
|
|
@@ -250,7 +304,7 @@ func remove_input_action(params: Dictionary) -> Dictionary:
|
|
|
250
304
|
)
|
|
251
305
|
)
|
|
252
306
|
ProjectSettings.set_setting(setting, null)
|
|
253
|
-
var err: Error =
|
|
307
|
+
var err: Error = GdhProjectFile.save()
|
|
254
308
|
if err != OK:
|
|
255
309
|
return _log.failure("Failed to save project.godot: " + error_string(err))
|
|
256
310
|
return {"action_name": action_name, "removed": true}
|
|
@@ -273,29 +327,29 @@ func build_input_action(deadzone: float, events: Array) -> Dictionary:
|
|
|
273
327
|
match evt_class:
|
|
274
328
|
"InputEventKey":
|
|
275
329
|
var key: InputEventKey = InputEventKey.new()
|
|
276
|
-
key.keycode =
|
|
277
|
-
key.ctrl_pressed =
|
|
278
|
-
key.alt_pressed =
|
|
279
|
-
key.shift_pressed =
|
|
330
|
+
key.keycode = GdhRead.as_int(event.get("keycode", 0)) as Key
|
|
331
|
+
key.ctrl_pressed = GdhRead.as_bool(event.get("ctrl_pressed", false))
|
|
332
|
+
key.alt_pressed = GdhRead.as_bool(event.get("alt_pressed", false))
|
|
333
|
+
key.shift_pressed = GdhRead.as_bool(event.get("shift_pressed", false))
|
|
280
334
|
built.append(key)
|
|
281
335
|
|
|
282
336
|
"InputEventMouseButton":
|
|
283
337
|
var mouse: InputEventMouseButton = InputEventMouseButton.new()
|
|
284
338
|
mouse.button_index = (
|
|
285
|
-
|
|
339
|
+
GdhRead.as_int(event.get("button_index", MOUSE_BUTTON_LEFT), MOUSE_BUTTON_LEFT)
|
|
286
340
|
as MouseButton
|
|
287
341
|
)
|
|
288
342
|
built.append(mouse)
|
|
289
343
|
|
|
290
344
|
"InputEventJoypadButton":
|
|
291
345
|
var pad: InputEventJoypadButton = InputEventJoypadButton.new()
|
|
292
|
-
pad.button_index =
|
|
346
|
+
pad.button_index = GdhRead.as_int(event.get("button_index", 0)) as JoyButton
|
|
293
347
|
built.append(pad)
|
|
294
348
|
|
|
295
349
|
"InputEventJoypadMotion":
|
|
296
350
|
var motion: InputEventJoypadMotion = InputEventJoypadMotion.new()
|
|
297
|
-
motion.axis =
|
|
298
|
-
motion.axis_value =
|
|
351
|
+
motion.axis = GdhRead.as_int(event.get("axis", 0)) as JoyAxis
|
|
352
|
+
motion.axis_value = GdhRead.as_float(event.get("axis_value", 1.0), 1.0)
|
|
299
353
|
built.append(motion)
|
|
300
354
|
|
|
301
355
|
_:
|
|
@@ -305,6 +359,24 @@ func build_input_action(deadzone: float, events: Array) -> Dictionary:
|
|
|
305
359
|
|
|
306
360
|
|
|
307
361
|
# The keycode for a key name as the tool spells it, or KEY_NONE for a name the table lacks.
|
|
362
|
+
## The refusal for [param key_name], which names no key: the names nearest it, then every name taken,
|
|
363
|
+
## since a name the caller has to guess at is the same guess again.
|
|
364
|
+
static func _unknown_key(key_name: String) -> String:
|
|
365
|
+
var every: PackedStringArray = []
|
|
366
|
+
var named: PackedStringArray = []
|
|
367
|
+
for name: String in KEY_NAMES:
|
|
368
|
+
var _every: bool = every.append(name)
|
|
369
|
+
if name.length() > 1 and not (name.begins_with("F") and name.substr(1).is_valid_int()):
|
|
370
|
+
var _named: bool = named.append(name)
|
|
371
|
+
var nearest: PackedStringArray = GdhRead.nearest(key_name, every)
|
|
372
|
+
var said: String = "Unknown key: %s." % key_name
|
|
373
|
+
if nearest.size() == 1:
|
|
374
|
+
said += " The nearest key name is %s." % nearest[0]
|
|
375
|
+
elif not nearest.is_empty():
|
|
376
|
+
said += " The nearest key names are %s." % ", ".join(nearest)
|
|
377
|
+
return said + " A key is named by its letter, its digit, F1 to F12, or one of: %s." % ", ".join(named)
|
|
378
|
+
|
|
379
|
+
|
|
308
380
|
func _keycode_value(key_name: String) -> int:
|
|
309
381
|
var wanted: String = key_name.to_lower()
|
|
310
382
|
for name: String in KEY_NAMES:
|
|
@@ -3,7 +3,7 @@ extends RefCounted
|
|
|
3
3
|
# Everything printed is diagnostics; the answer goes to a file. The server reads `[ERROR]` lines as
|
|
4
4
|
# the operation's own errors, and the prefixes let a human read the rest without parsing it.
|
|
5
5
|
#
|
|
6
|
-
# Every module preloads this as `
|
|
6
|
+
# Every module preloads this as `GdhLog`. `Logger` is a native class from Godot 4.5 on, and a
|
|
7
7
|
# constant of that name is a parse error rather than a shadowing warning.
|
|
8
8
|
|
|
9
9
|
var debug_mode: bool = false
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
2
|
|
|
3
|
-
const
|
|
4
|
-
const
|
|
5
|
-
const
|
|
3
|
+
const GdhPatterns = preload("patterns.gd")
|
|
4
|
+
const GdhLog = preload("logger.gd")
|
|
5
|
+
const GdhProjectFile = preload("project_file.gd")
|
|
6
6
|
|
|
7
|
-
var _log:
|
|
7
|
+
var _log: GdhLog
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
func _init(p_log:
|
|
10
|
+
func _init(p_log: GdhLog) -> void:
|
|
11
11
|
_log = p_log
|
|
12
12
|
|
|
13
13
|
|
|
@@ -148,7 +148,7 @@ func _enabled_paths() -> Array[String]:
|
|
|
148
148
|
paths.assign(packed)
|
|
149
149
|
elif enabled_value is String:
|
|
150
150
|
var listed: String = enabled_value
|
|
151
|
-
for m: RegExMatch in
|
|
151
|
+
for m: RegExMatch in GdhPatterns.compiled("res://[^\"',)]+?plugin\\.cfg").search_all(listed):
|
|
152
152
|
paths.append(m.get_string())
|
|
153
153
|
return paths
|
|
154
154
|
|
|
@@ -172,11 +172,11 @@ static func _names_of(paths: Array[String]) -> Array[String]:
|
|
|
172
172
|
func _save_enabled_paths(paths: Array[String]) -> Error:
|
|
173
173
|
if paths.is_empty():
|
|
174
174
|
ProjectSettings.set_setting("editor_plugins/enabled", null)
|
|
175
|
-
return
|
|
175
|
+
return GdhProjectFile.save()
|
|
176
176
|
|
|
177
177
|
# Kept in an Array[String] and converted here, because appending to a packed array answers with
|
|
178
178
|
# whether it worked and a project holding return_value_discarded at error level will not compile
|
|
179
179
|
# a script that drops that answer. The conversion is what matters: an Array[String] handed to
|
|
180
180
|
# set_setting is written as Array[String]([...]), which the editor does not read as a plugin list.
|
|
181
181
|
ProjectSettings.set_setting("editor_plugins/enabled", PackedStringArray(paths))
|
|
182
|
-
return
|
|
182
|
+
return GdhProjectFile.save()
|