gdharness 1.1.21 → 1.1.23

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.
Files changed (51) hide show
  1. package/build/cli.js +1470 -726
  2. package/build/godot/addons/gdharness_editor/bridge_client.gd +12 -2
  3. package/build/godot/addons/gdharness_editor/property_values.gd +484 -65
  4. package/build/godot/addons/gdharness_editor/reading.gd +17 -0
  5. package/build/godot/addons/gdharness_editor/scene_file.gd +229 -0
  6. package/build/godot/addons/gdharness_editor/scene_file.gd.uid +1 -0
  7. package/build/godot/addons/gdharness_editor/serialisation.gd +116 -7
  8. package/build/godot/addons/gdharness_editor/tool_executor.gd +10 -3
  9. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +374 -242
  10. package/build/godot/addons/gdharness_editor/tools/class_tools.gd +47 -9
  11. package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +206 -0
  12. package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd.uid +1 -0
  13. package/build/godot/addons/gdharness_editor/tools/play_tools.gd +90 -15
  14. package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +373 -140
  15. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +566 -472
  16. package/build/godot/addons/gdharness_runtime/call_errors.gd +42 -0
  17. package/build/godot/addons/gdharness_runtime/call_errors.gd.uid +1 -0
  18. package/build/godot/addons/gdharness_runtime/reading.gd +17 -0
  19. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +15 -5
  20. package/build/godot/addons/gdharness_runtime/runtime_capture.gd +51 -5
  21. package/build/godot/addons/gdharness_runtime/runtime_changes.gd +440 -0
  22. package/build/godot/addons/gdharness_runtime/runtime_changes.gd.uid +1 -0
  23. package/build/godot/addons/gdharness_runtime/runtime_input.gd +184 -338
  24. package/build/godot/addons/gdharness_runtime/runtime_paths.gd +89 -16
  25. package/build/godot/addons/gdharness_runtime/runtime_queries.gd +107 -343
  26. package/build/godot/addons/gdharness_runtime/runtime_screen.gd +13 -1
  27. package/build/godot/addons/gdharness_runtime/runtime_targets.gd +90 -24
  28. package/build/godot/addons/gdharness_runtime/runtime_typing.gd +351 -0
  29. package/build/godot/addons/gdharness_runtime/runtime_typing.gd.uid +1 -0
  30. package/build/godot/addons/gdharness_runtime/runtime_values.gd +92 -33
  31. package/build/godot/addons/gdharness_runtime/runtime_waits.gd +61 -28
  32. package/build/godot/addons/gdharness_runtime/serialisation.gd +116 -7
  33. package/build/godot/operations/audio_buses.gd +47 -11
  34. package/build/godot/operations/class_cache.gd +18 -2
  35. package/build/godot/operations/classdb_queries.gd +28 -17
  36. package/build/godot/operations/dependencies.gd +128 -17
  37. package/build/godot/operations/file_walk.gd +20 -0
  38. package/build/godot/operations/gdscript_analysis.gd +59 -20
  39. package/build/godot/operations/gdscript_authoring.gd +127 -60
  40. package/build/godot/operations/godot_operations.gd +24 -5
  41. package/build/godot/operations/import_pipeline.gd +179 -19
  42. package/build/godot/operations/logger.gd +2 -2
  43. package/build/godot/operations/plugins.gd +88 -70
  44. package/build/godot/operations/project_config.gd +41 -6
  45. package/build/godot/operations/project_diagnostics.gd +27 -11
  46. package/build/godot/operations/reading.gd +17 -0
  47. package/build/godot/operations/resource_files.gd +60 -17
  48. package/build/godot/operations/serialisation.gd +116 -7
  49. package/build/index.js +1385 -553
  50. package/build/keeper.js +4 -0
  51. package/package.json +1 -1
@@ -67,6 +67,9 @@ const PACKED: Array[int] = [
67
67
  ## The calls an empty list has nothing to answer, which the engine reports as an error when asked.
68
68
  const NEEDS_AN_ELEMENT: Array[String] = ["front", "back", "pick_random"]
69
69
 
70
+ ## Said after every call a path refuses for doing something rather than reading.
71
+ const READS_ONLY: String = ", and a path, which a wait reads every frame, only calls methods that read"
72
+
70
73
 
71
74
  ## What [param reaching] names something on, which is the node itself until the name has a colon
72
75
  ## in it, and the last name along that path.
@@ -198,16 +201,33 @@ static func can_read(holder: Variant, named: String) -> bool:
198
201
  var map: Dictionary = holder
199
202
  if not method_of(named).is_empty():
200
203
  return method_of(named) in MAP_CALLS
201
- return map.has(named) or map.has(StringName(named))
204
+ return not _key_in(map, named).is_empty()
202
205
  if holder is Object:
203
206
  var object: Object = holder
204
207
  var method: String = method_of(named)
205
208
  if not method.is_empty():
206
- return _arguments_required(object, method) == 0
209
+ return call_refused(object, method, "").is_empty()
207
210
  return _has_property(object, named)
208
211
  return false
209
212
 
210
213
 
214
+ ## The key of [param map] a step names, in a list of one, or an empty list when it names none.
215
+ ##
216
+ ## A step is text, and a map is keyed by whatever the game put in it: text, a StringName, or a
217
+ ## number, which a map of units by id is. Keyed by 3, "3" is not a key of it, measured on 4.7.2, so a
218
+ ## step that reads as a number is tried as one after the two spellings of text.
219
+ static func _key_in(map: Dictionary, named: String) -> Array:
220
+ var spellings: Array = [named, StringName(named)]
221
+ if named.is_valid_int():
222
+ spellings.append(int(named))
223
+ if named.is_valid_float():
224
+ spellings.append(float(named))
225
+ for key: Variant in spellings:
226
+ if map.has(key):
227
+ return [key]
228
+ return []
229
+
230
+
211
231
  ## The method a step written as a call names, "get_viewport()" being get_viewport, or "" for a
212
232
  ## step that is a name.
213
233
  static func method_of(named: String) -> String:
@@ -228,6 +248,62 @@ static func _arguments_required(object: Object, method: String) -> int:
228
248
  return -1
229
249
 
230
250
 
251
+ ## Why a path may not call [param method] on [param object], said about [param called_as], or ""
252
+ ## when it may.
253
+ ##
254
+ ## A path is read, and a wait reads it every frame, so a call in one has to be a read. The engine
255
+ ## marks its own reading methods const and `queue_free()`, `free()` and `set_name()` not, measured on
256
+ ## 4.7.2. A game's methods carry no such mark, so one declared `-> void` is taken at its word, as
257
+ ## doing something and answering nothing, and one left untyped is trusted. Without this a find
258
+ ## with property "queue_free()" freed every node it matched, and a wait on "advance_day()" called
259
+ ## it once a frame and then reported the state it had driven the game to.
260
+ static func call_refused(object: Object, method: String, called_as: String) -> String:
261
+ var required: int = _arguments_required(object, method)
262
+ if required == -1:
263
+ return "%s has no method %s" % [called_as, method]
264
+ if required > 0:
265
+ return (
266
+ "%s.%s takes %d argument%s, and a path can only call a method that takes none"
267
+ % [called_as, method, required, "" if required == 1 else "s"]
268
+ )
269
+ var scripted: Dictionary = _script_method(object, method)
270
+ if not scripted.is_empty():
271
+ var returned: Dictionary = scripted.get("return", {})
272
+ var usage: int = returned.get("usage", 0)
273
+ var type: int = returned.get("type", TYPE_NIL)
274
+ if type == TYPE_NIL and usage & PROPERTY_USAGE_NIL_IS_VARIANT == 0:
275
+ return (
276
+ "%s.%s is declared -> void, so it does something rather than answering%s"
277
+ % [called_as, method, READS_ONLY]
278
+ )
279
+ return ""
280
+ if _native_flags(object, method) & METHOD_FLAG_CONST == 0:
281
+ return "%s.%s changes what it is called on rather than reading it%s" % [called_as, method, READS_ONLY]
282
+ return ""
283
+
284
+
285
+ ## The entry a script attached to [param object] declares for [param method], or an empty one when
286
+ ## the method is the engine's.
287
+ static func _script_method(object: Object, method: String) -> Dictionary:
288
+ var attached: Variant = object.get_script()
289
+ if not attached is Script:
290
+ return {}
291
+ var script: Script = attached
292
+ for entry: Dictionary in script.get_script_method_list():
293
+ if str(entry.get("name", "")) == method:
294
+ return entry
295
+ return {}
296
+
297
+
298
+ ## The flags the engine declares [param method] with on [param object].
299
+ static func _native_flags(object: Object, method: String) -> int:
300
+ for entry: Dictionary in object.get_method_list():
301
+ if str(entry.get("name", "")) == method:
302
+ var flags: int = entry.get("flags", 0)
303
+ return flags
304
+ return 0
305
+
306
+
231
307
  ## Put [param value] where [param named] points. Only ever called once [method can_read] agrees,
232
308
  ## and lists and maps are reference types, so writing into one reaches the game's own copy.
233
309
  static func write(holder: Variant, named: String, value: Variant) -> void:
@@ -243,10 +319,7 @@ static func write(holder: Variant, named: String, value: Variant) -> void:
243
319
  var map: Dictionary = holder
244
320
  # Written back under the key it was found under, since a map keyed by StringName and one
245
321
  # keyed by String both read the same way and a write to the wrong one adds a second entry.
246
- if map.has(named):
247
- map[named] = value
248
- else:
249
- map[StringName(named)] = value
322
+ map[_key_in(map, named)[0]] = value
250
323
  return
251
324
  var object: Object = holder
252
325
  object.set(named, value)
@@ -265,9 +338,7 @@ static func read_under(holder: Variant, named: String) -> Variant:
265
338
  return list[_index_in(named, len(holder))]
266
339
  if holder is Dictionary:
267
340
  var map: Dictionary = holder
268
- if map.has(named):
269
- return map[named]
270
- return map[StringName(named)]
341
+ return map[_key_in(map, named)[0]]
271
342
  var object: Object = holder
272
343
  var method: String = method_of(named)
273
344
  if not method.is_empty():
@@ -311,6 +382,14 @@ static func nothing_there(holder: Variant, named: String, called_as: String) ->
311
382
  return container_calls_refused(called_as, "a map", container_call)
312
383
  var map: Dictionary = holder
313
384
  var keys: Array = map.keys()
385
+ # A key reading as the step and still not reached is one of a type a step cannot spell, and
386
+ # saying the map has no key 3 while listing 3 among its keys contradicts itself.
387
+ for key: Variant in keys:
388
+ if str(key) == named:
389
+ return (
390
+ "%s has a key that reads %s, but it is %s, which a path step cannot name"
391
+ % [called_as, named, type_string(typeof(key))]
392
+ )
314
393
  # Eight of them, because a map keyed by something unexpected is told by the first few and a
315
394
  # map of two hundred would bury the sentence saying which key was missing.
316
395
  var some: Array[String] = []
@@ -323,13 +402,7 @@ static func nothing_there(holder: Variant, named: String, called_as: String) ->
323
402
  var object: Object = holder
324
403
  var method: String = method_of(named)
325
404
  if not method.is_empty():
326
- var required: int = _arguments_required(object, method)
327
- if required == -1:
328
- return "%s has no method %s" % [called_as, method]
329
- return (
330
- "%s.%s takes %d argument%s, and a path can only call a method that takes none"
331
- % [called_as, method, required, "" if required == 1 else "s"]
332
- )
405
+ return call_refused(object, method, called_as)
333
406
  # A method spelled as a property is the likeliest thing behind a name the object has no property
334
407
  # for, and the caller is one pair of brackets away from what they meant.
335
408
  if object.has_method(named):