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/index.js CHANGED
@@ -25128,6 +25128,34 @@ function scriptsWithoutUid(projectPath) {
25128
25128
  visit(projectPath, "");
25129
25129
  return found.sort();
25130
25130
  }
25131
+ function pathsInUidCache(projectPath) {
25132
+ let data;
25133
+ try {
25134
+ data = readFileSync3(join2(projectPath, ".godot", "uid_cache.bin"));
25135
+ } catch {
25136
+ return null;
25137
+ }
25138
+ if (data.length < 4) {
25139
+ return null;
25140
+ }
25141
+ const count = data.readUInt32LE(0);
25142
+ const paths = new Map;
25143
+ let at = 4;
25144
+ for (let entry = 0;entry < count; entry++) {
25145
+ if (at + 12 > data.length) {
25146
+ return null;
25147
+ }
25148
+ const id = data.readBigUInt64LE(at);
25149
+ const length = data.readUInt32LE(at + 8);
25150
+ at += 12;
25151
+ if (at + length > data.length) {
25152
+ return null;
25153
+ }
25154
+ paths.set(id, data.toString("utf8", at, at + length));
25155
+ at += length;
25156
+ }
25157
+ return new Set(paths.values());
25158
+ }
25131
25159
  function isBinary(bytes) {
25132
25160
  return bytes.subarray(0, 8000).includes(0);
25133
25161
  }
@@ -29305,6 +29333,22 @@ function lateAnswer(id) {
29305
29333
  }
29306
29334
  var UNTAKEN_PER_GAME = 4;
29307
29335
  var REDIAL_MS = 50;
29336
+ function notSent(game, command, timeoutMs, refusals, dialling) {
29337
+ if (refusals > 0) {
29338
+ 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`;
29339
+ return {
29340
+ ok: false,
29341
+ reason: "refused",
29342
+ 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.`
29343
+ };
29344
+ }
29345
+ 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`;
29346
+ return {
29347
+ ok: false,
29348
+ reason: "busy",
29349
+ message: `'${command}' was not sent to the game (${game}) within ${timeoutMs}ms: ${why}. Nothing was sent, so nothing ran.`
29350
+ };
29351
+ }
29308
29352
  var untaken = new Map;
29309
29353
  function placeOf(endpoint) {
29310
29354
  return `${dialAddress(endpoint.address)}:${endpoint.port}`;
@@ -29344,6 +29388,7 @@ function runtimeRequest(endpoint, command, params, timeoutMs, afterTheWait = "dr
29344
29388
  let buffered = "";
29345
29389
  let welcomed = false;
29346
29390
  let socket = null;
29391
+ let connected = false;
29347
29392
  let refusals = 0;
29348
29393
  let redial = null;
29349
29394
  let giveBackThePlace = null;
@@ -29380,16 +29425,8 @@ function runtimeRequest(endpoint, command, params, timeoutMs, afterTheWait = "dr
29380
29425
  settle(reply);
29381
29426
  };
29382
29427
  const timer = setTimeout(() => {
29383
- if (socket === null) {
29384
- finish(refusals > 0 ? {
29385
- ok: false,
29386
- reason: "refused",
29387
- 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.`
29388
- } : {
29389
- ok: false,
29390
- reason: "busy",
29391
- 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.`
29392
- });
29428
+ if (socket === null || !connected) {
29429
+ finish(notSent(describe2(endpoint), command, timeoutMs, refusals, socket !== null));
29393
29430
  return;
29394
29431
  }
29395
29432
  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.`;
@@ -29436,6 +29473,7 @@ function runtimeRequest(endpoint, command, params, timeoutMs, afterTheWait = "dr
29436
29473
  socket = opened;
29437
29474
  opened.setEncoding("utf8");
29438
29475
  opened.on("connect", () => {
29476
+ connected = true;
29439
29477
  opened.write(`${JSON.stringify({ id, command, params })}
29440
29478
  `);
29441
29479
  });
@@ -35498,7 +35536,7 @@ var SCENE_PATH = {
35498
35536
  type: "string",
35499
35537
  description: 'Scene file inside the project, such as "scenes/main.tscn" or "res://scenes/main.tscn".'
35500
35538
  };
35501
- var 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.";
35539
+ var 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.";
35502
35540
  var 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.';
35503
35541
  var SCRIPT_PATH = {
35504
35542
  type: "string",
@@ -35528,7 +35566,10 @@ var ANIMATION_TRACK = {
35528
35566
  type: { type: "string", enum: ["property", "method"] },
35529
35567
  nodePath: { type: "string", description: "Target node, relative to the AnimationPlayer root." },
35530
35568
  property: { type: "string", description: "For property tracks: the property to animate." },
35531
- method: { type: "string", description: "For method tracks: the method to call." },
35569
+ method: {
35570
+ type: "string",
35571
+ 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."
35572
+ },
35532
35573
  keyframes: {
35533
35574
  type: "array",
35534
35575
  items: {
@@ -35873,7 +35914,7 @@ var TOOL_SPECS = [
35873
35914
  operations: {
35874
35915
  status: {
35875
35916
  effect: "read",
35876
- 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",
35917
+ 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",
35877
35918
  requires: []
35878
35919
  },
35879
35920
  options: { effect: "read", summary: "the import options of one resource", requires: ["resourcePath"] },
@@ -35894,7 +35935,7 @@ var TOOL_SPECS = [
35894
35935
  },
35895
35936
  refresh_uids: {
35896
35937
  effect: "additive",
35897
- 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",
35938
+ 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",
35898
35939
  requires: []
35899
35940
  },
35900
35941
  refresh_classes: {
@@ -35993,7 +36034,12 @@ var TOOL_SPECS = [
35993
36034
  rootNodeName: {
35994
36035
  type: "string",
35995
36036
  ops: ["create"],
35996
- description: "create: the root node name. Default the class name, as the editor names a new root."
36037
+ 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."
36038
+ },
36039
+ inherits: {
36040
+ type: "string",
36041
+ ops: ["create"],
36042
+ 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.`
35997
36043
  },
35998
36044
  script: { type: "string", ops: ["create"], description: "create: a script to attach to the root." },
35999
36045
  newPath: { type: "string", ops: ["save_as"], description: "save_as: where the copy goes." }
@@ -36014,11 +36060,14 @@ var TOOL_SPECS = [
36014
36060
  name: "scene_tree",
36015
36061
  title: "Scene tree",
36016
36062
  effect: "read",
36017
- 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.",
36063
+ 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.",
36018
36064
  parameters: {
36019
36065
  projectPath: PROJECT_PATH2,
36020
36066
  scenePath: SCENE_PATH,
36021
- depth: { type: "number", description: "How many levels to descend. Default: all." },
36067
+ depth: {
36068
+ type: "number",
36069
+ 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."
36070
+ },
36022
36071
  includeProperties: { type: "boolean", description: "Include each node's properties. Default false." }
36023
36072
  },
36024
36073
  requires: ["projectPath", "scenePath"]
@@ -36026,7 +36075,7 @@ var TOOL_SPECS = [
36026
36075
  {
36027
36076
  name: "scene_node",
36028
36077
  title: "Scene nodes",
36029
- 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}`,
36078
+ 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}`,
36030
36079
  parameters: {
36031
36080
  projectPath: PROJECT_PATH2,
36032
36081
  scenePath: SCENE_PATH,
@@ -36039,7 +36088,7 @@ var TOOL_SPECS = [
36039
36088
  nodeType: {
36040
36089
  type: "string",
36041
36090
  ops: ["add"],
36042
- 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.'
36091
+ 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.`
36043
36092
  },
36044
36093
  instancePath: {
36045
36094
  type: "string",
@@ -36048,6 +36097,11 @@ var TOOL_SPECS = [
36048
36097
  },
36049
36098
  nodeName: { type: "string", ops: ["add"], description: "add: the new node's name." },
36050
36099
  properties: { ...PROPERTIES, ops: ["add", "set"] },
36100
+ editableChildren: {
36101
+ type: "boolean",
36102
+ ops: ["set"],
36103
+ 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."
36104
+ },
36051
36105
  newName: { type: "string", ops: ["duplicate"], description: "duplicate: the copy's name." },
36052
36106
  newParentPath: {
36053
36107
  type: "string",
@@ -36081,17 +36135,17 @@ var TOOL_SPECS = [
36081
36135
  },
36082
36136
  get: {
36083
36137
  effect: "read",
36084
- 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",
36138
+ 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",
36085
36139
  requires: ["nodePath"]
36086
36140
  },
36087
36141
  set: {
36088
36142
  effect: "destructive",
36089
- summary: "set properties on a node",
36090
- requires: ["nodePath", "properties"]
36143
+ summary: "set properties on a node, or make the children of an instance editable; give either or both",
36144
+ requires: ["nodePath"]
36091
36145
  },
36092
36146
  duplicate: {
36093
36147
  effect: "additive",
36094
- summary: "copy a node and its children",
36148
+ summary: "copy a node and its children, beside it or under parentNodePath, answering the copy's newNodePath, parentNodePath and index",
36095
36149
  requires: ["nodePath", "newName"]
36096
36150
  },
36097
36151
  reparent: {
@@ -36110,7 +36164,7 @@ var TOOL_SPECS = [
36110
36164
  {
36111
36165
  name: "scene_signal",
36112
36166
  title: "Scene signals",
36113
- 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}`,
36167
+ 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}`,
36114
36168
  parameters: {
36115
36169
  projectPath: PROJECT_PATH2,
36116
36170
  scenePath: SCENE_PATH,
@@ -36155,7 +36209,7 @@ var TOOL_SPECS = [
36155
36209
  {
36156
36210
  name: "scene_animation",
36157
36211
  title: "Animations",
36158
- 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.",
36212
+ 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.",
36159
36213
  parameters: {
36160
36214
  projectPath: PROJECT_PATH2,
36161
36215
  scenePath: SCENE_PATH,
@@ -36167,7 +36221,7 @@ var TOOL_SPECS = [
36167
36221
  animationName: {
36168
36222
  type: "string",
36169
36223
  ops: ["read", "create", "add_track", "add_state"],
36170
- 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."
36224
+ 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."
36171
36225
  },
36172
36226
  length: { type: "number", ops: ["create"], description: "create: seconds. Default 1." },
36173
36227
  loopMode: {
@@ -36422,7 +36476,7 @@ var TOOL_SPECS = [
36422
36476
  controlType: {
36423
36477
  type: "string",
36424
36478
  ops: ["set_theme_color", "set_theme_font_size"],
36425
- description: 'Theme ops: the Control class, such as "Button".'
36479
+ 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.'
36426
36480
  },
36427
36481
  colorName: {
36428
36482
  type: "string",
@@ -36435,7 +36489,11 @@ var TOOL_SPECS = [
36435
36489
  ops: ["set_theme_font_size"],
36436
36490
  description: 'set_theme_font_size: such as "font_size".'
36437
36491
  },
36438
- size: { type: "number", ops: ["set_theme_font_size"], description: "set_theme_font_size: pixels." }
36492
+ size: {
36493
+ type: "number",
36494
+ ops: ["set_theme_font_size"],
36495
+ description: "set_theme_font_size: pixels, a whole number of 1 or more."
36496
+ }
36439
36497
  },
36440
36498
  requires: ["projectPath", "resourcePath"],
36441
36499
  operations: {
@@ -36464,7 +36522,7 @@ var TOOL_SPECS = [
36464
36522
  },
36465
36523
  move: {
36466
36524
  effect: "destructive",
36467
- summary: "move or rename a file or a directory, and every path in the project naming it",
36525
+ 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",
36468
36526
  requires: ["newPath"]
36469
36527
  }
36470
36528
  }
@@ -36640,7 +36698,7 @@ var TOOL_SPECS = [
36640
36698
  requires: []
36641
36699
  }
36642
36700
  },
36643
- 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.",
36701
+ 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.",
36644
36702
  defaultOperation: "start"
36645
36703
  },
36646
36704
  {
@@ -36702,8 +36760,8 @@ var TOOL_SPECS = [
36702
36760
  name: "editor_status",
36703
36761
  title: "Editor status",
36704
36762
  effect: "read",
36705
- 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.",
36706
- 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.",
36763
+ 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.",
36764
+ 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.",
36707
36765
  parameters: {},
36708
36766
  requires: []
36709
36767
  },
@@ -37028,7 +37086,7 @@ var TOOL_SPECS = [
37028
37086
  name: "runtime_input",
37029
37087
  title: "Send input to the game",
37030
37088
  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.",
37031
- 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.',
37089
+ 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.`,
37032
37090
  parameters: {
37033
37091
  projectPath: RUNNING_PROJECT_PATH,
37034
37092
  pid: RUNNING_PID,
@@ -38413,6 +38471,7 @@ var OUTPUT_PATH_SOLUTIONS = [
38413
38471
  var PROJECT_FILE_ARGUMENTS = [
38414
38472
  "scenePath",
38415
38473
  "instancePath",
38474
+ "inherits",
38416
38475
  "scriptPath",
38417
38476
  "resourcePath",
38418
38477
  "newPath",
@@ -39366,7 +39425,7 @@ class GodotServer {
39366
39425
  project_addons_behind_this_server: {
39367
39426
  server_is: SERVER_VERSION,
39368
39427
  project_is: moved,
39369
- 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."
39428
+ 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."
39370
39429
  }
39371
39430
  });
39372
39431
  }
@@ -39638,13 +39697,13 @@ class GodotServer {
39638
39697
  stateMachinePath: readString(args, "stateMachinePath") ?? ""
39639
39698
  }), async () => await bridge("read_animation_states"));
39640
39699
  case "create":
39641
- return await bridge("create_animation");
39700
+ return await editorOrEngine("create_animation", "writes");
39642
39701
  case "add_track":
39643
- return await bridge("add_animation_track");
39702
+ return await editorOrEngine("add_animation_track", "writes");
39644
39703
  case "add_state":
39645
- return await bridge("add_animation_state");
39704
+ return await editorOrEngine("add_animation_state", "writes");
39646
39705
  default:
39647
- return await bridge("connect_animation_states");
39706
+ return await editorOrEngine("connect_animation_states", "writes");
39648
39707
  }
39649
39708
  case "script_edit":
39650
39709
  return await this.handleRenameSymbol(args);
@@ -40195,18 +40254,24 @@ class GodotServer {
40195
40254
  const projectPath = project.value.path;
40196
40255
  const scanned = await this.untilTheEditorsScanIsWritten(projectPath);
40197
40256
  const before = scriptsWithoutUid(projectPath);
40257
+ const cachedBefore = pathsInUidCache(projectPath);
40198
40258
  const imported = await runImport(engine.value, projectPath);
40199
40259
  if (!imported.ok) {
40200
40260
  return this.answer(withScanWait(imported, scanned));
40201
40261
  }
40202
40262
  const after = scriptsWithoutUid(projectPath);
40203
40263
  const given = before.filter((script) => !after.includes(script));
40264
+ const cachedAfter = pathsInUidCache(projectPath);
40265
+ const cached = cachedAfter === null ? null : [...cachedAfter].filter((path) => cachedBefore?.has(path) !== true).sort();
40204
40266
  return this.answer(withScanWait({
40205
40267
  ok: true,
40206
40268
  messages: imported.messages,
40207
40269
  ...imported.messagesOmitted === undefined ? {} : { messagesOmitted: imported.messagesOmitted },
40208
40270
  payload: {
40209
40271
  uidsCreated: given,
40272
+ ...cached === null ? {
40273
+ uidCacheNote: "The import pass left no uid cache this server could read, .godot/uid_cache.bin."
40274
+ } : { uidsCached: cached },
40210
40275
  stillWithoutUid: after,
40211
40276
  note: uidsLeftNote(after.length),
40212
40277
  ...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.2.40",
3
+ "version": "1.2.42",
4
4
  "mcpName": "io.github.Aureliolo/gdharness",
5
5
  "description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
6
6
  "type": "module",