gdharness 1.2.40 → 1.2.42

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 (31) hide show
  1. package/build/cli.js +103 -38
  2. package/build/godot/addons/gdharness_editor/animation_reads.gd +14 -2
  3. package/build/godot/addons/gdharness_editor/bridge_client.gd +2 -1
  4. package/build/godot/addons/gdharness_editor/file_reads.gd +134 -10
  5. package/build/godot/addons/gdharness_editor/headless_check.gd +56 -15
  6. package/build/godot/addons/gdharness_editor/property_values.gd +1 -1
  7. package/build/godot/addons/gdharness_editor/reading.gd +40 -0
  8. package/build/godot/addons/gdharness_editor/scene_file.gd +215 -12
  9. package/build/godot/addons/gdharness_editor/serialisation.gd +5 -1
  10. package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +39 -5
  11. package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +68 -15
  12. package/build/godot/addons/gdharness_editor/tools/scene_kin.gd +346 -0
  13. package/build/godot/addons/gdharness_editor/tools/scene_kin.gd.uid +1 -0
  14. package/build/godot/addons/gdharness_editor/tools/scene_paths.gd +438 -0
  15. package/build/godot/addons/gdharness_editor/tools/scene_paths.gd.uid +1 -0
  16. package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +102 -60
  17. package/build/godot/addons/gdharness_runtime/reading.gd +40 -0
  18. package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +3 -1
  19. package/build/godot/addons/gdharness_runtime/runtime_changes.gd +2 -2
  20. package/build/godot/addons/gdharness_runtime/runtime_targets.gd +11 -6
  21. package/build/godot/addons/gdharness_runtime/runtime_values.gd +3 -1
  22. package/build/godot/addons/gdharness_runtime/serialisation.gd +5 -1
  23. package/build/godot/operations/animation_reads.gd +14 -2
  24. package/build/godot/operations/editor_tools.gd +28 -1
  25. package/build/godot/operations/file_reads.gd +134 -10
  26. package/build/godot/operations/godot_operations.gd +2 -1
  27. package/build/godot/operations/property_values.gd +1 -1
  28. package/build/godot/operations/reading.gd +40 -0
  29. package/build/godot/operations/serialisation.gd +5 -1
  30. package/build/index.js +103 -38
  31. package/package.json +1 -1
package/build/cli.js CHANGED
@@ -10969,6 +10969,34 @@ function scriptsWithoutUid(projectPath) {
10969
10969
  visit(projectPath, "");
10970
10970
  return found.sort();
10971
10971
  }
10972
+ function pathsInUidCache(projectPath) {
10973
+ let data;
10974
+ try {
10975
+ data = readFileSync9(join13(projectPath, ".godot", "uid_cache.bin"));
10976
+ } catch {
10977
+ return null;
10978
+ }
10979
+ if (data.length < 4) {
10980
+ return null;
10981
+ }
10982
+ const count = data.readUInt32LE(0);
10983
+ const paths = new Map;
10984
+ let at = 4;
10985
+ for (let entry = 0;entry < count; entry++) {
10986
+ if (at + 12 > data.length) {
10987
+ return null;
10988
+ }
10989
+ const id = data.readBigUInt64LE(at);
10990
+ const length = data.readUInt32LE(at + 8);
10991
+ at += 12;
10992
+ if (at + length > data.length) {
10993
+ return null;
10994
+ }
10995
+ paths.set(id, data.toString("utf8", at, at + length));
10996
+ at += length;
10997
+ }
10998
+ return new Set(paths.values());
10999
+ }
10972
11000
  function isBinary(bytes) {
10973
11001
  return bytes.subarray(0, 8000).includes(0);
10974
11002
  }
@@ -11871,7 +11899,7 @@ function annotationsOf(spec) {
11871
11899
  openWorldHint: false
11872
11900
  };
11873
11901
  }
11874
- var PROJECT_INFO_SECTIONS, PROJECT_PATH, RUNNING_PROJECT_PATH, RUNNING_PID, SCENE_PATH, WITHOUT_AN_EDITOR = "Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around.", WHOLE_NUMBERS = 'A whole number of 2^53 or more, such as a 64-bit seed or a resource uid, is written as its digits, {"_type": "int", "value": "9007199254740993"}, the form a read answers with: a JSON number that large can arrive as a neighbour of the one sent, so sent as a number it is refused.', SCRIPT_PATH, RESOURCE_FILE = 'File inside the project, such as "sprites/hero.png" or "materials/steel.tres".', RESOURCE_PATH, NODE_PATH, PROPERTIES, XY, ANIMATION_TRACK, SCRIPT_MODIFICATIONS, INPUT_EVENTS, TILESET_SOURCES, TILEMAP_CELLS, COLOUR2, TOOL_SPECS, SPECS_BY_NAME;
11902
+ var PROJECT_INFO_SECTIONS, PROJECT_PATH, RUNNING_PROJECT_PATH, RUNNING_PID, SCENE_PATH, WITHOUT_AN_EDITOR = "Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. Each file the written one refers to is named by its uid, as the editor names it, except a file the project's uid cache has no uid for yet (one made since the project was last scanned), which is named by path and listed under namedByPath with the call that scans it. An editor of the project that is running and has not reached this server is waited for rather than written around.", WHOLE_NUMBERS = 'A whole number of 2^53 or more, such as a 64-bit seed or a resource uid, is written as its digits, {"_type": "int", "value": "9007199254740993"}, the form a read answers with: a JSON number that large can arrive as a neighbour of the one sent, so sent as a number it is refused.', SCRIPT_PATH, RESOURCE_FILE = 'File inside the project, such as "sprites/hero.png" or "materials/steel.tres".', RESOURCE_PATH, NODE_PATH, PROPERTIES, XY, ANIMATION_TRACK, SCRIPT_MODIFICATIONS, INPUT_EVENTS, TILESET_SOURCES, TILEMAP_CELLS, COLOUR2, TOOL_SPECS, SPECS_BY_NAME;
11875
11903
  var init_tool_definitions = __esm(() => {
11876
11904
  init_launch();
11877
11905
  PROJECT_INFO_SECTIONS = [
@@ -11925,7 +11953,10 @@ var init_tool_definitions = __esm(() => {
11925
11953
  type: { type: "string", enum: ["property", "method"] },
11926
11954
  nodePath: { type: "string", description: "Target node, relative to the AnimationPlayer root." },
11927
11955
  property: { type: "string", description: "For property tracks: the property to animate." },
11928
- method: { type: "string", description: "For method tracks: the method to call." },
11956
+ method: {
11957
+ type: "string",
11958
+ description: "For method tracks: the method to call. The answer's methodFound says whether the target node has it; a method it lacks is still added, since it may be written next, and until it exists each call the track makes fails."
11959
+ },
11929
11960
  keyframes: {
11930
11961
  type: "array",
11931
11962
  items: {
@@ -12270,7 +12301,7 @@ var init_tool_definitions = __esm(() => {
12270
12301
  operations: {
12271
12302
  status: {
12272
12303
  effect: "read",
12273
- summary: "which resources are outdated or failed, or one resource with resourcePath, each with its reason. Judged by content with the checks the editor makes before it reimports: a sidecar changed since its import, as setting an option without a reimport leaves it; a sidecar with no uid or written for another file; an output under .godot/imported that is gone (missing_outputs) or changed; a source whose hash differs from the one its import recorded, or with no record. Beyond the editor, a glTF scene whose import does not depend on an image it names, because it was built before that image was imported (imported_without; a scene whose import runs an import script is not judged on images). failed is an import that failed, which the editor never tries again on its own. Without resourcePath, a sidecar whose source is gone is listed as missing_source",
12304
+ summary: "which resources are outdated or failed, or one resource with resourcePath, each with its reason and a status the summary counts: needs_reimport (out of date, or a file the engine imports that has never been imported), failed, up_to_date, missing_source, or not_imported (a file the engine does not import at all, with why). Judged by content with the checks the editor makes before it reimports: a sidecar changed since its import, as setting an option without a reimport leaves it; a sidecar with no uid or written for another file; an output under .godot/imported that is gone (missing_outputs) or changed; a source whose hash differs from the one its import recorded, or with no record. Beyond the editor, a glTF scene whose import does not depend on an image it names, because it was built before that image was imported (imported_without; a scene whose import runs an import script is not judged on images). failed is an import that failed, which the editor never tries again on its own. Without resourcePath, a sidecar whose source is gone is listed as missing_source",
12274
12305
  requires: []
12275
12306
  },
12276
12307
  options: { effect: "read", summary: "the import options of one resource", requires: ["resourcePath"] },
@@ -12291,7 +12322,7 @@ var init_tool_definitions = __esm(() => {
12291
12322
  },
12292
12323
  refresh_uids: {
12293
12324
  effect: "additive",
12294
- summary: "import the project so every script and shader has its .uid sidecar, and name under uidsCreated the ones this made and under stillWithoutUid the ones the engine would not import: it writes no scene and no script, so a project whose sidecars are all present is left untouched",
12325
+ summary: "import the project so every script and shader has its .uid sidecar and the uid cache holds the uid of every file, scenes made with no editor among them, which a headless write names a reference by (see namedByPath), and name under uidsCreated the sidecars this made, under uidsCached the res:// files whose uid the cache holds now and did not before, and under stillWithoutUid the ones the engine would not import: it writes no scene and no script, so a project whose sidecars are all present is left untouched",
12295
12326
  requires: []
12296
12327
  },
12297
12328
  refresh_classes: {
@@ -12390,7 +12421,12 @@ var init_tool_definitions = __esm(() => {
12390
12421
  rootNodeName: {
12391
12422
  type: "string",
12392
12423
  ops: ["create"],
12393
- description: "create: the root node name. Default the class name, as the editor names a new root."
12424
+ description: "create: the root node name. Default the class name, as the editor names a new root, or with inherits the name of that scene's root."
12425
+ },
12426
+ inherits: {
12427
+ type: "string",
12428
+ ops: ["create"],
12429
+ description: `create: a scene for the new one to inherit, such as "parts/crate.tscn", as the editor's New Inherited Scene makes one. Its root is an instance of that scene, so changes there reach it, and scene_node set gives the nodes it inherits values of this scene's own. The root's class is that scene's, so rootNodeType is not given with it; the answer names it under rootNodeType and the scene under inherits.`
12394
12430
  },
12395
12431
  script: { type: "string", ops: ["create"], description: "create: a script to attach to the root." },
12396
12432
  newPath: { type: "string", ops: ["save_as"], description: "save_as: where the copy goes." }
@@ -12411,11 +12447,14 @@ var init_tool_definitions = __esm(() => {
12411
12447
  name: "scene_tree",
12412
12448
  title: "Scene tree",
12413
12449
  effect: "read",
12414
- description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. A node that instances another scene names that file under instanceOf, and the nodes listed under it come from that file rather than this one. Needs no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without making its nodes, so no script of theirs runs (autoloads still start once it has answered, as in every headless call). For the nodes of a game that is running, runtime_inspect tree.",
12450
+ description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. A node that instances another scene names that file under instanceOf, and the nodes listed under it come from that file rather than this one; its editableChildren says whether this scene, or a scene it inherits, keeps values of its own for them (scene_node set editableChildren). Needs no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without making its nodes, so no script of theirs runs (autoloads still start once it has answered, as in every headless call). For the nodes of a game that is running, runtime_inspect tree.",
12415
12451
  parameters: {
12416
12452
  projectPath: PROJECT_PATH,
12417
12453
  scenePath: SCENE_PATH,
12418
- depth: { type: "number", description: "How many levels to descend. Default: all." },
12454
+ depth: {
12455
+ type: "number",
12456
+ description: "How many levels to descend. Default: all. A node at the last level that has children of its own lists none and says how many under childrenNotListed."
12457
+ },
12419
12458
  includeProperties: { type: "boolean", description: "Include each node's properties. Default false." }
12420
12459
  },
12421
12460
  requires: ["projectPath", "scenePath"]
@@ -12423,7 +12462,7 @@ var init_tool_definitions = __esm(() => {
12423
12462
  {
12424
12463
  name: "scene_node",
12425
12464
  title: "Scene nodes",
12426
- description: `One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type or script class can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {"name": "Boss"} renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. ${WITHOUT_AN_EDITOR}`,
12465
+ description: `One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type or script class can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {"name": "Boss"} renames a node, and answers nodePath as its new path. A rename or a reparent carries the node paths naming the node, as the editor's rename does: NodePath values on nodes (in arrays, dictionaries, built-in resources, %unique paths) and the tracks its players' animations hold, saving a library kept in a .tres of its own. Each rewrite is listed under retargeted ({scenePath, nodePath, property or animation and track, from, to}), and under notRetargeted, with a reason, each left: a track kept in another scene or an imported file, or a value another scene gives that the move left naming nothing. The move is followed into every other .tscn that instances or inherits this scene at any depth: the editable children, values, added nodes and connections named by the old path are rewritten, since Godot finds a moved node's values by unique_id but editable children only by path and drops the values kept below them on the next save, and that scene's node paths and tracks are carried as above. A reparent also drops the node ids saved along each rewritten path, which ran through the old parent; the next save writes them again. The scenes rewritten are named under followed, and under notFollowed any left as they were, with why. A delete rewrites no path and lists under stillNaming each node path and track, here and in the scenes instancing or inheriting this one, that named the deleted node and now names nothing. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene. A node this scene inherits takes values of this scene's own, and so does a node inside an instance of another scene once that instance's editableChildren is true; neither can be moved, renamed, copied or deleted, since the other scene makes it again on load. ${WITHOUT_AN_EDITOR}`,
12427
12466
  parameters: {
12428
12467
  projectPath: PROJECT_PATH,
12429
12468
  scenePath: SCENE_PATH,
@@ -12436,7 +12475,7 @@ var init_tool_definitions = __esm(() => {
12436
12475
  nodeType: {
12437
12476
  type: "string",
12438
12477
  ops: ["add"],
12439
- description: 'add: the node class, such as "CharacterBody2D", or a class_name a project script declares, which the node is made as with that script attached. Give this or instancePath.'
12478
+ description: `add: the node class, such as "CharacterBody2D", or a class_name a project script declares, which the node is made as with that script attached. Give this or instancePath. The answer's nodeType is the class of the node made, for an instance the class of its scene's root.`
12440
12479
  },
12441
12480
  instancePath: {
12442
12481
  type: "string",
@@ -12445,6 +12484,11 @@ var init_tool_definitions = __esm(() => {
12445
12484
  },
12446
12485
  nodeName: { type: "string", ops: ["add"], description: "add: the new node's name." },
12447
12486
  properties: { ...PROPERTIES, ops: ["add", "set"] },
12487
+ editableChildren: {
12488
+ type: "boolean",
12489
+ ops: ["set"],
12490
+ description: "set: on an instance of another scene, whether its children are editable here, as the editor's Editable Children makes them. True lets scene_node set give the nodes inside that instance values of this scene's own, which no other instance shares. An instance nested inside another takes it once the one around it has editable children. False drops every value this scene keeps for them, and every node it added below one of them. The answer names the nodes whose values went under droppedValues (they stay, with the instance's own values) and the added nodes that went under droppedNodes; a node added straight under the instance stays. The answer says editableChildren as the file now holds it."
12491
+ },
12448
12492
  newName: { type: "string", ops: ["duplicate"], description: "duplicate: the copy's name." },
12449
12493
  newParentPath: {
12450
12494
  type: "string",
@@ -12478,17 +12522,17 @@ var init_tool_definitions = __esm(() => {
12478
12522
  },
12479
12523
  get: {
12480
12524
  effect: "read",
12481
- summary: "read a node's properties, and a TileMapLayer's or TileMap's cells as set_tilemap_cells takes them, the first 1000 with cellCount for all",
12525
+ summary: "read a node's properties, and a TileMapLayer's or TileMap's cells as set_tilemap_cells takes them, the first 1000 with cellCount for all. An instance of another scene also answers instanceOf and editableChildren, as scene_tree does",
12482
12526
  requires: ["nodePath"]
12483
12527
  },
12484
12528
  set: {
12485
12529
  effect: "destructive",
12486
- summary: "set properties on a node",
12487
- requires: ["nodePath", "properties"]
12530
+ summary: "set properties on a node, or make the children of an instance editable; give either or both",
12531
+ requires: ["nodePath"]
12488
12532
  },
12489
12533
  duplicate: {
12490
12534
  effect: "additive",
12491
- summary: "copy a node and its children",
12535
+ summary: "copy a node and its children, beside it or under parentNodePath, answering the copy's newNodePath, parentNodePath and index",
12492
12536
  requires: ["nodePath", "newName"]
12493
12537
  },
12494
12538
  reparent: {
@@ -12507,7 +12551,7 @@ var init_tool_definitions = __esm(() => {
12507
12551
  {
12508
12552
  name: "scene_signal",
12509
12553
  title: "Scene signals",
12510
- description: `Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. ${WITHOUT_AN_EDITOR}`,
12554
+ description: `Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. A connect to a method the target does not have is still written, as the editor's own dialog allows, since the method may be written next; methodFound false says so, and until the method exists the signal fails each time it is emitted. list marks editable false on a connection an instanced scene makes inside itself, which only that scene can change. ${WITHOUT_AN_EDITOR}`,
12511
12555
  parameters: {
12512
12556
  projectPath: PROJECT_PATH,
12513
12557
  scenePath: SCENE_PATH,
@@ -12552,7 +12596,7 @@ var init_tool_definitions = __esm(() => {
12552
12596
  {
12553
12597
  name: "scene_animation",
12554
12598
  title: "Animations",
12555
- description: "Animations in an AnimationPlayer and states in an AnimationTree state machine. read answers with every track of an animation in the shape add_track takes, so a track read off one animation can be added to another. read and read_states need no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without running the scene's scripts. The ops that write need the editor connected.",
12599
+ description: "Animations in an AnimationPlayer and states in an AnimationTree state machine. read answers with every track of an animation in the shape add_track takes, so a track read off one animation can be added to another. read and read_states need no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without running the scene's scripts. The ops that write go through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. An editor of the project that is running and has not reached this server is waited for rather than written around.",
12556
12600
  parameters: {
12557
12601
  projectPath: PROJECT_PATH,
12558
12602
  scenePath: SCENE_PATH,
@@ -12564,7 +12608,7 @@ var init_tool_definitions = __esm(() => {
12564
12608
  animationName: {
12565
12609
  type: "string",
12566
12610
  ops: ["read", "create", "add_track", "add_state"],
12567
- description: "The animation in the player. read: the one to answer with whole; without it, read lists them. add_state: the one that state plays."
12611
+ description: "The animation in the player. read: the one to answer with whole, its tracks a list; without it, read lists them, each with the number of its tracks under tracks. add_state: the one that state plays."
12568
12612
  },
12569
12613
  length: { type: "number", ops: ["create"], description: "create: seconds. Default 1." },
12570
12614
  loopMode: {
@@ -12819,7 +12863,7 @@ var init_tool_definitions = __esm(() => {
12819
12863
  controlType: {
12820
12864
  type: "string",
12821
12865
  ops: ["set_theme_color", "set_theme_font_size"],
12822
- description: 'Theme ops: the Control class, such as "Button".'
12866
+ description: 'Theme ops: the theme type, a Control or Window class such as "Button", or the name of a type variation, which can be any name. A name that is no such class is set as a variation and answered with a note saying so.'
12823
12867
  },
12824
12868
  colorName: {
12825
12869
  type: "string",
@@ -12832,7 +12876,11 @@ var init_tool_definitions = __esm(() => {
12832
12876
  ops: ["set_theme_font_size"],
12833
12877
  description: 'set_theme_font_size: such as "font_size".'
12834
12878
  },
12835
- size: { type: "number", ops: ["set_theme_font_size"], description: "set_theme_font_size: pixels." }
12879
+ size: {
12880
+ type: "number",
12881
+ ops: ["set_theme_font_size"],
12882
+ description: "set_theme_font_size: pixels, a whole number of 1 or more."
12883
+ }
12836
12884
  },
12837
12885
  requires: ["projectPath", "resourcePath"],
12838
12886
  operations: {
@@ -12861,7 +12909,7 @@ var init_tool_definitions = __esm(() => {
12861
12909
  },
12862
12910
  move: {
12863
12911
  effect: "destructive",
12864
- summary: "move or rename a file or a directory, and every path in the project naming it",
12912
+ summary: "move or rename a file or a directory, and every path in the project naming it. With no editor an import pass then rebuilds the project's uid cache, so a reference by uid leads to the new path, and uidTable says whether it ran",
12865
12913
  requires: ["newPath"]
12866
12914
  }
12867
12915
  }
@@ -13037,7 +13085,7 @@ var init_tool_definitions = __esm(() => {
13037
13085
  requires: []
13038
13086
  }
13039
13087
  },
13040
- answer: "start: scene is the scene the game started in, the project's main scene when none was given. refreshedClasses names the class_name declarations on disk that the engine's class cache was missing, which were added to it before the game started so the game could resolve them; it is empty when the cache was current. stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. The runtime addon's own functions, gdharness answering its tools inside the game, are not among them: their self time is runtimeAddonMs, over runtimeAddonFunctions functions. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. frames counts the frames of the profile that arrived, and the engine's totals also hold the work after the last of them, so once complete a function run every frame can show a call more than frames. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked. A profile with mark answers marked: true; one with since answers since: \"mark\" and markSetMsAgo, and lessThanAtMark counts the functions left out for reading less than at the mark, which the engine's own totals, capped, can do when they replace the frames' sum.",
13088
+ answer: "start: through is gdharness for a game this server started itself, and editor for one the editor plays. scene is the scene the game started in, the project's main scene when none was given. refreshedClasses names the class_name declarations on disk that the engine's class cache was missing, which were added to it before the game started so the game could resolve them; it is empty when the cache was current. stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. The runtime addon's own functions, gdharness answering its tools inside the game, are not among them: their self time is runtimeAddonMs, over runtimeAddonFunctions functions. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. frames counts the frames of the profile that arrived, and the engine's totals also hold the work after the last of them, so once complete a function run every frame can show a call more than frames. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked. A profile with mark answers marked: true; one with since answers since: \"mark\" and markSetMsAgo, and lessThanAtMark counts the functions left out for reading less than at the mark, which the engine's own totals, capped, can do when they replace the frames' sum.",
13041
13089
  defaultOperation: "start"
13042
13090
  },
13043
13091
  {
@@ -13099,8 +13147,8 @@ var init_tool_definitions = __esm(() => {
13099
13147
  name: "editor_status",
13100
13148
  title: "Editor status",
13101
13149
  effect: "read",
13102
- description: "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and which games with the runtime addon are reachable, of this project and of every other on the machine. Read connected with mayYetConnect: the editor dials this server and backs off between tries, so for the first half-minute after a server starts false means not reached yet as often as no editor; mayYetConnect true is worth waiting out, false is final, and editorCannotConnect, restartInterrupted, restartUnderway, closedHere and editorsNotYetConnected say why. An editor that has connected and not yet greeted reports nothing about itself, and every other call made in that window waits up to ten seconds for it to settle. playingInEditor null means unanswered rather than nothing playing: processActive and runtimes do not depend on the editor answering. game.run names the run editor_output and editor_run stop answer for, and stays named after it ends. addonIsStale, on the editor and on each game, says it runs addon code other than what this server ships, and staleNote says which half moves: the project, by a gdharness upgrade it spells out to run in a shell, the editor by a restart, or this server by a reconnect. A false editor.lspListening or editor.dapListening comes with serversNote, and editor_launch restart binds the port again. editor.scan says whether the editor is scanning, which editor_run waits out before starting a game. A game announced in a protocol this server does not speak is listed under unreadable, during an upgrade that moves the protocol, until the halves match.",
13103
- answer: "server names this server: its pid, its version, and lifecycleLog, the file every gdharness server of the user appends a line to when it starts, when its client first names itself, when it replaces another server of its project, and when it ends, with why it ended (the client closing its end as stdin:end, a signal by name, superseded with the successor's pid, an uncaught exception or rejection with its stack, or the exit code) and the calls it was answering then. The parent's command line follows on its own line a few seconds after the start. A server killed without warning writes no ended line, and on Windows every kill is that, so a started line with no ended line after it is a server something killed. " + "runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. Its play says which play the editor is on: play.play counts the plays that editor has started since it opened, whoever started them, and play.editorPid is the editor, so two reads with different pairs are two plays even when both say playing. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it. It stays named after the run ends, beside processActive false, so editor_output can still read what that run printed; null when there is no run at all. startedAt says when it began, and endedAt when it ended, wherever something saw the end: a run adopted from a note another server wrote can have ended hours before this server started. editor.lspListening and editor.dapListening say whether anything accepts connections on the ports the editor names, lspPort and dapPort: Godot binds each once, as the editor starts, and reports a failure only in the log panel of the editor itself, so a false comes with serversNote, and editor_launch restart binds it again. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs the gdharness upgrade command, spelled out to run in a shell, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. Every runtime_* answer from such a game carries addonIsStale and staleNote too. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside editorCannotConnect is a project without the gdharness editor addon installed or enabled, where no editor can ever dial in, however young the server or whichever editor is open; its remedies say how to install or enable it. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and while that is read identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window.",
13150
+ description: "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and which games with the runtime addon are reachable, of this project and of every other on the machine. Read connected with mayYetConnect: the editor dials this server and backs off between tries, so for the first half-minute after a server starts false means not reached yet as often as no editor; mayYetConnect true is worth waiting out, false is final, and editorCannotConnect, restartInterrupted, restartUnderway, closedHere and editorsNotYetConnected say why. An editor that has connected and not yet greeted reports nothing about itself, and every other call made in that window waits up to ten seconds for it to settle. playingInEditor null means unanswered rather than nothing playing: processActive and runtimes do not depend on the editor answering. game.run names the run editor_output and editor_run stop answer for, and stays named after it ends. addonIsStale, on a connected editor and on each running game (absent with neither), says it runs addon code other than what this server ships, and staleNote says which half moves: the project, by a gdharness upgrade it spells out to run in a shell, the editor by a restart, or this server by a reconnect. A false editor.lspListening or editor.dapListening comes with serversNote, and editor_launch restart binds the port again. editor.scan says whether the editor is scanning, which editor_run waits out before starting a game. A game announced in a protocol this server does not speak is listed under unreadable, during an upgrade that moves the protocol, until the halves match.",
13151
+ answer: "projectIs, when present, is the gdharness version of the addons in the project, which differs from this server's (project_info says which of the two to move), and configIs, when present, the version the project's MCP config starts, which differs from this server's, so the next connection runs that one. server names this server: its pid, its version, and lifecycleLog, the file every gdharness server of the user appends a line to when it starts, when its client first names itself, when it replaces another server of its project, and when it ends, with why it ended (the client closing its end as stdin:end, a signal by name, superseded with the successor's pid, an uncaught exception or rejection with its stack, or the exit code) and the calls it was answering then. The parent's command line follows on its own line a few seconds after the start. A server killed without warning writes no ended line, and on Windows every kill is that, so a started line with no ended line after it is a server something killed. " + "runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. Its play says which play the editor is on: play.play counts the plays that editor has started since it opened, whoever started them, and play.editorPid is the editor, so two reads with different pairs are two plays even when both say playing. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it. It stays named after the run ends, beside processActive false, so editor_output can still read what that run printed; null when there is no run at all. startedAt says when it began, and endedAt when it ended, wherever something saw the end: a run adopted from a note another server wrote can have ended hours before this server started. editor.lspListening and editor.dapListening say whether anything accepts connections on the ports the editor names, lspPort and dapPort: Godot binds each once, as the editor starts, and reports a failure only in the log panel of the editor itself, so a false comes with serversNote, and editor_launch restart binds it again. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs the gdharness upgrade command, spelled out to run in a shell, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. Every runtime_* answer from such a game carries addonIsStale and staleNote too. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside editorCannotConnect is a project without the gdharness editor addon installed or enabled, where no editor can ever dial in, however young the server or whichever editor is open; its remedies say how to install or enable it. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and while that is read identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window. serverVersion is the gdharness version of this server. bridgeAvailable says the editor bridge is listening; when it failed to start, startupError gives the error, and retryingBridge is true while this server tries again. announcedAt is the file where this server told editors of its project which port to dial. connectedAt is when the editor connected, and lastPongAt when it last answered this server's keep-alive ping. addonDigest is a hash of the editor addon's code, which addonIsStale compares with what this server ships. debugPort is the port of the editor's remote debugger, the one games it plays connect to. openedByAServer says a gdharness server started this editor, and syncsBreakpoints that it keeps its breakpoints when a debug session opens, which Godot otherwise clears. pendingRequests counts the requests sent to the editor and not yet answered, and queuedResources the files with writes waiting behind another write to the same file.",
13104
13152
  parameters: {},
13105
13153
  requires: []
13106
13154
  },
@@ -13425,7 +13473,7 @@ var init_tool_definitions = __esm(() => {
13425
13473
  name: "runtime_input",
13426
13474
  title: "Send input to the game",
13427
13475
  description: "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button, mouse motion or drag. All of it works headless, where the viewport is the project's window size and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. A native window no real pointer has entered gets no hover from Godot, so the runtime supplies it there and the answers say hover_supplied.",
13428
- answer: 'click: what was under the pointer, and what became of the control: in_tree, removed or freed. Named by says, found says which match it was, found.item names the tab, list entry, tree row or menu bar title pressed (kind, index where its control numbers them, says, and for a row the column of the cell saying the words), and found.pressedOn says "the words" for words in a RichTextLabel pressed where they are drawn, or "the middle of the label" with a note saying why they could not be placed; on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control scrolled into view first is scrolled each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A refusal for a pointer turned off names where mouse_behavior_recursive was set. On a 3D node landed says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room. choose: what was chosen, read before the press, which is heard rather than assumed; what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick); and what the button in front of the menu shows now when it is still there. A menu is opened rather than clicked because its items are drawn rather than built, and the item is looked for as the menu stands once open, since a game can fill a menu as it opens. action, key, mouse_click and joypad_button: the event as it was sent, with whole saying whether the press was let go of again, for a key the keycode, physical_keycode and unicode it carried, and for a controller button its number, its name and the device. mouse_motion: the position and the relative movement sent. mouse_drag: from, to, motions sent and relative_total. joypad_motion: the axis by number and name, the value and the device. None of these is aimed at anything, so none says what the event reached: click on a nodePath or says answers what was under the pointer.',
13476
+ answer: `click: what was under the pointer, and what became of the control: in_tree, removed or freed. Named by says, found says which match it was, its index being the index argument that presses the same match again, found.item names the tab, list entry, tree row or menu bar title pressed (kind; index, the item's own number in its control, which is not found.index; says; and for a row the column of the cell saying the words), and found.pressedOn says "the words" for words in a RichTextLabel pressed where they are drawn, or "the middle of the label" with a note saying why they could not be placed; on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control scrolled into view first is scrolled each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A refusal for a pointer turned off names where mouse_behavior_recursive was set. On a 3D node landed says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room. choose: what was chosen, read before the press, which is heard rather than assumed; what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick); and what the button in front of the menu shows now when it is still there. A menu is opened rather than clicked because its items are drawn rather than built, and the item is looked for as the menu stands once open, since a game can fill a menu as it opens. action, key, mouse_click and joypad_button: the event as it was sent, with whole saying whether the press was let go of again, for a key the keycode, physical_keycode and unicode it carried, and for a controller button its number, its name and the device. mouse_motion: the position and the relative movement sent. mouse_drag: from, to, motions sent and relative_total. joypad_motion: the axis by number and name, the value and the device. None of these is aimed at anything, so none says what the event reached: click on a nodePath or says answers what was under the pointer.`,
13429
13477
  parameters: {
13430
13478
  projectPath: RUNNING_PROJECT_PATH,
13431
13479
  pid: RUNNING_PID,
@@ -35936,6 +35984,22 @@ function keepLate(id, answer) {
35936
35984
  function lateAnswer(id) {
35937
35985
  return lateAnswers.get(id);
35938
35986
  }
35987
+ function notSent(game, command, timeoutMs, refusals, dialling) {
35988
+ if (refusals > 0) {
35989
+ const refused = dialling ? `it refused ${refusals === 1 ? "one connection" : `${refusals} connections`} and had not accepted the next when the wait ended, after ${timeoutMs}ms` : `it refused ${refusals === 1 ? "the connection" : `all ${refusals} connections tried`} within ${timeoutMs}ms`;
35990
+ return {
35991
+ ok: false,
35992
+ reason: "refused",
35993
+ message: `The game (${game}) is running but nothing answered on its port: ${refused}. Nothing listens there while a game is starting or shutting down, and the engine refuses connections while more are waiting for the game than the eight it queues, which several servers calling one game at once, or a long frame or a breakpoint holding it, can make it do. Nothing was sent, so nothing ran.`
35994
+ };
35995
+ }
35996
+ const why = dialling ? "its port had not accepted the connection, which is how Linux and macOS hold one while the game's queue is full, as it is when the game has not taken the connections before it during a long frame or at a breakpoint" : `${UNTAKEN_PER_GAME} connections from this server were already waiting for the game to take them, which it does between frames, so a long frame or a breakpoint holds them`;
35997
+ return {
35998
+ ok: false,
35999
+ reason: "busy",
36000
+ message: `'${command}' was not sent to the game (${game}) within ${timeoutMs}ms: ${why}. Nothing was sent, so nothing ran.`
36001
+ };
36002
+ }
35939
36003
  function placeOf(endpoint) {
35940
36004
  return `${dialAddress(endpoint.address)}:${endpoint.port}`;
35941
36005
  }
@@ -35974,6 +36038,7 @@ function runtimeRequest(endpoint, command, params, timeoutMs, afterTheWait = "dr
35974
36038
  let buffered = "";
35975
36039
  let welcomed = false;
35976
36040
  let socket = null;
36041
+ let connected = false;
35977
36042
  let refusals = 0;
35978
36043
  let redial = null;
35979
36044
  let giveBackThePlace = null;
@@ -36010,16 +36075,8 @@ function runtimeRequest(endpoint, command, params, timeoutMs, afterTheWait = "dr
36010
36075
  settle(reply);
36011
36076
  };
36012
36077
  const timer = setTimeout(() => {
36013
- if (socket === null) {
36014
- finish(refusals > 0 ? {
36015
- ok: false,
36016
- reason: "refused",
36017
- message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: it refused ${refusals === 1 ? "the connection" : `all ${refusals} connections tried`} within ${timeoutMs}ms. Nothing listens there while a game is starting or shutting down, and the engine refuses connections while more are waiting for the game than the eight it queues, which several servers calling one game at once, or a long frame or a breakpoint holding it, can make it do. Nothing was sent, so nothing ran.`
36018
- } : {
36019
- ok: false,
36020
- reason: "busy",
36021
- message: `'${command}' was not sent to the game (${describe2(endpoint)}) within ${timeoutMs}ms: ${UNTAKEN_PER_GAME} connections from this server were already waiting for the game to take them, which it does between frames, so a long frame or a breakpoint holds them. Nothing was sent, so nothing ran.`
36022
- });
36078
+ if (socket === null || !connected) {
36079
+ finish(notSent(describe2(endpoint), command, timeoutMs, refusals, socket !== null));
36023
36080
  return;
36024
36081
  }
36025
36082
  const still = welcomed ? `The game (${describe2(endpoint)}) has the request and did not answer '${command}' within ${timeoutMs}ms.` : `The game (${describe2(endpoint)}) did not answer '${command}' within ${timeoutMs}ms and had not taken the request: its frame has not ended since it was sent, which a long frame or a breakpoint does, and the request runs when it does.`;
@@ -36066,6 +36123,7 @@ function runtimeRequest(endpoint, command, params, timeoutMs, afterTheWait = "dr
36066
36123
  socket = opened;
36067
36124
  opened.setEncoding("utf8");
36068
36125
  opened.on("connect", () => {
36126
+ connected = true;
36069
36127
  opened.write(`${JSON.stringify({ id, command, params })}
36070
36128
  `);
36071
36129
  });
@@ -46629,7 +46687,7 @@ class GodotServer {
46629
46687
  project_addons_behind_this_server: {
46630
46688
  server_is: SERVER_VERSION,
46631
46689
  project_is: moved,
46632
- what_to_do: `The project's gdharness addons are ${moved}, older than this ${SERVER_VERSION} server, so an ` + "editor restart loads the old code and reconnecting spawns this same server again. Tell the " + `user, and offer to run ${gdharnessCommand(SERVER_VERSION, this.ownProject === null ? "upgrade" : `upgrade "${this.ownProject}"`)} ` + "in a shell, which replaces the addons with this server's; ask first, since it changes " + "files in their project. editor_launch restart then loads them."
46690
+ what_to_do: `The project's gdharness addons are ${moved}, older than this ${SERVER_VERSION} server. ` + "Restarting the editor would load that older addon code again, and reconnecting would start " + "this same server version again, so neither brings the two into step. Tell the " + `user, and offer to run ${gdharnessCommand(SERVER_VERSION, this.ownProject === null ? "upgrade" : `upgrade "${this.ownProject}"`)} ` + "in a shell, which replaces the addons with this server's; ask first, since it changes " + "files in their project. editor_launch restart then loads them."
46633
46691
  }
46634
46692
  });
46635
46693
  }
@@ -46901,13 +46959,13 @@ class GodotServer {
46901
46959
  stateMachinePath: readString(args, "stateMachinePath") ?? ""
46902
46960
  }), async () => await bridge("read_animation_states"));
46903
46961
  case "create":
46904
- return await bridge("create_animation");
46962
+ return await editorOrEngine("create_animation", "writes");
46905
46963
  case "add_track":
46906
- return await bridge("add_animation_track");
46964
+ return await editorOrEngine("add_animation_track", "writes");
46907
46965
  case "add_state":
46908
- return await bridge("add_animation_state");
46966
+ return await editorOrEngine("add_animation_state", "writes");
46909
46967
  default:
46910
- return await bridge("connect_animation_states");
46968
+ return await editorOrEngine("connect_animation_states", "writes");
46911
46969
  }
46912
46970
  case "script_edit":
46913
46971
  return await this.handleRenameSymbol(args);
@@ -47458,18 +47516,24 @@ class GodotServer {
47458
47516
  const projectPath = project.value.path;
47459
47517
  const scanned = await this.untilTheEditorsScanIsWritten(projectPath);
47460
47518
  const before = scriptsWithoutUid(projectPath);
47519
+ const cachedBefore = pathsInUidCache(projectPath);
47461
47520
  const imported = await runImport(engine.value, projectPath);
47462
47521
  if (!imported.ok) {
47463
47522
  return this.answer(withScanWait(imported, scanned));
47464
47523
  }
47465
47524
  const after = scriptsWithoutUid(projectPath);
47466
47525
  const given = before.filter((script) => !after.includes(script));
47526
+ const cachedAfter = pathsInUidCache(projectPath);
47527
+ const cached = cachedAfter === null ? null : [...cachedAfter].filter((path) => cachedBefore?.has(path) !== true).sort();
47467
47528
  return this.answer(withScanWait({
47468
47529
  ok: true,
47469
47530
  messages: imported.messages,
47470
47531
  ...imported.messagesOmitted === undefined ? {} : { messagesOmitted: imported.messagesOmitted },
47471
47532
  payload: {
47472
47533
  uidsCreated: given,
47534
+ ...cached === null ? {
47535
+ uidCacheNote: "The import pass left no uid cache this server could read, .godot/uid_cache.bin."
47536
+ } : { uidsCached: cached },
47473
47537
  stillWithoutUid: after,
47474
47538
  note: uidsLeftNote(after.length),
47475
47539
  ...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote },
@@ -52381,6 +52445,7 @@ var init_server = __esm(() => {
52381
52445
  PROJECT_FILE_ARGUMENTS = [
52382
52446
  "scenePath",
52383
52447
  "instancePath",
52448
+ "inherits",
52384
52449
  "scriptPath",
52385
52450
  "resourcePath",
52386
52451
  "newPath",
@@ -5,6 +5,7 @@ extends RefCounted
5
5
  ## stores, which runs no script of the scene's, so a read answers the same either way.
6
6
 
7
7
  const FileReads = preload("file_reads.gd")
8
+ const Read = preload("reading.gd")
8
9
  const Serialisation = preload("serialisation.gd")
9
10
 
10
11
  const LOOP_MODE_NAMES: Dictionary[int, String] = {
@@ -60,7 +61,15 @@ static func _bare(scene_path: String, node_path: String, kind: String) -> Array:
60
61
  return [null, read]
61
62
  var type: String = str(read.get("type", ""))
62
63
  if not ClassDB.class_exists(type) or not ClassDB.is_parent_class(type, kind):
63
- return [null, {"ok": false, "error": "%s not found at: %s" % [kind, node_path]}]
64
+ var said: String = "%s not found at: %s" % [kind, node_path]
65
+ if read.is_empty():
66
+ var hinted: String = said
67
+ if FileReads.scene_node(scene_path, node_path.get_slice("/", 0)).is_empty():
68
+ hinted = Read.from_the_root(
69
+ said, node_path, str(FileReads.scene_node(scene_path, ".").get("name", ""))
70
+ )
71
+ said = hinted if hinted != said else FileReads.missing_from(said, scene_path, node_path)
72
+ return [null, {"ok": false, "error": said}]
64
73
  var bare: Node = ClassDB.instantiate(type)
65
74
  bare.name = str(read["name"])
66
75
  var given: Dictionary = read["properties"]
@@ -164,7 +173,10 @@ static func state_machine(tree: AnimationTree, state_machine_path: String) -> An
164
173
  ## the path that failed, and what is there instead.
165
174
  static func no_machine(tree: AnimationTree, state_machine_path: String) -> String:
166
175
  if tree == null or tree.tree_root == null:
167
- return "The AnimationTree has no tree_root."
176
+ return (
177
+ "The AnimationTree has no tree_root. Make one with resource_edit create of an "
178
+ + "AnimationNodeStateMachine, then give its res:// path to tree_root with scene_node set."
179
+ )
168
180
  var current: AnimationNodeStateMachine = tree.tree_root as AnimationNodeStateMachine
169
181
  if current == null:
170
182
  return (
@@ -544,7 +544,8 @@ func send_tool_result(request_id: String, success: bool, result: Variant = null,
544
544
 
545
545
  func _send_message(message: Dictionary) -> void:
546
546
  if socket.get_ready_state() == WebSocketPeer.STATE_OPEN:
547
- var sent: Error = socket.send_text(JSON.stringify(message))
547
+ # Keys in the order they were put in, which for a map read is the order the project keeps.
548
+ var sent: Error = socket.send_text(JSON.stringify(message, "", false))
548
549
  if sent != OK:
549
550
  push_error("gdharness: could not send to the server: " + error_string(sent))
550
551