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,6 +1,5 @@
|
|
|
1
1
|
@tool
|
|
2
2
|
extends Node
|
|
3
|
-
|
|
4
3
|
## Resource files, shaders and themes, written through the open editor.
|
|
5
4
|
|
|
6
5
|
const GdhFileReads = preload("../file_reads.gd")
|
|
@@ -12,7 +11,6 @@ const GdhSame = preload("../same_values.gd")
|
|
|
12
11
|
const GdhSceneFile = preload("../scene_file.gd")
|
|
13
12
|
const GdhSceneUids = preload("../scene_uids.gd")
|
|
14
13
|
const GdhSerialisation = preload("../serialisation.gd")
|
|
15
|
-
|
|
16
14
|
# The minimal shader of a type: its declaration and the one function the engine calls for it.
|
|
17
15
|
const SHADER_EMPTY: String = """shader_type %s;
|
|
18
16
|
|
|
@@ -20,7 +18,11 @@ void %s() {
|
|
|
20
18
|
}
|
|
21
19
|
"""
|
|
22
20
|
const SHADER_ENTRY: Dictionary[String, String] = {
|
|
23
|
-
"canvas_item": "fragment",
|
|
21
|
+
"canvas_item": "fragment",
|
|
22
|
+
"spatial": "fragment",
|
|
23
|
+
"particles": "process",
|
|
24
|
+
"sky": "sky",
|
|
25
|
+
"fog": "fog",
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
var _editor_plugin: EditorPlugin = null
|
|
@@ -59,7 +61,10 @@ func _taken(path: String, op: String, remedy: String = "Change it with op=modify
|
|
|
59
61
|
if not FileAccess.file_exists(path):
|
|
60
62
|
return ""
|
|
61
63
|
return (
|
|
62
|
-
|
|
64
|
+
(
|
|
65
|
+
"%s already exists, and %s would replace everything in it. %s, or create the new one "
|
|
66
|
+
+ "under another path."
|
|
67
|
+
)
|
|
63
68
|
% [path, op, remedy]
|
|
64
69
|
)
|
|
65
70
|
|
|
@@ -84,7 +89,9 @@ func _parse_properties_dict(raw: Variant) -> Dictionary:
|
|
|
84
89
|
func _load_theme(theme_path: String) -> Theme:
|
|
85
90
|
if not ResourceLoader.exists(theme_path, "Theme"):
|
|
86
91
|
return null
|
|
87
|
-
var loaded: Resource = ResourceLoader.load(
|
|
92
|
+
var loaded: Resource = ResourceLoader.load(
|
|
93
|
+
theme_path, "Theme", ResourceLoader.CACHE_MODE_IGNORE
|
|
94
|
+
)
|
|
88
95
|
if loaded is Theme:
|
|
89
96
|
return loaded
|
|
90
97
|
return null
|
|
@@ -97,7 +104,9 @@ func _load_theme(theme_path: String) -> Theme:
|
|
|
97
104
|
## Read back because a property can be set and still not be saved: one without storage, a
|
|
98
105
|
## script variable that is not exported. Both were answered as changed.
|
|
99
106
|
func _save_checked(resource: Resource, path: String, names: Array, previous: Variant) -> String:
|
|
100
|
-
var carried: Dictionary = GdhHeadlessCheck.embedded_in(
|
|
107
|
+
var carried: Dictionary = GdhHeadlessCheck.embedded_in(
|
|
108
|
+
GdhHeadlessCheck.stored(resource).values(), {}
|
|
109
|
+
)
|
|
101
110
|
var saving: Error = GdhSceneUids.save_keeping_uid(resource, path)
|
|
102
111
|
if saving != OK:
|
|
103
112
|
return "Failed to save %s: %s" % [path, error_string(saving)]
|
|
@@ -117,8 +126,8 @@ func _save_checked(resource: Resource, path: String, names: Array, previous: Var
|
|
|
117
126
|
if not GdhSame.same(read, _properties.read_at(resource, property)):
|
|
118
127
|
lost = (
|
|
119
128
|
(
|
|
120
|
-
"%s.%s would load from the file as %s: it is not a property the file
|
|
121
|
-
+ "nothing was changed."
|
|
129
|
+
"%s.%s would load from the file as %s: it is not a property the file "
|
|
130
|
+
+ "keeps, so nothing was changed."
|
|
122
131
|
)
|
|
123
132
|
% [path, property, _properties.shown(read)]
|
|
124
133
|
)
|
|
@@ -145,8 +154,9 @@ func _unkept_headless(
|
|
|
145
154
|
return (
|
|
146
155
|
(
|
|
147
156
|
(
|
|
148
|
-
"%s is a binary resource with a script that is not a tool, and what that
|
|
149
|
-
+ "changed when a headless engine loaded it cannot be told apart
|
|
157
|
+
"%s is a binary resource with a script that is not a tool, and what that "
|
|
158
|
+
+ "script changed when a headless engine loaded it cannot be told apart "
|
|
159
|
+
+ "from the file here. "
|
|
150
160
|
)
|
|
151
161
|
% path
|
|
152
162
|
)
|
|
@@ -157,22 +167,23 @@ func _unkept_headless(
|
|
|
157
167
|
if previous is PackedByteArray:
|
|
158
168
|
var bytes: PackedByteArray = previous
|
|
159
169
|
before = bytes.get_string_from_utf8()
|
|
160
|
-
var asked: Dictionary = {"script": true}
|
|
170
|
+
var asked: Dictionary = { "script": true }
|
|
161
171
|
var reached: Dictionary = {}
|
|
162
172
|
for key: Variant in names:
|
|
163
173
|
var property: String = str(key)
|
|
164
174
|
asked[property] = true
|
|
165
175
|
asked[property.get_slice(":", 0)] = true
|
|
166
176
|
GdhHeadlessCheck.spare(reached, GdhHeadlessCheck.reached(resource, property))
|
|
177
|
+
var just_saved: GdhHeadlessCheck.GdhSaved = GdhHeadlessCheck.GdhSaved.new(
|
|
178
|
+
path, before, func() -> Error: return GdhSceneUids.save_keeping_uid(resource, path)
|
|
179
|
+
)
|
|
167
180
|
return GdhHeadlessCheck.settle_saved(
|
|
168
|
-
|
|
169
|
-
before,
|
|
181
|
+
just_saved,
|
|
170
182
|
GdhHeadlessCheck.stored(resource).values(),
|
|
171
183
|
resource if scripted else null,
|
|
172
184
|
asked,
|
|
173
185
|
reached,
|
|
174
|
-
carried
|
|
175
|
-
func() -> Error: return GdhSceneUids.save_keeping_uid(resource, path)
|
|
186
|
+
carried
|
|
176
187
|
)
|
|
177
188
|
|
|
178
189
|
|
|
@@ -180,17 +191,20 @@ func create_resource(args: Dictionary) -> Dictionary:
|
|
|
180
191
|
var res_path: String = _ensure_res_path(str(args.get("resourcePath", "")))
|
|
181
192
|
var resource_type: String = str(args.get("resourceType", "Resource"))
|
|
182
193
|
if res_path == "res://":
|
|
183
|
-
return {"ok": false, "error": "resourcePath is required"}
|
|
194
|
+
return { "ok": false, "error": "resourcePath is required" }
|
|
184
195
|
var taken: String = _taken(res_path, "resource_edit op=create")
|
|
185
196
|
if not taken.is_empty():
|
|
186
|
-
return {"ok": false, "error": taken}
|
|
197
|
+
return { "ok": false, "error": taken }
|
|
187
198
|
|
|
188
199
|
var script_path: String = str(args.get("script", ""))
|
|
189
200
|
var made: Dictionary = GdhSceneFile.made_as(
|
|
190
|
-
resource_type,
|
|
201
|
+
resource_type,
|
|
202
|
+
"" if script_path == "" else _ensure_res_path(script_path),
|
|
203
|
+
"Resource",
|
|
204
|
+
"resourceType"
|
|
191
205
|
)
|
|
192
206
|
if made.has("error"):
|
|
193
|
-
return {"ok": false, "error": made["error"]}
|
|
207
|
+
return { "ok": false, "error": made["error"] }
|
|
194
208
|
resource_type = str(made["type"])
|
|
195
209
|
script_path = str(made["script"])
|
|
196
210
|
if not ClassDB.class_exists(resource_type):
|
|
@@ -200,10 +214,15 @@ func create_resource(args: Dictionary) -> Dictionary:
|
|
|
200
214
|
(
|
|
201
215
|
"%s is neither an engine class nor a class_name a script in this project declares."
|
|
202
216
|
% resource_type
|
|
203
|
-
)
|
|
217
|
+
),
|
|
218
|
+
}
|
|
219
|
+
if (
|
|
220
|
+
not ClassDB.is_parent_class(resource_type, "Resource")
|
|
221
|
+
or not ClassDB.can_instantiate(resource_type)
|
|
222
|
+
):
|
|
223
|
+
return {
|
|
224
|
+
"ok": false, "error": "%s is not a resource class that can be created." % resource_type,
|
|
204
225
|
}
|
|
205
|
-
if not ClassDB.is_parent_class(resource_type, "Resource") or not ClassDB.can_instantiate(resource_type):
|
|
206
|
-
return {"ok": false, "error": "%s is not a resource class that can be created." % resource_type}
|
|
207
226
|
var resource: Resource = ClassDB.instantiate(resource_type)
|
|
208
227
|
|
|
209
228
|
# A script that did not load was skipped, and a plain resource was saved in place of the custom
|
|
@@ -212,30 +231,37 @@ func create_resource(args: Dictionary) -> Dictionary:
|
|
|
212
231
|
var script_file: String = _ensure_res_path(script_path)
|
|
213
232
|
var script: Script = load(script_file) as Script
|
|
214
233
|
if script == null:
|
|
215
|
-
return {"ok": false, "error": "No script loads from " + script_file}
|
|
234
|
+
return { "ok": false, "error": "No script loads from " + script_file }
|
|
216
235
|
var uncompiled: String = GdhSceneFile.uncompiled(script, script_file)
|
|
217
236
|
if not uncompiled.is_empty():
|
|
218
|
-
return {"ok": false, "error": uncompiled}
|
|
237
|
+
return { "ok": false, "error": uncompiled }
|
|
219
238
|
var base: String = str(script.get_instance_base_type())
|
|
220
239
|
if not ClassDB.is_parent_class(resource_type, base):
|
|
221
240
|
return {
|
|
222
241
|
"ok": false,
|
|
223
|
-
"error":
|
|
242
|
+
"error":
|
|
243
|
+
"%s extends %s, so it cannot be on a %s." % [script_file, base, resource_type],
|
|
224
244
|
}
|
|
225
245
|
resource.set_script(script)
|
|
226
246
|
|
|
227
247
|
var properties: Dictionary = _parse_properties_dict(args.get("properties", {}))
|
|
228
248
|
var refused: String = _properties.write_all(resource, properties)
|
|
229
249
|
if not refused.is_empty():
|
|
230
|
-
return {
|
|
250
|
+
return {
|
|
251
|
+
"ok": false, "error": GdhHeadlessCheck.setter_ran(
|
|
252
|
+
resource,
|
|
253
|
+
_properties.unheld,
|
|
254
|
+
refused,
|
|
255
|
+
),
|
|
256
|
+
}
|
|
231
257
|
|
|
232
258
|
var folder: String = GdhSceneFile.make_folder_for(res_path)
|
|
233
259
|
if not folder.is_empty():
|
|
234
|
-
return {"ok": false, "error": folder}
|
|
235
|
-
# As a read names it, the engine class under resourceType and the script's class beside it,
|
|
236
|
-
# a create given resourceType Resource and a script answered nothing of the class it had
|
|
237
|
-
# one given a class_name answered it where a read of the same file answers Resource.
|
|
238
|
-
var answer: Dictionary = {"ok": true, "resourcePath": res_path, "resourceType": resource_type}
|
|
260
|
+
return { "ok": false, "error": folder }
|
|
261
|
+
# As a read names it, the engine class under resourceType and the script's class beside it,
|
|
262
|
+
# since a create given resourceType Resource and a script answered nothing of the class it had
|
|
263
|
+
# made, and one given a class_name answered it where a read of the same file answers Resource.
|
|
264
|
+
var answer: Dictionary = { "ok": true, "resourcePath": res_path, "resourceType": resource_type }
|
|
239
265
|
var scripted: Variant = resource.get_script()
|
|
240
266
|
if scripted is Script:
|
|
241
267
|
var attached: Script = scripted
|
|
@@ -245,7 +271,8 @@ func create_resource(args: Dictionary) -> Dictionary:
|
|
|
245
271
|
_properties.note_write(answer)
|
|
246
272
|
var failed: String = _save_checked(resource, res_path, properties.keys(), null)
|
|
247
273
|
if not failed.is_empty():
|
|
248
|
-
return {"ok": false, "error": failed}
|
|
274
|
+
return { "ok": false, "error": failed }
|
|
275
|
+
_note_put_back(answer, resource, res_path)
|
|
249
276
|
if not properties.is_empty():
|
|
250
277
|
answer["properties"] = _held(resource, properties.keys())
|
|
251
278
|
return answer
|
|
@@ -254,9 +281,9 @@ func create_resource(args: Dictionary) -> Dictionary:
|
|
|
254
281
|
func modify_resource(args: Dictionary) -> Dictionary:
|
|
255
282
|
var res_path: String = _ensure_res_path(str(args.get("resourcePath", "")))
|
|
256
283
|
if res_path == "res://":
|
|
257
|
-
return {"ok": false, "error": "resourcePath is required"}
|
|
284
|
+
return { "ok": false, "error": "resourcePath is required" }
|
|
258
285
|
if not FileAccess.file_exists(res_path):
|
|
259
|
-
return {"ok": false, "error": "No resource at %s; op=create makes one there." % res_path}
|
|
286
|
+
return { "ok": false, "error": "No resource at %s; op=create makes one there." % res_path }
|
|
260
287
|
# The editor's copy of an imported file is rebuilt from the source on every import, and a save
|
|
261
288
|
# to it fails; edited through the cache, the editor kept the changed copy after the refusal.
|
|
262
289
|
if FileAccess.file_exists(res_path + ".import"):
|
|
@@ -265,17 +292,17 @@ func modify_resource(args: Dictionary) -> Dictionary:
|
|
|
265
292
|
"error":
|
|
266
293
|
(
|
|
267
294
|
(
|
|
268
|
-
"%s is imported, so what it holds comes from its source file and its import
|
|
269
|
-
+ "Change those with project_import op=set_options."
|
|
295
|
+
"%s is imported, so what it holds comes from its source file and its import "
|
|
296
|
+
+ "options. Change those with project_import op=set_options."
|
|
270
297
|
)
|
|
271
298
|
% res_path
|
|
272
|
-
)
|
|
299
|
+
),
|
|
273
300
|
}
|
|
274
301
|
|
|
275
302
|
# Past the cache, so a refusal leaves the copy open scenes hold as it was.
|
|
276
303
|
var resource: Resource = ResourceLoader.load(res_path, "", ResourceLoader.CACHE_MODE_IGNORE)
|
|
277
304
|
if resource == null:
|
|
278
|
-
return {"ok": false, "error": "%s did not load as a resource." % res_path}
|
|
305
|
+
return { "ok": false, "error": "%s did not load as a resource." % res_path }
|
|
279
306
|
# A scene's own properties are its packed bundle, so a property named for a scene is one of its
|
|
280
307
|
# nodes', and "PackedScene has no property" left the caller looking for the tool that sets it.
|
|
281
308
|
if resource is PackedScene:
|
|
@@ -283,9 +310,12 @@ func modify_resource(args: Dictionary) -> Dictionary:
|
|
|
283
310
|
"ok": false,
|
|
284
311
|
"error":
|
|
285
312
|
(
|
|
286
|
-
|
|
313
|
+
(
|
|
314
|
+
"%s is a scene: scene_node set changes one node's properties, the root's at "
|
|
315
|
+
+ "nodePath \".\"."
|
|
316
|
+
)
|
|
287
317
|
% res_path
|
|
288
|
-
)
|
|
318
|
+
),
|
|
289
319
|
}
|
|
290
320
|
|
|
291
321
|
var properties: Dictionary = _parse_properties_dict(args.get("properties", ""))
|
|
@@ -295,17 +325,31 @@ func modify_resource(args: Dictionary) -> Dictionary:
|
|
|
295
325
|
else:
|
|
296
326
|
refused = GdhHeadlessCheck.setter_ran(resource, _properties.unheld, refused)
|
|
297
327
|
if not refused.is_empty():
|
|
298
|
-
return {"ok": false, "error": refused}
|
|
299
|
-
var answer: Dictionary = {"ok": true, "resourcePath": res_path}
|
|
328
|
+
return { "ok": false, "error": refused }
|
|
329
|
+
var answer: Dictionary = { "ok": true, "resourcePath": res_path }
|
|
300
330
|
_properties.note_write(answer)
|
|
301
331
|
var previous: PackedByteArray = FileAccess.get_file_as_bytes(res_path)
|
|
302
332
|
var failed: String = _save_checked(resource, res_path, properties.keys(), previous)
|
|
303
333
|
if not failed.is_empty():
|
|
304
|
-
return {"ok": false, "error": failed}
|
|
334
|
+
return { "ok": false, "error": failed }
|
|
335
|
+
_note_put_back(answer, resource, res_path)
|
|
305
336
|
answer["properties"] = _held(resource, properties.keys())
|
|
306
337
|
return answer
|
|
307
338
|
|
|
308
339
|
|
|
340
|
+
## Moves to setByScript each property alsoChanged in [param answer] gave a value [param resource] no
|
|
341
|
+
## longer holds once saved at [param path]: the save put it back as its script's doing.
|
|
342
|
+
func _note_put_back(answer: Dictionary, resource: Resource, path: String) -> void:
|
|
343
|
+
var noted: Dictionary = answer.get("alsoChanged", {})
|
|
344
|
+
var restored: Array = []
|
|
345
|
+
for key: Variant in noted:
|
|
346
|
+
var now: Variant = _values.serialize_value(_properties.read_at(resource, str(key)))
|
|
347
|
+
var entry: Dictionary = noted[key]
|
|
348
|
+
if JSON.stringify(now) != JSON.stringify(entry.get("now")):
|
|
349
|
+
restored.append(key)
|
|
350
|
+
GdhHeadlessCheck.move_script_set(answer, restored, path)
|
|
351
|
+
|
|
352
|
+
|
|
309
353
|
## Why [param resource], saved at [param path], cannot hold what [param names] were given, or "": a
|
|
310
354
|
## resource cannot hold the file it is saved as. Godot saves the reference as null with only a
|
|
311
355
|
## warning in its log, and the read back after the save reported the property as one the file does
|
|
@@ -313,12 +357,14 @@ func modify_resource(args: Dictionary) -> Dictionary:
|
|
|
313
357
|
func _holding_itself(resource: Resource, path: String, names: Array) -> String:
|
|
314
358
|
for key: Variant in names:
|
|
315
359
|
var property: String = str(key)
|
|
316
|
-
for held: Resource in GdhHeadlessCheck.resources_in(
|
|
360
|
+
for held: Resource in GdhHeadlessCheck.resources_in(
|
|
361
|
+
_properties.read_at(resource, property)
|
|
362
|
+
):
|
|
317
363
|
if held == resource or held.resource_path == path:
|
|
318
364
|
return (
|
|
319
365
|
(
|
|
320
|
-
"%s.%s would hold %s itself, and a resource cannot hold the file it is
|
|
321
|
-
+ "Godot saves the reference as null. Nothing was written."
|
|
366
|
+
"%s.%s would hold %s itself, and a resource cannot hold the file it is "
|
|
367
|
+
+ "saved as: Godot saves the reference as null. Nothing was written."
|
|
322
368
|
)
|
|
323
369
|
% [GdhPropertyPaths.class_called(resource), property, path]
|
|
324
370
|
)
|
|
@@ -339,7 +385,7 @@ func _held(resource: Resource, asked: Array) -> Dictionary:
|
|
|
339
385
|
func read_resource(args: Dictionary) -> Dictionary:
|
|
340
386
|
var res_path: String = _ensure_res_path(str(args.get("resourcePath", "")))
|
|
341
387
|
if res_path == "res://":
|
|
342
|
-
return {"ok": false, "error": "resourcePath is required"}
|
|
388
|
+
return { "ok": false, "error": "resourcePath is required" }
|
|
343
389
|
# The server takes a resource kept inside a file apart, so the boundary judges the file alone.
|
|
344
390
|
var inner: String = str(args.get("subResource", ""))
|
|
345
391
|
if not inner.is_empty():
|
|
@@ -351,10 +397,10 @@ func create_shader(args: Dictionary) -> Dictionary:
|
|
|
351
397
|
var shader_path: String = _ensure_res_path(str(args.get("resourcePath", "")))
|
|
352
398
|
var shader_type: String = str(args.get("shaderType", "canvas_item"))
|
|
353
399
|
if shader_path == "res://":
|
|
354
|
-
return {"ok": false, "error": "resourcePath is required"}
|
|
400
|
+
return { "ok": false, "error": "resourcePath is required" }
|
|
355
401
|
var taken: String = _taken(shader_path, "resource_edit op=create_shader", "Edit its text")
|
|
356
402
|
if not taken.is_empty():
|
|
357
|
-
return {"ok": false, "error": taken}
|
|
403
|
+
return { "ok": false, "error": taken }
|
|
358
404
|
|
|
359
405
|
var code: String = str(args.get("code", ""))
|
|
360
406
|
if code == "":
|
|
@@ -362,21 +408,24 @@ func create_shader(args: Dictionary) -> Dictionary:
|
|
|
362
408
|
|
|
363
409
|
var folder: String = GdhSceneFile.make_folder_for(shader_path)
|
|
364
410
|
if not folder.is_empty():
|
|
365
|
-
return {"ok": false, "error": folder}
|
|
411
|
+
return { "ok": false, "error": folder }
|
|
366
412
|
var file: FileAccess = FileAccess.open(shader_path, FileAccess.WRITE)
|
|
367
413
|
if file == null:
|
|
368
414
|
return {
|
|
369
415
|
"ok": false,
|
|
370
416
|
"error":
|
|
371
|
-
|
|
417
|
+
(
|
|
418
|
+
"Could not open %s for writing: %s"
|
|
419
|
+
% [shader_path, error_string(FileAccess.get_open_error())]
|
|
420
|
+
),
|
|
372
421
|
}
|
|
373
422
|
var written: bool = file.store_string(code)
|
|
374
423
|
file.close()
|
|
375
424
|
if not written:
|
|
376
|
-
return {"ok": false, "error": "Failed to write the shader file " + shader_path}
|
|
425
|
+
return { "ok": false, "error": "Failed to write the shader file " + shader_path }
|
|
377
426
|
|
|
378
427
|
_written(shader_path)
|
|
379
|
-
var answer: Dictionary = {"ok": true, "shaderPath": shader_path, "shaderType": shader_type}
|
|
428
|
+
var answer: Dictionary = { "ok": true, "shaderPath": shader_path, "shaderType": shader_type }
|
|
380
429
|
var errors: Array[Dictionary] = _shader_errors(shader_path)
|
|
381
430
|
answer["compiles"] = errors.is_empty()
|
|
382
431
|
if not errors.is_empty():
|
|
@@ -390,42 +439,21 @@ func create_shader(args: Dictionary) -> Dictionary:
|
|
|
390
439
|
func _shader_errors(path: String) -> Array[Dictionary]:
|
|
391
440
|
var caught: GdhShaderErrors = GdhShaderErrors.new()
|
|
392
441
|
OS.add_logger(caught)
|
|
393
|
-
var shader: Shader =
|
|
442
|
+
var shader: Shader = (
|
|
443
|
+
ResourceLoader.load(path, "Shader", ResourceLoader.CACHE_MODE_IGNORE) as Shader
|
|
444
|
+
)
|
|
394
445
|
if shader != null:
|
|
395
446
|
var _uniforms: Array = shader.get_shader_uniform_list()
|
|
396
447
|
OS.remove_logger(caught)
|
|
397
448
|
return caught.raised
|
|
398
449
|
|
|
399
450
|
|
|
400
|
-
## Every shader error the engine raises while it is attached, in order.
|
|
401
|
-
class GdhShaderErrors:
|
|
402
|
-
extends Logger
|
|
403
|
-
|
|
404
|
-
var raised: Array[Dictionary] = []
|
|
405
|
-
|
|
406
|
-
func _log_message(_message: String, _error: bool) -> void:
|
|
407
|
-
pass
|
|
408
|
-
|
|
409
|
-
# The engine's own signature for a Logger, eight parameters and not this project's to shape.
|
|
410
|
-
# gdlint:ignore = function-arguments-number
|
|
411
|
-
func _log_error(
|
|
412
|
-
_function: String,
|
|
413
|
-
_file: String,
|
|
414
|
-
line: int,
|
|
415
|
-
code: String,
|
|
416
|
-
rationale: String,
|
|
417
|
-
_editor_notify: bool,
|
|
418
|
-
error_type: int,
|
|
419
|
-
_script_backtraces: Array[ScriptBacktrace],
|
|
420
|
-
) -> void:
|
|
421
|
-
if error_type == Logger.ERROR_TYPE_SHADER:
|
|
422
|
-
raised.append({"line": line, "message": code if rationale.is_empty() else rationale})
|
|
423
|
-
|
|
424
|
-
|
|
425
451
|
## Saves [param theme] over [param theme_path] with [param items] changed, answering "" or why not.
|
|
426
452
|
func _save_theme(theme: Theme, theme_path: String, items: Array) -> String:
|
|
427
453
|
var previous: PackedByteArray = FileAccess.get_file_as_bytes(theme_path)
|
|
428
|
-
var carried: Dictionary = GdhHeadlessCheck.embedded_in(
|
|
454
|
+
var carried: Dictionary = GdhHeadlessCheck.embedded_in(
|
|
455
|
+
GdhHeadlessCheck.stored(theme).values(), {}
|
|
456
|
+
)
|
|
429
457
|
var saving: Error = GdhSceneUids.save_keeping_uid(theme, theme_path)
|
|
430
458
|
if saving != OK:
|
|
431
459
|
return "Failed to save %s: %s" % [theme_path, error_string(saving)]
|
|
@@ -439,32 +467,37 @@ func _save_theme(theme: Theme, theme_path: String, items: Array) -> String:
|
|
|
439
467
|
func set_theme_color(args: Dictionary) -> Dictionary:
|
|
440
468
|
var theme_path: String = _ensure_res_path(str(args.get("resourcePath", "")))
|
|
441
469
|
if theme_path == "res://":
|
|
442
|
-
return {"ok": false, "error": "resourcePath is required"}
|
|
470
|
+
return { "ok": false, "error": "resourcePath is required" }
|
|
443
471
|
|
|
444
472
|
var color_name: String = str(args.get("colorName", ""))
|
|
445
473
|
var control_type: String = str(args.get("controlType", ""))
|
|
446
474
|
if color_name.is_empty() or control_type.is_empty():
|
|
447
|
-
return {"ok": false, "error": "colorName and controlType are required"}
|
|
475
|
+
return { "ok": false, "error": "colorName and controlType are required" }
|
|
448
476
|
|
|
449
477
|
var theme: Theme = _load_theme(theme_path)
|
|
450
478
|
if theme == null:
|
|
451
|
-
return {"ok": false, "error": "No Theme at %s. Create one there first." % theme_path}
|
|
479
|
+
return { "ok": false, "error": "No Theme at %s. Create one there first." % theme_path }
|
|
452
480
|
|
|
453
481
|
# Read as a property write reads a colour, html code included, so a value scene_node takes for a
|
|
454
482
|
# Button's modulate is taken for its theme colour too; "#ff8000" was refused here alone.
|
|
455
483
|
var parsed: Array = _properties.colour_from(args.get("color", {}), "color")
|
|
456
484
|
if not parsed[0]:
|
|
457
|
-
return {"ok": false, "error": parsed[1]}
|
|
485
|
+
return { "ok": false, "error": parsed[1] }
|
|
458
486
|
var color: Color = parsed[1]
|
|
459
487
|
theme.set_color(color_name, control_type, color)
|
|
460
488
|
|
|
461
|
-
var unsaved: String = _save_theme(
|
|
489
|
+
var unsaved: String = _save_theme(
|
|
490
|
+
theme, theme_path, ["%s/colors/%s" % [control_type, color_name]]
|
|
491
|
+
)
|
|
462
492
|
if not unsaved.is_empty():
|
|
463
|
-
return {"ok": false, "error": unsaved}
|
|
493
|
+
return { "ok": false, "error": unsaved }
|
|
464
494
|
|
|
465
495
|
var saved: Theme = _load_theme(theme_path)
|
|
466
496
|
if saved == null or not saved.has_color(color_name, control_type):
|
|
467
|
-
return {
|
|
497
|
+
return {
|
|
498
|
+
"ok": false,
|
|
499
|
+
"error": "The theme saved without %s/%s" % [control_type, color_name],
|
|
500
|
+
}
|
|
468
501
|
|
|
469
502
|
var stored: Color = saved.get_color(color_name, control_type)
|
|
470
503
|
return _noting_variation(
|
|
@@ -473,7 +506,7 @@ func set_theme_color(args: Dictionary) -> Dictionary:
|
|
|
473
506
|
"resourcePath": theme_path,
|
|
474
507
|
"controlType": control_type,
|
|
475
508
|
"colorName": color_name,
|
|
476
|
-
"color": {"r": stored.r, "g": stored.g, "b": stored.b, "a": stored.a}
|
|
509
|
+
"color": { "r": stored.r, "g": stored.g, "b": stored.b, "a": stored.a },
|
|
477
510
|
}
|
|
478
511
|
)
|
|
479
512
|
|
|
@@ -481,33 +514,36 @@ func set_theme_color(args: Dictionary) -> Dictionary:
|
|
|
481
514
|
func set_theme_font_size(args: Dictionary) -> Dictionary:
|
|
482
515
|
var theme_path: String = _ensure_res_path(str(args.get("resourcePath", "")))
|
|
483
516
|
if theme_path == "res://":
|
|
484
|
-
return {"ok": false, "error": "resourcePath is required"}
|
|
517
|
+
return { "ok": false, "error": "resourcePath is required" }
|
|
485
518
|
|
|
486
519
|
var font_size_name: String = str(args.get("fontSizeName", ""))
|
|
487
520
|
var control_type: String = str(args.get("controlType", ""))
|
|
488
521
|
if font_size_name.is_empty() or control_type.is_empty():
|
|
489
|
-
return {"ok": false, "error": "fontSizeName and controlType are required"}
|
|
522
|
+
return { "ok": false, "error": "fontSizeName and controlType are required" }
|
|
490
523
|
|
|
491
524
|
var theme: Theme = _load_theme(theme_path)
|
|
492
525
|
if theme == null:
|
|
493
|
-
return {"ok": false, "error": "No Theme at %s. Create one there first." % theme_path}
|
|
526
|
+
return { "ok": false, "error": "No Theme at %s. Create one there first." % theme_path }
|
|
494
527
|
|
|
495
528
|
# Godot reads a size of 0 or less as unset and answers the theme's default, so -3 would be read
|
|
496
529
|
# back as 16.
|
|
497
530
|
var pixels: float = GdhRead.as_float(args.get("size"), 0.0)
|
|
498
531
|
if not is_finite(pixels) or pixels != floorf(pixels):
|
|
499
|
-
return {
|
|
532
|
+
return {
|
|
533
|
+
"ok": false,
|
|
534
|
+
"error": "size is a whole number of pixels, and %s is not one." % str(pixels),
|
|
535
|
+
}
|
|
500
536
|
if pixels < 1:
|
|
501
537
|
return {
|
|
502
538
|
"ok": false,
|
|
503
539
|
"error":
|
|
504
540
|
(
|
|
505
541
|
(
|
|
506
|
-
"size must be 1 or more, not %d: the theme would hold it, and Godot reads a
|
|
507
|
-
+ "less as unset and draws the default size instead."
|
|
542
|
+
"size must be 1 or more, not %d: the theme would hold it, and Godot reads a "
|
|
543
|
+
+ "size of 0 or less as unset and draws the default size instead."
|
|
508
544
|
)
|
|
509
545
|
% int(pixels)
|
|
510
|
-
)
|
|
546
|
+
),
|
|
511
547
|
}
|
|
512
548
|
theme.set_font_size(font_size_name, control_type, int(pixels))
|
|
513
549
|
|
|
@@ -515,11 +551,13 @@ func set_theme_font_size(args: Dictionary) -> Dictionary:
|
|
|
515
551
|
theme, theme_path, ["%s/font_sizes/%s" % [control_type, font_size_name]]
|
|
516
552
|
)
|
|
517
553
|
if not unsaved.is_empty():
|
|
518
|
-
return {"ok": false, "error": unsaved}
|
|
554
|
+
return { "ok": false, "error": unsaved }
|
|
519
555
|
|
|
520
556
|
var saved: Theme = _load_theme(theme_path)
|
|
521
557
|
if saved == null or not saved.has_font_size(font_size_name, control_type):
|
|
522
|
-
return {
|
|
558
|
+
return {
|
|
559
|
+
"ok": false, "error": "The theme saved without %s/%s" % [control_type, font_size_name],
|
|
560
|
+
}
|
|
523
561
|
|
|
524
562
|
return _noting_variation(
|
|
525
563
|
{
|
|
@@ -527,7 +565,7 @@ func set_theme_font_size(args: Dictionary) -> Dictionary:
|
|
|
527
565
|
"resourcePath": theme_path,
|
|
528
566
|
"controlType": control_type,
|
|
529
567
|
"fontSizeName": font_size_name,
|
|
530
|
-
"size": saved.get_font_size(font_size_name, control_type)
|
|
568
|
+
"size": saved.get_font_size(font_size_name, control_type),
|
|
531
569
|
}
|
|
532
570
|
)
|
|
533
571
|
|
|
@@ -539,11 +577,11 @@ func remove_theme_item(args: Dictionary) -> Dictionary:
|
|
|
539
577
|
var control_type: String = str(args.get("controlType", ""))
|
|
540
578
|
var theme: Theme = _load_theme(theme_path)
|
|
541
579
|
if theme == null:
|
|
542
|
-
return {"ok": false, "error": "No Theme at %s." % theme_path}
|
|
580
|
+
return { "ok": false, "error": "No Theme at %s." % theme_path }
|
|
543
581
|
if not theme.get_type_list().has(control_type):
|
|
544
582
|
var types: PackedStringArray = theme.get_type_list()
|
|
545
583
|
var said: String = "%s holds no type %s" % [theme_path, control_type]
|
|
546
|
-
return {"ok": false, "error": GdhRead.offered(said, control_type, Array(types))}
|
|
584
|
+
return { "ok": false, "error": GdhRead.offered(said, control_type, Array(types)) }
|
|
547
585
|
var kinds: Array[String] = ["colors", "constants", "fonts", "font_sizes", "icons", "styles"]
|
|
548
586
|
var item: String = str(args.get("item", ""))
|
|
549
587
|
var removed: Array[String] = []
|
|
@@ -566,13 +604,21 @@ func remove_theme_item(args: Dictionary) -> Dictionary:
|
|
|
566
604
|
"%s holds no items, only the type it varies, %s; "
|
|
567
605
|
+ "without item, remove_theme_item takes it out."
|
|
568
606
|
)
|
|
569
|
-
return {"ok": false, "error": only % [control_type, base]}
|
|
607
|
+
return { "ok": false, "error": only % [control_type, base] }
|
|
570
608
|
elif removed.is_empty():
|
|
571
|
-
return {
|
|
609
|
+
return {
|
|
610
|
+
"ok": false,
|
|
611
|
+
"error": GdhRead.offered("%s has no item %s" % [control_type, item], item, held),
|
|
612
|
+
}
|
|
572
613
|
var unsaved: String = _save_theme(theme, theme_path, removed)
|
|
573
614
|
if not unsaved.is_empty():
|
|
574
|
-
return {"ok": false, "error": unsaved}
|
|
575
|
-
return {
|
|
615
|
+
return { "ok": false, "error": unsaved }
|
|
616
|
+
return {
|
|
617
|
+
"ok": true,
|
|
618
|
+
"resourcePath": theme_path,
|
|
619
|
+
"controlType": control_type,
|
|
620
|
+
"removed": removed,
|
|
621
|
+
}
|
|
576
622
|
|
|
577
623
|
|
|
578
624
|
## [param answer] with a note when its controlType names no class a theme styles. Any name is a
|
|
@@ -587,8 +633,8 @@ func _noting_variation(answer: Dictionary) -> Dictionary:
|
|
|
587
633
|
return answer
|
|
588
634
|
answer["note"] = (
|
|
589
635
|
(
|
|
590
|
-
"%s is not a Control or Window class, so this is a type variation: it styles only a
|
|
591
|
-
+ "whose theme_type_variation is %s."
|
|
636
|
+
"%s is not a Control or Window class, so this is a type variation: it styles only a "
|
|
637
|
+
+ "control whose theme_type_variation is %s."
|
|
592
638
|
)
|
|
593
639
|
% [type, type]
|
|
594
640
|
)
|
|
@@ -601,7 +647,35 @@ func _noting_variation(answer: Dictionary) -> Dictionary:
|
|
|
601
647
|
var close: PackedStringArray = GdhRead.nearest(type, themed)
|
|
602
648
|
if not close.is_empty():
|
|
603
649
|
answer["note"] += (
|
|
604
|
-
(
|
|
650
|
+
(
|
|
651
|
+
" If a class was meant, the nearest %s %s, and remove_theme_item with controlType "
|
|
652
|
+
+ "%s takes this out."
|
|
653
|
+
)
|
|
605
654
|
% ["is" if close.size() == 1 else "are", ", ".join(close), type]
|
|
606
655
|
)
|
|
607
656
|
return answer
|
|
657
|
+
|
|
658
|
+
|
|
659
|
+
## Every shader error the engine raises while it is attached, in order.
|
|
660
|
+
class GdhShaderErrors:
|
|
661
|
+
extends Logger
|
|
662
|
+
|
|
663
|
+
var raised: Array[Dictionary] = []
|
|
664
|
+
|
|
665
|
+
func _log_message(_message: String, _error: bool) -> void:
|
|
666
|
+
pass
|
|
667
|
+
|
|
668
|
+
# The engine's own signature for a Logger, eight parameters and not this project's to shape.
|
|
669
|
+
# gdstyle:ignore=quality/max-parameters
|
|
670
|
+
func _log_error(
|
|
671
|
+
_function: String,
|
|
672
|
+
_file: String,
|
|
673
|
+
line: int,
|
|
674
|
+
code: String,
|
|
675
|
+
rationale: String,
|
|
676
|
+
_editor_notify: bool,
|
|
677
|
+
error_type: int,
|
|
678
|
+
_script_backtraces: Array[ScriptBacktrace],
|
|
679
|
+
) -> void:
|
|
680
|
+
if error_type == Logger.ERROR_TYPE_SHADER:
|
|
681
|
+
raised.append({ "line": line, "message": code if rationale.is_empty() else rationale })
|