gdharness 1.3.54 → 1.3.56
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.
|
@@ -89,7 +89,8 @@ var _unmade: Array[Dictionary] = []
|
|
|
89
89
|
##
|
|
90
90
|
## A write is refused while the editor holds unsaved changes to the scene: the file is reloaded
|
|
91
91
|
## into the editor after a write, which discards them without a prompt, and the editor saving its
|
|
92
|
-
## copy later would discard the write instead.
|
|
92
|
+
## copy later would discard the write instead. One is refused too while the scene names a file
|
|
93
|
+
## that is gone ([method missing_said]).
|
|
93
94
|
func open(scene_path: String, for_writing: bool) -> Dictionary:
|
|
94
95
|
path = scene_path
|
|
95
96
|
if not FileAccess.file_exists(path):
|
|
@@ -98,6 +99,9 @@ func open(scene_path: String, for_writing: bool) -> Dictionary:
|
|
|
98
99
|
var held_back: String = unsaved_in_editor(path)
|
|
99
100
|
if not held_back.is_empty():
|
|
100
101
|
return { "ok": false, "error": held_back }
|
|
102
|
+
var missing: String = missing_said(path, FileAccess.get_file_as_string(path))
|
|
103
|
+
if not missing.is_empty():
|
|
104
|
+
return { "ok": false, "error": missing }
|
|
101
105
|
var packed: PackedScene = (
|
|
102
106
|
ResourceLoader.load(path, "PackedScene", ResourceLoader.CACHE_MODE_IGNORE) as PackedScene
|
|
103
107
|
)
|
|
@@ -125,6 +129,92 @@ func open(scene_path: String, for_writing: bool) -> Dictionary:
|
|
|
125
129
|
return {}
|
|
126
130
|
|
|
127
131
|
|
|
132
|
+
## Why a write to the scene at [param scene_path], whose text is [param text], is refused, or "":
|
|
133
|
+
## an [ext_resource] naming no file the project has, by its uid or by its path. The engine loads the
|
|
134
|
+
## scene without it, so a save drops the entry and every value set from it, and the answer could
|
|
135
|
+
## only call those values empty.
|
|
136
|
+
static func missing_said(scene_path: String, text: String) -> String:
|
|
137
|
+
var gone: Dictionary = {}
|
|
138
|
+
var entry: RegEx = RegEx.create_from_string("(?m)^\\[ext_resource\\b[^\\n]*\\]")
|
|
139
|
+
var field: RegEx = RegEx.create_from_string('\\b(path|uid|id)="([^"]*)"')
|
|
140
|
+
for found: RegExMatch in entry.search_all(text):
|
|
141
|
+
var fields: Dictionary = {}
|
|
142
|
+
for one: RegExMatch in field.search_all(found.get_string()):
|
|
143
|
+
fields[one.get_string(1)] = one.get_string(2)
|
|
144
|
+
var uid: String = str(fields.get("uid", ""))
|
|
145
|
+
var by_uid: String = ""
|
|
146
|
+
if uid.begins_with("uid://") and ResourceUID.has_id(ResourceUID.text_to_id(uid)):
|
|
147
|
+
by_uid = ResourceUID.get_id_path(ResourceUID.text_to_id(uid))
|
|
148
|
+
var named: String = str(fields.get("path", ""))
|
|
149
|
+
if FileAccess.file_exists(by_uid) or FileAccess.file_exists(named):
|
|
150
|
+
continue
|
|
151
|
+
gone[str(fields.get("id", ""))] = named if not named.is_empty() else uid
|
|
152
|
+
if gone.is_empty():
|
|
153
|
+
return ""
|
|
154
|
+
var uses: PackedStringArray = []
|
|
155
|
+
var on: String = ""
|
|
156
|
+
var lines: PackedStringArray = text.split("\n")
|
|
157
|
+
var header: RegEx = RegEx.create_from_string('^\\[node name="([^"]*)"')
|
|
158
|
+
var parented: RegEx = RegEx.create_from_string('\\bparent="([^"]*)"')
|
|
159
|
+
for index: int in lines.size():
|
|
160
|
+
var line: String = lines[index]
|
|
161
|
+
if line.begins_with("["):
|
|
162
|
+
var node: RegExMatch = header.search(line)
|
|
163
|
+
var under: RegExMatch = parented.search(line)
|
|
164
|
+
if node == null:
|
|
165
|
+
on = line.get_slice(" ", 0).trim_prefix("[")
|
|
166
|
+
elif under == null or under.get_string(1) == ".":
|
|
167
|
+
on = node.get_string(1)
|
|
168
|
+
else:
|
|
169
|
+
on = under.get_string(1).path_join(node.get_string(1))
|
|
170
|
+
continue
|
|
171
|
+
for id: String in gone:
|
|
172
|
+
if line.contains('ExtResource("%s")' % id):
|
|
173
|
+
var said: String = "line %d on %s, %s" % [index + 1, on, line.strip_edges()]
|
|
174
|
+
var _used: bool = uses.append(said)
|
|
175
|
+
break
|
|
176
|
+
var paths: PackedStringArray = []
|
|
177
|
+
for id: String in gone:
|
|
178
|
+
var _named: bool = paths.append(str(gone[id]))
|
|
179
|
+
var one_file: bool = paths.size() == 1
|
|
180
|
+
return (
|
|
181
|
+
(
|
|
182
|
+
"Nothing was written: %s names %s, which %s not there, so the engine loads the scene "
|
|
183
|
+
+ "without %s, and a save would drop %s%s. Put the %s back at %s, or change the "
|
|
184
|
+
+ "[ext_resource] %s in the scene to %s that exists."
|
|
185
|
+
)
|
|
186
|
+
% [
|
|
187
|
+
scene_path,
|
|
188
|
+
", ".join(paths),
|
|
189
|
+
"is" if one_file else "are",
|
|
190
|
+
"it" if one_file else "them",
|
|
191
|
+
"it" if one_file else "them",
|
|
192
|
+
"" if uses.is_empty() else " and what is set from %s: %s" % [
|
|
193
|
+
"it" if one_file else "them", "; ".join(uses),
|
|
194
|
+
],
|
|
195
|
+
"file" if one_file else "files",
|
|
196
|
+
"that path" if one_file else "those paths",
|
|
197
|
+
"line" if one_file else "lines",
|
|
198
|
+
"one" if one_file else "ones",
|
|
199
|
+
]
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
## The connections the file holds, as it was opened, by their paths from [member root].
|
|
204
|
+
func held_connections() -> Array[Dictionary]:
|
|
205
|
+
var held: Array[Dictionary] = []
|
|
206
|
+
for index: int in _state.get_connection_count():
|
|
207
|
+
held.append(
|
|
208
|
+
{
|
|
209
|
+
"sourceNodePath": str(_state.get_connection_source(index)),
|
|
210
|
+
"signalName": str(_state.get_connection_signal(index)),
|
|
211
|
+
"targetNodePath": str(_state.get_connection_target(index)),
|
|
212
|
+
"methodName": str(_state.get_connection_method(index)),
|
|
213
|
+
}
|
|
214
|
+
)
|
|
215
|
+
return held
|
|
216
|
+
|
|
217
|
+
|
|
128
218
|
## The connections in [param state] the engine did not make on [member root]: Object.connect
|
|
129
219
|
## refuses a callable that binds or unbinds arguments unless its target has the method, so the scene
|
|
130
220
|
## loads without them, in the editor as well, and every save drops them.
|
|
@@ -392,6 +392,7 @@ func delete_node(args: Dictionary) -> Dictionary:
|
|
|
392
392
|
for under: Node in node.find_children("*", "", true, false):
|
|
393
393
|
if under.owner == scene.root:
|
|
394
394
|
went.append(scene.path_of(under))
|
|
395
|
+
var cut: Array[Dictionary] = _connections_with(scene, at)
|
|
395
396
|
node.get_parent().remove_child(node)
|
|
396
397
|
node.free()
|
|
397
398
|
var carried: GdhPaths = GdhPaths.new()
|
|
@@ -409,10 +410,25 @@ func delete_node(args: Dictionary) -> Dictionary:
|
|
|
409
410
|
var answer: Dictionary = { "ok": true, "deletedNodePath": at }
|
|
410
411
|
if not went.is_empty():
|
|
411
412
|
answer["deletedWithIt"] = went
|
|
413
|
+
if not cut.is_empty():
|
|
414
|
+
answer["connectionsRemoved"] = cut
|
|
412
415
|
answer.merge(GdhKin.follow(scene_path, at, "", carried))
|
|
413
416
|
return answer
|
|
414
417
|
|
|
415
418
|
|
|
419
|
+
## The connections [param scene]'s file holds from or to [param at] or a node under it, which go
|
|
420
|
+
## with the node; one an instanced scene makes inside itself is that scene's, and stays there.
|
|
421
|
+
static func _connections_with(scene: GdhSceneFile, at: String) -> Array[Dictionary]:
|
|
422
|
+
var cut: Array[Dictionary] = []
|
|
423
|
+
for held: Dictionary in scene.held_connections():
|
|
424
|
+
var ends: Array[String] = [str(held["sourceNodePath"]), str(held["targetNodePath"])]
|
|
425
|
+
for end: String in ends:
|
|
426
|
+
if end == at or end.begins_with(at + "/"):
|
|
427
|
+
cut.append(held)
|
|
428
|
+
break
|
|
429
|
+
return cut
|
|
430
|
+
|
|
431
|
+
|
|
416
432
|
func duplicate_node(args: Dictionary) -> Dictionary:
|
|
417
433
|
var project_path: String = str(args.get("projectPath", ""))
|
|
418
434
|
var scene_path: String = GdhSceneFile.res_path(project_path, str(args.get("scenePath", "")))
|
|
@@ -405,7 +405,9 @@ func _add_inherited(answer: Dictionary, seen: Array[String]) -> void:
|
|
|
405
405
|
if above.is_empty():
|
|
406
406
|
break
|
|
407
407
|
inherits_from.append(base_path)
|
|
408
|
-
for list_name: String in [
|
|
408
|
+
for list_name: String in [
|
|
409
|
+
"signals", "variables", "functions", "constants", "enums", "inner_classes",
|
|
410
|
+
]:
|
|
409
411
|
var mine: Array[Dictionary] = answer[list_name]
|
|
410
412
|
var theirs: Array[Dictionary] = above[list_name]
|
|
411
413
|
for member: Dictionary in theirs:
|