gdharness 1.3.0 → 1.3.1
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 +14 -13
- package/build/godot/addons/gdharness_editor/animation_reads.gd +7 -19
- package/build/godot/addons/gdharness_editor/bridge_client.gd +8 -8
- package/build/godot/addons/gdharness_editor/file_reads.gd +60 -25
- package/build/godot/addons/gdharness_editor/headless_check.gd +7 -7
- package/build/godot/addons/gdharness_editor/plugin.gd +6 -6
- package/build/godot/addons/gdharness_editor/property_values.gd +120 -53
- package/build/godot/addons/gdharness_editor/reading.gd +28 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +28 -23
- package/build/godot/addons/gdharness_editor/serialisation.gd +45 -94
- package/build/godot/addons/gdharness_editor/tool_executor.gd +24 -24
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +52 -41
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +4 -4
- package/build/godot/addons/gdharness_editor/tools/play_tools.gd +2 -2
- package/build/godot/addons/gdharness_editor/tools/profile_tools.gd +14 -14
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +48 -41
- package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +17 -17
- package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +14 -12
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +7 -7
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +81 -77
- package/build/godot/addons/gdharness_editor/tools/settings_tools.gd +4 -4
- package/build/godot/addons/gdharness_editor/value_shapes.gd +111 -0
- package/build/godot/addons/gdharness_editor/value_shapes.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +28 -0
- package/build/godot/addons/gdharness_runtime/runtime_accessibility.gd +18 -18
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +29 -29
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +21 -18
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +86 -80
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +65 -64
- package/build/godot/addons/gdharness_runtime/runtime_items.gd +6 -6
- package/build/godot/addons/gdharness_runtime/runtime_menus.gd +8 -8
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +6 -6
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +10 -10
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +7 -7
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +41 -41
- package/build/godot/addons/gdharness_runtime/runtime_says.gd +3 -3
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +33 -33
- package/build/godot/addons/gdharness_runtime/runtime_typing.gd +20 -20
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +25 -24
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +56 -50
- package/build/godot/addons/gdharness_runtime/serialisation.gd +45 -94
- package/build/godot/addons/gdharness_runtime/value_shapes.gd +111 -0
- package/build/godot/addons/gdharness_runtime/value_shapes.gd.uid +1 -0
- package/build/godot/operations/animation_reads.gd +7 -19
- package/build/godot/operations/audio_buses.gd +10 -10
- package/build/godot/operations/class_cache.gd +16 -16
- package/build/godot/operations/classdb_queries.gd +12 -12
- package/build/godot/operations/dependencies.gd +33 -29
- package/build/godot/operations/editor_tools.gd +12 -12
- package/build/godot/operations/export_presets.gd +10 -8
- package/build/godot/operations/file_reads.gd +60 -25
- package/build/godot/operations/gdscript_analysis.gd +20 -20
- package/build/godot/operations/gdscript_authoring.gd +17 -15
- package/build/godot/operations/godot_operations.gd +64 -63
- package/build/godot/operations/import_pipeline.gd +20 -20
- package/build/godot/operations/input_actions.gd +17 -17
- package/build/godot/operations/logger.gd +1 -1
- package/build/godot/operations/plugins.gd +8 -8
- package/build/godot/operations/project_config.gd +24 -17
- package/build/godot/operations/project_diagnostics.gd +9 -9
- package/build/godot/operations/property_values.gd +120 -53
- package/build/godot/operations/reading.gd +28 -0
- package/build/godot/operations/rename_checks.gd +3 -3
- package/build/godot/operations/resource_files.gd +5 -5
- package/build/godot/operations/script_check.gd +7 -7
- package/build/godot/operations/serialisation.gd +45 -94
- package/build/godot/operations/value_shapes.gd +111 -0
- package/package.json +1 -1
|
@@ -3,19 +3,19 @@ extends Node
|
|
|
3
3
|
|
|
4
4
|
## Scenes and their nodes, edited in the open editor and read back from what it holds.
|
|
5
5
|
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
12
|
-
const
|
|
13
|
-
const
|
|
14
|
-
const
|
|
6
|
+
const GdhFileReads = preload("../file_reads.gd")
|
|
7
|
+
const GdhRead = preload("../reading.gd")
|
|
8
|
+
const GdhKin = preload("scene_kin.gd")
|
|
9
|
+
const GdhPaths = preload("scene_paths.gd")
|
|
10
|
+
const GdhPlacing = preload("scene_placing.gd")
|
|
11
|
+
const GdhPropertyValues = preload("../property_values.gd")
|
|
12
|
+
const GdhResourceTools = preload("resource_tools.gd")
|
|
13
|
+
const GdhSceneFile = preload("../scene_file.gd")
|
|
14
|
+
const GdhSerialisation = preload("../serialisation.gd")
|
|
15
15
|
|
|
16
16
|
var _editor_plugin: EditorPlugin = null
|
|
17
|
-
var _values:
|
|
18
|
-
var _properties:
|
|
17
|
+
var _values: GdhSerialisation = GdhSerialisation.new()
|
|
18
|
+
var _properties: GdhPropertyValues = GdhPropertyValues.new()
|
|
19
19
|
|
|
20
20
|
|
|
21
21
|
func set_editor_plugin(plugin: EditorPlugin) -> void:
|
|
@@ -47,7 +47,7 @@ func _to_scene_res_path(project_path: String, scene_path: String) -> String:
|
|
|
47
47
|
|
|
48
48
|
## [param scene_path] opened for a tool, as [scene, refusal]: the refusal is {} when it opened.
|
|
49
49
|
func _open(scene_path: String, for_writing: bool) -> Array:
|
|
50
|
-
var scene:
|
|
50
|
+
var scene: GdhSceneFile = GdhSceneFile.new()
|
|
51
51
|
return [scene, scene.open(scene_path, for_writing)]
|
|
52
52
|
|
|
53
53
|
|
|
@@ -76,7 +76,7 @@ func _missing_on(node: Node, missing: String) -> String:
|
|
|
76
76
|
|
|
77
77
|
## Whether each of [param names] reads the same off [param saved] as off [param node], answering
|
|
78
78
|
## the first that does not, or "".
|
|
79
|
-
func _kept(scene:
|
|
79
|
+
func _kept(scene: GdhSceneFile, saved: Node, node: Node, names: Array) -> String:
|
|
80
80
|
var at: String = scene.path_of(node)
|
|
81
81
|
var copy: Node = saved.get_node_or_null(at)
|
|
82
82
|
if copy == null:
|
|
@@ -106,7 +106,7 @@ func _properties_of(node: Node, include_defaults: bool, set_here: Dictionary) ->
|
|
|
106
106
|
scripted[str(declared.get("name", ""))] = true
|
|
107
107
|
var props: Dictionary = {}
|
|
108
108
|
for p: Dictionary in node.get_property_list():
|
|
109
|
-
if not (
|
|
109
|
+
if not (GdhRead.as_int(p.get("usage", 0)) & PROPERTY_USAGE_STORAGE):
|
|
110
110
|
continue
|
|
111
111
|
var property: String = str(p.get("name", ""))
|
|
112
112
|
if property.is_empty():
|
|
@@ -118,7 +118,7 @@ func _properties_of(node: Node, include_defaults: bool, set_here: Dictionary) ->
|
|
|
118
118
|
if scripted.has(property)
|
|
119
119
|
else ClassDB.class_get_property_default_value(node.get_class(), property)
|
|
120
120
|
)
|
|
121
|
-
if
|
|
121
|
+
if GdhPropertyValues.same(value, default):
|
|
122
122
|
continue
|
|
123
123
|
props[property] = _readable(node, value)
|
|
124
124
|
return props
|
|
@@ -141,7 +141,7 @@ func _parse_properties_arg(raw_properties: Variant) -> Dictionary:
|
|
|
141
141
|
var text: String = str(raw_properties)
|
|
142
142
|
if text.strip_edges().is_empty():
|
|
143
143
|
return {}
|
|
144
|
-
var parsed: Variant =
|
|
144
|
+
var parsed: Variant = GdhRead.json_or_null(text)
|
|
145
145
|
if typeof(parsed) == TYPE_DICTIONARY:
|
|
146
146
|
return parsed
|
|
147
147
|
return {}
|
|
@@ -157,7 +157,7 @@ func _collect_nodes_recursive(node: Node, path: String, out_nodes: Array) -> voi
|
|
|
157
157
|
func _inherited(args: Dictionary) -> Dictionary:
|
|
158
158
|
var project_path: String = str(args.get("projectPath", ""))
|
|
159
159
|
var script_path: String = str(args.get("script", ""))
|
|
160
|
-
return
|
|
160
|
+
return GdhKin.inherited(
|
|
161
161
|
args,
|
|
162
162
|
_new_scene_path(args),
|
|
163
163
|
_to_scene_res_path(project_path, str(args.get("inherits", ""))),
|
|
@@ -191,7 +191,7 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
191
191
|
if scene_path.is_empty():
|
|
192
192
|
return {"ok": false, "error": "Missing scenePath"}
|
|
193
193
|
var asked_type: String = root_node_type
|
|
194
|
-
var made: Dictionary =
|
|
194
|
+
var made: Dictionary = GdhSceneFile.made_as(
|
|
195
195
|
root_node_type,
|
|
196
196
|
"" if script_path.is_empty() else _to_scene_res_path(project_path, script_path),
|
|
197
197
|
"Node",
|
|
@@ -209,15 +209,15 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
209
209
|
}
|
|
210
210
|
if not ClassDB.can_instantiate(root_node_type):
|
|
211
211
|
return {"ok": false, "error": "%s cannot be instantiated." % root_node_type}
|
|
212
|
-
var unfit: String =
|
|
212
|
+
var unfit: String = GdhSceneFile.unfit_name(root_node_name)
|
|
213
213
|
if not unfit.is_empty():
|
|
214
214
|
return {"ok": false, "error": unfit}
|
|
215
215
|
if FileAccess.file_exists(scene_path):
|
|
216
|
-
return
|
|
216
|
+
return GdhKin.taken(scene_path)
|
|
217
217
|
|
|
218
218
|
var root: Node = ClassDB.instantiate(root_node_type)
|
|
219
219
|
root.name = root_node_name
|
|
220
|
-
var scene:
|
|
220
|
+
var scene: GdhSceneFile = GdhSceneFile.new()
|
|
221
221
|
scene.root = root
|
|
222
222
|
scene.path = scene_path
|
|
223
223
|
scene.asked(root, ["script"])
|
|
@@ -228,7 +228,7 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
228
228
|
if script == null:
|
|
229
229
|
root.free()
|
|
230
230
|
return {"ok": false, "error": "No script loads from " + full_script_path}
|
|
231
|
-
var uncompiled: String =
|
|
231
|
+
var uncompiled: String = GdhSceneFile.uncompiled(script, full_script_path)
|
|
232
232
|
if not uncompiled.is_empty():
|
|
233
233
|
root.free()
|
|
234
234
|
return {"ok": false, "error": uncompiled}
|
|
@@ -245,7 +245,7 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
245
245
|
# name is no property the write compares.
|
|
246
246
|
root.name = root_node_name
|
|
247
247
|
|
|
248
|
-
var folder: String =
|
|
248
|
+
var folder: String = GdhSceneFile.make_folder_for(scene_path)
|
|
249
249
|
if not folder.is_empty():
|
|
250
250
|
root.free()
|
|
251
251
|
return {"ok": false, "error": folder}
|
|
@@ -259,14 +259,14 @@ func _create_of_a_class(args: Dictionary) -> Dictionary:
|
|
|
259
259
|
func list_scene_nodes(args: Dictionary) -> Dictionary:
|
|
260
260
|
var project_path: String = str(args.get("projectPath", ""))
|
|
261
261
|
var scene_path: String = _to_scene_res_path(project_path, str(args.get("scenePath", "")))
|
|
262
|
-
var depth: int =
|
|
263
|
-
var include_properties: bool =
|
|
262
|
+
var depth: int = GdhRead.as_int(args.get("depth", -1), -1)
|
|
263
|
+
var include_properties: bool = GdhRead.as_bool(args.get("includeProperties", false))
|
|
264
264
|
|
|
265
265
|
# Read as a headless engine reads it with no editor connected, so the answer does not depend on
|
|
266
266
|
# whether one is.
|
|
267
|
-
var read: Dictionary =
|
|
267
|
+
var read: Dictionary = GdhFileReads.scene_tree(scene_path, depth, include_properties)
|
|
268
268
|
if read.get("ok", false):
|
|
269
|
-
read["unsavedInEditor"] =
|
|
269
|
+
read["unsavedInEditor"] = GdhSceneFile.unsaved(scene_path)
|
|
270
270
|
return read
|
|
271
271
|
|
|
272
272
|
|
|
@@ -284,20 +284,20 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
284
284
|
var asked_type: String = node_type
|
|
285
285
|
var class_script: Script = null
|
|
286
286
|
if not node_type.is_empty() and not ClassDB.class_exists(node_type):
|
|
287
|
-
var declared: Dictionary =
|
|
287
|
+
var declared: Dictionary = GdhSceneFile.script_class(node_type, "Node")
|
|
288
288
|
if declared.has("error"):
|
|
289
289
|
return {"ok": false, "error": declared["error"]}
|
|
290
290
|
var held: Variant = declared.get("script")
|
|
291
291
|
if held is Script:
|
|
292
292
|
class_script = held
|
|
293
293
|
node_type = str(declared["base"])
|
|
294
|
-
var making: Array =
|
|
294
|
+
var making: Array = GdhPlacing.what_to_add(node_type, instance_path, scene_path)
|
|
295
295
|
var packed: PackedScene = making[0]
|
|
296
296
|
if not str(making[1]).is_empty():
|
|
297
297
|
return {"ok": false, "error": making[1]}
|
|
298
298
|
|
|
299
299
|
var opened: Array = _open(scene_path, true)
|
|
300
|
-
var scene:
|
|
300
|
+
var scene: GdhSceneFile = opened[0]
|
|
301
301
|
var refused: Dictionary = opened[1]
|
|
302
302
|
if not refused.is_empty():
|
|
303
303
|
return refused
|
|
@@ -313,19 +313,19 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
313
313
|
if not unusable.is_empty():
|
|
314
314
|
return scene.refuse(unusable)
|
|
315
315
|
var place: Variant = args.get("index")
|
|
316
|
-
var unplaceable: String =
|
|
316
|
+
var unplaceable: String = GdhPlacing.unplaceable(parent, null, place)
|
|
317
317
|
if not unplaceable.is_empty():
|
|
318
318
|
return scene.refuse(unplaceable)
|
|
319
319
|
|
|
320
320
|
# In the tree before its properties, so a property holding a node can find it by path.
|
|
321
|
-
var new_node: Node =
|
|
321
|
+
var new_node: Node = GdhPlacing.made(packed, node_type)
|
|
322
322
|
if class_script != null:
|
|
323
323
|
scene.asked(new_node, ["script"])
|
|
324
324
|
new_node.set_script(class_script)
|
|
325
325
|
new_node.name = node_name
|
|
326
326
|
parent.add_child(new_node)
|
|
327
|
-
|
|
328
|
-
|
|
327
|
+
GdhPlacing.place(new_node, place)
|
|
328
|
+
GdhPlacing.own(new_node, root)
|
|
329
329
|
var unsettled: String = scene.settle()
|
|
330
330
|
if not unsettled.is_empty():
|
|
331
331
|
return scene.refuse("Nothing was written: " + unsettled)
|
|
@@ -343,6 +343,7 @@ func add_node(args: Dictionary) -> Dictionary:
|
|
|
343
343
|
"nodePath": new_path,
|
|
344
344
|
"index": new_node.get_index(),
|
|
345
345
|
}
|
|
346
|
+
_properties.note_also_changed(answer, new_node)
|
|
346
347
|
var written: Dictionary = scene.write(
|
|
347
348
|
func(saved: Node) -> String:
|
|
348
349
|
return _kept(
|
|
@@ -373,7 +374,7 @@ func delete_node(args: Dictionary) -> Dictionary:
|
|
|
373
374
|
}
|
|
374
375
|
|
|
375
376
|
var opened: Array = _open(scene_path, true)
|
|
376
|
-
var scene:
|
|
377
|
+
var scene: GdhSceneFile = opened[0]
|
|
377
378
|
var refused: Dictionary = opened[1]
|
|
378
379
|
if not refused.is_empty():
|
|
379
380
|
return refused
|
|
@@ -388,7 +389,7 @@ func delete_node(args: Dictionary) -> Dictionary:
|
|
|
388
389
|
var at: String = scene.path_of(node)
|
|
389
390
|
node.get_parent().remove_child(node)
|
|
390
391
|
node.free()
|
|
391
|
-
var carried:
|
|
392
|
+
var carried: GdhPaths = GdhPaths.new()
|
|
392
393
|
var _rewritten: int = carried.carry(scene, {at: ""})
|
|
393
394
|
|
|
394
395
|
var written: Dictionary = scene.write(
|
|
@@ -401,7 +402,7 @@ func delete_node(args: Dictionary) -> Dictionary:
|
|
|
401
402
|
return written
|
|
402
403
|
|
|
403
404
|
var answer: Dictionary = {"ok": true, "deletedNodePath": at}
|
|
404
|
-
answer.merge(
|
|
405
|
+
answer.merge(GdhKin.follow(scene_path, at, "", carried))
|
|
405
406
|
return answer
|
|
406
407
|
|
|
407
408
|
|
|
@@ -416,7 +417,7 @@ func duplicate_node(args: Dictionary) -> Dictionary:
|
|
|
416
417
|
return {"ok": false, "error": "Missing nodePath or newName"}
|
|
417
418
|
|
|
418
419
|
var opened: Array = _open(scene_path, true)
|
|
419
|
-
var scene:
|
|
420
|
+
var scene: GdhSceneFile = opened[0]
|
|
420
421
|
var refused: Dictionary = opened[1]
|
|
421
422
|
if not refused.is_empty():
|
|
422
423
|
return refused
|
|
@@ -444,14 +445,14 @@ func duplicate_node(args: Dictionary) -> Dictionary:
|
|
|
444
445
|
return scene.refuse(unusable)
|
|
445
446
|
|
|
446
447
|
var copies: Dictionary = {}
|
|
447
|
-
var duplicated_node: Node =
|
|
448
|
+
var duplicated_node: Node = GdhPlacing.copied(source, root, copies)
|
|
448
449
|
if not duplicated_node:
|
|
449
450
|
return scene.refuse("Failed to duplicate node: " + node_path)
|
|
450
451
|
|
|
451
452
|
duplicated_node.name = new_name
|
|
452
453
|
target_parent.add_child(duplicated_node)
|
|
453
|
-
|
|
454
|
-
|
|
454
|
+
GdhPlacing.own(duplicated_node, root)
|
|
455
|
+
GdhPlacing.reconnect(copies, source)
|
|
455
456
|
scene.copied(copies)
|
|
456
457
|
|
|
457
458
|
var new_path: String = scene.path_of(duplicated_node)
|
|
@@ -487,7 +488,7 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
487
488
|
return {"ok": false, "error": "Missing newParentPath"}
|
|
488
489
|
|
|
489
490
|
var opened: Array = _open(scene_path, true)
|
|
490
|
-
var scene:
|
|
491
|
+
var scene: GdhSceneFile = opened[0]
|
|
491
492
|
var refused: Dictionary = opened[1]
|
|
492
493
|
if not refused.is_empty():
|
|
493
494
|
return refused
|
|
@@ -515,7 +516,7 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
515
516
|
if not unusable.is_empty():
|
|
516
517
|
return scene.refuse(unusable)
|
|
517
518
|
var place: Variant = args.get("index")
|
|
518
|
-
var unplaceable: String =
|
|
519
|
+
var unplaceable: String = GdhPlacing.unplaceable(new_parent, node, place)
|
|
519
520
|
if not unplaceable.is_empty():
|
|
520
521
|
return scene.refuse(unplaceable)
|
|
521
522
|
|
|
@@ -525,12 +526,12 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
525
526
|
node.owner = null
|
|
526
527
|
node.get_parent().remove_child(node)
|
|
527
528
|
new_parent.add_child(node)
|
|
528
|
-
|
|
529
|
-
|
|
529
|
+
GdhPlacing.place(node, place)
|
|
530
|
+
GdhPlacing.own(node, root)
|
|
530
531
|
|
|
531
532
|
var new_path: String = scene.path_of(node)
|
|
532
533
|
var index: int = node.get_index()
|
|
533
|
-
var carried:
|
|
534
|
+
var carried: GdhPaths = GdhPaths.new()
|
|
534
535
|
var _rewritten: int = carried.carry(scene, {old_path: new_path} if new_path != old_path else {})
|
|
535
536
|
var written: Dictionary = scene.write(
|
|
536
537
|
func(saved: Node) -> String:
|
|
@@ -555,7 +556,7 @@ func reparent_node(args: Dictionary) -> Dictionary:
|
|
|
555
556
|
"index": index,
|
|
556
557
|
}
|
|
557
558
|
if new_path != old_path:
|
|
558
|
-
answer.merge(
|
|
559
|
+
answer.merge(GdhKin.follow(scene_path, old_path, new_path, carried))
|
|
559
560
|
return answer
|
|
560
561
|
|
|
561
562
|
|
|
@@ -569,7 +570,7 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
569
570
|
return {"ok": false, "error": "set takes properties, editableChildren, or both."}
|
|
570
571
|
|
|
571
572
|
var opened: Array = _open(scene_path, true)
|
|
572
|
-
var scene:
|
|
573
|
+
var scene: GdhSceneFile = opened[0]
|
|
573
574
|
var refused: Dictionary = opened[1]
|
|
574
575
|
if not refused.is_empty():
|
|
575
576
|
return refused
|
|
@@ -590,10 +591,10 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
590
591
|
|
|
591
592
|
var dropped: Dictionary = {}
|
|
592
593
|
if editable != null:
|
|
593
|
-
var not_an_instance: String =
|
|
594
|
+
var not_an_instance: String = GdhKin.set_editable(scene, node, GdhRead.as_bool(editable))
|
|
594
595
|
if not not_an_instance.is_empty():
|
|
595
596
|
return scene.refuse(not_an_instance)
|
|
596
|
-
if not
|
|
597
|
+
if not GdhRead.as_bool(editable):
|
|
597
598
|
dropped = scene.held_under(scene.path_of(node))
|
|
598
599
|
|
|
599
600
|
var old_path: String = scene.path_of(node)
|
|
@@ -606,28 +607,31 @@ func set_node_properties(args: Dictionary) -> Dictionary:
|
|
|
606
607
|
for key: Variant in properties:
|
|
607
608
|
held[str(key)] = _readable(node, _properties.read_at(node, str(key)))
|
|
608
609
|
var new_path: String = scene.path_of(node)
|
|
609
|
-
var
|
|
610
|
+
var answer: Dictionary = {"ok": true, "nodePath": new_path, "properties": held}
|
|
611
|
+
_properties.note_also_changed(answer, node)
|
|
612
|
+
var carried: GdhPaths = GdhPaths.new()
|
|
610
613
|
var _rewritten: int = carried.carry(scene, {old_path: new_path} if new_path != old_path else {})
|
|
611
614
|
var written: Dictionary = scene.write(
|
|
612
615
|
func(saved: Node) -> String:
|
|
613
616
|
var unkept: String = _kept(scene, saved, node, properties.keys())
|
|
614
617
|
if unkept.is_empty() and editable != null:
|
|
615
|
-
if saved.is_editable_instance(saved.get_node(new_path)) !=
|
|
616
|
-
return
|
|
618
|
+
if saved.is_editable_instance(saved.get_node(new_path)) != GdhRead.as_bool(editable):
|
|
619
|
+
return (
|
|
620
|
+
"%s would load with editableChildren %s." % [new_path, not GdhRead.as_bool(editable)]
|
|
621
|
+
)
|
|
617
622
|
return unkept
|
|
618
623
|
)
|
|
619
624
|
if not written.is_empty():
|
|
620
625
|
carried.put_back(scene_path)
|
|
621
626
|
return written
|
|
622
627
|
|
|
623
|
-
var answer: Dictionary = {"ok": true, "nodePath": new_path, "properties": held}
|
|
624
628
|
if editable != null:
|
|
625
|
-
answer["editableChildren"] =
|
|
629
|
+
answer["editableChildren"] = GdhRead.as_bool(editable)
|
|
626
630
|
if not dropped.is_empty():
|
|
627
631
|
answer["droppedValues"] = dropped["values"]
|
|
628
632
|
answer["droppedNodes"] = dropped["nodes"]
|
|
629
633
|
if new_path != old_path:
|
|
630
|
-
answer.merge(
|
|
634
|
+
answer.merge(GdhKin.follow(scene_path, old_path, new_path, carried))
|
|
631
635
|
return answer
|
|
632
636
|
|
|
633
637
|
|
|
@@ -635,10 +639,10 @@ func get_node_properties(args: Dictionary) -> Dictionary:
|
|
|
635
639
|
var project_path: String = str(args.get("projectPath", ""))
|
|
636
640
|
var scene_path: String = _to_scene_res_path(project_path, str(args.get("scenePath", "")))
|
|
637
641
|
var node_path: String = str(args.get("nodePath", "."))
|
|
638
|
-
var include_defaults: bool =
|
|
642
|
+
var include_defaults: bool = GdhRead.as_bool(args.get("includeDefaults", false))
|
|
639
643
|
|
|
640
644
|
var opened: Array = _open(scene_path, false)
|
|
641
|
-
var scene:
|
|
645
|
+
var scene: GdhSceneFile = opened[0]
|
|
642
646
|
var refused: Dictionary = opened[1]
|
|
643
647
|
if not refused.is_empty():
|
|
644
648
|
return refused
|
|
@@ -652,7 +656,7 @@ func get_node_properties(args: Dictionary) -> Dictionary:
|
|
|
652
656
|
# variable read as changed; and a value this scene sets back to the default was hidden, so the
|
|
653
657
|
# names this scene's file sets are always answered.
|
|
654
658
|
var props: Dictionary = _properties_of(node, include_defaults, scene.set_here(node))
|
|
655
|
-
var cells: Dictionary =
|
|
659
|
+
var cells: Dictionary = GdhResourceTools.cells_of(node)
|
|
656
660
|
var answer: Dictionary = {"ok": true, "nodePath": node_path, "properties": props}
|
|
657
661
|
if node != scene.root and node.owner != null and not node.scene_file_path.is_empty():
|
|
658
662
|
answer["instanceOf"] = node.scene_file_path
|
|
@@ -660,7 +664,7 @@ func get_node_properties(args: Dictionary) -> Dictionary:
|
|
|
660
664
|
scene.close()
|
|
661
665
|
# Only an editor holds anything unsaved; a headless engine answering false would say it looked.
|
|
662
666
|
if Engine.is_editor_hint():
|
|
663
|
-
answer["unsavedInEditor"] =
|
|
667
|
+
answer["unsavedInEditor"] = GdhSceneFile.unsaved(scene_path)
|
|
664
668
|
answer.merge(cells)
|
|
665
669
|
# An empty answer read as a node with nothing on it, rather than one with nothing changed.
|
|
666
670
|
if props.is_empty() and not include_defaults:
|
|
@@ -680,7 +684,7 @@ func save_scene(args: Dictionary) -> Dictionary:
|
|
|
680
684
|
target_path = _to_scene_res_path(project_path, new_path_raw)
|
|
681
685
|
|
|
682
686
|
if target_path != scene_path:
|
|
683
|
-
var unsaved: String =
|
|
687
|
+
var unsaved: String = GdhSceneFile.unsaved_in_editor(target_path)
|
|
684
688
|
if not unsaved.is_empty():
|
|
685
689
|
return {"ok": false, "error": unsaved}
|
|
686
690
|
|
|
@@ -703,9 +707,9 @@ func save_scene(args: Dictionary) -> Dictionary:
|
|
|
703
707
|
var packing: Error = packed.pack(open_root)
|
|
704
708
|
if packing != OK:
|
|
705
709
|
return {"ok": false, "error": "Failed to pack %s: %s" % [scene_path, error_string(packing)]}
|
|
706
|
-
written =
|
|
710
|
+
written = GdhSceneFile.save_packed(packed, target_path)
|
|
707
711
|
else:
|
|
708
|
-
var scene:
|
|
712
|
+
var scene: GdhSceneFile = GdhSceneFile.new()
|
|
709
713
|
var refused: Dictionary = scene.open(scene_path, true)
|
|
710
714
|
if not refused.is_empty():
|
|
711
715
|
return refused
|
|
@@ -743,7 +747,7 @@ func _save_open_scene(scene_path: String) -> Dictionary:
|
|
|
743
747
|
"scenePath": scene_path,
|
|
744
748
|
"savedPath": scene_path,
|
|
745
749
|
"from": "editor",
|
|
746
|
-
"unsavedInEditor":
|
|
750
|
+
"unsavedInEditor": GdhSceneFile.unsaved(scene_path)
|
|
747
751
|
}
|
|
748
752
|
|
|
749
753
|
|
|
@@ -756,7 +760,7 @@ func connect_signal(args: Dictionary) -> Dictionary:
|
|
|
756
760
|
var method_name: String = str(args.get("methodName", ""))
|
|
757
761
|
# A connection without CONNECT_PERSIST is a runtime one, and PackedScene.pack drops those on
|
|
758
762
|
# the way out: without this the scene saves unchanged and this answers success over nothing.
|
|
759
|
-
var flags: int =
|
|
763
|
+
var flags: int = GdhRead.as_int(args.get("flags", 0)) | Object.CONNECT_PERSIST
|
|
760
764
|
|
|
761
765
|
if (
|
|
762
766
|
source_node_path.is_empty()
|
|
@@ -767,7 +771,7 @@ func connect_signal(args: Dictionary) -> Dictionary:
|
|
|
767
771
|
return {"ok": false, "error": "Missing required signal connection arguments"}
|
|
768
772
|
|
|
769
773
|
var opened: Array = _open(scene_path, true)
|
|
770
|
-
var scene:
|
|
774
|
+
var scene: GdhSceneFile = opened[0]
|
|
771
775
|
var refused: Dictionary = opened[1]
|
|
772
776
|
if not refused.is_empty():
|
|
773
777
|
return refused
|
|
@@ -785,7 +789,7 @@ func connect_signal(args: Dictionary) -> Dictionary:
|
|
|
785
789
|
var flags_before: int = -1
|
|
786
790
|
for connection: Dictionary in source.get_signal_connection_list(signal_name):
|
|
787
791
|
if connection.get("callable") == callable:
|
|
788
|
-
flags_before =
|
|
792
|
+
flags_before = GdhRead.as_int(connection.get("flags", 0))
|
|
789
793
|
if source.is_connected(signal_name, callable):
|
|
790
794
|
source.disconnect(signal_name, callable)
|
|
791
795
|
var connect_result: Error = source.connect(signal_name, callable, flags)
|
|
@@ -812,7 +816,7 @@ func connect_signal(args: Dictionary) -> Dictionary:
|
|
|
812
816
|
"targetNodePath": to,
|
|
813
817
|
"methodName": method_name,
|
|
814
818
|
"flags": saved_flags[0],
|
|
815
|
-
"flagNames":
|
|
819
|
+
"flagNames": GdhSceneFile.flag_names(GdhRead.as_int(saved_flags[0])),
|
|
816
820
|
# Connected either way, as the editor's own dialog allows, since the method may be written
|
|
817
821
|
# next; a caller told nothing learnt of a misspelt one only when the signal fired.
|
|
818
822
|
"methodFound": method_found
|
|
@@ -822,14 +826,14 @@ func connect_signal(args: Dictionary) -> Dictionary:
|
|
|
822
826
|
# looked like a lost write.
|
|
823
827
|
if flags_before >= 0:
|
|
824
828
|
answer["alreadyConnected"] = true
|
|
825
|
-
if flags_before !=
|
|
826
|
-
answer["flagsBefore"] =
|
|
829
|
+
if flags_before != GdhRead.as_int(saved_flags[0]):
|
|
830
|
+
answer["flagsBefore"] = GdhSceneFile.flag_names(flags_before)
|
|
827
831
|
return answer
|
|
828
832
|
|
|
829
833
|
|
|
830
834
|
## The two ends of a connection in [param scene], as [source, target], or [null, why not].
|
|
831
835
|
func _connection_ends(
|
|
832
|
-
scene:
|
|
836
|
+
scene: GdhSceneFile, source_node_path: String, signal_name: String, target_node_path: String
|
|
833
837
|
) -> Array:
|
|
834
838
|
var source: Node = _find_node(scene.root, source_node_path)
|
|
835
839
|
var target: Node = _find_node(scene.root, target_node_path)
|
|
@@ -856,7 +860,7 @@ func _connection_flags(root: Node, from: String, signal_name: String, to: String
|
|
|
856
860
|
if source == null or target == null or not source.has_signal(signal_name):
|
|
857
861
|
return -1
|
|
858
862
|
for conn: Dictionary in source.get_signal_connection_list(signal_name):
|
|
859
|
-
var flags: int =
|
|
863
|
+
var flags: int = GdhRead.as_int(conn.get("flags", 0))
|
|
860
864
|
var callable: Callable = conn.get("callable", Callable())
|
|
861
865
|
if (
|
|
862
866
|
flags & Object.CONNECT_PERSIST
|
|
@@ -884,7 +888,7 @@ func disconnect_signal(args: Dictionary) -> Dictionary:
|
|
|
884
888
|
return {"ok": false, "error": "Missing required signal disconnection arguments"}
|
|
885
889
|
|
|
886
890
|
var opened: Array = _open(scene_path, true)
|
|
887
|
-
var scene:
|
|
891
|
+
var scene: GdhSceneFile = opened[0]
|
|
888
892
|
var refused: Dictionary = opened[1]
|
|
889
893
|
if not refused.is_empty():
|
|
890
894
|
return refused
|
|
@@ -929,7 +933,7 @@ func list_connections(args: Dictionary) -> Dictionary:
|
|
|
929
933
|
var filter_path: String = str(args.get("nodePath", ""))
|
|
930
934
|
|
|
931
935
|
var opened: Array = _open(scene_path, false)
|
|
932
|
-
var scene:
|
|
936
|
+
var scene: GdhSceneFile = opened[0]
|
|
933
937
|
var refused: Dictionary = opened[1]
|
|
934
938
|
if not refused.is_empty():
|
|
935
939
|
return refused
|
|
@@ -955,7 +959,7 @@ func list_connections(args: Dictionary) -> Dictionary:
|
|
|
955
959
|
if signal_name.is_empty():
|
|
956
960
|
continue
|
|
957
961
|
for conn: Dictionary in node.get_signal_connection_list(signal_name):
|
|
958
|
-
var flags: int =
|
|
962
|
+
var flags: int = GdhRead.as_int(conn.get("flags", 0))
|
|
959
963
|
if not flags & Object.CONNECT_PERSIST:
|
|
960
964
|
continue
|
|
961
965
|
var callable: Callable = conn.get("callable", Callable())
|
|
@@ -976,7 +980,7 @@ func list_connections(args: Dictionary) -> Dictionary:
|
|
|
976
980
|
"targetNodePath": target_path,
|
|
977
981
|
"methodName": str(callable.get_method()),
|
|
978
982
|
"flags": flags,
|
|
979
|
-
"flagNames":
|
|
983
|
+
"flagNames": GdhSceneFile.flag_names(flags),
|
|
980
984
|
# False for a connection an instanced scene makes inside itself, which only that
|
|
981
985
|
# scene can change.
|
|
982
986
|
"editable": editable
|
|
@@ -987,5 +991,5 @@ func list_connections(args: Dictionary) -> Dictionary:
|
|
|
987
991
|
scene.close()
|
|
988
992
|
var listed: Dictionary = {"ok": true, "connections": connections}
|
|
989
993
|
if Engine.is_editor_hint():
|
|
990
|
-
listed["unsavedInEditor"] =
|
|
994
|
+
listed["unsavedInEditor"] = GdhSceneFile.unsaved(scene_path)
|
|
991
995
|
return listed
|
|
@@ -14,10 +14,10 @@ extends Node
|
|
|
14
14
|
## question asked of two places: a value spelled differently here would read as the editor and the
|
|
15
15
|
## file disagreeing about something they agree about.
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
const
|
|
17
|
+
const GdhRead = preload("../reading.gd")
|
|
18
|
+
const GdhSerialisation = preload("../serialisation.gd")
|
|
19
19
|
|
|
20
|
-
var _values:
|
|
20
|
+
var _values: GdhSerialisation = GdhSerialisation.new()
|
|
21
21
|
|
|
22
22
|
|
|
23
23
|
func get_project_setting(args: Dictionary) -> Dictionary:
|
|
@@ -47,7 +47,7 @@ func _settings_under(prefix: String) -> Dictionary:
|
|
|
47
47
|
var setting: String = str(property.get("name", ""))
|
|
48
48
|
if not setting.begins_with(prefix):
|
|
49
49
|
continue
|
|
50
|
-
var kind: int =
|
|
50
|
+
var kind: int = GdhRead.as_int(property.get("type", TYPE_NIL), TYPE_NIL)
|
|
51
51
|
var described: Dictionary = {
|
|
52
52
|
"setting": setting,
|
|
53
53
|
"type": type_string(kind),
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
extends RefCounted
|
|
2
|
+
|
|
3
|
+
## How each engine type built from fields is written: the fields it needs, the ones it reads when
|
|
4
|
+
## they are there, and what a refusal says about a value that is not one.
|
|
5
|
+
##
|
|
6
|
+
## This file is the one beside the operations, and `bun run sync:gd` copies it into each addon.
|
|
7
|
+
|
|
8
|
+
# What each tag has to carry before it is built. A tag naming a type whose keys are not there is a
|
|
9
|
+
# tag somebody wrote by mistake or wrote for a different shape, and building one out of defaults
|
|
10
|
+
# answers with a Transform2D of zeros where the caller meant something else entirely. Unbuilt, it
|
|
11
|
+
# comes back as it arrived, and whoever asked can read it against the type they were expecting.
|
|
12
|
+
const REQUIRED: Dictionary = {
|
|
13
|
+
"Vector2": [["x", "y"]],
|
|
14
|
+
"Vector2i": [["x", "y"]],
|
|
15
|
+
"Vector3": [["x", "y", "z"]],
|
|
16
|
+
"Vector3i": [["x", "y", "z"]],
|
|
17
|
+
"Vector4": [["x", "y", "z", "w"]],
|
|
18
|
+
"Vector4i": [["x", "y", "z", "w"]],
|
|
19
|
+
"Rect2": [["position", "size"], ["x", "y", "width", "height"]],
|
|
20
|
+
"Rect2i": [["position", "size"], ["x", "y", "width", "height"]],
|
|
21
|
+
"Plane": [["normal", "d"]],
|
|
22
|
+
"Quaternion": [["x", "y", "z", "w"]],
|
|
23
|
+
"AABB": [["position", "size"]],
|
|
24
|
+
"Basis": [["x", "y", "z"]],
|
|
25
|
+
"Transform2D": [["x", "y", "origin"]],
|
|
26
|
+
"Transform3D": [["basis", "origin"]],
|
|
27
|
+
"Projection": [["x", "y", "z", "w"]],
|
|
28
|
+
"Color": [["r", "g", "b"]],
|
|
29
|
+
"NodePath": [["path"]],
|
|
30
|
+
"PackedByteArray": [["base64"]],
|
|
31
|
+
"float": [["value"]],
|
|
32
|
+
"int": [["value"]],
|
|
33
|
+
"RID": [["id"]],
|
|
34
|
+
"Dictionary": [["entries"]],
|
|
35
|
+
}
|
|
36
|
+
## The fields a builder reads when they are there and does without when not.
|
|
37
|
+
const OPTIONAL: Dictionary = {
|
|
38
|
+
"Color": ["a"],
|
|
39
|
+
"PackedByteArray": ["size"],
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
## How a value of [param type] is written to be read as one, for a refusal to name, or empty for a
|
|
44
|
+
## type that is not built from fields. A refusal naming only the two types leaves a caller to find
|
|
45
|
+
## the shape in the description of another op.
|
|
46
|
+
static func written_as(type: int) -> String:
|
|
47
|
+
var tag: String = type_string(type)
|
|
48
|
+
var alternatives: Array = REQUIRED.get(tag, [])
|
|
49
|
+
if alternatives.is_empty() or tag in ["float", "int", "Dictionary"]:
|
|
50
|
+
return ""
|
|
51
|
+
var keys: Array = alternatives[0]
|
|
52
|
+
var optional: Array = OPTIONAL.get(tag, [])
|
|
53
|
+
var parts: Array[String] = []
|
|
54
|
+
for key: Variant in keys + optional:
|
|
55
|
+
parts.append('"%s": ...' % key)
|
|
56
|
+
var shape: String = "{%s}" % ", ".join(parts)
|
|
57
|
+
if type == TYPE_COLOR:
|
|
58
|
+
return shape + ' or as its html code, "#ff8000"'
|
|
59
|
+
return shape
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
## The keys of [param fields] that a [param tag] built from them would not read, one list for each
|
|
63
|
+
## spelling of its fields carried whole. A rectangle has two: its corners, and four numbers.
|
|
64
|
+
##
|
|
65
|
+
## A builder drops a key it does not know without a word, so {"x": 1, "y": 2, "z": 3} for a Vector2
|
|
66
|
+
## would be written as (1, 2) and answered as written: a value is built only when a list is empty.
|
|
67
|
+
static func spare(fields: Dictionary, tag: String) -> Array:
|
|
68
|
+
var optional: Array = OPTIONAL.get(tag, [])
|
|
69
|
+
var lists: Array = []
|
|
70
|
+
var alternatives: Array = REQUIRED.get(tag, [])
|
|
71
|
+
for alternative: Variant in alternatives:
|
|
72
|
+
var keys: Array = alternative
|
|
73
|
+
if keys.all(func(key: Variant) -> bool: return fields.has(key)):
|
|
74
|
+
var over: Callable = func(key: Variant) -> bool:
|
|
75
|
+
return str(key) != "_type" and not keys.has(key) and not optional.has(key)
|
|
76
|
+
lists.append(fields.keys().filter(over))
|
|
77
|
+
return lists
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## The sentences a refusal of [param value] where a [param type] goes ends with: which of its keys
|
|
81
|
+
## kept it from being read as one, and how one is written. Empty for a type not built from fields.
|
|
82
|
+
##
|
|
83
|
+
## The refusal names only the two types, and the key is what the caller has to take out.
|
|
84
|
+
static func remedy(value: Variant, type: int) -> String:
|
|
85
|
+
var shape: String = written_as(type)
|
|
86
|
+
if shape.is_empty():
|
|
87
|
+
return ""
|
|
88
|
+
return _unread(value, type) + " A %s is sent as %s." % [type_string(type), shape]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
static func _unread(value: Variant, type: int) -> String:
|
|
92
|
+
if not value is Dictionary:
|
|
93
|
+
return ""
|
|
94
|
+
var fields: Dictionary = value
|
|
95
|
+
var tag: String = type_string(type)
|
|
96
|
+
var lists: Array = spare(fields, tag)
|
|
97
|
+
var built: bool = lists.any(func(over: Array) -> bool: return over.is_empty())
|
|
98
|
+
if lists.is_empty() or built or str(fields.get("_type", tag)) != tag:
|
|
99
|
+
return ""
|
|
100
|
+
lists.sort_custom(func(a: Array, b: Array) -> bool: return a.size() < b.size())
|
|
101
|
+
var left: Array = lists[0]
|
|
102
|
+
var named: PackedStringArray = []
|
|
103
|
+
for key: Variant in left:
|
|
104
|
+
var _named: bool = named.append('"%s"' % key)
|
|
105
|
+
var fields_called: String = "field called" if named.size() == 1 else "fields called"
|
|
106
|
+
var note: String = (
|
|
107
|
+
" A %s has no %s %s, so the value is not read as one" % [tag, fields_called, " or ".join(named)]
|
|
108
|
+
)
|
|
109
|
+
if left.has("type") and not fields.has("_type"):
|
|
110
|
+
return note + ': a value names its type under "_type".'
|
|
111
|
+
return note + "."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://c3dfdk66o90zz
|