gdharness 1.3.59 → 1.3.61
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/README.md +20 -20
- package/build/cli.js +2264 -1706
- package/build/godot/addons/auto_reload/auto_reload.gd +3 -3
- package/build/godot/addons/gdharness_editor/bridge_client.gd +4 -4
- package/build/godot/addons/gdharness_editor/declared_values.gd +13 -13
- package/build/godot/addons/gdharness_editor/file_reads.gd +6 -6
- package/build/godot/addons/gdharness_editor/headless_check.gd +9 -6
- package/build/godot/addons/gdharness_editor/property_paths.gd +3 -3
- package/build/godot/addons/gdharness_editor/property_values.gd +18 -11
- package/build/godot/addons/gdharness_editor/reading.gd +22 -18
- package/build/godot/addons/gdharness_editor/scene_file.gd +5 -5
- package/build/godot/addons/gdharness_editor/serialisation.gd +4 -4
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +5 -5
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +6 -6
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +9 -9
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +3 -3
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +5 -5
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +5 -5
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +2 -2
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +1 -1
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +5 -5
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +3 -3
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +3 -3
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +4 -4
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +3 -3
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +10 -10
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +1 -1
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +5 -5
- package/build/godot/addons/gdharness_runtime/declared_values.gd +13 -13
- package/build/godot/addons/gdharness_runtime/reading.gd +22 -18
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +2 -2
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +89 -18
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +6 -6
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +10 -10
- package/build/godot/addons/gdharness_runtime/runtime_frames.gd +5 -4
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +3 -4
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +8 -8
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +1 -1
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +3 -3
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +5 -5
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +20 -20
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +13 -13
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +5 -5
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +39 -39
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +3 -3
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +7 -7
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +8 -8
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +5 -5
- package/build/godot/addons/gdharness_runtime/serialisation.gd +4 -4
- package/build/godot/addons/gdharness_tests/bench.gd +1 -1
- package/build/godot/addons/gdharness_tests/share.gd +6 -6
- package/build/godot/addons/gdharness_tests/suites.gd +1 -1
- package/build/godot/operations/audio_buses.gd +4 -4
- package/build/godot/operations/class_cache.gd +3 -3
- package/build/godot/operations/classdb_queries.gd +1 -1
- package/build/godot/operations/declared_values.gd +13 -13
- package/build/godot/operations/dependencies.gd +6 -6
- package/build/godot/operations/export_presets.gd +7 -7
- package/build/godot/operations/file_reads.gd +6 -6
- package/build/godot/operations/gdscript_analysis.gd +6 -6
- package/build/godot/operations/gdscript_authoring.gd +11 -11
- package/build/godot/operations/godot_operations.gd +1 -1
- package/build/godot/operations/import_pipeline.gd +12 -12
- package/build/godot/operations/input_actions.gd +3 -3
- package/build/godot/operations/logger.gd +1 -1
- package/build/godot/operations/plugins.gd +2 -2
- package/build/godot/operations/project_config.gd +11 -11
- package/build/godot/operations/project_validation.gd +1 -1
- package/build/godot/operations/property_paths.gd +3 -3
- package/build/godot/operations/property_values.gd +18 -11
- package/build/godot/operations/reading.gd +22 -18
- package/build/godot/operations/resource_files.gd +1 -1
- package/build/godot/operations/script_check.gd +2 -2
- package/build/godot/operations/serialisation.gd +4 -4
- package/build/godot/tests/listed.gd +28 -10
- package/build/keeper.js +511 -161
- package/package.json +1 -1
|
@@ -180,7 +180,7 @@ func _members(
|
|
|
180
180
|
own.last = i - 1
|
|
181
181
|
return own
|
|
182
182
|
|
|
183
|
-
# An odd count opens a string that runs on
|
|
183
|
+
# An odd count opens a string that runs on. An even one is a string that closed on the line
|
|
184
184
|
# it opened on. Toggling on any count lost `const HELP := """Usage"""`, and everything after
|
|
185
185
|
# it up to the next triple quote, with the state inverted from there to the end of the file.
|
|
186
186
|
# The line that opens one is still read: it can be the declaration the string belongs to.
|
|
@@ -359,7 +359,7 @@ func _mark_virtuals(functions: Array[Dictionary], extends_name: String, from_pat
|
|
|
359
359
|
|
|
360
360
|
# The native class at the top of this script's chain, or empty when the chain does not reach one.
|
|
361
361
|
#
|
|
362
|
-
# A base named by a script is followed
|
|
362
|
+
# A base named by a script is followed. A base that is neither a script nor a class the engine knows
|
|
363
363
|
# leaves nothing to ask, and then no function is called virtual rather than every underscore being
|
|
364
364
|
# guessed at.
|
|
365
365
|
func _native_ancestor(base: String, from_path: String) -> String:
|
|
@@ -388,7 +388,7 @@ func _native_ancestor(base: String, from_path: String) -> String:
|
|
|
388
388
|
# `res://` path or as a `class_name` in the project's class list, and a script whose base is neither
|
|
389
389
|
# has no ancestor to read, which the answer says by leaving `inherits_from` short rather than by
|
|
390
390
|
# refusing. Each entry carries `inherited_from`, so a caller can tell a member the script declares
|
|
391
|
-
# from one it is given
|
|
391
|
+
# from one it is given. A name a script overrides appears twice, at both lines, on purpose.
|
|
392
392
|
func _add_inherited(answer: Dictionary, seen: Array[String]) -> void:
|
|
393
393
|
var inherits_from: Array[String] = []
|
|
394
394
|
var base: String = str(answer.get("extends", ""))
|
|
@@ -634,7 +634,7 @@ func _parse_variable(line: String, line_num: int) -> Dictionary:
|
|
|
634
634
|
"is_static": GdhPatterns.without_annotations(line).begins_with("static "),
|
|
635
635
|
"line": line_num,
|
|
636
636
|
}
|
|
637
|
-
# Only := types a variable by its value
|
|
637
|
+
# Only := types a variable by its value. One written with = alone holds any type.
|
|
638
638
|
var walrus: bool = (
|
|
639
639
|
"=" in var_text and var_text.split("=", true, 1)[0].strip_edges().ends_with(":")
|
|
640
640
|
)
|
|
@@ -677,7 +677,7 @@ func _parse_function(line: String, line_num: int, annotated: String = "") -> Dic
|
|
|
677
677
|
"name": name,
|
|
678
678
|
"params": params,
|
|
679
679
|
"return_type": return_type,
|
|
680
|
-
# Filled in after the whole file is read, once the base is known
|
|
680
|
+
# Filled in after the whole file is read, once the base is known. See `_mark_virtuals`.
|
|
681
681
|
"is_virtual": false,
|
|
682
682
|
"is_static": is_static,
|
|
683
683
|
# Said rather than left to be inferred: an abstract method has no body, so a caller that
|
|
@@ -857,7 +857,7 @@ static func _based_on_paths(extends_name: String, inner_classes: Array) -> Array
|
|
|
857
857
|
return paths
|
|
858
858
|
|
|
859
859
|
|
|
860
|
-
## The text of [param literal], a string literal as written
|
|
860
|
+
## The text of [param literal], a string literal as written. Empty for anything else.
|
|
861
861
|
static func _unquoted(literal: String) -> String:
|
|
862
862
|
var text: String = literal.strip_edges()
|
|
863
863
|
if text.length() >= 2 and text[0] in ["\"", "'"] and text[-1] == text[0]:
|
|
@@ -138,7 +138,7 @@ func create_gdscript(params: Dictionary) -> Dictionary:
|
|
|
138
138
|
|
|
139
139
|
|
|
140
140
|
## The uid in the .uid file beside [param path], written first when there is none, as the editor
|
|
141
|
-
## writes it for a script it makes
|
|
141
|
+
## writes it for a script it makes. Empty when it could not be written. A sidecar already there is
|
|
142
142
|
## the one the engine reads the script's uid from, so it is kept.
|
|
143
143
|
static func _uid_beside(path: String) -> String:
|
|
144
144
|
var sidecar: String = path + ".uid"
|
|
@@ -232,7 +232,7 @@ func modify_gdscript(params: Dictionary) -> Dictionary:
|
|
|
232
232
|
var refusal: String = "Nothing was changed: " + "; ".join(problems)
|
|
233
233
|
if _redeclares(modifications, declared):
|
|
234
234
|
refusal += (
|
|
235
|
-
". modify only adds members
|
|
235
|
+
". modify only adds members. To change one the script declares already,"
|
|
236
236
|
+ " edit its lines in the file"
|
|
237
237
|
)
|
|
238
238
|
return _log.failure(refusal)
|
|
@@ -290,7 +290,7 @@ func modify_gdscript(params: Dictionary) -> Dictionary:
|
|
|
290
290
|
OS.remove_logger(caught)
|
|
291
291
|
var parses: bool = _parses(made)
|
|
292
292
|
# The names checked above are the script's own, and a member the base class declares, or a body
|
|
293
|
-
# that does not compile, is found only by the engine
|
|
293
|
+
# that does not compile, is found only by the engine. A script left broken by an addition breaks
|
|
294
294
|
# every scene that uses it, so it goes back as it was. One already broken is written, as the
|
|
295
295
|
# addition can be what mends it.
|
|
296
296
|
if parsed_before and not parses:
|
|
@@ -335,7 +335,7 @@ static func _reasons(raised: Array[Dictionary], path: String) -> String:
|
|
|
335
335
|
|
|
336
336
|
# Why a script with [param named] as its class_name and extending [param base] cannot be written,
|
|
337
337
|
# or "". A class_name another script holds does not compile ("hides a global script class"), and nor
|
|
338
|
-
# does a base no class answers to
|
|
338
|
+
# does a base no class answers to. Both were written and answered as made. Classes are read off the
|
|
339
339
|
# scripts as well as the class list, which a fresh clone has not written yet.
|
|
340
340
|
static func _header_refusal(named: String, base: String) -> String:
|
|
341
341
|
var declared: Dictionary = _declared_classes()
|
|
@@ -366,7 +366,7 @@ static func _header_refusal(named: String, base: String) -> String:
|
|
|
366
366
|
close = GdhRead.nearest(base, ClassDB.get_class_list())
|
|
367
367
|
if close.is_empty():
|
|
368
368
|
return said + "."
|
|
369
|
-
return said + "
|
|
369
|
+
return said + ". The nearest %s %s." % ["is" if close.size() == 1 else "are", ", ".join(close)]
|
|
370
370
|
|
|
371
371
|
|
|
372
372
|
## Why [param template] cannot be written on [param base], or "". Each template's code calls
|
|
@@ -427,7 +427,7 @@ static func _declared_classes() -> Dictionary:
|
|
|
427
427
|
# Every reason [param modifications] cannot be made as asked, each naming the entry by position.
|
|
428
428
|
#
|
|
429
429
|
# A name the script already declares is one of them, as is one the call adds twice. GDScript gives a
|
|
430
|
-
# class one namespace for its members, so a second `maximum` does not parse
|
|
430
|
+
# class one namespace for its members, so a second `maximum` does not parse, and the engine reports
|
|
431
431
|
# only the first error it meets, so a second `func damage` behind it was written with nothing said.
|
|
432
432
|
static func _modification_problems(modifications: Array, declared: Dictionary) -> Array[String]:
|
|
433
433
|
var problems: Array[String] = []
|
|
@@ -541,7 +541,7 @@ static func _insert(
|
|
|
541
541
|
# Whether the engine accepts [param script], parsed under this project's own warning settings: a
|
|
542
542
|
# script that does not load is one the caller wants to hear about now, and the reason is on stderr,
|
|
543
543
|
# which comes back with the answer. A script that fails to parse still loads, as one that cannot be
|
|
544
|
-
# instantiated
|
|
544
|
+
# instantiated. An abstract one that parses can be, on 4.7.
|
|
545
545
|
static func _parses(script: Script) -> bool:
|
|
546
546
|
return script != null and script.can_instantiate()
|
|
547
547
|
|
|
@@ -583,7 +583,7 @@ func _add_constant(lines: Array[String], mod: Dictionary, placed: Array[Dictiona
|
|
|
583
583
|
break
|
|
584
584
|
after = _past_class_doc(lines, after)
|
|
585
585
|
# Set apart from the header or the signals above and from what follows, as the style guide lays
|
|
586
|
-
# out its groups
|
|
586
|
+
# out its groups. One below the constants already there joins them.
|
|
587
587
|
var block: Array[String] = [const_line]
|
|
588
588
|
var above: String = lines[after - 1].strip_edges() if after > 0 else ""
|
|
589
589
|
var below: String = lines[after].strip_edges() if after < lines.size() else ""
|
|
@@ -620,7 +620,7 @@ func _add_variable(lines: Array[String], mod: Dictionary, placed: Array[Dictiona
|
|
|
620
620
|
|
|
621
621
|
# Every declaration this writes carries a type, so it parses in a project that treats an
|
|
622
622
|
# untyped or an inferred declaration as an error, which is the strictest setting Godot has.
|
|
623
|
-
# A value with no type gets the type the value evaluates to
|
|
623
|
+
# A value with no type gets the type the value evaluates to. A value nothing can evaluate
|
|
624
624
|
# here, and a declaration with neither, are spelled out as Variant.
|
|
625
625
|
if not var_type.is_empty():
|
|
626
626
|
var_line += ": " + var_type
|
|
@@ -767,7 +767,7 @@ func _variable_insertion_point(lines: Array[String]) -> int:
|
|
|
767
767
|
# Annotations off first. `@abstract class_name X` is one line in Godot 4.5 and later, and
|
|
768
768
|
# unstripped it matches nothing here, so the header is only found when some other line
|
|
769
769
|
# carries it. A script whose whole header is that one line leaves `after_header` at 0 and
|
|
770
|
-
# takes the new line above the `class_name`, where it does not parse
|
|
770
|
+
# takes the new line above the `class_name`, where it does not parse. One that declares
|
|
771
771
|
# `extends` first takes it between the two, which does not parse either. Stripping also
|
|
772
772
|
# turns `@export var x` into `var x`, which is why the annotation names are gone below.
|
|
773
773
|
var line: String = GdhPatterns.without_annotations(lines[i])
|
|
@@ -886,7 +886,7 @@ signal state_changed(old_state: String, new_state: String)
|
|
|
886
886
|
|
|
887
887
|
var current_state: String = ""
|
|
888
888
|
## State objects by name. A state may define enter(), exit(), process(delta) and
|
|
889
|
-
## physics_process(delta)
|
|
889
|
+
## physics_process(delta). Whichever it has are called.
|
|
890
890
|
var states: Dictionary = {}
|
|
891
891
|
|
|
892
892
|
|
|
@@ -4,7 +4,7 @@ extends SceneTree
|
|
|
4
4
|
# The command line the server runs is
|
|
5
5
|
# `godot --headless --path <project> --script <this> <operation> @file:<params.json> <answer.json>`,
|
|
6
6
|
# and the one JSON object written to the answer file is the answer. Everything an operation does
|
|
7
|
-
# lives in a sibling module
|
|
7
|
+
# lives in a sibling module. This file is the command table and the wire format.
|
|
8
8
|
#
|
|
9
9
|
# A file rather than a line on stdout, because the project's autoloads run after `_init` and print
|
|
10
10
|
# into the same stream: an autoload printing a dictionary in `_process` was the last JSON line out
|
|
@@ -4,8 +4,8 @@ const GdhRead = preload("reading.gd")
|
|
|
4
4
|
const GdhSerialisation = preload("serialisation.gd")
|
|
5
5
|
const GdhFileWalk = preload("file_walk.gd")
|
|
6
6
|
const GdhLog = preload("logger.gd")
|
|
7
|
-
## What the engine imports with no tool outside it, for finding what has not been imported yet
|
|
8
|
-
##
|
|
7
|
+
## What the engine imports with no tool outside it, for finding what has not been imported yet.
|
|
8
|
+
## Everything imported already is found by its sidecar whatever its extension.
|
|
9
9
|
const IMPORTABLE_EXTENSIONS: Array[String] = [
|
|
10
10
|
"png",
|
|
11
11
|
"jpg",
|
|
@@ -95,7 +95,7 @@ func get_import_status(params: Dictionary) -> Dictionary:
|
|
|
95
95
|
if not full_path.begins_with("res://"):
|
|
96
96
|
full_path = "res://" + full_path
|
|
97
97
|
|
|
98
|
-
# missing_source is a sidecar whose source has gone
|
|
98
|
+
# missing_source is a sidecar whose source has gone. With neither, the path names nothing
|
|
99
99
|
# the engine ever imported, and answering it as a lost source sent a caller to restore a
|
|
100
100
|
# file.
|
|
101
101
|
if (
|
|
@@ -300,7 +300,7 @@ static func _as_option(given: Variant, held: Variant) -> Variant:
|
|
|
300
300
|
## written as {"1818847073": 0.0}. The importer hands that to FontFile.opentype_feature_overrides,
|
|
301
301
|
## which keys features by their tag as an int, so the call reported success and the font kept its
|
|
302
302
|
## ligatures. A key is an int where the importer keys the option by OpenType tags, or where every
|
|
303
|
-
## key the option holds already is one
|
|
303
|
+
## key the option holds already is one. A value is an int where every value held is, or for a tag.
|
|
304
304
|
## Answers the dictionary under value, or why it cannot be written under refused.
|
|
305
305
|
static func _keyed_as_option(
|
|
306
306
|
name: String, given: Dictionary, held: Variant, importer: String
|
|
@@ -421,7 +421,7 @@ func _import_status_of(resource_path: String, import_file_path: String) -> Dicti
|
|
|
421
421
|
# The checks are the ones EditorFileSystem::_test_for_reimport makes, in its order, so a
|
|
422
422
|
# resource reads as needing an import when the editor's next scan would import it. The
|
|
423
423
|
# importer's own two cannot be asked here, where no importer is loaded, so they are made from
|
|
424
|
-
# what was measured of the built-in ones
|
|
424
|
+
# what was measured of the built-in ones. An importer a plugin adds is judged by its files
|
|
425
425
|
# alone.
|
|
426
426
|
var sidecar: ConfigFile = ConfigFile.new()
|
|
427
427
|
var stale: Dictionary = (
|
|
@@ -497,7 +497,7 @@ func _never_imported(resource_path: String) -> String:
|
|
|
497
497
|
+ extension
|
|
498
498
|
+ " files through Blender, which it finds through the editor setting "
|
|
499
499
|
+ BLENDER_SETTING
|
|
500
|
-
+ ". An editor with it set imports them on its scan
|
|
500
|
+
+ ". An editor with it set imports them on its scan. An import pass with it unset "
|
|
501
501
|
+ "stops at the first one and imports nothing, and setting "
|
|
502
502
|
+ BLENDER_ENABLED
|
|
503
503
|
+ " to false has the engine leave them unimported"
|
|
@@ -508,7 +508,7 @@ func _never_imported(resource_path: String) -> String:
|
|
|
508
508
|
return (
|
|
509
509
|
"no importer built into the engine takes ."
|
|
510
510
|
+ extension
|
|
511
|
-
+ " files
|
|
511
|
+
+ " files. One added by a plugin or an extension is known only to the editor, whose scan "
|
|
512
512
|
+ "imports them"
|
|
513
513
|
)
|
|
514
514
|
|
|
@@ -706,7 +706,7 @@ static func _md5_of_all(paths: Array[String]) -> String:
|
|
|
706
706
|
|
|
707
707
|
|
|
708
708
|
## The md5 of each resource's sidecar as the editor last imported it, keyed by path, from the cache
|
|
709
|
-
## the editor writes under .godot/editor
|
|
709
|
+
## the editor writes under .godot/editor. Empty when there is no cache or it cannot be read.
|
|
710
710
|
func _imported_sidecar_md5s() -> Dictionary:
|
|
711
711
|
if _sidecar_md5s != null:
|
|
712
712
|
return _sidecar_md5s
|
|
@@ -716,7 +716,7 @@ func _imported_sidecar_md5s() -> Dictionary:
|
|
|
716
716
|
var file: FileAccess = null if cache.is_empty() else FileAccess.open(cache, FileAccess.READ)
|
|
717
717
|
if file == null:
|
|
718
718
|
return found
|
|
719
|
-
# The first line is the import settings version
|
|
719
|
+
# The first line is the import settings version, then a "::<dir>::<time>" line opens each
|
|
720
720
|
# directory, and each file line is nine fields split by "::", the last holding the file's
|
|
721
721
|
# dependencies, which may contain the splitter. The eighth field is "<>"-separated, with the
|
|
722
722
|
# sidecar's md5 sixth.
|
|
@@ -765,12 +765,12 @@ static func _path_of(entry: String) -> String:
|
|
|
765
765
|
return entry if at < 0 else entry.substr(at + 2)
|
|
766
766
|
|
|
767
767
|
|
|
768
|
-
## Every path [param resource_path] depends on, through the resources it depends on in turn
|
|
768
|
+
## Every path [param resource_path] depends on, through the resources it depends on in turn. A set,
|
|
769
769
|
## keyed by path.
|
|
770
770
|
static func _depended_on(resource_path: String) -> Dictionary[String, bool]:
|
|
771
771
|
var found: Dictionary[String, bool] = {}
|
|
772
772
|
var pending: Array[String] = [resource_path]
|
|
773
|
-
# Every path is followed once, which ends the walk and survives a cycle
|
|
773
|
+
# Every path is followed once, which ends the walk and survives a cycle. A cap or a list of
|
|
774
774
|
# extensions would leave out a material behind a long chain or in a saved .mesh.
|
|
775
775
|
while not pending.is_empty():
|
|
776
776
|
var next: String = pending.pop_back()
|
|
@@ -787,7 +787,7 @@ static func _shaped_by_a_script(sidecar: ConfigFile) -> bool:
|
|
|
787
787
|
return not str(sidecar.get_value("params", "import_script/path", "")).is_empty()
|
|
788
788
|
|
|
789
789
|
|
|
790
|
-
## The files an import wrote, from its sidecar's [code]dest_files[/code]
|
|
790
|
+
## The files an import wrote, from its sidecar's [code]dest_files[/code]. Empty when it lists none.
|
|
791
791
|
static func _outputs_of(sidecar: ConfigFile) -> Array[String]:
|
|
792
792
|
var outputs: Array[String] = []
|
|
793
793
|
var listed: Variant = sidecar.get_value("deps", "dest_files", [])
|
|
@@ -235,7 +235,7 @@ func add_input_action(params: Dictionary) -> Dictionary:
|
|
|
235
235
|
|
|
236
236
|
_log.info("Adding input action: " + action_name)
|
|
237
237
|
# The editor's Input Map refuses these, since project.godot keys an action by its name and
|
|
238
|
-
# the characters below are its own syntax there
|
|
238
|
+
# the characters below are its own syntax there. A name holding one is written escaped and
|
|
239
239
|
# reads back as a name nobody typed.
|
|
240
240
|
for index: int in action_name.length():
|
|
241
241
|
var code: int = action_name.unicode_at(index)
|
|
@@ -299,7 +299,7 @@ func add_input_action(params: Dictionary) -> Dictionary:
|
|
|
299
299
|
answer["old_deadzone"] = old_deadzone
|
|
300
300
|
|
|
301
301
|
# Through ProjectSettings so the file is saved the way the editor saves it, header and
|
|
302
|
-
# every other line kept
|
|
302
|
+
# every other line kept. A ConfigFile of project.godot drops the comments on the way out.
|
|
303
303
|
ProjectSettings.set_setting(setting, action)
|
|
304
304
|
var err: Error = GdhProjectFile.save([setting])
|
|
305
305
|
if err != OK:
|
|
@@ -423,7 +423,7 @@ func remove_input_action(params: Dictionary) -> Dictionary:
|
|
|
423
423
|
# The value project.godot stores for one input action.
|
|
424
424
|
#
|
|
425
425
|
# It has to be a Dictionary holding real InputEvent objects. The engine writes those as the
|
|
426
|
-
# unquoted expression it parses back into an action
|
|
426
|
+
# unquoted expression it parses back into an action. Hand it the same thing as assembled
|
|
427
427
|
# text and it writes a quoted, escaped string, which loads as a String and leaves InputMap
|
|
428
428
|
# with no action at all while add_input_action still reports the events it was given.
|
|
429
429
|
func build_input_action(deadzone: float, events: Array) -> Dictionary:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
2
|
|
|
3
|
-
# Everything printed is diagnostics
|
|
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
6
|
# Every module preloads this as `GdhLog`. `Logger` is a native class from Godot 4.5 on, and a
|
|
@@ -135,7 +135,7 @@ static func _why_not_loaded(plugin_cfg_path: String) -> String:
|
|
|
135
135
|
var script_path: String = plugin_cfg_path.get_base_dir().path_join(script_name)
|
|
136
136
|
if not FileAccess.file_exists(script_path):
|
|
137
137
|
return "%s does not exist, so the editor will not enable the plugin." % script_path
|
|
138
|
-
# Loaded only to learn whether the editor could
|
|
138
|
+
# Loaded only to learn whether the editor could. What the engine says of a script that does not
|
|
139
139
|
# parse is about a file the answer does not describe.
|
|
140
140
|
printerr(GdhLog.ASIDE)
|
|
141
141
|
var script: Script = ResourceLoader.load(
|
|
@@ -189,7 +189,7 @@ func disable_plugin(params: Dictionary) -> Dictionary:
|
|
|
189
189
|
# folder names, because the list is written back whole: reading names off a pattern for
|
|
190
190
|
# `res://addons/<name>/plugin.cfg` dropped a plugin one folder down, and the next enable or disable
|
|
191
191
|
# wrote the list without it, disabling a plugin nobody named. The editor stores the engine
|
|
192
|
-
# expression PackedStringArray(...), which loads as that type
|
|
192
|
+
# expression PackedStringArray(...), which loads as that type. The String form is what an older
|
|
193
193
|
# hand-edited file can carry.
|
|
194
194
|
func _enabled_paths() -> Array[String]:
|
|
195
195
|
var paths: Array[String] = []
|
|
@@ -174,7 +174,7 @@ func set_project_setting(params: Dictionary) -> Dictionary:
|
|
|
174
174
|
% [setting_path, "setting is" if near.size() == 1 else "settings are", ", ".join(near)]
|
|
175
175
|
)
|
|
176
176
|
# Written as given, since a file can be made after the setting names it, as an import makes a
|
|
177
|
-
# translation
|
|
177
|
+
# translation. Said, since until then the engine complains of it on every boot.
|
|
178
178
|
var missing: Array[String] = _missing_files(final_value)
|
|
179
179
|
if not missing.is_empty():
|
|
180
180
|
answer["missing_files"] = missing
|
|
@@ -190,7 +190,7 @@ func set_project_setting(params: Dictionary) -> Dictionary:
|
|
|
190
190
|
var made: Array[String] = _translations_made_by(missing)
|
|
191
191
|
if not made.is_empty():
|
|
192
192
|
said += (
|
|
193
|
-
"
|
|
193
|
+
". project_import reimport makes the translation files from " + ", ".join(made)
|
|
194
194
|
)
|
|
195
195
|
answer["note"] = (str(answer["note"]) + " " if answer.has("note") else "") + said + "."
|
|
196
196
|
ProjectSettings.set_setting(setting_path, final_value)
|
|
@@ -289,7 +289,7 @@ func unset_project_setting(params: Dictionary) -> Dictionary:
|
|
|
289
289
|
var near: PackedStringArray = PackedStringArray() if held else _settings_near(setting_path)
|
|
290
290
|
if not near.is_empty():
|
|
291
291
|
answer["note"] += (
|
|
292
|
-
" Nor does the engine declare it
|
|
292
|
+
" Nor does the engine declare it. The nearest %s %s."
|
|
293
293
|
% ["setting is" if near.size() == 1 else "settings are", ", ".join(near)]
|
|
294
294
|
)
|
|
295
295
|
return _with_what_holds(answer, setting_path)
|
|
@@ -415,7 +415,7 @@ func add_autoload(params: Dictionary) -> Dictionary:
|
|
|
415
415
|
(
|
|
416
416
|
"An autoload cannot be added disabled: Godot loads every autoload in the list "
|
|
417
417
|
+ "at every start. global: false keeps its name out of the global scope and "
|
|
418
|
-
+ "still loads it
|
|
418
|
+
+ "still loads it. remove_autoload is what stops it loading"
|
|
419
419
|
)
|
|
420
420
|
)
|
|
421
421
|
)
|
|
@@ -443,7 +443,7 @@ func add_autoload(params: Dictionary) -> Dictionary:
|
|
|
443
443
|
var was_updated: bool = ProjectSettings.has_setting(setting)
|
|
444
444
|
|
|
445
445
|
# An asterisk in front of the path makes the autoload's name a global variable, the editor's
|
|
446
|
-
# "Global Variable" column. Without it the node still comes up under the root
|
|
446
|
+
# "Global Variable" column. Without it the node still comes up under the root. Only the name
|
|
447
447
|
# stops resolving.
|
|
448
448
|
var entry: String = ("*" if global else "") + path
|
|
449
449
|
if was_updated and str(ProjectSettings.get_setting(setting)) == entry:
|
|
@@ -455,7 +455,7 @@ func add_autoload(params: Dictionary) -> Dictionary:
|
|
|
455
455
|
"action": "unchanged",
|
|
456
456
|
"saved": false,
|
|
457
457
|
}
|
|
458
|
-
# The editor's Add replaces an entry of the same name too
|
|
458
|
+
# The editor's Add replaces an entry of the same name too. Said here, since the replaced script
|
|
459
459
|
# is no longer loaded and nothing else in the answer names it.
|
|
460
460
|
var held: String = str(ProjectSettings.get_setting(setting)) if was_updated else ""
|
|
461
461
|
ProjectSettings.set_setting(setting, entry)
|
|
@@ -484,7 +484,7 @@ func add_autoload(params: Dictionary) -> Dictionary:
|
|
|
484
484
|
static func _not_an_autoload(path: String) -> String:
|
|
485
485
|
var refused: String = "%s is %s, and an autoload is a scene or a script extending Node"
|
|
486
486
|
if not ResourceLoader.exists(path):
|
|
487
|
-
return (refused + "
|
|
487
|
+
return (refused + ". Nothing was written.") % [path, "a file Godot does not load"]
|
|
488
488
|
var loaded: Resource = load(path)
|
|
489
489
|
if loaded is PackedScene:
|
|
490
490
|
return ""
|
|
@@ -492,7 +492,7 @@ static func _not_an_autoload(path: String) -> String:
|
|
|
492
492
|
var kind: String = "nothing Godot could load"
|
|
493
493
|
if loaded != null:
|
|
494
494
|
kind = "a " + loaded.get_class()
|
|
495
|
-
return (refused + "
|
|
495
|
+
return (refused + ". Nothing was written.") % [path, kind]
|
|
496
496
|
var script: Script = loaded
|
|
497
497
|
if not script.can_instantiate():
|
|
498
498
|
return ""
|
|
@@ -502,7 +502,7 @@ static func _not_an_autoload(path: String) -> String:
|
|
|
502
502
|
return (
|
|
503
503
|
(
|
|
504
504
|
"%s extends %s, which is not a Node, and Godot instantiates an autoload under the root "
|
|
505
|
-
+ "as a node, so every start of the game would fail to load it
|
|
505
|
+
+ "as a node, so every start of the game would fail to load it. Nothing was written."
|
|
506
506
|
)
|
|
507
507
|
% [path, base]
|
|
508
508
|
)
|
|
@@ -530,7 +530,7 @@ static func _autoload_name_refusal(name: String) -> String:
|
|
|
530
530
|
return ""
|
|
531
531
|
return (
|
|
532
532
|
(
|
|
533
|
-
"%s cannot name an autoload: %s. Nothing was written
|
|
533
|
+
"%s cannot name an autoload: %s. Nothing was written. The editor's Autoload tab "
|
|
534
534
|
+ "refuses it too."
|
|
535
535
|
)
|
|
536
536
|
% [JSON.stringify(name), reason]
|
|
@@ -569,7 +569,7 @@ func list_autoloads(_params: Dictionary) -> Dictionary:
|
|
|
569
569
|
var value: String = str(ProjectSettings.get_setting(setting))
|
|
570
570
|
var path: String = value.trim_prefix("*")
|
|
571
571
|
|
|
572
|
-
# Enabled whatever the asterisk says, because every autoload listed is loaded
|
|
572
|
+
# Enabled whatever the asterisk says, because every autoload listed is loaded. The asterisk
|
|
573
573
|
# is whether its name is a global variable.
|
|
574
574
|
var autoload_info: Dictionary = {
|
|
575
575
|
"name": setting.trim_prefix("autoload/"),
|
|
@@ -264,7 +264,7 @@ func validate_project(params: Dictionary) -> Dictionary:
|
|
|
264
264
|
)
|
|
265
265
|
|
|
266
266
|
# A connection whose method or signal is not there loads without a word and fails only once the
|
|
267
|
-
# game runs, on every emission or not at all
|
|
267
|
+
# game runs, on every emission or not at all. The references check sees files, not members.
|
|
268
268
|
checks_performed.append("connections")
|
|
269
269
|
for entry: Dictionary in GdhSceneConnections.new(_log).broken():
|
|
270
270
|
warnings.append(
|
|
@@ -75,7 +75,7 @@ static func _stepped(target: Object, name: String, at: int) -> Array:
|
|
|
75
75
|
|
|
76
76
|
|
|
77
77
|
## The refusal for [param name], which [param target] does not declare, when a step of it indexes a
|
|
78
|
-
## list [param target] holds and names no resource in it
|
|
78
|
+
## list [param target] holds and names no resource in it, or "".
|
|
79
79
|
static func unlisted(target: Object, name: String) -> String:
|
|
80
80
|
var at: int = name.find("/")
|
|
81
81
|
while at > 0:
|
|
@@ -163,7 +163,7 @@ static func held_at(target: Object, property: String) -> Resource:
|
|
|
163
163
|
## only once the element exists, so a TileSet with no physics layer had no
|
|
164
164
|
## physics_layer_0/collision_layer to write. A list the engine declares as an array grows through
|
|
165
165
|
## the method named after its elements, add_physics_layer for physics_layer_, given the indices of
|
|
166
|
-
## the lists around it
|
|
166
|
+
## the lists around it. A list counted by a property, as a tile's polygons are by polygons_count,
|
|
167
167
|
## grows by raising the count.
|
|
168
168
|
static func grow(target: Object, name: String, called: String) -> String:
|
|
169
169
|
var steps: PackedStringArray = name.split("/")
|
|
@@ -255,7 +255,7 @@ static func class_called(target: Object) -> String:
|
|
|
255
255
|
|
|
256
256
|
|
|
257
257
|
## The refusal for [param name], which [param target] does not declare, when it runs through a
|
|
258
|
-
## resource saved in a file other than [param home], the one [param target] is saved in
|
|
258
|
+
## resource saved in a file other than [param home], the one [param target] is saved in, or "". The
|
|
259
259
|
## write belongs in that file, since this one would be saved and that one left as it was: through a
|
|
260
260
|
## TileMapLayer's tile_set kept in a file of its own, or through the mesh of an instance's editable
|
|
261
261
|
## child, which is built into the scene the instance comes from.
|
|
@@ -20,7 +20,7 @@ const GdhRead = preload("reading.gd")
|
|
|
20
20
|
const GdhSame = preload("same_values.gd")
|
|
21
21
|
const GdhSerialisation = preload("serialisation.gd")
|
|
22
22
|
const GdhShapes = preload("value_shapes.gd")
|
|
23
|
-
# The data types a Theme names its items by, as in
|
|
23
|
+
# The data types a Theme names its items by, as in `Button/colors/font_color`, and what each holds.
|
|
24
24
|
const THEME_ITEMS: Dictionary[String, Array] = {
|
|
25
25
|
"colors": [TYPE_COLOR, ""],
|
|
26
26
|
"constants": [TYPE_INT, ""],
|
|
@@ -40,6 +40,7 @@ const HEADINGS: int = PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_
|
|
|
40
40
|
## Control's layout_mode values, which the engine does not expose as constants.
|
|
41
41
|
const LAYOUT_MODE_POSITION: int = 0
|
|
42
42
|
const LAYOUT_MODE_ANCHORS: int = 1
|
|
43
|
+
const LAYOUT_MODE_UNCONTROLLED: int = 3
|
|
43
44
|
## What a container sets on each child it lays out, beside the anchors and offsets.
|
|
44
45
|
const PLACED_BY_A_CONTAINER: Array[String] = ["position", "size", "global_position"]
|
|
45
46
|
|
|
@@ -74,7 +75,7 @@ var _names_no_node: Dictionary = {}
|
|
|
74
75
|
##
|
|
75
76
|
## Three separate questions, asked in order, because a caller may say what it means in three
|
|
76
77
|
## ways: a dictionary that names its own type, a dictionary shaped like the type the property
|
|
77
|
-
## declares, or a bare array positional for a vector. Each is its own function
|
|
78
|
+
## declares, or a bare array positional for a vector. Each is its own function. Asking all three
|
|
78
79
|
## in one was twenty-four exits deep and impossible to follow.
|
|
79
80
|
##
|
|
80
81
|
## [param elements] is what a typed Array or Dictionary holds, as [method GdhDeclared.elements_of]
|
|
@@ -215,8 +216,8 @@ func write_all(target: Object, properties: Dictionary) -> String:
|
|
|
215
216
|
var written_at: Dictionary[String, int] = {}
|
|
216
217
|
var writes: int = 0
|
|
217
218
|
# Which other property's write last changed each one already written, by name. A Gradient keeps
|
|
218
|
-
# one offset for each colour, so offsets given after colors cut them back, and the refusal
|
|
219
|
-
# only that the engine had changed the colours.
|
|
219
|
+
# one offset for each colour, so offsets given after `colors` cut them back, and the refusal
|
|
220
|
+
# said only that the engine had changed the colours.
|
|
220
221
|
var undone_by: Dictionary[String, String] = {}
|
|
221
222
|
# A refusal of one of several leaves the others unwritten too, which a refusal naming only the
|
|
222
223
|
# one it refused left a caller to guess.
|
|
@@ -349,7 +350,7 @@ func _note_undone(
|
|
|
349
350
|
## sizes its children and the scene keeps none of that for them, so their anchors, offsets, position
|
|
350
351
|
## and size are refused with what a child does set. A control in Position mode ignores an anchor
|
|
351
352
|
## preset, and the inspector offers anchors only in Anchors mode, so writing one puts it there
|
|
352
|
-
## first, as picking a preset in the editor does
|
|
353
|
+
## first, as picking a preset in the editor does, and alsoChanged then names layout_mode.
|
|
353
354
|
func _laid_out(control: Control, name: String) -> String:
|
|
354
355
|
var base: String = name.get_slice(":", 0)
|
|
355
356
|
var anchoring: bool = base == "anchors_preset" or base.begins_with("anchor_")
|
|
@@ -366,8 +367,14 @@ func _laid_out(control: Control, name: String) -> String:
|
|
|
366
367
|
)
|
|
367
368
|
% [control.name, parent.name, parent.get_class(), base]
|
|
368
369
|
)
|
|
369
|
-
|
|
370
|
+
var mode: int = GdhRead.as_int(control.get("layout_mode"))
|
|
371
|
+
if anchoring and mode == LAYOUT_MODE_POSITION:
|
|
370
372
|
control.set("layout_mode", LAYOUT_MODE_ANCHORS)
|
|
373
|
+
# A scene's root reads as Uncontrolled but keeps the Position mode it was made with until it
|
|
374
|
+
# enters a tree, and the engine ignores a preset in that stored mode, so full rect read back as
|
|
375
|
+
# top left on every root a headless edit wrote.
|
|
376
|
+
elif anchoring and mode == LAYOUT_MODE_UNCONTROLLED:
|
|
377
|
+
control.set("layout_mode", LAYOUT_MODE_UNCONTROLLED)
|
|
371
378
|
return ""
|
|
372
379
|
|
|
373
380
|
|
|
@@ -447,7 +454,7 @@ func _write(target: Object, key: String, value: Variant) -> void:
|
|
|
447
454
|
|
|
448
455
|
|
|
449
456
|
## The declaration the last step of [param path] would have, typed as the component it reaches, so
|
|
450
|
-
## a fraction for an integer vector's axis is refused as one for an int property is
|
|
457
|
+
## a fraction for an integer vector's axis is refused as one for an int property is, or
|
|
451
458
|
## {"refused": why} for the first step that is not there.
|
|
452
459
|
func _component_slot(target: Object, path: String) -> Dictionary:
|
|
453
460
|
var steps: PackedStringArray = path.split(":")
|
|
@@ -487,7 +494,7 @@ func _component_slot(target: Object, path: String) -> Dictionary:
|
|
|
487
494
|
return {
|
|
488
495
|
"refused":
|
|
489
496
|
(
|
|
490
|
-
"%s.%s is %s, so it has no %s
|
|
497
|
+
"%s.%s is %s, so it has no %s. It has %s."
|
|
491
498
|
% [owner, walked, sort, step, ", ".join(has)]
|
|
492
499
|
),
|
|
493
500
|
}
|
|
@@ -562,7 +569,7 @@ func value_for(target: Object, declared: Dictionary, raw: Variant) -> Array:
|
|
|
562
569
|
|
|
563
570
|
|
|
564
571
|
## A property holding a Node is given the node the path leads to from [param target], as the
|
|
565
|
-
## editor's inspector gives it
|
|
572
|
+
## editor's inspector gives it. The scene is saved with the path. A res:// path could never name a
|
|
566
573
|
## node, so the remedy a caller is given is a node path.
|
|
567
574
|
func _node_reference(target: Object, property: String, raw: Variant) -> Array:
|
|
568
575
|
if raw == null:
|
|
@@ -739,7 +746,7 @@ func colour_from(raw: Variant, named: String) -> Array:
|
|
|
739
746
|
(
|
|
740
747
|
(
|
|
741
748
|
'%s is {"r": .., "g": .., "b": .., "a": ..} from 0 to 1, a optional, or an html '
|
|
742
|
-
+ 'code such as "#ff8000" or "#ff800080"
|
|
749
|
+
+ 'code such as "#ff8000" or "#ff800080". %s is neither.'
|
|
743
750
|
)
|
|
744
751
|
% [named, JSON.stringify(raw)]
|
|
745
752
|
),
|
|
@@ -891,7 +898,7 @@ func _parse_resource_tag(value: Dictionary) -> Array:
|
|
|
891
898
|
)
|
|
892
899
|
return [true, null]
|
|
893
900
|
# A plain Resource with a script or values of its own, as a read answers one built into a scene,
|
|
894
|
-
# is one to build
|
|
901
|
+
# is one to build. Taken as nothing, writing such a read back cleared the property.
|
|
895
902
|
if value.size() > 1:
|
|
896
903
|
return _parse_new_resource("Resource", value)
|
|
897
904
|
return [true, null]
|