gdharness 1.1.14 → 1.1.15

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 CHANGED
@@ -605,8 +605,18 @@ function resolveHeadless(requested, host) {
605
605
  }
606
606
  return envValue("DISPLAY", host.variables) === undefined && envValue("WAYLAND_DISPLAY", host.variables) === undefined;
607
607
  }
608
+ function resolveSilent(requested, variables) {
609
+ if (typeof requested === "boolean") {
610
+ return requested;
611
+ }
612
+ const said = envValue("GDHARNESS_SILENT", variables)?.toLowerCase();
613
+ return said !== undefined && said !== "0" && said !== "false";
614
+ }
608
615
  function runArguments(options) {
609
616
  const args = options.headless ? ["--headless", "--path", options.projectPath] : ["--path", options.projectPath];
617
+ if (options.silent === true && !options.headless) {
618
+ args.push("--audio-driver", "Dummy");
619
+ }
610
620
  if (options.quitAfter !== undefined && options.quitAfter !== null) {
611
621
  args.push("--quit-after", String(options.quitAfter));
612
622
  }
@@ -17137,6 +17147,11 @@ var init_tool_definitions = __esm(() => {
17137
17147
  ops: ["start"],
17138
17148
  description: "start: on Windows, show the window on the desktop in use. By default a windowed run this server starts goes on a desktop of its own, where it renders and runtime_capture works but the window never shows and never takes the keyboard. A game a visible editor plays opens on the desktop in use, and one a hidden editor (editor_launch hidden: true) plays goes on its desktop. Elsewhere than Windows the window shows either way. Default false."
17139
17149
  },
17150
+ silent: {
17151
+ type: "boolean",
17152
+ ops: ["start"],
17153
+ description: "start: run the game with no sound out of the machine, by starting the engine with --audio-driver Dummy: every stream still plays and every playback position still advances, so what is playing can still be asked. The editor takes no engine flags for a game it plays, so a silent windowed run is started by this server, and the debug_* tools do not answer for it. A headless run, check's included, is silent already. Default false, or true when GDHARNESS_SILENT is set in the server's environment to anything but 0 or false; silent: false then plays through the editor as before."
17154
+ },
17140
17155
  savesIn: {
17141
17156
  type: "string",
17142
17157
  ops: ["start", "check"],
@@ -41722,14 +41737,16 @@ class GodotServer {
41722
41737
  });
41723
41738
  const runtimeWaitMs = this.announceBudget(args, project.value.path);
41724
41739
  const editorWouldPlay = !headless || editorPlaysHeadless(project.value.file);
41725
- if (this.godotBridge.isConnected() && editorWouldPlay && given.value.length === 0 && !needsItsOwnProcess(asked.value)) {
41740
+ const silent = resolveSilent(args["silent"], process.env) && !headless;
41741
+ if (this.godotBridge.isConnected() && editorWouldPlay && given.value.length === 0 && !needsItsOwnProcess(asked.value) && !silent) {
41726
41742
  return await this.playThroughEditor(sceneArgument, refreshed.value, scanned, project.value.path, alreadyPlaying, runtimeWaitMs, ended);
41727
41743
  }
41728
41744
  const cmdArgs = runArguments({
41729
41745
  projectPath: project.value.path,
41730
41746
  headless,
41731
41747
  scene: sceneArgument,
41732
- userArgs: given.value
41748
+ userArgs: given.value,
41749
+ silent
41733
41750
  });
41734
41751
  this.logDebug(`Running Godot project: ${engine.value} ${cmdArgs.join(" ")}`);
41735
41752
  const started = await this.launchKeptGame(engine.value, cmdArgs, project.value.path, needsItsOwnProcess(asked.value) ? environmentFor({ ...asked.value, runtimeDirectory: runtimeDirectory() }) : undefined, headless || args["visible"] === true ? undefined : HIDDEN_DESKTOP);
@@ -41740,7 +41757,7 @@ class GodotServer {
41740
41757
  }
41741
41758
  started.announcedBefore = alreadyPlaying;
41742
41759
  this.activeProcess = started;
41743
- const spawnedInstead = this.godotBridge.isConnected() && editorWouldPlay && given.value.length > 0 ? " The editor cannot be handed arguments for a game it plays, so this one was started " + "here: the debug_* tools answer only for a game the editor is playing." : this.godotBridge.isConnected() && editorWouldPlay && needsItsOwnProcess(asked.value) ? " The editor plays a game with its own environment, so a run given one was started " + "here: the debug_* tools answer only for a game the editor is playing." : "";
41760
+ const spawnedInstead = this.godotBridge.isConnected() && editorWouldPlay && given.value.length > 0 ? " The editor cannot be handed arguments for a game it plays, so this one was started " + "here: the debug_* tools answer only for a game the editor is playing." : this.godotBridge.isConnected() && editorWouldPlay && needsItsOwnProcess(asked.value) ? " The editor plays a game with its own environment, so a run given one was started " + "here: the debug_* tools answer only for a game the editor is playing." : this.godotBridge.isConnected() && editorWouldPlay && silent ? " The editor cannot play a game silent, which takes an engine flag, so this one was " + `started here${args["silent"] === true ? "" : ", silent because GDHARNESS_SILENT says so"}: the debug_* tools answer only for a game the editor is playing, and silent: false plays it through the editor.` : "";
41744
41761
  const endedForThis = endedToStartThis(ended);
41745
41762
  const stranded = wereHereFirst.filter((one) => one.pid !== ended?.pid && alive(one.pid));
41746
41763
  const alsoRunning = leftRunningNote(stranded);
@@ -39,6 +39,11 @@ var _values: Values
39
39
  var _pointer: Vector2 = Vector2.ZERO
40
40
  var _pointer_placed: bool = false
41
41
 
42
+ ## When the pointer last arrived, and how long it took over the arrival before that, in seconds: what
43
+ ## turns a motion's distance into the speed a real one carries.
44
+ var _pointer_at_usec: int = 0
45
+ var _arrival_seconds: float = 1.0 / 60.0
46
+
42
47
 
43
48
  func _init(host: Node, values: Values) -> void:
44
49
  _host = host
@@ -46,9 +51,15 @@ func _init(host: Node, values: Values) -> void:
46
51
 
47
52
 
48
53
  ## The movement a pointer arriving at [param position] carries, which is the distance from where
49
- ## the last injected event put it, and none at all for the first. The arrival is recorded.
54
+ ## the last injected event put it, and none at all for the first. The arrival is recorded, with how
55
+ ## long it took: at least a frame, since two motions sent in one frame did not cross the screen in
56
+ ## no time at all.
50
57
  func _arrive(position: Vector2) -> Vector2:
58
+ var now: int = Time.get_ticks_usec()
51
59
  var relative: Vector2 = position - _pointer if _pointer_placed else Vector2.ZERO
60
+ var took: float = float(now - _pointer_at_usec) / 1_000_000.0 if _pointer_placed else 0.0
61
+ _arrival_seconds = maxf(took, 1.0 / 60.0)
62
+ _pointer_at_usec = now
52
63
  _pointer = position
53
64
  _pointer_placed = true
54
65
  return relative
@@ -839,12 +850,19 @@ func _click_in_the_world(node_path: String, item: Node3D, params: Dictionary) ->
839
850
  ## The buttons a real pointer event carries are the ones held as it happens, and Input keeps that
840
851
  ## from every button event it is given, injected ones included, so a motion between a held click
841
852
  ## and its release is a drag to a control that reads the mask rather than remembering the click.
853
+ ##
854
+ ## And the speed, the distance over the time the pointer took, which only the platform fills in. A
855
+ ## menu ignores a motion with none, so that one opening under a resting pointer lights nothing: a
856
+ ## motion over an open drop-down's item answered as injected and the menu highlighted nothing. A
857
+ ## motion that goes nowhere still has none, as a resting pointer does.
842
858
  func _motion(position: Vector2, relative: Vector2) -> InputEventMouseMotion:
843
859
  var event: InputEventMouseMotion = InputEventMouseMotion.new()
844
860
  event.position = position
845
861
  event.global_position = position
846
862
  event.relative = relative
847
863
  event.screen_relative = relative
864
+ event.velocity = relative / _arrival_seconds
865
+ event.screen_velocity = event.velocity
848
866
  event.button_mask = Input.get_mouse_button_mask()
849
867
  return event
850
868
 
package/build/index.js CHANGED
@@ -24825,8 +24825,18 @@ function resolveHeadless(requested, host) {
24825
24825
  }
24826
24826
  return envValue("DISPLAY", host.variables) === undefined && envValue("WAYLAND_DISPLAY", host.variables) === undefined;
24827
24827
  }
24828
+ function resolveSilent(requested, variables) {
24829
+ if (typeof requested === "boolean") {
24830
+ return requested;
24831
+ }
24832
+ const said = envValue("GDHARNESS_SILENT", variables)?.toLowerCase();
24833
+ return said !== undefined && said !== "0" && said !== "false";
24834
+ }
24828
24835
  function runArguments(options) {
24829
24836
  const args = options.headless ? ["--headless", "--path", options.projectPath] : ["--path", options.projectPath];
24837
+ if (options.silent === true && !options.headless) {
24838
+ args.push("--audio-driver", "Dummy");
24839
+ }
24830
24840
  if (options.quitAfter !== undefined && options.quitAfter !== null) {
24831
24841
  args.push("--quit-after", String(options.quitAfter));
24832
24842
  }
@@ -30794,6 +30804,11 @@ var TOOL_SPECS = [
30794
30804
  ops: ["start"],
30795
30805
  description: "start: on Windows, show the window on the desktop in use. By default a windowed run this server starts goes on a desktop of its own, where it renders and runtime_capture works but the window never shows and never takes the keyboard. A game a visible editor plays opens on the desktop in use, and one a hidden editor (editor_launch hidden: true) plays goes on its desktop. Elsewhere than Windows the window shows either way. Default false."
30796
30806
  },
30807
+ silent: {
30808
+ type: "boolean",
30809
+ ops: ["start"],
30810
+ description: "start: run the game with no sound out of the machine, by starting the engine with --audio-driver Dummy: every stream still plays and every playback position still advances, so what is playing can still be asked. The editor takes no engine flags for a game it plays, so a silent windowed run is started by this server, and the debug_* tools do not answer for it. A headless run, check's included, is silent already. Default false, or true when GDHARNESS_SILENT is set in the server's environment to anything but 0 or false; silent: false then plays through the editor as before."
30811
+ },
30797
30812
  savesIn: {
30798
30813
  type: "string",
30799
30814
  ops: ["start", "check"],
@@ -34182,14 +34197,16 @@ class GodotServer {
34182
34197
  });
34183
34198
  const runtimeWaitMs = this.announceBudget(args, project.value.path);
34184
34199
  const editorWouldPlay = !headless || editorPlaysHeadless(project.value.file);
34185
- if (this.godotBridge.isConnected() && editorWouldPlay && given.value.length === 0 && !needsItsOwnProcess(asked.value)) {
34200
+ const silent = resolveSilent(args["silent"], process.env) && !headless;
34201
+ if (this.godotBridge.isConnected() && editorWouldPlay && given.value.length === 0 && !needsItsOwnProcess(asked.value) && !silent) {
34186
34202
  return await this.playThroughEditor(sceneArgument, refreshed.value, scanned, project.value.path, alreadyPlaying, runtimeWaitMs, ended);
34187
34203
  }
34188
34204
  const cmdArgs = runArguments({
34189
34205
  projectPath: project.value.path,
34190
34206
  headless,
34191
34207
  scene: sceneArgument,
34192
- userArgs: given.value
34208
+ userArgs: given.value,
34209
+ silent
34193
34210
  });
34194
34211
  this.logDebug(`Running Godot project: ${engine.value} ${cmdArgs.join(" ")}`);
34195
34212
  const started = await this.launchKeptGame(engine.value, cmdArgs, project.value.path, needsItsOwnProcess(asked.value) ? environmentFor({ ...asked.value, runtimeDirectory: runtimeDirectory() }) : undefined, headless || args["visible"] === true ? undefined : HIDDEN_DESKTOP);
@@ -34200,7 +34217,7 @@ class GodotServer {
34200
34217
  }
34201
34218
  started.announcedBefore = alreadyPlaying;
34202
34219
  this.activeProcess = started;
34203
- const spawnedInstead = this.godotBridge.isConnected() && editorWouldPlay && given.value.length > 0 ? " The editor cannot be handed arguments for a game it plays, so this one was started " + "here: the debug_* tools answer only for a game the editor is playing." : this.godotBridge.isConnected() && editorWouldPlay && needsItsOwnProcess(asked.value) ? " The editor plays a game with its own environment, so a run given one was started " + "here: the debug_* tools answer only for a game the editor is playing." : "";
34220
+ const spawnedInstead = this.godotBridge.isConnected() && editorWouldPlay && given.value.length > 0 ? " The editor cannot be handed arguments for a game it plays, so this one was started " + "here: the debug_* tools answer only for a game the editor is playing." : this.godotBridge.isConnected() && editorWouldPlay && needsItsOwnProcess(asked.value) ? " The editor plays a game with its own environment, so a run given one was started " + "here: the debug_* tools answer only for a game the editor is playing." : this.godotBridge.isConnected() && editorWouldPlay && silent ? " The editor cannot play a game silent, which takes an engine flag, so this one was " + `started here${args["silent"] === true ? "" : ", silent because GDHARNESS_SILENT says so"}: the debug_* tools answer only for a game the editor is playing, and silent: false plays it through the editor.` : "";
34204
34221
  const endedForThis = endedToStartThis(ended);
34205
34222
  const stranded = wereHereFirst.filter((one) => one.pid !== ended?.pid && alive(one.pid));
34206
34223
  const alsoRunning = leftRunningNote(stranded);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.1.14",
3
+ "version": "1.1.15",
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",