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
|
@@ -226,16 +226,16 @@ static func no_action(action: String) -> String:
|
|
|
226
226
|
)
|
|
227
227
|
if not close.is_empty():
|
|
228
228
|
return (
|
|
229
|
-
"%s
|
|
229
|
+
"%s. The nearest %s %s."
|
|
230
230
|
% [said, "is" if close.size() == 1 else "are", ", ".join(close)]
|
|
231
231
|
)
|
|
232
232
|
if own.is_empty():
|
|
233
233
|
return (
|
|
234
|
-
"%s
|
|
234
|
+
"%s. The project defines no actions of its own, and the engine's are the ui_ ones."
|
|
235
235
|
% said
|
|
236
236
|
)
|
|
237
237
|
return (
|
|
238
|
-
"%s
|
|
238
|
+
"%s. The project's own actions are %s, beside the engine's ui_ ones."
|
|
239
239
|
% [said, ", ".join(own)]
|
|
240
240
|
)
|
|
241
241
|
|
|
@@ -246,7 +246,7 @@ static func no_action(action: String) -> String:
|
|
|
246
246
|
static func actions_of(event: InputEvent) -> Array[String]:
|
|
247
247
|
var named: Array[String] = []
|
|
248
248
|
# A motion matches every action on its axis, either way and at any distance, which would name
|
|
249
|
-
# ui_left for a stick held right
|
|
249
|
+
# ui_left for a stick held right. Input presses only those its direction reaches past their
|
|
250
250
|
# deadzone.
|
|
251
251
|
var moved: bool = event is InputEventJoypadMotion
|
|
252
252
|
for action: StringName in InputMap.get_actions():
|
|
@@ -291,7 +291,7 @@ static func focus_noted(said: Dictionary, focused: String, host: Node) -> Dictio
|
|
|
291
291
|
##
|
|
292
292
|
## A key goes to the shortcuts last in the tree first and stops at the first button that takes it,
|
|
293
293
|
## while an InputEventShortcut reaches only the buttons holding that one Shortcut. So each Shortcut
|
|
294
|
-
## is sent in that order, a frame apart, until a button is pressed
|
|
294
|
+
## is sent in that order, a frame apart, until a button is pressed. A button still decides by its
|
|
295
295
|
## disabled, its visibility and its shortcut context, as it does for the key.
|
|
296
296
|
static func press_shortcuts(root: Node, action: String) -> Dictionary:
|
|
297
297
|
var pressing: InputEventAction = InputEventAction.new()
|
|
@@ -404,7 +404,7 @@ static func class_at(root: Node, path: String) -> Dictionary:
|
|
|
404
404
|
"type": "error",
|
|
405
405
|
"message":
|
|
406
406
|
(
|
|
407
|
-
"%s did not compile, so none of its functions can be called
|
|
407
|
+
"%s did not compile, so none of its functions can be called. script_diagnostics on "
|
|
408
408
|
+ "it gives the error and its line."
|
|
409
409
|
) % script_path,
|
|
410
410
|
}
|
|
@@ -544,7 +544,7 @@ func parameter_class(object: Object, method: String, index: int) -> String:
|
|
|
544
544
|
## carries its enum or range, and empty for anything that is not a property.
|
|
545
545
|
##
|
|
546
546
|
## Asked rather than read off the value in the slot, because an object slot can be empty: a
|
|
547
|
-
## property typed `
|
|
547
|
+
## property typed `Part` holding null reads as TYPE_NIL, and anything written into it passed the
|
|
548
548
|
## check and was then dropped by the engine, which answered with the slot still null.
|
|
549
549
|
##
|
|
550
550
|
## The script only for a typed container, the one place the engine hands it over. A property typed
|
|
@@ -27,7 +27,7 @@ const LOOK_SLICE_USEC: int = 2000
|
|
|
27
27
|
## number that steps rather than lands, as a day counter on a fast clock goes from 31 to 34 between
|
|
28
28
|
## two looks and a wait for 32 never sees it.
|
|
29
29
|
const COMPARES: Array[String] = ["==", "!=", ">=", "<=", ">", "<"]
|
|
30
|
-
## The most signal watches kept at once
|
|
30
|
+
## The most signal watches kept at once. Arming one past this lets the oldest go. Property watches
|
|
31
31
|
## are counted apart, to the same number.
|
|
32
32
|
const MOST_WATCHES: int = 16
|
|
33
33
|
## The most changes a property watch keeps. Past it, each new one takes the last place, so what is
|
|
@@ -683,7 +683,7 @@ static func _not_comparable(
|
|
|
683
683
|
node_path, property, type_string(typeof(current)), GdhShapes.kind(wanted),
|
|
684
684
|
]
|
|
685
685
|
# A value that cannot become what the property holds is said as that, which names the remedy,
|
|
686
|
-
# before the comparison it would also fail
|
|
686
|
+
# before the comparison it would also fail. An object on either side is said as the comparison.
|
|
687
687
|
var objects: bool = typeof(current) == TYPE_OBJECT or typeof(wanted) == TYPE_OBJECT
|
|
688
688
|
if not objects and not GdhValues.acceptable(wanted, typeof(current)):
|
|
689
689
|
return (
|
|
@@ -777,22 +777,22 @@ static func _declared_slot(holder: Variant, named: String) -> Dictionary:
|
|
|
777
777
|
## A screen rather than one node, because a panel following a clock builds its labels again every
|
|
778
778
|
## time it redraws and the engine names those `@Label@1163`. A wait holding one of them is waiting
|
|
779
779
|
## on a node that was freed a frame later, and that is what it answered: the date along the top of a
|
|
780
|
-
##
|
|
781
|
-
## and the screen is the part that stays put.
|
|
780
|
+
## screen could not be waited on at all. What a caller is watching for is a word arriving on a
|
|
781
|
+
## screen, and the screen is the part that stays put.
|
|
782
782
|
##
|
|
783
783
|
## On the screen, which is to say shown: a control carrying the words while hidden satisfied the
|
|
784
|
-
## wait at once, so a
|
|
784
|
+
## wait at once, so a match screen whose "Keep going" button exists hidden through the whole sweep
|
|
785
785
|
## answered met in two milliseconds while the sweep was still animating, and every wait on that
|
|
786
786
|
## screen fell back to counting frames. [param include_hidden] asks the other question, for a
|
|
787
787
|
## caller waiting on words a hidden node holds.
|
|
788
788
|
##
|
|
789
789
|
## Paced by what a look costs, so the watching leaves the game its speed: every frame while a look
|
|
790
790
|
## is cheap, and on a screen large enough for one to cost a real share of a frame, often enough that
|
|
791
|
-
## looking takes about a tenth of the time. Every frame
|
|
791
|
+
## looking takes about a tenth of the time. Every frame over 3,500 nodes held the game at a
|
|
792
792
|
## sixth of its frame rate, and its clock, which advances by the frame's delta, lost three quarters
|
|
793
793
|
## of its time, so a wait for a date timed out on a clock that turned a day every thirty seconds.
|
|
794
794
|
## Each look is read a slice at a time, [constant LOOK_SLICE_USEC] to a frame, so the frames it runs
|
|
795
|
-
## in keep their length
|
|
795
|
+
## in keep their length. The last is read whole, since the wait answers on it.
|
|
796
796
|
##
|
|
797
797
|
## The answer says how many frames the wait spanned, how many looks it took, how long they took
|
|
798
798
|
## between them and the longest they held one frame, which is how a caller sees what it cost and
|
|
@@ -866,7 +866,7 @@ func _wait_until_said(
|
|
|
866
866
|
answer["unseen_note"] = (
|
|
867
867
|
(
|
|
868
868
|
"Something under %s says it, but is hidden or drawn outside the viewport or a "
|
|
869
|
-
+ "ScrollContainer, where the player cannot read it
|
|
869
|
+
+ "ScrollContainer, where the player cannot read it. includeHidden true counts it "
|
|
870
870
|
+ "there too"
|
|
871
871
|
)
|
|
872
872
|
% node_path
|
|
@@ -12,7 +12,7 @@ extends RefCounted
|
|
|
12
12
|
## is every label, button and field the interface is built out of.
|
|
13
13
|
##
|
|
14
14
|
## Read rather than looked up in the property list, which the engine builds afresh on every call: a
|
|
15
|
-
## wait asks this of every node on the screen every frame, and
|
|
15
|
+
## wait asks this of every node on the screen every frame, and over 3,500 nodes the lookup
|
|
16
16
|
## took the game it was watching from 60 frames a second to 11. A node without the property reads
|
|
17
17
|
## as null, which is not a string.
|
|
18
18
|
##
|
|
@@ -21,8 +21,8 @@ extends RefCounted
|
|
|
21
21
|
static func said_by(node: Node) -> String:
|
|
22
22
|
var own: String = drawn_by(node)
|
|
23
23
|
# A control showing a picture rather than words is named for a player who cannot see it, and
|
|
24
|
-
# that name is the one the words a caller asks by are meant for:
|
|
25
|
-
# square with an icon on it, and a click on "
|
|
24
|
+
# that name is the one the words a caller asks by are meant for: a side bar's "Away" tab is a
|
|
25
|
+
# square with an icon on it, and a click on "Away" found only the words around it.
|
|
26
26
|
if own.is_empty() and node is Control and not node is Label and not draws_a_list(node):
|
|
27
27
|
var control: Control = node
|
|
28
28
|
return control.atr(control.accessibility_name).strip_edges()
|
|
@@ -137,7 +137,7 @@ static func masked(field: LineEdit) -> String:
|
|
|
137
137
|
return field.secret_character.repeat(field.text.length())
|
|
138
138
|
|
|
139
139
|
|
|
140
|
-
## Whether [param node] draws a list of items rather than holding words in a `text
|
|
140
|
+
## Whether [param node] draws a list of items rather than holding words in a `text`. See
|
|
141
141
|
## [method items_of].
|
|
142
142
|
static func draws_a_list(node: Node) -> bool:
|
|
143
143
|
return (
|
|
@@ -225,7 +225,7 @@ static func _text_of(node: Node) -> String:
|
|
|
225
225
|
if label != null:
|
|
226
226
|
return _label_says(label)
|
|
227
227
|
# A RichTextLabel's text is its markup when it reads BBCode, and nobody reads the tags: a
|
|
228
|
-
#
|
|
228
|
+
# ledger line came back as `"[b][color=#3a7bd5]Osric Halcott[/color], [/b]..."` and a phrase
|
|
229
229
|
# running across a tag could not be found or waited for. The parsed text is what is drawn, and
|
|
230
230
|
# it also holds what a game added with append_text(), which the property never shows. It is
|
|
231
231
|
# parsed from the translation already.
|
|
@@ -15,7 +15,7 @@ const GdhShapes = preload("value_shapes.gd")
|
|
|
15
15
|
const NUMBERS: Array[int] = [TYPE_BOOL, TYPE_INT, TYPE_FLOAT]
|
|
16
16
|
const TEXTS: Array[int] = [TYPE_STRING, TYPE_STRING_NAME, TYPE_NODE_PATH]
|
|
17
17
|
# 2^53. A JSON number is a double, and every whole number below this one in size has a double of
|
|
18
|
-
# its own
|
|
18
|
+
# its own. From here on two or more share each double, so one arriving tells nobody which was sent.
|
|
19
19
|
const EXACT: int = 9_007_199_254_740_992
|
|
20
20
|
# The types that cross as a plain JSON list and have to be built back into themselves. A list is
|
|
21
21
|
# what a caller can write by hand and what the serialiser answers with, and the parameter or
|
|
@@ -136,7 +136,7 @@ func serialize_value(value: Variant) -> Variant:
|
|
|
136
136
|
return value
|
|
137
137
|
var carried: Variant = call(serialiser, value)
|
|
138
138
|
# The fixed shapes carry their components as bare floats, so an infinite one in a Vector2 is
|
|
139
|
-
# tagged here, and a 32-bit one shortened
|
|
139
|
+
# tagged here, and a 32-bit one shortened. Containers reach their members through this already.
|
|
140
140
|
var type: int = typeof(value)
|
|
141
141
|
return (
|
|
142
142
|
carried
|
|
@@ -345,7 +345,7 @@ static func comparable(one: Variant, other: Variant) -> bool:
|
|
|
345
345
|
##
|
|
346
346
|
## A list of what is accepted rather than of what is refused, which is the way round that errs
|
|
347
347
|
## safely. The engine's own conversion table is not readable from GDScript, so either list is a
|
|
348
|
-
## guess at it
|
|
348
|
+
## guess at it. Guessing narrow costs a caller a refusal naming both types, and guessing wide costs
|
|
349
349
|
## them the damage above. A refusal is the cheaper mistake, and it is the one they can act on.
|
|
350
350
|
static func acceptable(value: Variant, type: int) -> bool:
|
|
351
351
|
if type == TYPE_NIL:
|
|
@@ -560,7 +560,7 @@ func _serialize_rid(value: RID) -> Dictionary:
|
|
|
560
560
|
|
|
561
561
|
# A whole number from 2^53 on as its digits, because JSON numbers are doubles on the server's side
|
|
562
562
|
# and on the engine's, and from there on neighbouring whole numbers share a double. A RefCounted's
|
|
563
|
-
# instance id is one, with its top bit set, and a resource uid another
|
|
563
|
+
# instance id is one, with its top bit set, and a resource uid another. Read as numbers they came
|
|
564
564
|
# back as a neighbouring id, which named nothing or something else.
|
|
565
565
|
func _serialize_int(value: int) -> Variant:
|
|
566
566
|
if value > -EXACT and value < EXACT:
|
|
@@ -2,7 +2,7 @@ class_name GdhBench
|
|
|
2
2
|
extends RefCounted
|
|
3
3
|
## Work played a step a frame, for the script profiler. The engine sends the profiler what each
|
|
4
4
|
## frame called and holds names for only so many functions a frame, so work played inside one frame
|
|
5
|
-
## comes back with most of its functions unnamed
|
|
5
|
+
## comes back with most of its functions unnamed. Spread over frames, every step is named.
|
|
6
6
|
##
|
|
7
7
|
## func _ready() -> void:
|
|
8
8
|
## var timed: Dictionary = await GdhBench.each_frame(get_tree(), 400, _run.advance)
|
|
@@ -7,7 +7,7 @@ extends RefCounted
|
|
|
7
7
|
## from one run to the next) calls [method outcomes] for each arm with the number of runs and a
|
|
8
8
|
## callable playing run `i`. Run on its own, the scene plays every run here, as it always did. Under
|
|
9
9
|
## `gdharness spread`, each worker engine plays every Nth run from its own index and leaves what
|
|
10
|
-
## each run answered
|
|
10
|
+
## each run answered. Then one more engine runs the scene to gather, and there [method outcomes]
|
|
11
11
|
## plays nothing and answers every run's outcome, read back as the type it went in as. The bench
|
|
12
12
|
## folds them in run order either way, so a spread table is the same table to the last digit, floats
|
|
13
13
|
## included.
|
|
@@ -70,7 +70,7 @@ static func outcomes(arm: String, runs: int, play: Callable) -> Array:
|
|
|
70
70
|
_learn()
|
|
71
71
|
if _arms.has(arm):
|
|
72
72
|
push_error(
|
|
73
|
-
"GdhShare: the arm %s was played twice in one run
|
|
73
|
+
"GdhShare: the arm %s was played twice in one run. Give each arm its own name" % arm
|
|
74
74
|
)
|
|
75
75
|
return []
|
|
76
76
|
_arms[arm] = true
|
|
@@ -107,7 +107,7 @@ static func outcomes(arm: String, runs: int, play: Callable) -> Array:
|
|
|
107
107
|
return ordered
|
|
108
108
|
|
|
109
109
|
|
|
110
|
-
## [param counted], each a dictionary of whole numbers, added up key by key
|
|
110
|
+
## [param counted], each a dictionary of whole numbers, added up key by key. A key named in
|
|
111
111
|
## [param largest] keeps the largest instead. Adding and taking the largest come out the same in
|
|
112
112
|
## any order, which is what lets a bench fold its runs however it likes.
|
|
113
113
|
static func added(
|
|
@@ -143,7 +143,7 @@ static func added(
|
|
|
143
143
|
|
|
144
144
|
|
|
145
145
|
## Leaves one arm's outcomes, then the line saying so, which `gdharness spread` checks the share
|
|
146
|
-
## against
|
|
146
|
+
## against. The outcomes first, so a line is never there without them.
|
|
147
147
|
static func _leave(arm: String, runs: int, by_index: Dictionary[int, Variant]) -> void:
|
|
148
148
|
var left: Dictionary = { "arm": arm, "runs": runs, "outcomes": by_index }
|
|
149
149
|
if not _appended(
|
|
@@ -185,7 +185,7 @@ static func _gathered(arm: String, runs: int) -> Array:
|
|
|
185
185
|
(
|
|
186
186
|
(
|
|
187
187
|
"GdhShare: no worker played the arm %s, so this run went another way than "
|
|
188
|
-
+ "theirs
|
|
188
|
+
+ "theirs. Which arms a bench plays must depend on its command line alone"
|
|
189
189
|
)
|
|
190
190
|
% arm
|
|
191
191
|
)
|
|
@@ -271,7 +271,7 @@ static func _learn() -> void:
|
|
|
271
271
|
_index = halves[0].to_int()
|
|
272
272
|
_workers = halves[1].to_int()
|
|
273
273
|
if _index < 0 or _index >= _workers:
|
|
274
|
-
push_error("GdhShare: %s names no worker
|
|
274
|
+
push_error("GdhShare: %s names no worker. It takes <index>/<workers>" % given)
|
|
275
275
|
_index = -1
|
|
276
276
|
elif given.begins_with(GATHER):
|
|
277
277
|
_gathering = given.trim_prefix(GATHER).to_int()
|
|
@@ -20,7 +20,7 @@ func _ready() -> void:
|
|
|
20
20
|
(
|
|
21
21
|
(
|
|
22
22
|
"gdharness suites: no runner at '%s'. This scene is played by editor_run start "
|
|
23
|
-
+ "with suites, which names the runner after %s
|
|
23
|
+
+ "with suites, which names the runner after %s. It does nothing played any "
|
|
24
24
|
+ "other way."
|
|
25
25
|
)
|
|
26
26
|
% [path, RUNNER]
|
|
@@ -59,7 +59,7 @@ func remove_audio_bus(params: Dictionary) -> Dictionary:
|
|
|
59
59
|
)
|
|
60
60
|
|
|
61
61
|
# A bus sending to this one would be left sending to a name nothing has, which the engine reads
|
|
62
|
-
# as Master
|
|
62
|
+
# as Master. Sent on to where this one went instead, the chain it was part of keeps its shape.
|
|
63
63
|
var onward: String = AudioServer.get_bus_send(index)
|
|
64
64
|
var resent: Array[String] = []
|
|
65
65
|
for i: int in range(AudioServer.bus_count):
|
|
@@ -96,7 +96,7 @@ func get_audio_buses(_params: Dictionary) -> Dictionary:
|
|
|
96
96
|
answer["layout_on_disk"] = false
|
|
97
97
|
answer["note"] = (
|
|
98
98
|
(
|
|
99
|
-
"Nothing is at %s, so these are the engine's default buses
|
|
99
|
+
"Nothing is at %s, so these are the engine's default buses. The first change to a "
|
|
100
100
|
+ "bus writes the layout there."
|
|
101
101
|
)
|
|
102
102
|
% layout
|
|
@@ -225,7 +225,7 @@ func set_audio_bus_send(params: Dictionary) -> Dictionary:
|
|
|
225
225
|
if target == bus_idx:
|
|
226
226
|
return _log.failure(
|
|
227
227
|
(
|
|
228
|
-
"%s cannot send to itself
|
|
228
|
+
"%s cannot send to itself. It can send to %s."
|
|
229
229
|
% [bus_name, ", ".join(_names().slice(0, bus_idx))]
|
|
230
230
|
)
|
|
231
231
|
)
|
|
@@ -269,7 +269,7 @@ func _no_index(index: int) -> String:
|
|
|
269
269
|
var held: PackedStringArray = []
|
|
270
270
|
for i: int in range(AudioServer.bus_count):
|
|
271
271
|
var _added: bool = held.append("%d %s" % [i, AudioServer.get_bus_name(i)])
|
|
272
|
-
return "No bus at index %d
|
|
272
|
+
return "No bus at index %d. The buses are %s." % [index, ", ".join(held)]
|
|
273
273
|
|
|
274
274
|
|
|
275
275
|
func _bus(index: int) -> Dictionary:
|
|
@@ -118,10 +118,10 @@ func _entry_for(path: String, skipped: Array[Dictionary]) -> Dictionary:
|
|
|
118
118
|
#
|
|
119
119
|
# - nothing extended: RefCounted;
|
|
120
120
|
# - one name, a global class or a native one: that name, unresolved;
|
|
121
|
-
# - a dotted name, `
|
|
121
|
+
# - a dotted name, `Outer.Inner`, an inner class of a global class: nothing, as the scan does not
|
|
122
122
|
# look names up;
|
|
123
123
|
# - a path: what that script's class records, after walking into the inner classes named after
|
|
124
|
-
# the path, `"res://
|
|
124
|
+
# the path, `"res://outer.gd".Inner`, so a parent with a class_name is passed over for its own
|
|
125
125
|
# base. A path the engine cannot open records nothing, and it opens a relative one from the
|
|
126
126
|
# project root rather than from the script's folder.
|
|
127
127
|
#
|
|
@@ -157,7 +157,7 @@ func _base_of(path: String, header: Dictionary, skipped: Array[Dictionary]) -> V
|
|
|
157
157
|
|
|
158
158
|
|
|
159
159
|
# The path and the class names an `extends` names, from the text after the keyword:
|
|
160
|
-
# `"res://
|
|
160
|
+
# `"res://outer.gd".Inner` is the path and ["Inner"], `Outer.Inner` no path and two names. The
|
|
161
161
|
# reading stops at whatever follows, a colon or a comment.
|
|
162
162
|
static func _extends_parts(text: String) -> Dictionary:
|
|
163
163
|
var rest: String = text.strip_edges()
|
|
@@ -366,7 +366,7 @@ func _not_found(wanted: String, declared: Dictionary, answers_own: bool = false)
|
|
|
366
366
|
(
|
|
367
367
|
(
|
|
368
368
|
"Class not found: %s.%s editor_classes reads the engine's ClassDB, so a "
|
|
369
|
-
+ "class_name a project script declares is not in it either
|
|
369
|
+
+ "class_name a project script declares is not in it either. script_info "
|
|
370
370
|
+ "structure reads one."
|
|
371
371
|
)
|
|
372
372
|
% [wanted, offered]
|
|
@@ -288,7 +288,7 @@ static func outside(target: Object, declared: Dictionary, value: Variant, shown:
|
|
|
288
288
|
return ""
|
|
289
289
|
return (
|
|
290
290
|
(
|
|
291
|
-
"%s is none of the names %s lists: %s, which the inspector holds it to
|
|
291
|
+
"%s is none of the names %s lists: %s, which the inspector holds it to. The engine "
|
|
292
292
|
+ "stores it as given."
|
|
293
293
|
)
|
|
294
294
|
% [shown, property, ", ".join(names)]
|
|
@@ -311,7 +311,7 @@ static func outside(target: Object, declared: Dictionary, value: Variant, shown:
|
|
|
311
311
|
if flags.is_empty() or stray == 0:
|
|
312
312
|
return ""
|
|
313
313
|
return (
|
|
314
|
-
"%d holds bits no flag of %s has (%d): its flags are %s
|
|
314
|
+
"%d holds bits no flag of %s has (%d): its flags are %s. The engine stores it as given."
|
|
315
315
|
% [whole, property, stray, _listed(flags)]
|
|
316
316
|
)
|
|
317
317
|
if hint != PROPERTY_HINT_RANGE:
|
|
@@ -327,7 +327,7 @@ static func outside(target: Object, declared: Dictionary, value: Variant, shown:
|
|
|
327
327
|
return ""
|
|
328
328
|
return (
|
|
329
329
|
(
|
|
330
|
-
"%s is outside the range %s declares, %s to %s, which the inspector holds it to
|
|
330
|
+
"%s is outside the range %s declares, %s to %s, which the inspector holds it to. The "
|
|
331
331
|
+ "engine stores it as given."
|
|
332
332
|
)
|
|
333
333
|
% [shown, property, _bound(low), _bound(high)]
|
|
@@ -544,7 +544,7 @@ static func not_held(
|
|
|
544
544
|
return (
|
|
545
545
|
(
|
|
546
546
|
"%s.bus takes the name of one of the project's audio buses, and %s is none of "
|
|
547
|
-
+ "them, so it would play on %s
|
|
547
|
+
+ "them, so it would play on %s. %s. project_info include audio_buses lists them, "
|
|
548
548
|
+ "and project_settings add_audio_bus adds one. Nothing was written."
|
|
549
549
|
)
|
|
550
550
|
% [
|
|
@@ -552,21 +552,21 @@ static func not_held(
|
|
|
552
552
|
given[1],
|
|
553
553
|
AudioServer.get_bus_name(0),
|
|
554
554
|
(
|
|
555
|
-
"
|
|
555
|
+
"The buses are " + ", ".join(names)
|
|
556
556
|
if nearest.is_empty()
|
|
557
|
-
else "
|
|
557
|
+
else "The nearest " + nearest
|
|
558
558
|
),
|
|
559
559
|
]
|
|
560
560
|
)
|
|
561
561
|
return (
|
|
562
|
-
"%s: it takes one of the names it lists, and %s is none of them
|
|
562
|
+
"%s: it takes one of the names it lists, and %s is none of them. %s. Nothing was written."
|
|
563
563
|
% [
|
|
564
564
|
said,
|
|
565
565
|
given[1],
|
|
566
566
|
(
|
|
567
|
-
"
|
|
567
|
+
"It lists " + ", ".join(names)
|
|
568
568
|
if nearest.is_empty()
|
|
569
|
-
else "
|
|
569
|
+
else "The nearest it lists " + nearest
|
|
570
570
|
),
|
|
571
571
|
]
|
|
572
572
|
)
|
|
@@ -593,7 +593,7 @@ static func held_by_children(node: Node, property: String) -> String:
|
|
|
593
593
|
|
|
594
594
|
## A sentence for each node path in [param given], by property, that names no node from
|
|
595
595
|
## [param node]. A path is written whatever it names, as the inspector writes one, since the node
|
|
596
|
-
## may be added next
|
|
596
|
+
## may be added next. A joint whose node_a named nothing then joined nothing, and no answer said so.
|
|
597
597
|
static func naming_nothing(node: Node, given: Dictionary) -> Dictionary:
|
|
598
598
|
var said: Dictionary = {}
|
|
599
599
|
for property: String in given:
|
|
@@ -610,7 +610,7 @@ static func naming_nothing(node: Node, given: Dictionary) -> Dictionary:
|
|
|
610
610
|
said[property] = (
|
|
611
611
|
(
|
|
612
612
|
"%s names no node from %s. It is written, as the editor writes one, since that "
|
|
613
|
-
+ "node may be added next
|
|
613
|
+
+ "node may be added next. Until it is, %s finds nothing."
|
|
614
614
|
)
|
|
615
615
|
% [str(named), node.name, property]
|
|
616
616
|
)
|
|
@@ -624,7 +624,7 @@ static func no_uniform(material: ShaderMaterial, property: String) -> String:
|
|
|
624
624
|
var uniform: String = property.trim_prefix("shader_parameter/")
|
|
625
625
|
if material.shader == null:
|
|
626
626
|
return (
|
|
627
|
-
"The ShaderMaterial has no shader, so it has no parameter %s yet
|
|
627
|
+
"The ShaderMaterial has no shader, so it has no parameter %s yet. Set shader first."
|
|
628
628
|
% uniform
|
|
629
629
|
)
|
|
630
630
|
var shader: String = material.shader.resource_path
|
|
@@ -634,5 +634,5 @@ static func no_uniform(material: ShaderMaterial, property: String) -> String:
|
|
|
634
634
|
uniforms.append(str(entry["name"]))
|
|
635
635
|
var said: String = "%s declares no uniform %s, which %s would set" % [named, uniform, property]
|
|
636
636
|
if uniforms.is_empty():
|
|
637
|
-
return said + "
|
|
637
|
+
return said + ". It declares no uniforms."
|
|
638
638
|
return GdhRead.offered(said, uniform, uniforms) + "."
|
|
@@ -47,7 +47,7 @@ func _init(p_log: GdhLog) -> void:
|
|
|
47
47
|
# Get dependencies for a resource with circular reference detection
|
|
48
48
|
func get_dependencies(params: Dictionary) -> Dictionary:
|
|
49
49
|
var resource_path: String = str(params.get("resource_path", ""))
|
|
50
|
-
# No depth, or a depth of zero or less, means the whole chain
|
|
50
|
+
# No depth, or a depth of zero or less, means the whole chain. The walk stops at cycles.
|
|
51
51
|
var depth: int = GdhRead.as_int(params.get("depth", 0))
|
|
52
52
|
var max_depth: int = depth if depth > 0 else 1000
|
|
53
53
|
var include_built_in: bool = GdhRead.as_bool(params.get("include_builtin", false))
|
|
@@ -295,7 +295,7 @@ func find_resource_usages(params: Dictionary) -> Dictionary:
|
|
|
295
295
|
file.close()
|
|
296
296
|
var lines: PackedStringArray = text.split("\n")
|
|
297
297
|
# A class is used by name only in a script's code. In a scene or a resource the same word is
|
|
298
|
-
# a node's name or a caption, and each was counted as a use of the class
|
|
298
|
+
# a node's name or a caption, and each was counted as a use of the class. A shader has no
|
|
299
299
|
# use for one at all.
|
|
300
300
|
var code: bool = file_path.get_extension() == "gd"
|
|
301
301
|
# In a shader # opens a directive, so an #include was answered as a comment, and its
|
|
@@ -421,7 +421,7 @@ func find_resource_usages(params: Dictionary) -> Dictionary:
|
|
|
421
421
|
|
|
422
422
|
|
|
423
423
|
# The uid [param line], line [param index] of [param file_path], declares as the file's own: a
|
|
424
|
-
# scene's or a resource's header, or an import sidecar's uid of the file it imports
|
|
424
|
+
# scene's or a resource's header, or an import sidecar's uid of the file it imports. Empty for any
|
|
425
425
|
# other line.
|
|
426
426
|
static func _own_uid(line: String, index: int, file_path: String) -> String:
|
|
427
427
|
var trimmed: String = line.strip_edges()
|
|
@@ -738,7 +738,7 @@ static func _cycle_key(cycle: Array[String]) -> String:
|
|
|
738
738
|
return "\n".join(PackedStringArray(cycle.slice(first) + cycle.slice(0, first)))
|
|
739
739
|
|
|
740
740
|
|
|
741
|
-
## What [param path] names, each file once, as the entries the tree lists it by
|
|
741
|
+
## What [param path] names, each file once, as the entries the tree lists it by. Read once a walk.
|
|
742
742
|
func _references(path: String, walk: GdhDependencyWalk) -> Array[Dictionary]:
|
|
743
743
|
if walk.references.has(path):
|
|
744
744
|
var known: Array[Dictionary] = walk.references[path]
|
|
@@ -937,7 +937,7 @@ func _files_by_uid() -> Dictionary:
|
|
|
937
937
|
|
|
938
938
|
# The ext_resource paths in [param content] whose uid leads elsewhere, each with the file the uid
|
|
939
939
|
# names. The engine loads an ext_resource by its uid whenever the uid is known, whatever the path
|
|
940
|
-
# says, so a file moved outside the editor still loads, and the walk called it missing
|
|
940
|
+
# says, so a file moved outside the editor still loads, and the walk called it missing. Every
|
|
941
941
|
# pattern reads the old path, the bare res:// one included, so the paths are mapped before any of
|
|
942
942
|
# them runs.
|
|
943
943
|
func _moved_by_uid(content: String) -> Dictionary:
|
|
@@ -1009,7 +1009,7 @@ static func _shader_code(content: String) -> String:
|
|
|
1009
1009
|
return "".join(PackedStringArray(kept))
|
|
1010
1010
|
|
|
1011
1011
|
|
|
1012
|
-
# The path inside a preload(), load(), #include or ext_resource match
|
|
1012
|
+
# The path inside a preload(), load(), #include or ext_resource match. A bare res:// match is the
|
|
1013
1013
|
# path. An ext_resource is read first, since its type or its path can hold the word load.
|
|
1014
1014
|
func _referenced_path(matched: String) -> String:
|
|
1015
1015
|
var expression: String = ""
|
|
@@ -144,7 +144,7 @@ static func _runnable_name(config: ConfigFile, platform: String) -> String:
|
|
|
144
144
|
## Adds an export preset to export_presets.cfg, written with the keys the editor's Project > Export
|
|
145
145
|
## writes for a new one, and every option in [code]known_options[/code] (what
|
|
146
146
|
## [method export_options] answers for the platform) at its default. The editor also writes the
|
|
147
|
-
## options a new preset hides until another option shows them
|
|
147
|
+
## options a new preset hides until another option shows them. Its exporter fills those, and any
|
|
148
148
|
## option the file leaves out, with their defaults when it loads the file.
|
|
149
149
|
func add_export_preset(params: Dictionary) -> Dictionary:
|
|
150
150
|
var preset_name: String = str(params.get("preset", "")).strip_edges()
|
|
@@ -173,7 +173,7 @@ func add_export_preset(params: Dictionary) -> Dictionary:
|
|
|
173
173
|
(
|
|
174
174
|
(
|
|
175
175
|
"There is already an export preset named '%s', for %s, %s. "
|
|
176
|
-
+ "project_export run exports with it
|
|
176
|
+
+ "project_export run exports with it. Give another name to add a second."
|
|
177
177
|
)
|
|
178
178
|
% [preset_name, config.get_value(held, "platform", ""), to]
|
|
179
179
|
)
|
|
@@ -217,7 +217,7 @@ func add_export_preset(params: Dictionary) -> Dictionary:
|
|
|
217
217
|
str_to_var(str(described.get("defaultText", "null")))
|
|
218
218
|
)
|
|
219
219
|
# A section with nothing in it is not written, and the editor's loader reports a missing options
|
|
220
|
-
# section as an error
|
|
220
|
+
# section as an error. These two are options of every platform, empty in a new preset.
|
|
221
221
|
for name: String in TEMPLATE_OPTIONS:
|
|
222
222
|
if not config.has_section_key(section + ".options", name):
|
|
223
223
|
config.set_value(section + ".options", name, "")
|
|
@@ -587,7 +587,7 @@ func set_export_preset(params: Dictionary) -> Dictionary:
|
|
|
587
587
|
var saving: Error = config.save(PRESETS_FILE)
|
|
588
588
|
if saving != OK:
|
|
589
589
|
return _log.failure("Failed to save export_presets.cfg: " + error_string(saving))
|
|
590
|
-
# Written as given, since the file can come after the preset names it
|
|
590
|
+
# Written as given, since the file can come after the preset names it. Said, since an export can
|
|
591
591
|
# fail on it until it does. A secret's path is a secret too, so only its option is named.
|
|
592
592
|
if not missing.is_empty():
|
|
593
593
|
answer["missing_files"] = missing
|
|
@@ -687,7 +687,7 @@ static func _preset_named(preset_name: String) -> Variant:
|
|
|
687
687
|
if preset_name.is_empty():
|
|
688
688
|
return "preset is required: the name of the preset in export_presets.cfg"
|
|
689
689
|
if not FileAccess.file_exists(PRESETS_FILE):
|
|
690
|
-
return "This project has no export_presets.cfg
|
|
690
|
+
return "This project has no export_presets.cfg. project_export add_preset adds a preset."
|
|
691
691
|
var config: ConfigFile = ConfigFile.new()
|
|
692
692
|
var loading: Error = config.load(PRESETS_FILE)
|
|
693
693
|
if loading != OK:
|
|
@@ -733,7 +733,7 @@ static func _files_for(filter: String, given: Variant) -> Variant:
|
|
|
733
733
|
if not not_scenes.is_empty():
|
|
734
734
|
return (
|
|
735
735
|
(
|
|
736
|
-
"Nothing was written: export_filter scenes lists scenes, and %s %s not one
|
|
736
|
+
"Nothing was written: export_filter scenes lists scenes, and %s %s not one. "
|
|
737
737
|
+ "resources lists any file."
|
|
738
738
|
)
|
|
739
739
|
% [", ".join(not_scenes), "is" if not_scenes.size() == 1 else "are"]
|
|
@@ -859,7 +859,7 @@ static func _fitted(name: String, value: Variant, option: Dictionary) -> Variant
|
|
|
859
859
|
return {
|
|
860
860
|
"refused":
|
|
861
861
|
(
|
|
862
|
-
"%s is a %s, which this tool does not write
|
|
862
|
+
"%s is a %s, which this tool does not write. The editor's Export dialog does."
|
|
863
863
|
% [name, type_string(type)]
|
|
864
864
|
),
|
|
865
865
|
}
|
|
@@ -4,7 +4,7 @@ extends RefCounted
|
|
|
4
4
|
##
|
|
5
5
|
## A scene is read off its SceneState rather than instantiated. Instantiating outside the editor
|
|
6
6
|
## runs every script's member initialisers and _init (measured on 4.7.2, with or without -e), so a
|
|
7
|
-
## read with no editor would have run the game's code on every node
|
|
7
|
+
## read with no editor would have run the game's code on every node. The editor gets placeholders
|
|
8
8
|
## and runs none. The state holds everything the file says, and the scenes it instances theirs,
|
|
9
9
|
## without making a node. Loading the file still does what loading does in the game: static
|
|
10
10
|
## initialisers run, and a resource with a script is made with it.
|
|
@@ -53,7 +53,7 @@ static func scene_node(scene_path: String, node_path: String) -> Dictionary:
|
|
|
53
53
|
|
|
54
54
|
|
|
55
55
|
## The node at [param node_path] in [param scene_path] as [method scene_node] answers it, when it is
|
|
56
|
-
## a [param kind]
|
|
56
|
+
## a [param kind]. Otherwise a refusal saying what the scene has there and where its nodes of that
|
|
57
57
|
## kind are, so the caller has the path to pass. The file is read once, because every load prints
|
|
58
58
|
## the engine's warnings about it again.
|
|
59
59
|
static func scene_node_of_kind(scene_path: String, node_path: String, kind: String) -> Dictionary:
|
|
@@ -143,7 +143,7 @@ static func _laid(scene_path: String) -> Dictionary:
|
|
|
143
143
|
## file's own entries for it applied over it. Answers a refusal, or "".
|
|
144
144
|
##
|
|
145
145
|
## [param within] is the files being laid, outermost first, so a scene that instances one of them
|
|
146
|
-
## is refused rather than laid forever
|
|
146
|
+
## is refused rather than laid forever. The engine refuses to load such a scene, so this is only
|
|
147
147
|
## reached through a state built some other way. [param loaded] is the scene [param state] is read
|
|
148
148
|
## from, when the caller has it loaded already.
|
|
149
149
|
static func _lay(
|
|
@@ -200,7 +200,7 @@ static func _lay(
|
|
|
200
200
|
if prefix == "." and loaded == null:
|
|
201
201
|
nodes[path]["inheritedFrom"] = state.get_path()
|
|
202
202
|
# The name is the state's even for an entry laid already, since an inherited scene names its
|
|
203
|
-
# root itself
|
|
203
|
+
# root itself. A root instanced inside another scene is renamed by that scene once laid.
|
|
204
204
|
if not recovered:
|
|
205
205
|
nodes[path]["name"] = str(state.get_node_name(index))
|
|
206
206
|
if fresh:
|
|
@@ -229,7 +229,7 @@ static func _lay(
|
|
|
229
229
|
if loaded != null and not set_here.has(named):
|
|
230
230
|
set_here.append(named)
|
|
231
231
|
# Only the scene being read and the scenes it inherits share its root, the owner whose editable
|
|
232
|
-
# instances a write here sets
|
|
232
|
+
# instances a write here sets. A file instanced below makes its own owner's editable.
|
|
233
233
|
if prefix == ".":
|
|
234
234
|
for editable: String in _editable_in(state.get_path(), loaded):
|
|
235
235
|
var at: String = _joined(prefix, editable)
|
|
@@ -558,7 +558,7 @@ static func resource(res_path: String, include_defaults: bool) -> Dictionary:
|
|
|
558
558
|
var serialiser: GdhSerialisation = GdhSerialisation.new()
|
|
559
559
|
var properties: Dictionary = {}
|
|
560
560
|
# A headless engine runs a script that is not a tool as the file loads, so what its _init and
|
|
561
|
-
# setters gave other properties read as values the file sets
|
|
561
|
+
# setters gave other properties read as values the file sets. The file's own lines say which it
|
|
562
562
|
# does. The editor runs no such script.
|
|
563
563
|
var untooled: bool = script != null and not script.is_tool() and not Engine.is_editor_hint()
|
|
564
564
|
var from_file: bool = (
|