gdharness 1.2.14 → 1.2.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +393 -80
- package/build/godot/addons/gdharness_editor/property_values.gd +28 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +27 -8
- package/build/godot/addons/gdharness_editor/serialisation.gd +20 -0
- package/build/godot/addons/gdharness_editor/tool_executor.gd +4 -1
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +172 -0
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +60 -47
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +46 -4
- package/build/godot/addons/gdharness_runtime/runtime_capture.gd +13 -3
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +1 -17
- package/build/godot/addons/gdharness_runtime/runtime_targets.gd +29 -3
- package/build/godot/addons/gdharness_runtime/serialisation.gd +20 -0
- package/build/godot/operations/audio_buses.gd +35 -0
- package/build/godot/operations/classdb_queries.gd +31 -2
- package/build/godot/operations/file_walk.gd +9 -2
- package/build/godot/operations/gdscript_authoring.gd +4 -5
- package/build/godot/operations/godot_operations.gd +4 -0
- package/build/godot/operations/input_actions.gd +27 -0
- package/build/godot/operations/project_config.gd +8 -1
- package/build/godot/operations/project_diagnostics.gd +9 -5
- package/build/godot/operations/serialisation.gd +20 -0
- package/build/index.js +380 -66
- package/package.json +1 -1
package/build/index.js
CHANGED
|
@@ -3807,6 +3807,18 @@ function prefixIssues(path, issues) {
|
|
|
3807
3807
|
function unwrapMessage(message) {
|
|
3808
3808
|
return typeof message === "string" ? message : message?.message;
|
|
3809
3809
|
}
|
|
3810
|
+
function sizeCaps(defChecks) {
|
|
3811
|
+
const caps = [];
|
|
3812
|
+
for (const ch of defChecks ?? []) {
|
|
3813
|
+
const d = ch._zod.def;
|
|
3814
|
+
if (!/_(length|size)$|_equals$/.test(d.check))
|
|
3815
|
+
break;
|
|
3816
|
+
const max = d.maximum ?? d.length ?? d.size;
|
|
3817
|
+
if (max >= 0)
|
|
3818
|
+
caps.push([max, ch]);
|
|
3819
|
+
}
|
|
3820
|
+
return caps;
|
|
3821
|
+
}
|
|
3810
3822
|
function attachSchema(issues, start, inst) {
|
|
3811
3823
|
var _a;
|
|
3812
3824
|
for (let i = start;i < issues.length; i++) {
|
|
@@ -4808,7 +4820,7 @@ ${content.join(`
|
|
|
4808
4820
|
var version = {
|
|
4809
4821
|
major: 4,
|
|
4810
4822
|
minor: 6,
|
|
4811
|
-
patch:
|
|
4823
|
+
patch: 7
|
|
4812
4824
|
};
|
|
4813
4825
|
|
|
4814
4826
|
// node_modules/zod/v4/core/schemas.js
|
|
@@ -5381,6 +5393,29 @@ var $ZodNever = /* @__PURE__ */ $constructor("$ZodNever", (inst, def) => {
|
|
|
5381
5393
|
return payload;
|
|
5382
5394
|
};
|
|
5383
5395
|
});
|
|
5396
|
+
function capSize(inst, size) {
|
|
5397
|
+
const caps = sizeCaps(inst._zod.def.checks);
|
|
5398
|
+
if (!caps.length)
|
|
5399
|
+
return;
|
|
5400
|
+
const cap = Math.min(...caps.map((c) => c[0]));
|
|
5401
|
+
const parse = inst._zod.parse;
|
|
5402
|
+
inst._zod.parse = (payload, ctx) => {
|
|
5403
|
+
const n = size(payload.value);
|
|
5404
|
+
if (n > cap && !ctx?.skipChecks && ctx?.direction !== "backward") {
|
|
5405
|
+
for (const [max, ch] of sizeCaps(inst._zod.def.checks)) {
|
|
5406
|
+
if (n > max && ch._zod.def.when(payload)) {
|
|
5407
|
+
const start = payload.issues.length;
|
|
5408
|
+
ch._zod.check(payload);
|
|
5409
|
+
payload.issues[start].continue = false;
|
|
5410
|
+
attachSchema(payload.issues, start, inst);
|
|
5411
|
+
return payload;
|
|
5412
|
+
}
|
|
5413
|
+
}
|
|
5414
|
+
}
|
|
5415
|
+
return parse(payload, ctx);
|
|
5416
|
+
};
|
|
5417
|
+
}
|
|
5418
|
+
var arraySize = (input) => Array.isArray(input) ? input.length : -1;
|
|
5384
5419
|
function handleArrayResult(result, final, index) {
|
|
5385
5420
|
if (result.issues.length) {
|
|
5386
5421
|
final.issues.push(...prefixIssues(index, result.issues));
|
|
@@ -5424,6 +5459,7 @@ var $ZodArray = /* @__PURE__ */ $constructor("$ZodArray", (inst, def) => {
|
|
|
5424
5459
|
}
|
|
5425
5460
|
return payload;
|
|
5426
5461
|
};
|
|
5462
|
+
capSize(inst, arraySize);
|
|
5427
5463
|
});
|
|
5428
5464
|
function handlePropertyResult(result, final, key, input, optin, optout) {
|
|
5429
5465
|
const isPresent = key in input;
|
|
@@ -28316,10 +28352,58 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
28316
28352
|
}
|
|
28317
28353
|
const log = new GameLog;
|
|
28318
28354
|
const { text, of } = theFirstOf(path);
|
|
28319
|
-
log.append("transcript", text);
|
|
28355
|
+
log.append("transcript", foldProgress(text));
|
|
28320
28356
|
log.finish();
|
|
28321
28357
|
return { log, path, ...of === undefined ? {} : { readBytes: text.length, of } };
|
|
28322
28358
|
}
|
|
28359
|
+
var PROGRESS_STEP = /^\[\s*(\d+)% \] (\S+) \| (.*)$/;
|
|
28360
|
+
var PROGRESS_DONE = /^\[ DONE \] (\S+)\s*$/;
|
|
28361
|
+
function foldProgress(text) {
|
|
28362
|
+
const out = [];
|
|
28363
|
+
const open = new Map;
|
|
28364
|
+
for (const raw of text.split(`
|
|
28365
|
+
`)) {
|
|
28366
|
+
const line = raw.replace(/\r$/, "").replace(COLOUR_CODE, "");
|
|
28367
|
+
const step = PROGRESS_STEP.exec(line);
|
|
28368
|
+
if (step !== null) {
|
|
28369
|
+
const [, percent = "", name = "", said = ""] = step;
|
|
28370
|
+
const started = /^Started (.*)$/.exec(said);
|
|
28371
|
+
if (started !== null) {
|
|
28372
|
+
const task = { name, label: started[1] ?? "", percent, done: false };
|
|
28373
|
+
open.set(name, task);
|
|
28374
|
+
out.push(task);
|
|
28375
|
+
continue;
|
|
28376
|
+
}
|
|
28377
|
+
const task = open.get(name);
|
|
28378
|
+
if (task !== undefined) {
|
|
28379
|
+
task.percent = percent;
|
|
28380
|
+
if (said.trim() !== "") {
|
|
28381
|
+
task.last = said.trim();
|
|
28382
|
+
}
|
|
28383
|
+
continue;
|
|
28384
|
+
}
|
|
28385
|
+
}
|
|
28386
|
+
const finished = PROGRESS_DONE.exec(line);
|
|
28387
|
+
const task = finished === null ? undefined : open.get(finished[1] ?? "");
|
|
28388
|
+
if (task !== undefined) {
|
|
28389
|
+
task.done = true;
|
|
28390
|
+
open.delete(task.name);
|
|
28391
|
+
continue;
|
|
28392
|
+
}
|
|
28393
|
+
out.push(raw);
|
|
28394
|
+
}
|
|
28395
|
+
return out.map((entry) => {
|
|
28396
|
+
if (typeof entry === "string") {
|
|
28397
|
+
return entry;
|
|
28398
|
+
}
|
|
28399
|
+
if (entry.done) {
|
|
28400
|
+
return `[ DONE ] ${entry.name} | ${entry.label}`;
|
|
28401
|
+
}
|
|
28402
|
+
const last = entry.last === undefined ? "" : `, and the last step it named was "${entry.last}"`;
|
|
28403
|
+
return `[ ${entry.percent}% ] ${entry.name} | ${entry.label}, which has not finished${last}`;
|
|
28404
|
+
}).join(`
|
|
28405
|
+
`);
|
|
28406
|
+
}
|
|
28323
28407
|
var MOST_CONSOLE_BYTES = 8 * 1024 * 1024;
|
|
28324
28408
|
function theFirstOf(path) {
|
|
28325
28409
|
const whole = statSync3(path).size;
|
|
@@ -28579,6 +28663,12 @@ function theEditorHasComeBack(status, startedAt) {
|
|
|
28579
28663
|
function hasSaidWhoItIs(status) {
|
|
28580
28664
|
return status.connected && status.addonVersion !== undefined;
|
|
28581
28665
|
}
|
|
28666
|
+
function withoutTrailingSeparator(path) {
|
|
28667
|
+
if (path.length <= 1 || !/[\\/]$/.test(path) || /^[A-Za-z]:[\\/]$/.test(path)) {
|
|
28668
|
+
return path;
|
|
28669
|
+
}
|
|
28670
|
+
return path.slice(0, -1);
|
|
28671
|
+
}
|
|
28582
28672
|
function anEditorIsStillComing(listeningSince, launchedEditorIsAlive, now = Date.now()) {
|
|
28583
28673
|
return mayYetConnect(listeningSince, now) || launchedEditorIsAlive;
|
|
28584
28674
|
}
|
|
@@ -28879,7 +28969,7 @@ class GodotBridge extends EventEmitter {
|
|
|
28879
28969
|
this.elsewhereUnlessOneShot(message.project_path, message.editor_pid, message.opened_by_a_server === true);
|
|
28880
28970
|
return;
|
|
28881
28971
|
}
|
|
28882
|
-
this.connectionInfo.projectPath = message.project_path;
|
|
28972
|
+
this.connectionInfo.projectPath = withoutTrailingSeparator(message.project_path);
|
|
28883
28973
|
this.connectionInfo.addonVersion = message.addon_version ?? "";
|
|
28884
28974
|
this.connectionInfo.addonDigest = message.addon_digest;
|
|
28885
28975
|
this.connectionInfo.editorPid = message.editor_pid;
|
|
@@ -29628,9 +29718,11 @@ var HEADLESS_OPERATIONS = dictionary({
|
|
|
29628
29718
|
remove_autoload: "remove_autoload",
|
|
29629
29719
|
set_main_scene: "set_main_scene",
|
|
29630
29720
|
add_input_action: "add_input_action",
|
|
29721
|
+
remove_input_action: "remove_input_action",
|
|
29631
29722
|
enable_plugin: "enable_plugin",
|
|
29632
29723
|
disable_plugin: "disable_plugin",
|
|
29633
29724
|
add_audio_bus: "create_audio_bus",
|
|
29725
|
+
remove_audio_bus: "remove_audio_bus",
|
|
29634
29726
|
set_audio_bus_effect: "set_audio_bus_effect",
|
|
29635
29727
|
set_audio_bus_volume: "set_audio_bus_volume"
|
|
29636
29728
|
}),
|
|
@@ -30847,6 +30939,29 @@ function shownSymbol(entry) {
|
|
|
30847
30939
|
...children.length > 0 ? { children: children.map(shownSymbol) } : {}
|
|
30848
30940
|
};
|
|
30849
30941
|
}
|
|
30942
|
+
var MOST_COMPLETIONS = 100;
|
|
30943
|
+
function typedBefore(content, line, character) {
|
|
30944
|
+
const text = content.split(/\r?\n/)[line] ?? "";
|
|
30945
|
+
return /[\p{L}\p{N}_]*$/u.exec(text.slice(0, character))?.[0] ?? "";
|
|
30946
|
+
}
|
|
30947
|
+
function narrowedCompletions(completions, typed) {
|
|
30948
|
+
const wanted = typed.toLowerCase();
|
|
30949
|
+
const matching = wanted === "" ? completions : completions.filter((entry) => {
|
|
30950
|
+
const record = asRecord(entry);
|
|
30951
|
+
const name = typeof record["filterText"] === "string" ? record["filterText"] : record["label"];
|
|
30952
|
+
return typeof name === "string" && name.toLowerCase().startsWith(wanted);
|
|
30953
|
+
});
|
|
30954
|
+
const shown = matching.slice(0, MOST_COMPLETIONS);
|
|
30955
|
+
return {
|
|
30956
|
+
...typed === "" ? {} : { typed },
|
|
30957
|
+
completions: shown.map(shownCompletion),
|
|
30958
|
+
total: matching.length,
|
|
30959
|
+
...matching.length > shown.length ? {
|
|
30960
|
+
omitted: matching.length - shown.length,
|
|
30961
|
+
note: `${matching.length - shown.length} more are offered here than one answer carries; ask at a position after more of the name to narrow them.`
|
|
30962
|
+
} : {}
|
|
30963
|
+
};
|
|
30964
|
+
}
|
|
30850
30965
|
function shownCompletion(entry) {
|
|
30851
30966
|
const { data: _request, kind, ...rest } = asRecord(entry);
|
|
30852
30967
|
return { ...rest, kind: named(kind, COMPLETION_KINDS) };
|
|
@@ -30917,7 +31032,7 @@ async function handleLSPTool(client, toolName, args) {
|
|
|
30917
31032
|
case "lsp_get_completions": {
|
|
30918
31033
|
const { line, character } = protocolPosition(parsedArgs);
|
|
30919
31034
|
const completions = await client.getCompletions(scriptPath, content, line, character);
|
|
30920
|
-
return asToolResponse(
|
|
31035
|
+
return asToolResponse(narrowedCompletions(completions, typedBefore(content, line, character)));
|
|
30921
31036
|
}
|
|
30922
31037
|
case "lsp_get_hover": {
|
|
30923
31038
|
const { line, character } = protocolPosition(parsedArgs);
|
|
@@ -31087,9 +31202,16 @@ var SKIPPED = new Set(["node_modules"]);
|
|
|
31087
31202
|
function skipped(name) {
|
|
31088
31203
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
31089
31204
|
}
|
|
31205
|
+
var VERSION_MARKER = ".gdharness-version";
|
|
31090
31206
|
function steppedOver(directory) {
|
|
31207
|
+
return gdignored(directory) || installedByUs(directory);
|
|
31208
|
+
}
|
|
31209
|
+
function gdignored(directory) {
|
|
31091
31210
|
return existsSync13(join16(directory, ".gdignore"));
|
|
31092
31211
|
}
|
|
31212
|
+
function installedByUs(directory) {
|
|
31213
|
+
return existsSync13(join16(directory, VERSION_MARKER));
|
|
31214
|
+
}
|
|
31093
31215
|
var SCENE_EXTENSIONS = new Set(["tscn", "scn"]);
|
|
31094
31216
|
var ASSET_EXTENSIONS = new Set([
|
|
31095
31217
|
"png",
|
|
@@ -31245,7 +31367,10 @@ function projectTexts(projectPath) {
|
|
|
31245
31367
|
function ignoredTexts(projectPath) {
|
|
31246
31368
|
const found = [];
|
|
31247
31369
|
const visit = (directory, prefix, ignored) => {
|
|
31248
|
-
|
|
31370
|
+
if (installedByUs(directory)) {
|
|
31371
|
+
return;
|
|
31372
|
+
}
|
|
31373
|
+
const under = ignored || gdignored(directory);
|
|
31249
31374
|
for (const entry of readdirSync5(directory, { withFileTypes: true })) {
|
|
31250
31375
|
if (skipped(entry.name)) {
|
|
31251
31376
|
continue;
|
|
@@ -31348,7 +31473,11 @@ var PROMPT_SPECS = [
|
|
|
31348
31473
|
}
|
|
31349
31474
|
],
|
|
31350
31475
|
steps: [
|
|
31351
|
-
{
|
|
31476
|
+
{
|
|
31477
|
+
tool: "editor_status",
|
|
31478
|
+
passes: [],
|
|
31479
|
+
why: "see whether an editor is connected and its addon matches; without one the run is a game of its own, not a fault"
|
|
31480
|
+
},
|
|
31352
31481
|
{ tool: "editor_run", op: "start", passes: ["projectPath", "scene"], why: "start the run" },
|
|
31353
31482
|
{
|
|
31354
31483
|
tool: "editor_run",
|
|
@@ -31366,7 +31495,11 @@ var PROMPT_SPECS = [
|
|
|
31366
31495
|
description: "Check the editor connection, the project health report, export validation and the import state.",
|
|
31367
31496
|
arguments: [PROJECT_PATH],
|
|
31368
31497
|
steps: [
|
|
31369
|
-
{
|
|
31498
|
+
{
|
|
31499
|
+
tool: "editor_status",
|
|
31500
|
+
passes: [],
|
|
31501
|
+
why: "see whether an editor is connected and, if one is, whether its addon matches; no editor is no fault in the project, so leave it out of what is wrong"
|
|
31502
|
+
},
|
|
31370
31503
|
{
|
|
31371
31504
|
tool: "project_info",
|
|
31372
31505
|
passes: ["projectPath"],
|
|
@@ -33895,10 +34028,14 @@ var TOOL_SPECS = [
|
|
|
33895
34028
|
{
|
|
33896
34029
|
name: "project_settings",
|
|
33897
34030
|
title: "Project settings",
|
|
33898
|
-
description: "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is.",
|
|
34031
|
+
description: "Reads or writes project.godot: settings, autoloads, the main scene, input actions, plugins and audio buses. A write with an editor open on the project is handed to the editor, which takes the settings the write changed or names from the file (named under editorAdopted), including a setting the file already held, since the editor can hold another value after the file was put back, or reloads the bus layout, so it answers the new values and does not save the old ones back over them. editorNote is an editor that could not take the write, one on an addon from before this, which still holds the old values until editor_launch restart. An enabled plugin's code still loads on the editor's next start. set answers old_value and new_value, with was_new true when project.godot did not set the setting before, and was_default true when what it held was the engine's own default, which old_value then is. get answers exists, true for any setting the engine knows, and at_default, true when the value is the engine's own default and so has no line in project.godot.",
|
|
33899
34032
|
parameters: {
|
|
33900
34033
|
projectPath: PROJECT_PATH2,
|
|
33901
|
-
setting: {
|
|
34034
|
+
setting: {
|
|
34035
|
+
type: "string",
|
|
34036
|
+
ops: ["get", "set"],
|
|
34037
|
+
description: 'Setting path, such as "display/window/size/viewport_width".'
|
|
34038
|
+
},
|
|
33902
34039
|
from: {
|
|
33903
34040
|
type: "string",
|
|
33904
34041
|
enum: ["disk", "editor"],
|
|
@@ -33912,40 +34049,66 @@ var TOOL_SPECS = [
|
|
|
33912
34049
|
},
|
|
33913
34050
|
value: {
|
|
33914
34051
|
blank: true,
|
|
34052
|
+
ops: ["set"],
|
|
33915
34053
|
description: 'The value to write. Engine types may be tagged, {"_type": "Vector2", "x": 1, "y": 2}, and infinity as {"_type": "float", "value": "inf"}, the form a read answers with. "" writes an empty string.'
|
|
33916
34054
|
},
|
|
33917
|
-
name: { type: "string", description: "Autoload name." },
|
|
33918
|
-
path: {
|
|
34055
|
+
name: { type: "string", ops: ["add_autoload", "remove_autoload"], description: "Autoload name." },
|
|
34056
|
+
path: {
|
|
34057
|
+
type: "string",
|
|
34058
|
+
ops: ["add_autoload"],
|
|
34059
|
+
description: "Autoload script or scene inside the project."
|
|
34060
|
+
},
|
|
33919
34061
|
enabled: {
|
|
33920
34062
|
type: "boolean",
|
|
33921
|
-
|
|
34063
|
+
ops: ["add_autoload", "set_audio_bus_effect"],
|
|
34064
|
+
description: "add_autoload: Godot 4 loads every autoload in the list, so false is refused. Use global: false to keep the name out of the global scope, or remove_autoload to stop it loading. set_audio_bus_effect: whether the effect is on. Default true."
|
|
33922
34065
|
},
|
|
33923
34066
|
global: {
|
|
33924
34067
|
type: "boolean",
|
|
34068
|
+
ops: ["add_autoload"],
|
|
33925
34069
|
description: `Autoloads: make the name a global variable, the * in project.godot and the editor's "Global Variable" column. Default true. The autoload loads either way.`
|
|
33926
34070
|
},
|
|
33927
|
-
scenePath: SCENE_PATH,
|
|
33928
|
-
actionName: {
|
|
33929
|
-
|
|
34071
|
+
scenePath: { ...SCENE_PATH, ops: ["set_main_scene"] },
|
|
34072
|
+
actionName: {
|
|
34073
|
+
type: "string",
|
|
34074
|
+
ops: ["add_input_action", "remove_input_action"],
|
|
34075
|
+
description: 'Input action name, such as "jump".'
|
|
34076
|
+
},
|
|
34077
|
+
events: { ...INPUT_EVENTS, ops: ["add_input_action"] },
|
|
33930
34078
|
deadzone: {
|
|
33931
34079
|
type: "number",
|
|
34080
|
+
ops: ["add_input_action"],
|
|
33932
34081
|
minimum: 0,
|
|
33933
34082
|
maximum: 1,
|
|
33934
34083
|
description: "Input actions: analogue deadzone, 0 to 1. Default 0.5."
|
|
33935
34084
|
},
|
|
33936
|
-
pluginName: {
|
|
33937
|
-
|
|
34085
|
+
pluginName: {
|
|
34086
|
+
type: "string",
|
|
34087
|
+
ops: ["enable_plugin", "disable_plugin"],
|
|
34088
|
+
description: "Folder name under addons/."
|
|
34089
|
+
},
|
|
34090
|
+
busName: { type: "string", ops: ["add_audio_bus", "remove_audio_bus"], description: "Audio bus name." },
|
|
33938
34091
|
parentBusIndex: {
|
|
33939
34092
|
type: "number",
|
|
34093
|
+
ops: ["add_audio_bus"],
|
|
33940
34094
|
description: "Audio buses: the bus to send to. Default 0, Master. The new bus goes right after it, so every later bus moves one along, and the answer lists them all."
|
|
33941
34095
|
},
|
|
33942
|
-
busIndex: {
|
|
34096
|
+
busIndex: {
|
|
34097
|
+
type: "number",
|
|
34098
|
+
ops: ["set_audio_bus_effect", "set_audio_bus_volume"],
|
|
34099
|
+
description: "Audio bus index."
|
|
34100
|
+
},
|
|
33943
34101
|
effectIndex: {
|
|
33944
34102
|
type: "number",
|
|
34103
|
+
ops: ["set_audio_bus_effect"],
|
|
33945
34104
|
description: "Slot on the bus for the effect: the effect there is replaced, and one past the last adds one. Any other slot is refused."
|
|
33946
34105
|
},
|
|
33947
|
-
effectType: {
|
|
33948
|
-
|
|
34106
|
+
effectType: {
|
|
34107
|
+
type: "string",
|
|
34108
|
+
ops: ["set_audio_bus_effect"],
|
|
34109
|
+
description: 'Effect class, such as "AudioEffectReverb".'
|
|
34110
|
+
},
|
|
34111
|
+
volumeDb: { type: "number", ops: ["set_audio_bus_volume"], description: "Bus volume in decibels." }
|
|
33949
34112
|
},
|
|
33950
34113
|
requires: ["projectPath"],
|
|
33951
34114
|
operations: {
|
|
@@ -33967,9 +34130,19 @@ var TOOL_SPECS = [
|
|
|
33967
34130
|
summary: "register an input action and its events",
|
|
33968
34131
|
requires: ["actionName", "events"]
|
|
33969
34132
|
},
|
|
34133
|
+
remove_input_action: {
|
|
34134
|
+
effect: "destructive",
|
|
34135
|
+
summary: "remove an input action the project defines",
|
|
34136
|
+
requires: ["actionName"]
|
|
34137
|
+
},
|
|
33970
34138
|
enable_plugin: { effect: "destructive", summary: "enable an addon", requires: ["pluginName"] },
|
|
33971
34139
|
disable_plugin: { effect: "destructive", summary: "disable an addon", requires: ["pluginName"] },
|
|
33972
34140
|
add_audio_bus: { effect: "additive", summary: "add an audio bus", requires: ["busName"] },
|
|
34141
|
+
remove_audio_bus: {
|
|
34142
|
+
effect: "destructive",
|
|
34143
|
+
summary: "remove an audio bus, sending what sent to it on to where it sent",
|
|
34144
|
+
requires: ["busName"]
|
|
34145
|
+
},
|
|
33973
34146
|
set_audio_bus_effect: {
|
|
33974
34147
|
effect: "destructive",
|
|
33975
34148
|
summary: "put an effect in a slot on a bus",
|
|
@@ -33986,7 +34159,7 @@ var TOOL_SPECS = [
|
|
|
33986
34159
|
name: "project_search",
|
|
33987
34160
|
title: "Search the project",
|
|
33988
34161
|
effect: "read",
|
|
33989
|
-
description: "Searches text or a regular expression across project files and returns file paths with line numbers. What the engine steps over is not searched: directories spelled with a dot, node_modules, and any directory holding a .gdignore. A vendored engine or an export directory is therefore absent from the results rather than matching the code you are looking for in a copy nobody runs.",
|
|
34162
|
+
description: "Searches text or a regular expression across project files and returns file paths with line numbers. What the engine steps over is not searched: directories spelled with a dot, node_modules, and any directory holding a .gdignore. A vendored engine or an export directory is therefore absent from the results rather than matching the code you are looking for in a copy nobody runs. The addons gdharness installed are not searched either, since nobody on the project wrote them.",
|
|
33990
34163
|
parameters: {
|
|
33991
34164
|
projectPath: PROJECT_PATH2,
|
|
33992
34165
|
query: { type: "string", description: "The text or pattern to find." },
|
|
@@ -34148,8 +34321,18 @@ var TOOL_SPECS = [
|
|
|
34148
34321
|
parameters: {
|
|
34149
34322
|
projectPath: PROJECT_PATH2,
|
|
34150
34323
|
scenePath: SCENE_PATH,
|
|
34151
|
-
rootNodeType: {
|
|
34152
|
-
|
|
34324
|
+
rootNodeType: {
|
|
34325
|
+
type: "string",
|
|
34326
|
+
ops: ["create"],
|
|
34327
|
+
description: "create: the root node class. Default Node."
|
|
34328
|
+
},
|
|
34329
|
+
rootNodeName: {
|
|
34330
|
+
type: "string",
|
|
34331
|
+
ops: ["create"],
|
|
34332
|
+
description: "create: the root node name. Default the class name, as the editor names a new root."
|
|
34333
|
+
},
|
|
34334
|
+
script: { type: "string", ops: ["create"], description: "create: a script to attach to the root." },
|
|
34335
|
+
newPath: { type: "string", ops: ["save_as"], description: "save_as: where the copy goes." }
|
|
34153
34336
|
},
|
|
34154
34337
|
requires: ["projectPath", "scenePath"],
|
|
34155
34338
|
operations: {
|
|
@@ -34229,7 +34412,7 @@ var TOOL_SPECS = [
|
|
|
34229
34412
|
{
|
|
34230
34413
|
name: "scene_signal",
|
|
34231
34414
|
title: "Scene signals",
|
|
34232
|
-
description: "Signal connections in a scene file. Needs the editor connected.",
|
|
34415
|
+
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. Needs the editor connected.",
|
|
34233
34416
|
parameters: {
|
|
34234
34417
|
projectPath: PROJECT_PATH2,
|
|
34235
34418
|
scenePath: SCENE_PATH,
|
|
@@ -34258,43 +34441,78 @@ var TOOL_SPECS = [
|
|
|
34258
34441
|
{
|
|
34259
34442
|
name: "scene_animation",
|
|
34260
34443
|
title: "Animations",
|
|
34261
|
-
description: "Animations in an AnimationPlayer and states in an AnimationTree state machine. Needs the editor connected.",
|
|
34444
|
+
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. Needs the editor connected.",
|
|
34262
34445
|
parameters: {
|
|
34263
34446
|
projectPath: PROJECT_PATH2,
|
|
34264
34447
|
scenePath: SCENE_PATH,
|
|
34265
|
-
playerNodePath: {
|
|
34448
|
+
playerNodePath: {
|
|
34449
|
+
type: "string",
|
|
34450
|
+
ops: ["read", "create", "add_track"],
|
|
34451
|
+
description: "The AnimationPlayer node."
|
|
34452
|
+
},
|
|
34266
34453
|
animationName: {
|
|
34267
34454
|
type: "string",
|
|
34268
|
-
|
|
34455
|
+
ops: ["read", "create", "add_track", "add_state"],
|
|
34456
|
+
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."
|
|
34269
34457
|
},
|
|
34270
|
-
length: { type: "number", description: "create: seconds. Default 1." },
|
|
34458
|
+
length: { type: "number", ops: ["create"], description: "create: seconds. Default 1." },
|
|
34271
34459
|
loopMode: {
|
|
34272
34460
|
type: "string",
|
|
34461
|
+
ops: ["create"],
|
|
34273
34462
|
enum: ["none", "linear", "pingpong"],
|
|
34274
34463
|
description: "create: default none."
|
|
34275
34464
|
},
|
|
34276
|
-
step: {
|
|
34277
|
-
|
|
34278
|
-
|
|
34279
|
-
|
|
34465
|
+
step: {
|
|
34466
|
+
type: "number",
|
|
34467
|
+
ops: ["create"],
|
|
34468
|
+
description: "create: keyframe snap in seconds. Default 0.1."
|
|
34469
|
+
},
|
|
34470
|
+
track: { ...ANIMATION_TRACK, ops: ["add_track"] },
|
|
34471
|
+
animTreePath: {
|
|
34472
|
+
type: "string",
|
|
34473
|
+
ops: ["read_states", "add_state", "connect_states"],
|
|
34474
|
+
description: "The AnimationTree node."
|
|
34475
|
+
},
|
|
34476
|
+
stateName: { type: "string", ops: ["add_state"], description: "add_state: the state to add." },
|
|
34280
34477
|
stateMachinePath: {
|
|
34281
34478
|
type: "string",
|
|
34282
|
-
|
|
34479
|
+
ops: ["read_states", "add_state", "connect_states"],
|
|
34480
|
+
description: "A nested state machine, by the names of the states that lead to it. Default the root."
|
|
34481
|
+
},
|
|
34482
|
+
fromState: {
|
|
34483
|
+
type: "string",
|
|
34484
|
+
ops: ["connect_states"],
|
|
34485
|
+
description: "connect_states: the state the transition leaves."
|
|
34486
|
+
},
|
|
34487
|
+
toState: {
|
|
34488
|
+
type: "string",
|
|
34489
|
+
ops: ["connect_states"],
|
|
34490
|
+
description: "connect_states: the state it arrives at."
|
|
34283
34491
|
},
|
|
34284
|
-
fromState: { type: "string", description: "connect_states: the state the transition leaves." },
|
|
34285
|
-
toState: { type: "string", description: "connect_states: the state it arrives at." },
|
|
34286
34492
|
transitionType: {
|
|
34287
34493
|
type: "string",
|
|
34494
|
+
ops: ["connect_states"],
|
|
34288
34495
|
enum: ["immediate", "sync", "at_end"],
|
|
34289
34496
|
description: "connect_states: default immediate."
|
|
34290
34497
|
},
|
|
34291
34498
|
advanceCondition: {
|
|
34292
34499
|
type: "string",
|
|
34500
|
+
ops: ["connect_states"],
|
|
34293
34501
|
description: "connect_states: the condition parameter that advances."
|
|
34294
34502
|
}
|
|
34295
34503
|
},
|
|
34296
34504
|
requires: ["projectPath", "scenePath"],
|
|
34297
34505
|
operations: {
|
|
34506
|
+
read: {
|
|
34507
|
+
effect: "read",
|
|
34508
|
+
summary: "the animations of an AnimationPlayer, or one with its tracks",
|
|
34509
|
+
requires: ["playerNodePath"]
|
|
34510
|
+
},
|
|
34511
|
+
read_states: {
|
|
34512
|
+
effect: "read",
|
|
34513
|
+
summary: "the states and transitions of an AnimationTree state machine",
|
|
34514
|
+
requires: ["animTreePath"]
|
|
34515
|
+
},
|
|
34298
34516
|
create: {
|
|
34299
34517
|
effect: "additive",
|
|
34300
34518
|
summary: "a new animation in an AnimationPlayer",
|
|
@@ -34398,7 +34616,11 @@ var TOOL_SPECS = [
|
|
|
34398
34616
|
summary: "document symbols from the language server, each with its kind, the line and column of its name, and the lastLine of its declaration",
|
|
34399
34617
|
requires: []
|
|
34400
34618
|
},
|
|
34401
|
-
completion: {
|
|
34619
|
+
completion: {
|
|
34620
|
+
effect: "read",
|
|
34621
|
+
summary: "completions at a position, narrowed to the part of a name written before it, at most 100",
|
|
34622
|
+
requires: ["line", "character"]
|
|
34623
|
+
},
|
|
34402
34624
|
hover: { effect: "read", summary: "hover text at a position", requires: ["line", "character"] }
|
|
34403
34625
|
},
|
|
34404
34626
|
defaultOperation: "structure"
|
|
@@ -34421,34 +34643,59 @@ var TOOL_SPECS = [
|
|
|
34421
34643
|
{
|
|
34422
34644
|
name: "resource_edit",
|
|
34423
34645
|
title: "Edit resources",
|
|
34424
|
-
description:
|
|
34646
|
+
description: 'Resource files: read one, create any ClassDB resource as .tres, change one, write a shader, build a TileSet, or set a Theme colour or font size. A material is a create with resourceType StandardMaterial3D, ShaderMaterial or CanvasItemMaterial. read answers with the properties the file sets, in the form modify takes, and a Theme lists its items there as "Button/colors/font_color". Needs the editor connected.',
|
|
34425
34647
|
parameters: {
|
|
34426
34648
|
projectPath: PROJECT_PATH2,
|
|
34427
34649
|
resourcePath: RESOURCE_PATH,
|
|
34428
34650
|
resourceType: {
|
|
34429
34651
|
type: "string",
|
|
34652
|
+
ops: ["create"],
|
|
34430
34653
|
description: 'create: the resource class, such as "PhysicsMaterial" or "StandardMaterial3D".'
|
|
34431
34654
|
},
|
|
34432
|
-
properties: PROPERTIES,
|
|
34433
|
-
script: {
|
|
34655
|
+
properties: { ...PROPERTIES, ops: ["create", "modify"] },
|
|
34656
|
+
script: {
|
|
34657
|
+
type: "string",
|
|
34658
|
+
ops: ["create"],
|
|
34659
|
+
description: "create: a script to attach, for custom resources."
|
|
34660
|
+
},
|
|
34661
|
+
includeDefaults: {
|
|
34662
|
+
type: "boolean",
|
|
34663
|
+
ops: ["read"],
|
|
34664
|
+
description: "read: include properties still at their default. Default false."
|
|
34665
|
+
},
|
|
34434
34666
|
shaderType: {
|
|
34435
34667
|
type: "string",
|
|
34668
|
+
ops: ["create_shader"],
|
|
34436
34669
|
enum: ["canvas_item", "spatial", "particles", "sky", "fog"],
|
|
34437
34670
|
description: "create_shader."
|
|
34438
34671
|
},
|
|
34439
34672
|
code: {
|
|
34440
34673
|
type: "string",
|
|
34674
|
+
ops: ["create_shader"],
|
|
34441
34675
|
description: "create_shader: the shader source. Default: a minimal shader of that type."
|
|
34442
34676
|
},
|
|
34443
|
-
sources: TILESET_SOURCES,
|
|
34444
|
-
controlType: {
|
|
34445
|
-
|
|
34446
|
-
|
|
34447
|
-
|
|
34448
|
-
|
|
34677
|
+
sources: { ...TILESET_SOURCES, ops: ["create_tileset"] },
|
|
34678
|
+
controlType: {
|
|
34679
|
+
type: "string",
|
|
34680
|
+
ops: ["set_theme_color", "set_theme_font_size"],
|
|
34681
|
+
description: 'Theme ops: the Control class, such as "Button".'
|
|
34682
|
+
},
|
|
34683
|
+
colorName: {
|
|
34684
|
+
type: "string",
|
|
34685
|
+
ops: ["set_theme_color"],
|
|
34686
|
+
description: 'set_theme_color: such as "font_color".'
|
|
34687
|
+
},
|
|
34688
|
+
color: { ...COLOUR2, ops: ["set_theme_color"] },
|
|
34689
|
+
fontSizeName: {
|
|
34690
|
+
type: "string",
|
|
34691
|
+
ops: ["set_theme_font_size"],
|
|
34692
|
+
description: 'set_theme_font_size: such as "font_size".'
|
|
34693
|
+
},
|
|
34694
|
+
size: { type: "number", ops: ["set_theme_font_size"], description: "set_theme_font_size: pixels." }
|
|
34449
34695
|
},
|
|
34450
34696
|
requires: ["projectPath", "resourcePath"],
|
|
34451
34697
|
operations: {
|
|
34698
|
+
read: { effect: "read", summary: "the properties a resource file sets", requires: [] },
|
|
34452
34699
|
create: { effect: "destructive", summary: "a new resource of any class", requires: ["resourceType"] },
|
|
34453
34700
|
modify: {
|
|
34454
34701
|
effect: "destructive",
|
|
@@ -34475,8 +34722,8 @@ var TOOL_SPECS = [
|
|
|
34475
34722
|
},
|
|
34476
34723
|
{
|
|
34477
34724
|
name: "editor_launch",
|
|
34478
|
-
title: "Open or
|
|
34479
|
-
description: "Opens the Godot editor on a project, or restarts the one already connected. open refuses while an editor of the project is already running and has not reached this server, naming its pid, rather than putting a second on the project. The editor is an ordinary visible one on the desktop in use, and on Windows it is shown without being activated, so opening or reopening it does not take the keyboard from whatever the user is typing in. hidden: true opens it headless instead and, on Windows, on a desktop of its own, where the games it plays go too; a hidden editor serves the bridge, the language server and the debug adapter as a visible one does. A game a visible editor plays opens as a window on the desktop in use. An editor goes on serving the addon it read at startup, so restart is what puts a gdharness upgrade into effect, and nothing else here restarts one; it saves open scenes on the way out and answers with the version that came back. An editor a server opened is started again by the server, visible unless hidden: true is passed again, once the old one has gone: it waits 90 seconds for that, or GDHARNESS_EDITOR_RESTART_TIMEOUT_MS, and an old editor still running after it is not replaced, since the new one would find its ports held. One opened by hand restarts itself, which only one with a window can do, because the engine hands back none of the arguments it was started with. editor_status says which editor is connected and whether it is holding an old addon. Opening a project is a save and so is closing one, which is the part that is not obvious from outside: the editor imports the project on open and writes project.godot back, and Godot writes only what differs from its own defaults, so a key named deliberately at its default value is dropped either way. A restart says which under settingsDropped in its own answer because it waits for the editor to return. An open cannot, since it answers as soon as the process exists and the save happens during the import minutes later, so editor_status carries that reading once the editor has connected. Nothing else will say a key went until something depends on one. A restart of an editor this server opened is a quit and then a launch, and this server can be ended between them, usually by the reconnect the restart was being taken for: the editor is gone and nothing replaces it. That is written down before the quit, so the next server says so under restartInterrupted in editor_status rather than waiting for an editor that is not coming, and open finishes it, saying so under finishesRestart. An open is refused when the editor it would start could never reach this server: a project other than the one this server serves, whose editor dials that project's own server; a project without the gdharness editor addon installed and enabled; and a project whose editor a restart is already bringing back.",
|
|
34725
|
+
title: "Open, restart or close the editor",
|
|
34726
|
+
description: "Opens the Godot editor on a project, or restarts or closes the one already connected. close saves open scenes, quits the editor and waits for its process to go, which is the only way to end a hidden one short of killing it; settingsDropped in its answer says what that save took out of project.godot. open refuses while an editor of the project is already running and has not reached this server, naming its pid, rather than putting a second on the project. The editor is an ordinary visible one on the desktop in use, and on Windows it is shown without being activated, so opening or reopening it does not take the keyboard from whatever the user is typing in. hidden: true opens it headless instead and, on Windows, on a desktop of its own, where the games it plays go too; a hidden editor serves the bridge, the language server and the debug adapter as a visible one does. A game a visible editor plays opens as a window on the desktop in use. An editor goes on serving the addon it read at startup, so restart is what puts a gdharness upgrade into effect, and nothing else here restarts one; it saves open scenes on the way out and answers with the version that came back. An editor a server opened is started again by the server, visible unless hidden: true is passed again, once the old one has gone: it waits 90 seconds for that, or GDHARNESS_EDITOR_RESTART_TIMEOUT_MS, and an old editor still running after it is not replaced, since the new one would find its ports held. One opened by hand restarts itself, which only one with a window can do, because the engine hands back none of the arguments it was started with. editor_status says which editor is connected and whether it is holding an old addon. Opening a project is a save and so is closing one, which is the part that is not obvious from outside: the editor imports the project on open and writes project.godot back, and Godot writes only what differs from its own defaults, so a key named deliberately at its default value is dropped either way. A restart says which under settingsDropped in its own answer because it waits for the editor to return. An open cannot, since it answers as soon as the process exists and the save happens during the import minutes later, so editor_status carries that reading once the editor has connected. Nothing else will say a key went until something depends on one. A restart of an editor this server opened is a quit and then a launch, and this server can be ended between them, usually by the reconnect the restart was being taken for: the editor is gone and nothing replaces it. That is written down before the quit, so the next server says so under restartInterrupted in editor_status rather than waiting for an editor that is not coming, and open finishes it, saying so under finishesRestart. An open is refused when the editor it would start could never reach this server: a project other than the one this server serves, whose editor dials that project's own server; a project without the gdharness editor addon installed and enabled; and a project whose editor a restart is already bringing back.",
|
|
34480
34727
|
parameters: {
|
|
34481
34728
|
projectPath: { ...PROJECT_PATH2, ops: ["open"] },
|
|
34482
34729
|
hidden: {
|
|
@@ -34492,6 +34739,11 @@ var TOOL_SPECS = [
|
|
|
34492
34739
|
effect: "destructive",
|
|
34493
34740
|
summary: "restart the connected editor and wait for it",
|
|
34494
34741
|
requires: []
|
|
34742
|
+
},
|
|
34743
|
+
close: {
|
|
34744
|
+
effect: "destructive",
|
|
34745
|
+
summary: "save and quit the connected editor, and wait for it to go",
|
|
34746
|
+
requires: []
|
|
34495
34747
|
}
|
|
34496
34748
|
},
|
|
34497
34749
|
defaultOperation: "open"
|
|
@@ -34617,7 +34869,7 @@ var TOOL_SPECS = [
|
|
|
34617
34869
|
{
|
|
34618
34870
|
name: "editor_output",
|
|
34619
34871
|
title: "Game and editor output",
|
|
34620
|
-
description: "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. For a run the editor plays the reports come from the game itself, through the runtime addon, since the editor's debugger relays what a game prints and not what it reports; a played game without the runtime addon answers here with its prints alone, with errorsUnread true and no clean, since what it raised reaches nothing here. Answers with the counts and the verdict as well as the entries. heldAt is three answers: null for a run this server knows is not held, running or over, an object for one it knows is held at a breakpoint, whether the adapter said so or the runtime did by accepting a connection and never answering, and absent with heldUnknown beside it for a server that connected to the debugger after the stop and has no runtime to ask, since the adapter reports a stop only to the sessions connected when it happened. A run that has quit still answers here, with running false and its exit code, until the next one starts; a run ended by a signal has no exit code, and exitSignal names the signal instead. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away. Read the file for output and this answer for state: endedBy says whether gdharness ended the run, and a transcript that has stopped growing is a run between prints rather than a run that is over. running is asked of the operating system whenever there is a process to ask about, which is every run this server started and every editor-played run whose game announced its runtime. For an editor-played run that never announced, there is no process id here and running is the editor's answer, which can lag: one measured elsewhere reported a game as playing for fifteen seconds after its process had been ended from outside. editor_status names that game under runtimes, which does not depend on the editor answering. op: \"editor\" answers about the editor itself instead, which is a different log and the only way anything here can read what an editor printed: its console reaches no plugin, so the addon cannot scrape it, and an editor gdharness opened is asked for a log file as it starts. That covers the whole session from the editor's first line, which is where the output worth reading usually is, since the class cache, the plugins and the language server all say what they have to say before any harness has connected. The answer carries the entries in the order they were printed and, beside them under repeated, each message shape several lines share: how many, what varied in each slot of it and how many different values stood there, the first occurrence, and the ordinary lines above the group, found by walking back past the group rather than by counting lines. An editor somebody opened by hand has no console here and is refused saying so rather than answered empty, because an editor that printed nothing and an editor nobody captured are not the same thing. startedAt is when the run began and elapsedMs how long it has been going; for a run that is over, endedAt is when it ended and elapsedMs how long it ran, and both are left out when nobody saw the end, as for a game closed in the editor or a run picked up from a note written before ends were recorded.",
|
|
34872
|
+
description: "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. For a run the editor plays the reports come from the game itself, through the runtime addon, since the editor's debugger relays what a game prints and not what it reports; a played game without the runtime addon answers here with its prints alone, with errorsUnread true and no clean, since what it raised reaches nothing here. Answers with the counts and the verdict as well as the entries: clean is true when no error was reported, and warnings, counted beside it, do not make it false. heldAt is three answers: null for a run this server knows is not held, running or over, an object for one it knows is held at a breakpoint, whether the adapter said so or the runtime did by accepting a connection and never answering, and absent with heldUnknown beside it for a server that connected to the debugger after the stop and has no runtime to ask, since the adapter reports a stop only to the sessions connected when it happened. A run that has quit still answers here, with running false and its exit code, until the next one starts; a run ended by a signal has no exit code, and exitSignal names the signal instead. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away. Read the file for output and this answer for state: endedBy says whether gdharness ended the run, and a transcript that has stopped growing is a run between prints rather than a run that is over. running is asked of the operating system whenever there is a process to ask about, which is every run this server started and every editor-played run whose game announced its runtime. For an editor-played run that never announced, there is no process id here and running is the editor's answer, which can lag: one measured elsewhere reported a game as playing for fifteen seconds after its process had been ended from outside. editor_status names that game under runtimes, which does not depend on the editor answering. op: \"editor\" answers about the editor itself instead, which is a different log and the only way anything here can read what an editor printed: its console reaches no plugin, so the addon cannot scrape it, and an editor gdharness opened is asked for a log file as it starts. That covers the whole session from the editor's first line, which is where the output worth reading usually is, since the class cache, the plugins and the language server all say what they have to say before any harness has connected. The answer carries the entries in the order they were printed and, beside them under repeated, each message shape several lines share: how many, what varied in each slot of it and how many different values stood there, the first occurrence, and the ordinary lines above the group, found by walking back past the group rather than by counting lines. An editor somebody opened by hand has no console here and is refused saying so rather than answered empty, because an editor that printed nothing and an editor nobody captured are not the same thing. startedAt is when the run began and elapsedMs how long it has been going; for a run that is over, endedAt is when it ended and elapsedMs how long it ran, and both are left out when nobody saw the end, as for a game closed in the editor or a run picked up from a note written before ends were recorded.",
|
|
34621
34873
|
parameters: {
|
|
34622
34874
|
severity: {
|
|
34623
34875
|
type: "string",
|
|
@@ -34658,7 +34910,7 @@ var TOOL_SPECS = [
|
|
|
34658
34910
|
},
|
|
34659
34911
|
editor: {
|
|
34660
34912
|
effect: "read",
|
|
34661
|
-
summary: "What the connected editor itself has printed, from its first line, with repeated messages grouped and the ordinary lines above each group carried with it",
|
|
34913
|
+
summary: "What the connected editor itself has printed, from its first line, with repeated messages grouped and the ordinary lines above each group carried with it. The engine's progress bars are one line per task, saying how far an unfinished one got",
|
|
34662
34914
|
requires: []
|
|
34663
34915
|
}
|
|
34664
34916
|
},
|
|
@@ -35311,7 +35563,6 @@ var RUNTIME_AUTOLOAD = {
|
|
|
35311
35563
|
name: "GdharnessRuntime",
|
|
35312
35564
|
path: "addons/gdharness_runtime/runtime_autoload.gd"
|
|
35313
35565
|
};
|
|
35314
|
-
var VERSION_MARKER = ".gdharness-version";
|
|
35315
35566
|
var DIGEST_MARKER = ".gdharness-digest";
|
|
35316
35567
|
var RUNTIME_ADDON = "gdharness_runtime";
|
|
35317
35568
|
function editorAddonDigest(from = shippedAddonsDirectory()) {
|
|
@@ -36061,6 +36312,7 @@ var SCAN_SETTLED_MS = 500;
|
|
|
36061
36312
|
var REIMPORT_TIMEOUT_MS = 300000;
|
|
36062
36313
|
var AUDIO_BUS_OPS = new Set([
|
|
36063
36314
|
"add_audio_bus",
|
|
36315
|
+
"remove_audio_bus",
|
|
36064
36316
|
"set_audio_bus_effect",
|
|
36065
36317
|
"set_audio_bus_volume"
|
|
36066
36318
|
]);
|
|
@@ -36082,6 +36334,7 @@ function settingsNamedBy(op, args) {
|
|
|
36082
36334
|
case "set_main_scene":
|
|
36083
36335
|
return ["application/run/main_scene"];
|
|
36084
36336
|
case "add_input_action":
|
|
36337
|
+
case "remove_input_action":
|
|
36085
36338
|
return text("actionName") === "" ? [] : [`input/${text("actionName")}`];
|
|
36086
36339
|
case "enable_plugin":
|
|
36087
36340
|
case "disable_plugin":
|
|
@@ -36680,6 +36933,7 @@ class GodotServer {
|
|
|
36680
36933
|
launchedEditor = null;
|
|
36681
36934
|
launchedFrom = null;
|
|
36682
36935
|
launchedDropped = null;
|
|
36936
|
+
closedHere = null;
|
|
36683
36937
|
ownProject;
|
|
36684
36938
|
announcedAt = null;
|
|
36685
36939
|
handingOverFrom = null;
|
|
@@ -36689,6 +36943,7 @@ class GodotServer {
|
|
|
36689
36943
|
this.godotBridge = getDefaultBridge(this.ownProject);
|
|
36690
36944
|
this.godotBridge.identifyEditorsWith(oneShotOptionOf);
|
|
36691
36945
|
this.godotBridge.on("godot_connected", () => {
|
|
36946
|
+
this.closedHere = null;
|
|
36692
36947
|
this.settleTheLaunchReading();
|
|
36693
36948
|
});
|
|
36694
36949
|
this.setupShutdownHandlers();
|
|
@@ -37282,6 +37537,10 @@ class GodotServer {
|
|
|
37282
37537
|
}
|
|
37283
37538
|
case "scene_animation":
|
|
37284
37539
|
switch (op) {
|
|
37540
|
+
case "read":
|
|
37541
|
+
return await bridge("read_animations");
|
|
37542
|
+
case "read_states":
|
|
37543
|
+
return await bridge("read_animation_states");
|
|
37285
37544
|
case "create":
|
|
37286
37545
|
return await bridge("create_animation");
|
|
37287
37546
|
case "add_track":
|
|
@@ -37306,6 +37565,8 @@ class GodotServer {
|
|
|
37306
37565
|
return await this.handleScriptDiagnostics(args);
|
|
37307
37566
|
case "resource_edit":
|
|
37308
37567
|
switch (op) {
|
|
37568
|
+
case "read":
|
|
37569
|
+
return await bridge("read_resource");
|
|
37309
37570
|
case "create":
|
|
37310
37571
|
return await bridge("create_resource");
|
|
37311
37572
|
case "modify":
|
|
@@ -37320,6 +37581,9 @@ class GodotServer {
|
|
|
37320
37581
|
return await bridge("set_theme_font_size", { themePath: args["resourcePath"] });
|
|
37321
37582
|
}
|
|
37322
37583
|
case "editor_launch":
|
|
37584
|
+
if (op === "close") {
|
|
37585
|
+
return await this.handleCloseEditor();
|
|
37586
|
+
}
|
|
37323
37587
|
return op === "restart" ? await this.handleRestartEditor(args) : await this.handleLaunchEditor(args);
|
|
37324
37588
|
case "editor_run":
|
|
37325
37589
|
if (op === "stop") {
|
|
@@ -38217,21 +38481,31 @@ class GodotServer {
|
|
|
38217
38481
|
}
|
|
38218
38482
|
const missing = paths.value.filter((path) => !existsSync17(join22(project.value.path, path.slice("res://".length))));
|
|
38219
38483
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
38484
|
+
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
38485
|
+
const noRunner = !existsSync17(join22(project.value.path, runner));
|
|
38220
38486
|
if (asked.length === 0) {
|
|
38221
38487
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
38488
|
+
const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
|
|
38222
38489
|
return {
|
|
38223
38490
|
content: [
|
|
38224
38491
|
{
|
|
38225
38492
|
type: "text",
|
|
38226
|
-
text: `No tests ran: ${verdict}.${elsewhereIn(project.value.path, missing[0] ?? "")}`
|
|
38493
|
+
text: `No tests ran: ${verdict}${andNoRunner}.${elsewhereIn(project.value.path, missing[0] ?? "")}`
|
|
38227
38494
|
},
|
|
38228
|
-
{
|
|
38495
|
+
{
|
|
38496
|
+
type: "text",
|
|
38497
|
+
text: answerJson({
|
|
38498
|
+
passed: false,
|
|
38499
|
+
verdict,
|
|
38500
|
+
tests: 0,
|
|
38501
|
+
...noRunner ? { gdUnit4Installed: false } : {}
|
|
38502
|
+
})
|
|
38503
|
+
}
|
|
38229
38504
|
],
|
|
38230
38505
|
isError: true
|
|
38231
38506
|
};
|
|
38232
38507
|
}
|
|
38233
|
-
|
|
38234
|
-
if (!existsSync17(join22(project.value.path, runner))) {
|
|
38508
|
+
if (noRunner) {
|
|
38235
38509
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
38236
38510
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
38237
38511
|
]);
|
|
@@ -38814,8 +39088,12 @@ class GodotServer {
|
|
|
38814
39088
|
addonIsStale: greeted ? stale : undefined,
|
|
38815
39089
|
greeting: status.connected && !greeted ? "The editor has connected and has not yet said who it is: its project, pid, addon version and ports arrive with its greeting a moment after the socket opens, later while it imports. Ask again for them." : undefined,
|
|
38816
39090
|
bridgeAvailable: this.bridgeStartupError === null && status.port !== null,
|
|
38817
|
-
mayYetConnect: status.connected ? undefined : arriving.length > 0 ? true : unfinished !== null ? unfinished.underway : anEditorIsStillComing(status.listeningSince, launchedIsUp),
|
|
39091
|
+
mayYetConnect: status.connected ? undefined : arriving.length > 0 ? true : unfinished !== null ? unfinished.underway : this.closedHere !== null ? false : anEditorIsStillComing(status.listeningSince, launchedIsUp),
|
|
38818
39092
|
editorsNotYetConnected: arriving.length > 0 ? arriving : undefined,
|
|
39093
|
+
closedHere: status.connected || arriving.length > 0 || this.closedHere === null ? undefined : {
|
|
39094
|
+
...this.closedHere,
|
|
39095
|
+
note: "This server closed the editor on request, so nothing is coming until editor_launch open starts one."
|
|
39096
|
+
},
|
|
38819
39097
|
restartInterrupted: unfinished === null || unfinished.underway || arriving.length > 0 ? undefined : {
|
|
38820
39098
|
quitEditorPid: unfinished.note.editorPid,
|
|
38821
39099
|
quitAt: unfinished.note.quitAt,
|
|
@@ -39060,6 +39338,42 @@ class GodotServer {
|
|
|
39060
39338
|
tookMs: Date.now() - began
|
|
39061
39339
|
});
|
|
39062
39340
|
}
|
|
39341
|
+
async handleCloseEditor() {
|
|
39342
|
+
const before = this.godotBridge.getStatus();
|
|
39343
|
+
if (!before.connected) {
|
|
39344
|
+
return this.createErrorResponse("No editor is connected, so there is nothing to close.", [
|
|
39345
|
+
"editor_status says whether the bridge is up and what has reached it"
|
|
39346
|
+
]);
|
|
39347
|
+
}
|
|
39348
|
+
if (!hasSaidWhoItIs(before) || before.editorPid === undefined) {
|
|
39349
|
+
return this.createErrorResponse(`The editor has connected and has not said who it is within ${greetingWaitMs() / 1000}s, so there is no process to wait on once it is asked to go.`, ["editor_status says when it has, under greeting while it has not", "Then ask for the close again"]);
|
|
39350
|
+
}
|
|
39351
|
+
const pid = before.editorPid;
|
|
39352
|
+
const settingsBefore = this.settingKeysOf(before.projectPath);
|
|
39353
|
+
const asked = await this.handleViaBridge("quit_editor", {});
|
|
39354
|
+
if (asked.isError === true) {
|
|
39355
|
+
return asked;
|
|
39356
|
+
}
|
|
39357
|
+
const began = Date.now();
|
|
39358
|
+
const gone = await this.waitForBridge(() => !alive(pid), began + editorRestartTimeoutMs());
|
|
39359
|
+
if (!gone) {
|
|
39360
|
+
return this.createErrorResponse(`The editor saved and was asked to go, and pid ${pid} is still running after ${editorRestartTimeoutMs() / 1000}s.`, [
|
|
39361
|
+
"It may be asking what to do about unsaved changes, or a script may be holding up its exit",
|
|
39362
|
+
"editor_status says whether it is still connected"
|
|
39363
|
+
]);
|
|
39364
|
+
}
|
|
39365
|
+
this.closedHere = { editorPid: pid, at: new Date().toISOString() };
|
|
39366
|
+
if (this.launchedEditor?.pid === pid) {
|
|
39367
|
+
this.launchedEditor = null;
|
|
39368
|
+
}
|
|
39369
|
+
return this.jsonTextResponse({
|
|
39370
|
+
closed: true,
|
|
39371
|
+
editorPid: pid,
|
|
39372
|
+
projectPath: before.projectPath,
|
|
39373
|
+
...this.whatTheEditorDropped(settingsBefore, this.settingKeysOf(before.projectPath)),
|
|
39374
|
+
tookMs: Date.now() - began
|
|
39375
|
+
});
|
|
39376
|
+
}
|
|
39063
39377
|
settleTheLaunchReading() {
|
|
39064
39378
|
const watched = this.launchedFrom;
|
|
39065
39379
|
const status = this.godotBridge.getStatus();
|
|
@@ -39231,6 +39545,7 @@ class GodotServer {
|
|
|
39231
39545
|
pid: launched.pid,
|
|
39232
39546
|
startedAt: (await startTimesOf([launched.pid])).get(launched.pid) ?? null
|
|
39233
39547
|
};
|
|
39548
|
+
this.closedHere = null;
|
|
39234
39549
|
return { pid: launched.pid, error: null };
|
|
39235
39550
|
}
|
|
39236
39551
|
async portsForAnEditor() {
|
|
@@ -39476,7 +39791,7 @@ class GodotServer {
|
|
|
39476
39791
|
frameFunctions: profileFrameFunctions(projectPath)
|
|
39477
39792
|
});
|
|
39478
39793
|
if (armed.isError === true) {
|
|
39479
|
-
return
|
|
39794
|
+
return armed;
|
|
39480
39795
|
}
|
|
39481
39796
|
}
|
|
39482
39797
|
const answer = await this.handleViaBridge("play_scene", scene === null ? {} : { scenePath: `res://${scene}` });
|
|
@@ -40326,7 +40641,7 @@ class GodotServer {
|
|
|
40326
40641
|
async profileOfThePlayedGame(going, projectPath, fromProject, limit) {
|
|
40327
40642
|
const read = await this.handleViaBridge("profile_read", {});
|
|
40328
40643
|
if (read.isError === true) {
|
|
40329
|
-
return
|
|
40644
|
+
return read;
|
|
40330
40645
|
}
|
|
40331
40646
|
const held = asParams(JSON.parse(read.content[0]?.text ?? "{}"));
|
|
40332
40647
|
if (readBoolean(held, "profiled") !== true && readBoolean(held, "armed") !== true) {
|
|
@@ -40339,7 +40654,7 @@ class GodotServer {
|
|
|
40339
40654
|
frameFunctions: profileFrameFunctions(projectPath)
|
|
40340
40655
|
});
|
|
40341
40656
|
if (started.isError === true) {
|
|
40342
|
-
return
|
|
40657
|
+
return started;
|
|
40343
40658
|
}
|
|
40344
40659
|
if (readBoolean(asParams(JSON.parse(started.content[0]?.text ?? "{}")), "started") !== true) {
|
|
40345
40660
|
return this.createErrorResponse("The editor says it is playing and has no debugger session for the game, so there is nothing to switch the profiler on in yet.", ["Ask again in a moment, once the game is up"]);
|
|
@@ -40395,13 +40710,6 @@ class GodotServer {
|
|
|
40395
40710
|
}
|
|
40396
40711
|
return false;
|
|
40397
40712
|
}
|
|
40398
|
-
profilerRefusal(refused) {
|
|
40399
|
-
const said = refused.content[0]?.text ?? "";
|
|
40400
|
-
return said.includes("Unknown tool: profile_") ? this.createErrorResponse("The editor's gdharness addon is older than the profiler, so it cannot profile the game it plays.", [
|
|
40401
|
-
"editor_launch restart loads the addon this server installed",
|
|
40402
|
-
"editor_status says which addon the editor holds"
|
|
40403
|
-
]) : refused;
|
|
40404
|
-
}
|
|
40405
40713
|
async handleWaitForRun(args) {
|
|
40406
40714
|
await this.pickUpWhatTheEditorIsPlaying();
|
|
40407
40715
|
const run = await this.currentRun();
|
|
@@ -41365,7 +41673,13 @@ class GodotServer {
|
|
|
41365
41673
|
try {
|
|
41366
41674
|
return this.jsonTextResponse(markIfStale(await this.godotBridge.invokeTool(toolName, args), this.godotBridge.getStatus().addonVersion, SERVER_VERSION, this.godotBridge.getStatus().addonDigest, shippedEditorDigest(), this.editorCodeOnDisk()));
|
|
41367
41675
|
} catch (error) {
|
|
41368
|
-
|
|
41676
|
+
const message = errorMessage(error);
|
|
41677
|
+
if (message.includes(`Unknown tool: ${toolName}`)) {
|
|
41678
|
+
const status = this.godotBridge.getStatus();
|
|
41679
|
+
const remedy = addonMismatch(status.addonVersion, SERVER_VERSION, status.addonDigest, shippedEditorDigest(), this.editorCodeOnDisk());
|
|
41680
|
+
return this.createErrorResponse(`The editor's gdharness addon is older than this ${SERVER_VERSION} server and has no ${toolName}, so it cannot answer this call. ${remedy ?? "Restart it with editor_launch restart to load the addon the project holds."}`, ["editor_status says which addon the editor holds"]);
|
|
41681
|
+
}
|
|
41682
|
+
return this.createErrorResponse(`The editor answered ${toolName} with an error: ${message}`);
|
|
41369
41683
|
}
|
|
41370
41684
|
}
|
|
41371
41685
|
runtimeTimeoutMs() {
|