gdharness 1.3.41 → 1.3.43
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +131 -47
- package/build/godot/addons/auto_reload/auto_reload.gd +18 -19
- package/build/godot/addons/gdharness_editor/animation_reads.gd +84 -35
- package/build/godot/addons/gdharness_editor/bridge_client.gd +53 -58
- package/build/godot/addons/gdharness_editor/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_editor/file_reads.gd +114 -39
- package/build/godot/addons/gdharness_editor/file_walk.gd +12 -5
- package/build/godot/addons/gdharness_editor/headless_check.gd +94 -41
- package/build/godot/addons/gdharness_editor/plugin.gd +2 -3
- package/build/godot/addons/gdharness_editor/property_paths.gd +22 -12
- package/build/godot/addons/gdharness_editor/property_values.gd +143 -78
- package/build/godot/addons/gdharness_editor/reading.gd +37 -12
- package/build/godot/addons/gdharness_editor/same_values.gd +4 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +136 -79
- package/build/godot/addons/gdharness_editor/scene_uids.gd +12 -6
- package/build/godot/addons/gdharness_editor/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_editor/setting_reads.gd +7 -6
- package/build/godot/addons/gdharness_editor/tool_executor.gd +3 -6
- package/build/godot/addons/gdharness_editor/tools/animation_states.gd +41 -28
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +84 -50
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +70 -40
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +34 -30
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +24 -22
- package/build/godot/addons/gdharness_editor/tools/navigation_bake.gd +49 -29
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +26 -21
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +40 -40
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +181 -107
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +16 -9
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +75 -43
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +35 -20
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +28 -13
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd +66 -0
- package/build/godot/addons/gdharness_editor/tools/scene_sharing.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +171 -62
- package/build/godot/addons/gdharness_editor/tools/script_paths.gd +20 -8
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +11 -8
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +47 -34
- package/build/godot/addons/gdharness_editor/tools/tileset_tools.gd +77 -50
- package/build/godot/addons/gdharness_editor/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_runtime/call_errors.gd +1 -2
- package/build/godot/addons/gdharness_runtime/declared_values.gd +68 -32
- package/build/godot/addons/gdharness_runtime/error_report.gd +1 -2
- package/build/godot/addons/gdharness_runtime/reading.gd +37 -12
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +84 -63
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +153 -148
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +64 -42
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +184 -122
- package/build/godot/addons/gdharness_runtime/runtime_frames.gd +2 -2
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +24 -20
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +134 -92
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +29 -13
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +0 -1
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +38 -20
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +17 -13
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +84 -46
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +117 -79
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +14 -17
- package/build/godot/addons/gdharness_runtime/runtime_screen.gd +23 -16
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +117 -66
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd +211 -0
- package/build/godot/addons/gdharness_runtime/runtime_touch.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +44 -27
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +108 -73
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +111 -84
- package/build/godot/addons/gdharness_runtime/runtime_words.gd +10 -7
- package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -51
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +5 -6
- package/build/godot/addons/gdharness_tests/bench.gd +4 -5
- package/build/godot/addons/gdharness_tests/event.gd +10 -5
- package/build/godot/addons/gdharness_tests/share.gd +38 -18
- package/build/godot/addons/gdharness_tests/suites.gd +3 -3
- package/build/godot/addons/gdharness_tests/wait.gd +3 -2
- package/build/godot/loader/loader.gd +9 -9
- package/build/godot/operations/animation_reads.gd +84 -35
- package/build/godot/operations/audio_buses.gd +48 -19
- package/build/godot/operations/class_cache.gd +16 -11
- package/build/godot/operations/classdb_queries.gd +89 -44
- package/build/godot/operations/declared_values.gd +68 -32
- package/build/godot/operations/dependencies.gd +139 -93
- package/build/godot/operations/editor_tools.gd +9 -10
- package/build/godot/operations/export_presets.gd +100 -54
- package/build/godot/operations/file_reads.gd +114 -39
- package/build/godot/operations/file_walk.gd +12 -5
- package/build/godot/operations/gdscript_analysis.gd +105 -66
- package/build/godot/operations/gdscript_authoring.gd +138 -39
- package/build/godot/operations/godot_operations.gd +17 -11
- package/build/godot/operations/import_pipeline.gd +110 -59
- package/build/godot/operations/input_actions.gd +63 -39
- package/build/godot/operations/patterns.gd +3 -1
- package/build/godot/operations/plugins.gd +36 -16
- package/build/godot/operations/project_config.gd +144 -27
- package/build/godot/operations/project_diagnostics.gd +24 -11
- package/build/godot/operations/project_file.gd +16 -16
- package/build/godot/operations/project_validation.gd +71 -44
- package/build/godot/operations/property_paths.gd +22 -12
- package/build/godot/operations/property_values.gd +143 -78
- package/build/godot/operations/reading.gd +37 -12
- package/build/godot/operations/rename_checks.gd +2 -2
- package/build/godot/operations/resource_files.gd +11 -9
- package/build/godot/operations/same_values.gd +4 -4
- package/build/godot/operations/scene_connections.gd +27 -13
- package/build/godot/operations/script_check.gd +52 -49
- package/build/godot/operations/serialisation.gd +75 -51
- package/build/godot/operations/setting_reads.gd +7 -6
- package/build/godot/operations/value_shapes.gd +5 -6
- package/build/godot/tests/listed.gd +321 -268
- package/package.json +1 -1
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## Godot values as JSON can carry them, and JSON back into the values the engine wants.
|
|
4
3
|
##
|
|
5
4
|
## This file is the one beside the operations, and `bun run sync:gd` copies it into each addon,
|
|
@@ -10,17 +9,14 @@ extends RefCounted
|
|
|
10
9
|
|
|
11
10
|
const GdhRead = preload("reading.gd")
|
|
12
11
|
const GdhShapes = preload("value_shapes.gd")
|
|
13
|
-
|
|
14
12
|
# What the engine carries from one to another without complaint, which is what a value written to a
|
|
15
13
|
# property or handed to a parameter may arrive as. Numbers include bool because the engine counts it
|
|
16
14
|
# as one, and the three text types are one string wearing three hats.
|
|
17
15
|
const NUMBERS: Array[int] = [TYPE_BOOL, TYPE_INT, TYPE_FLOAT]
|
|
18
16
|
const TEXTS: Array[int] = [TYPE_STRING, TYPE_STRING_NAME, TYPE_NODE_PATH]
|
|
19
|
-
|
|
20
17
|
# 2^53. A JSON number is a double, and every whole number below this one in size has a double of
|
|
21
18
|
# its own; from here on two or more share each double, so one arriving tells nobody which was sent.
|
|
22
|
-
const EXACT: int =
|
|
23
|
-
|
|
19
|
+
const EXACT: int = 9_007_199_254_740_992
|
|
24
20
|
# The types that cross as a plain JSON list and have to be built back into themselves. A list is
|
|
25
21
|
# what a caller can write by hand and what the serialiser answers with, and the parameter or
|
|
26
22
|
# property being written is the only thing that knows which of these it wanted.
|
|
@@ -35,7 +31,6 @@ const LIST_TYPES: Array[int] = [
|
|
|
35
31
|
TYPE_PACKED_VECTOR4_ARRAY,
|
|
36
32
|
TYPE_PACKED_COLOR_ARRAY,
|
|
37
33
|
]
|
|
38
|
-
|
|
39
34
|
# What each Godot type becomes on the wire, as one table you can read rather than an order you
|
|
40
35
|
# have to trust. Keyed on typeof() rather than written as a chain of `is` tests, because the
|
|
41
36
|
# order mattered: Resource had to be tested before Object or every resource came back as a bare
|
|
@@ -92,7 +87,6 @@ const SERIALISERS: Dictionary = {
|
|
|
92
87
|
TYPE_PACKED_VECTOR4_ARRAY: "_serialize_vector4s",
|
|
93
88
|
TYPE_PACKED_COLOR_ARRAY: "_serialize_colors",
|
|
94
89
|
}
|
|
95
|
-
|
|
96
90
|
# What each tag builds back into. The other half of the table above, and separate from it because
|
|
97
91
|
# they are keyed differently: one by the type of a value in hand, the other by the name in a
|
|
98
92
|
# dictionary that arrived from somewhere else.
|
|
@@ -144,7 +138,11 @@ func serialize_value(value: Variant) -> Variant:
|
|
|
144
138
|
# The fixed shapes carry their components as bare floats, so an infinite one in a Vector2 is
|
|
145
139
|
# tagged here, and a 32-bit one shortened; containers reach their members through this already.
|
|
146
140
|
var type: int = typeof(value)
|
|
147
|
-
return
|
|
141
|
+
return (
|
|
142
|
+
carried
|
|
143
|
+
if type in [TYPE_ARRAY, TYPE_DICTIONARY]
|
|
144
|
+
else _finite(carried, GdhRead.holds_singles(type))
|
|
145
|
+
)
|
|
148
146
|
|
|
149
147
|
|
|
150
148
|
# [param carried] with every non-finite float in it tagged. JSON has no spelling for infinity or
|
|
@@ -172,7 +170,10 @@ func _finite(carried: Variant, single: bool = false) -> Variant:
|
|
|
172
170
|
func _serialize_float(value: float) -> Variant:
|
|
173
171
|
if is_finite(value):
|
|
174
172
|
return value
|
|
175
|
-
return {
|
|
173
|
+
return {
|
|
174
|
+
"_type": "float",
|
|
175
|
+
"value": "nan" if is_nan(value) else ("inf" if value > 0 else "-inf"),
|
|
176
|
+
}
|
|
176
177
|
|
|
177
178
|
|
|
178
179
|
# A packed float array stays the JSON list it is unless something in it has no JSON spelling.
|
|
@@ -400,8 +401,9 @@ func fitted(value: Variant, type: int) -> Variant:
|
|
|
400
401
|
var simple: Array[int] = [TYPE_BOOL, TYPE_INT, TYPE_FLOAT, TYPE_STRING]
|
|
401
402
|
if not simple.has(type) or not simple.has(typeof(rebuilt)):
|
|
402
403
|
return rebuilt
|
|
403
|
-
# A number or a truth value stays as sent where text is wanted, for the refusal to name.
|
|
404
|
-
# 1 arrived as the double it is in JSON and was saved as "1.0", which is not the text
|
|
404
|
+
# A number or a truth value stays as sent where text is wanted, for the refusal to name.
|
|
405
|
+
# Converted, 1 arrived as the double it is in JSON and was saved as "1.0", which is not the text
|
|
406
|
+
# anyone sends.
|
|
405
407
|
if type == TYPE_STRING and not rebuilt is String:
|
|
406
408
|
return rebuilt
|
|
407
409
|
|
|
@@ -426,9 +428,10 @@ func fitted(value: Variant, type: int) -> Variant:
|
|
|
426
428
|
|
|
427
429
|
|
|
428
430
|
## Why [param given], meant for an int, may not be the number that was sent, or "" when it is. A
|
|
429
|
-
## JSON number is a double wherever it is read, so a whole number from 2^53 on arrives as the
|
|
430
|
-
## double to the one written: 9007199254740993 sent as a number arrives as 9007199254740992,
|
|
431
|
-
## seed written that way would be stored as its neighbour with the answer saying it was
|
|
431
|
+
## JSON number is a double wherever it is read, so a whole number from 2^53 on arrives as the
|
|
432
|
+
## nearest double to the one written: 9007199254740993 sent as a number arrives as 9007199254740992,
|
|
433
|
+
## and a seed written that way would be stored as its neighbour with the answer saying it was
|
|
434
|
+
## written.
|
|
432
435
|
static func inexact(given: Variant, type: int) -> String:
|
|
433
436
|
if type != TYPE_INT or (typeof(given) != TYPE_FLOAT and typeof(given) != TYPE_INT):
|
|
434
437
|
return ""
|
|
@@ -437,9 +440,9 @@ static func inexact(given: Variant, type: int) -> String:
|
|
|
437
440
|
return ""
|
|
438
441
|
return (
|
|
439
442
|
(
|
|
440
|
-
"%s is 2^53 or more, where a JSON number holds a whole number only to the nearest
|
|
441
|
-
+ ' so it may not be the number sent: send the digits as {"_type": "int",
|
|
442
|
-
+ ", the form a read answers with
|
|
443
|
+
"%s is 2^53 or more, where a JSON number holds a whole number only to the nearest "
|
|
444
|
+
+ 'double, so it may not be the number sent: send the digits as {"_type": "int", '
|
|
445
|
+
+ '"value": "..."}, the form a read answers with'
|
|
443
446
|
)
|
|
444
447
|
% String.num(number, 0)
|
|
445
448
|
)
|
|
@@ -450,51 +453,59 @@ func _serialize_nil(_value: Variant) -> Variant:
|
|
|
450
453
|
|
|
451
454
|
|
|
452
455
|
func _serialize_vector2(value: Vector2) -> Dictionary:
|
|
453
|
-
return {"x": value.x, "y": value.y, "_type": "Vector2"}
|
|
456
|
+
return { "x": value.x, "y": value.y, "_type": "Vector2" }
|
|
454
457
|
|
|
455
458
|
|
|
456
459
|
func _serialize_vector2i(value: Vector2i) -> Dictionary:
|
|
457
|
-
return {"x": value.x, "y": value.y, "_type": "Vector2i"}
|
|
460
|
+
return { "x": value.x, "y": value.y, "_type": "Vector2i" }
|
|
458
461
|
|
|
459
462
|
|
|
460
463
|
func _serialize_vector3(value: Vector3) -> Dictionary:
|
|
461
|
-
return {"x": value.x, "y": value.y, "z": value.z, "_type": "Vector3"}
|
|
464
|
+
return { "x": value.x, "y": value.y, "z": value.z, "_type": "Vector3" }
|
|
462
465
|
|
|
463
466
|
|
|
464
467
|
func _serialize_vector3i(value: Vector3i) -> Dictionary:
|
|
465
|
-
return {"x": value.x, "y": value.y, "z": value.z, "_type": "Vector3i"}
|
|
468
|
+
return { "x": value.x, "y": value.y, "z": value.z, "_type": "Vector3i" }
|
|
466
469
|
|
|
467
470
|
|
|
468
471
|
func _serialize_vector4(value: Vector4) -> Dictionary:
|
|
469
|
-
return {"x": value.x, "y": value.y, "z": value.z, "w": value.w, "_type": "Vector4"}
|
|
472
|
+
return { "x": value.x, "y": value.y, "z": value.z, "w": value.w, "_type": "Vector4" }
|
|
470
473
|
|
|
471
474
|
|
|
472
475
|
func _serialize_vector4i(value: Vector4i) -> Dictionary:
|
|
473
|
-
return {"x": value.x, "y": value.y, "z": value.z, "w": value.w, "_type": "Vector4i"}
|
|
476
|
+
return { "x": value.x, "y": value.y, "z": value.z, "w": value.w, "_type": "Vector4i" }
|
|
474
477
|
|
|
475
478
|
|
|
476
479
|
func _serialize_rect2(value: Rect2) -> Dictionary:
|
|
477
480
|
return {
|
|
478
|
-
"position": serialize_value(value.position),
|
|
481
|
+
"position": serialize_value(value.position),
|
|
482
|
+
"size": serialize_value(value.size),
|
|
483
|
+
"_type": "Rect2",
|
|
479
484
|
}
|
|
480
485
|
|
|
481
486
|
|
|
482
487
|
func _serialize_rect2i(value: Rect2i) -> Dictionary:
|
|
483
488
|
return {
|
|
484
|
-
"position": serialize_value(value.position),
|
|
489
|
+
"position": serialize_value(value.position),
|
|
490
|
+
"size": serialize_value(value.size),
|
|
491
|
+
"_type": "Rect2i",
|
|
485
492
|
}
|
|
486
493
|
|
|
487
494
|
|
|
488
495
|
func _serialize_plane(value: Plane) -> Dictionary:
|
|
489
|
-
return {"normal": serialize_value(value.normal), "d": value.d, "_type": "Plane"}
|
|
496
|
+
return { "normal": serialize_value(value.normal), "d": value.d, "_type": "Plane" }
|
|
490
497
|
|
|
491
498
|
|
|
492
499
|
func _serialize_quaternion(value: Quaternion) -> Dictionary:
|
|
493
|
-
return {"x": value.x, "y": value.y, "z": value.z, "w": value.w, "_type": "Quaternion"}
|
|
500
|
+
return { "x": value.x, "y": value.y, "z": value.z, "w": value.w, "_type": "Quaternion" }
|
|
494
501
|
|
|
495
502
|
|
|
496
503
|
func _serialize_aabb(value: AABB) -> Dictionary:
|
|
497
|
-
return {
|
|
504
|
+
return {
|
|
505
|
+
"position": serialize_value(value.position),
|
|
506
|
+
"size": serialize_value(value.size),
|
|
507
|
+
"_type": "AABB",
|
|
508
|
+
}
|
|
498
509
|
|
|
499
510
|
|
|
500
511
|
func _serialize_basis(value: Basis) -> Dictionary:
|
|
@@ -502,7 +513,7 @@ func _serialize_basis(value: Basis) -> Dictionary:
|
|
|
502
513
|
"x": serialize_value(value.x),
|
|
503
514
|
"y": serialize_value(value.y),
|
|
504
515
|
"z": serialize_value(value.z),
|
|
505
|
-
"_type": "Basis"
|
|
516
|
+
"_type": "Basis",
|
|
506
517
|
}
|
|
507
518
|
|
|
508
519
|
|
|
@@ -511,13 +522,15 @@ func _serialize_transform2d(value: Transform2D) -> Dictionary:
|
|
|
511
522
|
"origin": serialize_value(value.origin),
|
|
512
523
|
"x": serialize_value(value.x),
|
|
513
524
|
"y": serialize_value(value.y),
|
|
514
|
-
"_type": "Transform2D"
|
|
525
|
+
"_type": "Transform2D",
|
|
515
526
|
}
|
|
516
527
|
|
|
517
528
|
|
|
518
529
|
func _serialize_transform3d(value: Transform3D) -> Dictionary:
|
|
519
530
|
return {
|
|
520
|
-
"origin": serialize_value(value.origin),
|
|
531
|
+
"origin": serialize_value(value.origin),
|
|
532
|
+
"basis": serialize_value(value.basis),
|
|
533
|
+
"_type": "Transform3D",
|
|
521
534
|
}
|
|
522
535
|
|
|
523
536
|
|
|
@@ -527,22 +540,22 @@ func _serialize_projection(value: Projection) -> Dictionary:
|
|
|
527
540
|
"y": serialize_value(value.y),
|
|
528
541
|
"z": serialize_value(value.z),
|
|
529
542
|
"w": serialize_value(value.w),
|
|
530
|
-
"_type": "Projection"
|
|
543
|
+
"_type": "Projection",
|
|
531
544
|
}
|
|
532
545
|
|
|
533
546
|
|
|
534
547
|
func _serialize_color(value: Color) -> Dictionary:
|
|
535
|
-
return {"r": value.r, "g": value.g, "b": value.b, "a": value.a, "_type": "Color"}
|
|
548
|
+
return { "r": value.r, "g": value.g, "b": value.b, "a": value.a, "_type": "Color" }
|
|
536
549
|
|
|
537
550
|
|
|
538
551
|
func _serialize_node_path(value: NodePath) -> Dictionary:
|
|
539
|
-
return {"path": str(value), "_type": "NodePath"}
|
|
552
|
+
return { "path": str(value), "_type": "NodePath" }
|
|
540
553
|
|
|
541
554
|
|
|
542
555
|
# By its id, which is all an RID is from outside the server holding it, and which builds it back:
|
|
543
556
|
# a viewport's RID read off one call is what RenderingServer's render timing takes in the next.
|
|
544
557
|
func _serialize_rid(value: RID) -> Dictionary:
|
|
545
|
-
return {"id": _serialize_int(value.get_id()), "_type": "RID"}
|
|
558
|
+
return { "id": _serialize_int(value.get_id()), "_type": "RID" }
|
|
546
559
|
|
|
547
560
|
|
|
548
561
|
# A whole number from 2^53 on as its digits, because JSON numbers are doubles on the server's side
|
|
@@ -552,7 +565,7 @@ func _serialize_rid(value: RID) -> Dictionary:
|
|
|
552
565
|
func _serialize_int(value: int) -> Variant:
|
|
553
566
|
if value > -EXACT and value < EXACT:
|
|
554
567
|
return value
|
|
555
|
-
return {"_type": "int", "value": str(value)}
|
|
568
|
+
return { "_type": "int", "value": str(value) }
|
|
556
569
|
|
|
557
570
|
|
|
558
571
|
# A packed list of 64-bit numbers stays the JSON list it is unless something in it is too large for
|
|
@@ -570,7 +583,7 @@ func _serialize_ints(value: PackedInt64Array) -> Variant:
|
|
|
570
583
|
# A bound method, said in the terms a caller could act on: what it is on, and what it is called.
|
|
571
584
|
# It cannot be rebuilt from here, and saying so is the answer rather than a silent null.
|
|
572
585
|
func _serialize_callable(value: Callable) -> Dictionary:
|
|
573
|
-
var described: Dictionary = {"method": str(value.get_method()), "_type": "Callable"}
|
|
586
|
+
var described: Dictionary = { "method": str(value.get_method()), "_type": "Callable" }
|
|
574
587
|
var holder: Object = value.get_object()
|
|
575
588
|
if holder != null:
|
|
576
589
|
described["object"] = _describe_object(holder)
|
|
@@ -578,7 +591,7 @@ func _serialize_callable(value: Callable) -> Dictionary:
|
|
|
578
591
|
|
|
579
592
|
|
|
580
593
|
func _serialize_signal(value: Signal) -> Dictionary:
|
|
581
|
-
var described: Dictionary = {"name": str(value.get_name()), "_type": "Signal"}
|
|
594
|
+
var described: Dictionary = { "name": str(value.get_name()), "_type": "Signal" }
|
|
582
595
|
var holder: Object = value.get_object()
|
|
583
596
|
if holder != null:
|
|
584
597
|
described["object"] = _describe_object(holder)
|
|
@@ -589,12 +602,14 @@ func _serialize_signal(value: Signal) -> Dictionary:
|
|
|
589
602
|
## int or Vector2 key was answered as its text, and a dictionary read and written back had its keys
|
|
590
603
|
## replaced by words: `{1: "a"}` came back as `{"1": "a"}`.
|
|
591
604
|
func _serialize_dictionary(value: Dictionary) -> Dictionary:
|
|
592
|
-
var texts: bool = value.keys().all(
|
|
605
|
+
var texts: bool = value.keys().all(
|
|
606
|
+
func(key: Variant) -> bool: return key is String or key is StringName
|
|
607
|
+
)
|
|
593
608
|
if not texts:
|
|
594
609
|
var entries: Array = []
|
|
595
610
|
for key: Variant in value:
|
|
596
611
|
entries.append([serialize_value(key), serialize_value(value[key])])
|
|
597
|
-
return {"_type": "Dictionary", "entries": entries}
|
|
612
|
+
return { "_type": "Dictionary", "entries": entries }
|
|
598
613
|
var serialised: Dictionary = {}
|
|
599
614
|
for key: Variant in value:
|
|
600
615
|
serialised[str(key)] = serialize_value(value[key])
|
|
@@ -612,7 +627,7 @@ func _serialize_array(value: Array) -> Array:
|
|
|
612
627
|
# TileMapLayer with nothing painted yet, so a read of one carried an engine error in its answer.
|
|
613
628
|
func _serialize_bytes(value: PackedByteArray) -> Dictionary:
|
|
614
629
|
var text: String = "" if value.is_empty() else Marshalls.raw_to_base64(value)
|
|
615
|
-
return {"base64": text, "size": value.size(), "_type": "PackedByteArray"}
|
|
630
|
+
return { "base64": text, "size": value.size(), "_type": "PackedByteArray" }
|
|
616
631
|
|
|
617
632
|
|
|
618
633
|
# The packed vector and colour arrays, each element serialised the way one of them on its own is.
|
|
@@ -660,7 +675,7 @@ func _serialize_object(value: Object) -> Variant:
|
|
|
660
675
|
if resource.resource_path.is_empty() or resource.resource_path.contains("::"):
|
|
661
676
|
return _describe_built_in(resource)
|
|
662
677
|
var filed: Dictionary = {
|
|
663
|
-
"path": resource.resource_path, "class": resource.get_class(), "_type": "Resource"
|
|
678
|
+
"path": resource.resource_path, "class": resource.get_class(), "_type": "Resource",
|
|
664
679
|
}
|
|
665
680
|
# The engine class of a resource with a script is the one its script extends, so a Stats
|
|
666
681
|
# file read as a Resource and nothing said it was a Stats.
|
|
@@ -673,7 +688,7 @@ func _serialize_object(value: Object) -> Variant:
|
|
|
673
688
|
if value is Node:
|
|
674
689
|
var node: Node = value
|
|
675
690
|
if node.is_inside_tree():
|
|
676
|
-
return {"path": str(node.get_path()), "class": node.get_class(), "_type": "Node"}
|
|
691
|
+
return { "path": str(node.get_path()), "class": node.get_class(), "_type": "Node" }
|
|
677
692
|
return _describe_object(value)
|
|
678
693
|
|
|
679
694
|
|
|
@@ -683,16 +698,16 @@ func _serialize_object(value: Object) -> Variant:
|
|
|
683
698
|
## written back.
|
|
684
699
|
func _describe_built_in(resource: Resource) -> Dictionary:
|
|
685
700
|
if _describing.has(resource):
|
|
686
|
-
return {"class": resource.get_class(), "_type": "Resource"}
|
|
701
|
+
return { "class": resource.get_class(), "_type": "Resource" }
|
|
687
702
|
_describing.append(resource)
|
|
688
|
-
var described: Dictionary = {"_type": resource.get_class()}
|
|
703
|
+
var described: Dictionary = { "_type": resource.get_class() }
|
|
689
704
|
var attached: Variant = resource.get_script()
|
|
690
705
|
var script: Script = null
|
|
691
706
|
if attached is Script:
|
|
692
707
|
script = attached
|
|
693
708
|
# By the class its script declares, which a write takes as the _type and which says what it
|
|
694
|
-
# is: as Resource with the script beside it, one answer named a Shop's built-in item that
|
|
695
|
-
# and the file item beside it as an Item.
|
|
709
|
+
# is: as Resource with the script beside it, one answer named a Shop's built-in item that
|
|
710
|
+
# way and the file item beside it as an Item.
|
|
696
711
|
if script.get_global_name().is_empty():
|
|
697
712
|
described["script"] = serialize_value(script)
|
|
698
713
|
else:
|
|
@@ -741,11 +756,17 @@ static func _fresh(script: Script) -> Object:
|
|
|
741
756
|
## away from its default. As a bare resource it was its class and nothing else, so an input action
|
|
742
757
|
## read back from project.godot could not say whether it was bound to Space or to a joypad button.
|
|
743
758
|
func _describe_input_event(event: InputEvent) -> Dictionary:
|
|
744
|
-
var described: Dictionary = {
|
|
759
|
+
var described: Dictionary = {
|
|
760
|
+
"class": event.get_class(), "_type": "Resource", "text": event.as_text(),
|
|
761
|
+
}
|
|
745
762
|
for property: Dictionary in event.get_property_list():
|
|
746
763
|
var usage: int = property.get("usage", 0)
|
|
747
764
|
var name: String = str(property.get("name", ""))
|
|
748
|
-
if
|
|
765
|
+
if (
|
|
766
|
+
not (usage & PROPERTY_USAGE_STORAGE)
|
|
767
|
+
or name.begins_with("resource_")
|
|
768
|
+
or name == "script"
|
|
769
|
+
):
|
|
749
770
|
continue
|
|
750
771
|
var held: Variant = event.get(name)
|
|
751
772
|
if held == ClassDB.class_get_property_default_value(event.get_class(), name):
|
|
@@ -764,7 +785,7 @@ func _describe_input_event(event: InputEvent) -> Dictionary:
|
|
|
764
785
|
## different per object. None of it is invented: every field is left out when there is nothing to
|
|
765
786
|
## put in it, because a key holding "" is a worse answer than a key that is not there.
|
|
766
787
|
func _describe_object(value: Object) -> Dictionary:
|
|
767
|
-
var described: Dictionary = {"class": value.get_class(), "_type": "Object"}
|
|
788
|
+
var described: Dictionary = { "class": value.get_class(), "_type": "Object" }
|
|
768
789
|
# Asked as a Variant and narrowed, because get_script answers one and an object without a
|
|
769
790
|
# script answers null: casting that to Script is the unsafe cast the engine refuses to compile.
|
|
770
791
|
var attached: Variant = value.get_script()
|
|
@@ -892,7 +913,10 @@ func _build_transform3d(fields: Dictionary) -> Transform3D:
|
|
|
892
913
|
|
|
893
914
|
func _build_projection(fields: Dictionary) -> Projection:
|
|
894
915
|
return Projection(
|
|
895
|
-
_vector4_at(fields, "x"),
|
|
916
|
+
_vector4_at(fields, "x"),
|
|
917
|
+
_vector4_at(fields, "y"),
|
|
918
|
+
_vector4_at(fields, "z"),
|
|
919
|
+
_vector4_at(fields, "w")
|
|
896
920
|
)
|
|
897
921
|
|
|
898
922
|
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
|
-
|
|
3
2
|
## How each engine type built from fields is written: the fields it needs, the ones it reads when
|
|
4
3
|
## they are there, and what a refusal says about a value that is not one.
|
|
5
4
|
##
|
|
6
5
|
## This file is the one beside the operations, and `bun run sync:gd` copies it into each addon.
|
|
7
6
|
|
|
8
7
|
const GdhRead = preload("reading.gd")
|
|
9
|
-
|
|
10
8
|
## The classes that store what the inspector shows, and a write takes, under other names: a Curve
|
|
11
9
|
## keeps its points as _data, numbers and modes in one list, and its range as _limits, where the
|
|
12
10
|
## inspector and resource_edit modify have point_count, point_0/position and min_value. A read
|
|
@@ -17,7 +15,6 @@ const STORED_APART: Dictionary[String, Array] = {
|
|
|
17
15
|
"Curve3D": ["_data"],
|
|
18
16
|
}
|
|
19
17
|
const HEADINGS: int = PROPERTY_USAGE_CATEGORY | PROPERTY_USAGE_GROUP | PROPERTY_USAGE_SUBGROUP
|
|
20
|
-
|
|
21
18
|
## What each packed array holds, one value of which every element is fitted to.
|
|
22
19
|
const PACKED_ELEMENTS: Dictionary = {
|
|
23
20
|
TYPE_PACKED_BYTE_ARRAY: TYPE_INT,
|
|
@@ -43,7 +40,6 @@ const LIST_TAGS: Dictionary = {
|
|
|
43
40
|
"PackedVector4Array": TYPE_PACKED_VECTOR4_ARRAY,
|
|
44
41
|
"PackedColorArray": TYPE_PACKED_COLOR_ARRAY,
|
|
45
42
|
}
|
|
46
|
-
|
|
47
43
|
# What each tag has to carry before it is built. A tag naming a type whose keys are not there is a
|
|
48
44
|
# tag somebody wrote by mistake or wrote for a different shape, and building one out of defaults
|
|
49
45
|
# answers with a Transform2D of zeros where the caller meant something else entirely. Unbuilt, it
|
|
@@ -254,7 +250,9 @@ static func remedy(value: Variant, type: int) -> String:
|
|
|
254
250
|
if shape.is_empty():
|
|
255
251
|
return ""
|
|
256
252
|
return (
|
|
257
|
-
_unread(value, type)
|
|
253
|
+
_unread(value, type)
|
|
254
|
+
+ _no_number(value, type)
|
|
255
|
+
+ " A %s is sent as %s." % [type_string(type), shape]
|
|
258
256
|
)
|
|
259
257
|
|
|
260
258
|
|
|
@@ -288,7 +286,8 @@ static func _unread(value: Variant, type: int) -> String:
|
|
|
288
286
|
var _named: bool = named.append('"%s"' % key)
|
|
289
287
|
var fields_called: String = "field called" if named.size() == 1 else "fields called"
|
|
290
288
|
var note: String = (
|
|
291
|
-
" A %s has no %s %s, so the value is not read as one"
|
|
289
|
+
" A %s has no %s %s, so the value is not read as one"
|
|
290
|
+
% [tag, fields_called, " or ".join(named)]
|
|
292
291
|
)
|
|
293
292
|
if left.has("type") and not fields.has("_type"):
|
|
294
293
|
return note + ': a value names its type under "_type".'
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
class_name GdhBench
|
|
2
2
|
extends RefCounted
|
|
3
|
-
|
|
4
3
|
## Work played a step a frame, for the script profiler. The engine sends the profiler what each
|
|
5
4
|
## frame called and holds names for only so many functions a frame, so work played inside one frame
|
|
6
5
|
## comes back with most of its functions unnamed; spread over frames, every step is named.
|
|
@@ -11,9 +10,9 @@ extends RefCounted
|
|
|
11
10
|
## get_tree().quit()
|
|
12
11
|
|
|
13
12
|
|
|
14
|
-
## Calls [param step] once a frame, up to [param most] times, and stops early when it returns
|
|
15
|
-
##
|
|
16
|
-
##
|
|
13
|
+
## Calls [param step] once a frame, up to [param most] times, and stops early when it returns false.
|
|
14
|
+
## Answers how many steps ran under "steps", and under "ms_each" the wall-clock milliseconds a step
|
|
15
|
+
## took on average, the step alone and not the frame around it.
|
|
17
16
|
static func each_frame(tree: SceneTree, most: int, step: Callable) -> Dictionary:
|
|
18
17
|
var steps: int = 0
|
|
19
18
|
var spent_usec: int = 0
|
|
@@ -25,4 +24,4 @@ static func each_frame(tree: SceneTree, most: int, step: Callable) -> Dictionary
|
|
|
25
24
|
steps += 1
|
|
26
25
|
if going is bool and going == false:
|
|
27
26
|
break
|
|
28
|
-
return {"steps": steps, "ms_each": spent_usec / 1000.0 / maxf(1.0, float(steps))}
|
|
27
|
+
return { "steps": steps, "ms_each": spent_usec / 1000.0 / maxf(1.0, float(steps)) }
|
|
@@ -34,7 +34,9 @@ static func wheel(at: Vector2, up: bool = true) -> InputEventMouseButton:
|
|
|
34
34
|
|
|
35
35
|
## The mouse moved to [param at] by [param moved], holding [param held], a mask of buttons such
|
|
36
36
|
## as MOUSE_BUTTON_MASK_LEFT for a drag.
|
|
37
|
-
static func motion(
|
|
37
|
+
static func motion(
|
|
38
|
+
at: Vector2, moved: Vector2 = Vector2.ZERO, held: int = 0
|
|
39
|
+
) -> InputEventMouseMotion:
|
|
38
40
|
var made: InputEventMouseMotion = InputEventMouseMotion.new()
|
|
39
41
|
made.position = at
|
|
40
42
|
made.global_position = at
|
|
@@ -43,8 +45,8 @@ static func motion(at: Vector2, moved: Vector2 = Vector2.ZERO, held: int = 0) ->
|
|
|
43
45
|
return made
|
|
44
46
|
|
|
45
47
|
|
|
46
|
-
## [param key_pressed] pressed, or released when [param pressed] is false. The key is set as both
|
|
47
|
-
## keycode and the physical keycode, so a binding to either matches it.
|
|
48
|
+
## [param key_pressed] pressed, or released when [param pressed] is false. The key is set as both
|
|
49
|
+
## the keycode and the physical keycode, so a binding to either matches it.
|
|
48
50
|
static func key(key_pressed: Key, pressed: bool = true) -> InputEventKey:
|
|
49
51
|
var made: InputEventKey = InputEventKey.new()
|
|
50
52
|
made.keycode = key_pressed
|
|
@@ -72,8 +74,11 @@ static func axis(axis_moved: JoyAxis, value: float, device: int = 0) -> InputEve
|
|
|
72
74
|
return made
|
|
73
75
|
|
|
74
76
|
|
|
75
|
-
## The action [param action_named] pressed at [param strength], or released when [param pressed] is
|
|
76
|
-
|
|
77
|
+
## The action [param action_named] pressed at [param strength], or released when [param pressed] is
|
|
78
|
+
## false.
|
|
79
|
+
static func action(
|
|
80
|
+
action_named: StringName, pressed: bool = true, strength: float = 1.0
|
|
81
|
+
) -> InputEventAction:
|
|
77
82
|
var made: InputEventAction = InputEventAction.new()
|
|
78
83
|
made.action = action_named
|
|
79
84
|
made.pressed = pressed
|
|
@@ -6,10 +6,11 @@ extends RefCounted
|
|
|
6
6
|
## A bench whose run `i` depends on `i` alone (its seed worked out from the index, nothing carried
|
|
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
|
-
## `gdharness spread`, each worker engine plays every Nth run from its own index and leaves what
|
|
10
|
-
## run answered; then one more engine runs the scene to gather, and there [method outcomes]
|
|
11
|
-
## nothing and answers every run's outcome, read back as the type it went in as. The bench
|
|
12
|
-
## in run order either way, so a spread table is the same table to the last digit, floats
|
|
9
|
+
## `gdharness spread`, each worker engine plays every Nth run from its own index and leaves what
|
|
10
|
+
## each run answered; then one more engine runs the scene to gather, and there [method outcomes]
|
|
11
|
+
## plays nothing and answers every run's outcome, read back as the type it went in as. The bench
|
|
12
|
+
## folds them in run order either way, so a spread table is the same table to the last digit, floats
|
|
13
|
+
## included.
|
|
13
14
|
##
|
|
14
15
|
## An outcome is a plain value: a number, a string, a vector, or an array or dictionary of them. An
|
|
15
16
|
## object cannot be handed from one engine to another, so one is refused with an error.
|
|
@@ -18,7 +19,8 @@ extends RefCounted
|
|
|
18
19
|
const SHARE: String = "--gdh-share="
|
|
19
20
|
## What it tells the engine gathering: how many workers left outcomes.
|
|
20
21
|
const GATHER: String = "--gdh-share-gather="
|
|
21
|
-
## Where the workers leave their outcomes, an absolute path, since each engine has its own
|
|
22
|
+
## Where the workers leave their outcomes, an absolute path, since each engine has its own
|
|
23
|
+
## `user://`.
|
|
22
24
|
const INTO: String = "--gdh-share-dir="
|
|
23
25
|
## What a worker prints as it goes, which `gdharness spread` reads to say how far each has got.
|
|
24
26
|
const PROGRESS: String = "gdharness share: %s %d of %d runs"
|
|
@@ -67,7 +69,9 @@ static func dealt(runs: int) -> PackedInt32Array:
|
|
|
67
69
|
static func outcomes(arm: String, runs: int, play: Callable) -> Array:
|
|
68
70
|
_learn()
|
|
69
71
|
if _arms.has(arm):
|
|
70
|
-
push_error(
|
|
72
|
+
push_error(
|
|
73
|
+
"GdhShare: the arm %s was played twice in one run; give each arm its own name" % arm
|
|
74
|
+
)
|
|
71
75
|
return []
|
|
72
76
|
_arms[arm] = true
|
|
73
77
|
if gathering():
|
|
@@ -83,7 +87,10 @@ static func outcomes(arm: String, runs: int, play: Callable) -> Array:
|
|
|
83
87
|
if not wrong.is_empty():
|
|
84
88
|
push_error(
|
|
85
89
|
(
|
|
86
|
-
|
|
90
|
+
(
|
|
91
|
+
"GdhShare: run %d of %s answered %s, which cannot be handed from one "
|
|
92
|
+
+ "engine to another"
|
|
93
|
+
)
|
|
87
94
|
% [run, arm, wrong]
|
|
88
95
|
)
|
|
89
96
|
)
|
|
@@ -109,7 +116,9 @@ static func added(
|
|
|
109
116
|
var total: Dictionary[String, int] = {}
|
|
110
117
|
for outcome: Variant in counted:
|
|
111
118
|
if not outcome is Dictionary:
|
|
112
|
-
push_error(
|
|
119
|
+
push_error(
|
|
120
|
+
"GdhShare.added: an outcome is %s, not a dictionary" % type_string(typeof(outcome))
|
|
121
|
+
)
|
|
113
122
|
continue
|
|
114
123
|
var counts: Dictionary = outcome
|
|
115
124
|
for key: Variant in counts:
|
|
@@ -117,7 +126,10 @@ static func added(
|
|
|
117
126
|
var count: Variant = counts[key]
|
|
118
127
|
if not count is int:
|
|
119
128
|
push_error(
|
|
120
|
-
|
|
129
|
+
(
|
|
130
|
+
"GdhShare.added: %s is %s, not a whole number"
|
|
131
|
+
% [named, type_string(typeof(count))]
|
|
132
|
+
)
|
|
121
133
|
)
|
|
122
134
|
continue
|
|
123
135
|
var now: int = count
|
|
@@ -133,10 +145,12 @@ static func added(
|
|
|
133
145
|
## Leaves one arm's outcomes, then the line saying so, which `gdharness spread` checks the share
|
|
134
146
|
## against; the outcomes first, so a line is never there without them.
|
|
135
147
|
static func _leave(arm: String, runs: int, by_index: Dictionary[int, Variant]) -> void:
|
|
136
|
-
var left: Dictionary = {"arm": arm, "runs": runs, "outcomes": by_index}
|
|
137
|
-
if not _appended(
|
|
148
|
+
var left: Dictionary = { "arm": arm, "runs": runs, "outcomes": by_index }
|
|
149
|
+
if not _appended(
|
|
150
|
+
"worker-%d.bin" % _index, func(file: FileAccess) -> bool: return file.store_var(left)
|
|
151
|
+
):
|
|
138
152
|
return
|
|
139
|
-
var said: String = JSON.stringify({"arm": arm, "runs": runs, "played": by_index.size()})
|
|
153
|
+
var said: String = JSON.stringify({ "arm": arm, "runs": runs, "played": by_index.size() })
|
|
140
154
|
var _noted: bool = _appended(
|
|
141
155
|
"worker-%d.arms" % _index, func(file: FileAccess) -> bool: return file.store_line(said)
|
|
142
156
|
)
|
|
@@ -149,7 +163,9 @@ static func _appended(name: String, write: Callable) -> bool:
|
|
|
149
163
|
path, FileAccess.READ_WRITE if FileAccess.file_exists(path) else FileAccess.WRITE
|
|
150
164
|
)
|
|
151
165
|
if file == null:
|
|
152
|
-
push_error(
|
|
166
|
+
push_error(
|
|
167
|
+
"GdhShare: cannot write %s: %s" % [path, error_string(FileAccess.get_open_error())]
|
|
168
|
+
)
|
|
153
169
|
return false
|
|
154
170
|
file.seek_end()
|
|
155
171
|
var wrote: bool = write.call(file)
|
|
@@ -168,8 +184,8 @@ static func _gathered(arm: String, runs: int) -> Array:
|
|
|
168
184
|
push_error(
|
|
169
185
|
(
|
|
170
186
|
(
|
|
171
|
-
"GdhShare: no worker played the arm %s, so this run went another way than
|
|
172
|
-
+ "which arms a bench plays must depend on its command line alone"
|
|
187
|
+
"GdhShare: no worker played the arm %s, so this run went another way than "
|
|
188
|
+
+ "theirs; which arms a bench plays must depend on its command line alone"
|
|
173
189
|
)
|
|
174
190
|
% arm
|
|
175
191
|
)
|
|
@@ -190,7 +206,7 @@ static func _gathered(arm: String, runs: int) -> Array:
|
|
|
190
206
|
push_error("GdhShare: no worker played run %d of %s" % [run, arm])
|
|
191
207
|
return []
|
|
192
208
|
ordered.append(held[run])
|
|
193
|
-
var said: String = JSON.stringify({"arm": arm, "runs": runs, "played": runs})
|
|
209
|
+
var said: String = JSON.stringify({ "arm": arm, "runs": runs, "played": runs })
|
|
194
210
|
var _noted: bool = _appended(
|
|
195
211
|
"gathered.arms", func(file: FileAccess) -> bool: return file.store_line(said)
|
|
196
212
|
)
|
|
@@ -205,7 +221,9 @@ static func _read_workers() -> void:
|
|
|
205
221
|
var path: String = _directory.path_join("worker-%d.bin" % worker)
|
|
206
222
|
var file: FileAccess = FileAccess.open(path, FileAccess.READ)
|
|
207
223
|
if file == null:
|
|
208
|
-
_unread =
|
|
224
|
+
_unread = (
|
|
225
|
+
"GdhShare: cannot read %s: %s" % [path, error_string(FileAccess.get_open_error())]
|
|
226
|
+
)
|
|
209
227
|
return
|
|
210
228
|
while file.get_position() < file.get_length():
|
|
211
229
|
_unread = _took(file.get_var(), path)
|
|
@@ -260,7 +278,9 @@ static func _learn() -> void:
|
|
|
260
278
|
elif given.begins_with(INTO):
|
|
261
279
|
_directory = given.trim_prefix(INTO)
|
|
262
280
|
if (_index >= 0 or _gathering > 0) and _directory.is_empty():
|
|
263
|
-
push_error(
|
|
281
|
+
push_error(
|
|
282
|
+
"GdhShare: a spread engine was given no %s<directory> to share outcomes in" % INTO
|
|
283
|
+
)
|
|
264
284
|
|
|
265
285
|
|
|
266
286
|
## What in [param value] cannot be written by one engine and read by another, or empty.
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
extends Node
|
|
2
|
-
|
|
3
2
|
## gdUnit4 suites run inside a played scene, which the engine's script profiler sees and a script
|
|
4
3
|
## run with `-s` never does. `editor_run start` given `suites` plays this scene with
|
|
5
4
|
## [constant RUNNER] naming the runner `gdharness test` uses, where the server ships it, followed by
|
|
@@ -20,8 +19,9 @@ func _ready() -> void:
|
|
|
20
19
|
push_error(
|
|
21
20
|
(
|
|
22
21
|
(
|
|
23
|
-
"gdharness suites: no runner at '%s'. This scene is played by editor_run start
|
|
24
|
-
+ " which names the runner after %s; it does nothing played any
|
|
22
|
+
"gdharness suites: no runner at '%s'. This scene is played by editor_run start "
|
|
23
|
+
+ "with suites, which names the runner after %s; it does nothing played any "
|
|
24
|
+
+ "other way."
|
|
25
25
|
)
|
|
26
26
|
% [path, RUNNER]
|
|
27
27
|
)
|
|
@@ -10,7 +10,6 @@ extends RefCounted
|
|
|
10
10
|
|
|
11
11
|
## How long [method until] and [method still] wait by default, in milliseconds.
|
|
12
12
|
const MOST_MS: int = 30000
|
|
13
|
-
|
|
14
13
|
## Frames in a row with no tween running before [method still] counts the tree as still. A layout
|
|
15
14
|
## often starts its slide a frame after the change that asks for it, so the frame straight after
|
|
16
15
|
## had no tween yet and passed for still.
|
|
@@ -28,7 +27,9 @@ static func until(tree: SceneTree, done: Callable, most_ms: int = MOST_MS) -> bo
|
|
|
28
27
|
|
|
29
28
|
|
|
30
29
|
## Whether the tree ran no tween for [param quiet_frames] frames in a row within [param most_ms].
|
|
31
|
-
static func still(
|
|
30
|
+
static func still(
|
|
31
|
+
tree: SceneTree, quiet_frames: int = QUIET_FRAMES, most_ms: int = MOST_MS
|
|
32
|
+
) -> bool:
|
|
32
33
|
var began: int = Time.get_ticks_msec()
|
|
33
34
|
var quiet: int = 0
|
|
34
35
|
while quiet < quiet_frames:
|