gdharness 1.3.39 → 1.3.40
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 +40 -7
- package/build/godot/addons/gdharness_editor/animation_reads.gd +7 -6
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +6 -3
- package/build/godot/addons/gdharness_editor/tools/animation_tracks.gd +1 -1
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +5 -3
- package/build/godot/operations/animation_reads.gd +7 -6
- package/build/keeper.js +38 -5
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -852,7 +852,18 @@ public static class GdharnessDesktop {
|
|
|
852
852
|
}
|
|
853
853
|
|
|
854
854
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
855
|
-
static extern IntPtr
|
|
855
|
+
static extern IntPtr CreateDesktopExW(string name, IntPtr device, IntPtr mode, uint flags, uint access, IntPtr attributes, uint heapKilobytes, IntPtr reserved);
|
|
856
|
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
857
|
+
static extern IntPtr CreateWindowStationW(string name, uint flags, uint access, IntPtr attributes);
|
|
858
|
+
[DllImport("user32.dll", SetLastError = true)] static extern IntPtr GetProcessWindowStation();
|
|
859
|
+
[DllImport("user32.dll", SetLastError = true)] static extern bool SetProcessWindowStation(IntPtr station);
|
|
860
|
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
861
|
+
static extern bool GetUserObjectInformationW(IntPtr handle, int index, StringBuilder into, int size, out int needed);
|
|
862
|
+
const uint WindowStationAllAccess = 0x37F;
|
|
863
|
+
const int ObjectName = 2;
|
|
864
|
+
// What Windows gives an interactive desktop. A non-interactive station's desktops get a small
|
|
865
|
+
// fraction of that by default, and one desktop here holds every engine a run starts.
|
|
866
|
+
const uint DesktopHeapKilobytes = 20480;
|
|
856
867
|
|
|
857
868
|
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
858
869
|
static extern bool CreateProcessW(string application, StringBuilder commandLine, IntPtr processAttributes, IntPtr threadAttributes, bool inherit, uint flags, IntPtr environment, string directory, ref StartupInfo startup, out Started started);
|
|
@@ -943,12 +954,34 @@ public static class GdharnessDesktop {
|
|
|
943
954
|
StartupInfo startup = new StartupInfo();
|
|
944
955
|
startup.cb = Marshal.SizeOf(typeof(StartupInfo));
|
|
945
956
|
if (desktop.Length > 0) {
|
|
946
|
-
Step(stepFile, "
|
|
947
|
-
IntPtr
|
|
957
|
+
Step(stepFile, "opening the session's own window station");
|
|
958
|
+
IntPtr station = CreateWindowStationW(null, 0, WindowStationAllAccess, IntPtr.Zero);
|
|
959
|
+
if (station == IntPtr.Zero) {
|
|
960
|
+
throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateWindowStationW");
|
|
961
|
+
}
|
|
962
|
+
StringBuilder stationName = new StringBuilder(256);
|
|
963
|
+
int needed;
|
|
964
|
+
if (!GetUserObjectInformationW(station, ObjectName, stationName, stationName.Capacity * 2, out needed)) {
|
|
965
|
+
throw new Win32Exception(Marshal.GetLastWin32Error(), "GetUserObjectInformationW");
|
|
966
|
+
}
|
|
967
|
+
// A desktop is made in the calling process's station, so this one is the caller's while it is.
|
|
968
|
+
IntPtr own = GetProcessWindowStation();
|
|
969
|
+
if (!SetProcessWindowStation(station)) {
|
|
970
|
+
throw new Win32Exception(Marshal.GetLastWin32Error(), "SetProcessWindowStation");
|
|
971
|
+
}
|
|
972
|
+
IntPtr held;
|
|
973
|
+
int made;
|
|
974
|
+
try {
|
|
975
|
+
Step(stepFile, "creating the " + desktop + " desktop");
|
|
976
|
+
held = CreateDesktopExW(desktop, IntPtr.Zero, IntPtr.Zero, 0, GenericAll, IntPtr.Zero, DesktopHeapKilobytes, IntPtr.Zero);
|
|
977
|
+
made = Marshal.GetLastWin32Error();
|
|
978
|
+
} finally {
|
|
979
|
+
SetProcessWindowStation(own);
|
|
980
|
+
}
|
|
948
981
|
if (held == IntPtr.Zero) {
|
|
949
|
-
throw new Win32Exception(
|
|
982
|
+
throw new Win32Exception(made, "CreateDesktopExW " + stationName + "\\\\" + desktop);
|
|
950
983
|
}
|
|
951
|
-
startup.desktop = desktop;
|
|
984
|
+
startup.desktop = stationName + "\\\\" + desktop;
|
|
952
985
|
}
|
|
953
986
|
startup.flags = UseStdHandles;
|
|
954
987
|
if (noActivate) {
|
|
@@ -17604,7 +17637,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17604
17637
|
visible: {
|
|
17605
17638
|
type: "boolean",
|
|
17606
17639
|
ops: ["start"],
|
|
17607
|
-
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."
|
|
17640
|
+
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 or the pointer. 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."
|
|
17608
17641
|
},
|
|
17609
17642
|
silent: {
|
|
17610
17643
|
type: "boolean",
|
|
@@ -58235,7 +58268,7 @@ that is running, and the project on disk. ${TOOL_SPECS.length} tools, named \`do
|
|
|
58235
58268
|
These need the runtime autoload, which \`gdharness setup\` registers. \`runtime_capture\` needs a
|
|
58236
58269
|
window and refuses headless rather than handing back the last frame anything drew. On Windows a
|
|
58237
58270
|
windowed run this server starts is on a desktop of its own, where it draws and can be captured but
|
|
58238
|
-
never shows or takes the keyboard.
|
|
58271
|
+
never shows or takes the keyboard or the pointer.
|
|
58239
58272
|
|
|
58240
58273
|
The autoload reads these settings and no others. \`${BIND_ADDRESS_SETTING}\`
|
|
58241
58274
|
is \`127.0.0.1\` and should stay there: the command set includes \`call_method\`, \`set_property\`
|
|
@@ -125,15 +125,16 @@ static func no_animation(player: AnimationPlayer, called: String, animation_name
|
|
|
125
125
|
|
|
126
126
|
static func _summary(anim: Animation) -> Dictionary:
|
|
127
127
|
return {
|
|
128
|
-
"length": anim.length,
|
|
128
|
+
"length": held_seconds(anim.length),
|
|
129
129
|
"loopMode": LOOP_MODE_NAMES.get(anim.loop_mode, str(anim.loop_mode)),
|
|
130
|
-
"step": anim.step,
|
|
130
|
+
"step": held_seconds(anim.step),
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
|
|
134
|
-
## [param seconds], a key's time, as the shortest decimal holding
|
|
135
|
-
##
|
|
136
|
-
|
|
134
|
+
## [param seconds], a key's time or an animation's length or step, as the shortest decimal holding
|
|
135
|
+
## the same 32-bit float. An animation keeps all three as 32-bit floats, so a key put at 0.7 read
|
|
136
|
+
## back as 0.699999988079071 and a step of 0.1 as 0.100000001490116.
|
|
137
|
+
static func held_seconds(seconds: float) -> float:
|
|
137
138
|
var held: float = PackedFloat32Array([seconds])[0]
|
|
138
139
|
for places: int in range(0, 10):
|
|
139
140
|
var shorter: float = String.num(held, places).to_float()
|
|
@@ -159,7 +160,7 @@ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dic
|
|
|
159
160
|
var keyframes: Array[Dictionary] = []
|
|
160
161
|
var methods: Dictionary = {}
|
|
161
162
|
for key: int in anim.track_get_key_count(track):
|
|
162
|
-
var keyframe: Dictionary = {"time":
|
|
163
|
+
var keyframe: Dictionary = {"time": held_seconds(anim.track_get_key_time(track, key))}
|
|
163
164
|
if kind == Animation.TYPE_METHOD:
|
|
164
165
|
var method: String = str(anim.method_track_get_name(track, key))
|
|
165
166
|
methods[method] = true
|
|
@@ -320,7 +320,10 @@ func create_animation(args: Dictionary) -> Dictionary:
|
|
|
320
320
|
return "%s would not be in the scene when it loads." % animation_name
|
|
321
321
|
return ""
|
|
322
322
|
var answer: Dictionary = {
|
|
323
|
-
"ok": true,
|
|
323
|
+
"ok": true,
|
|
324
|
+
"animationName": animation_name,
|
|
325
|
+
"length": GdhAnimationReads.held_seconds(anim.length),
|
|
326
|
+
"loopMode": loop_mode_name
|
|
324
327
|
}
|
|
325
328
|
if library[2]:
|
|
326
329
|
answer["libraryMade"] = animation_name.get_slice("/", 0) if animation_name.contains("/") else ""
|
|
@@ -477,9 +480,9 @@ func set_animation(args: Dictionary) -> Dictionary:
|
|
|
477
480
|
var answer: Dictionary = {
|
|
478
481
|
"ok": true,
|
|
479
482
|
"animationName": animation_name,
|
|
480
|
-
"length": anim.length,
|
|
483
|
+
"length": GdhAnimationReads.held_seconds(anim.length),
|
|
481
484
|
"loopMode": GdhAnimationReads.LOOP_MODE_NAMES.get(anim.loop_mode, str(anim.loop_mode)),
|
|
482
|
-
"step": anim.step,
|
|
485
|
+
"step": GdhAnimationReads.held_seconds(anim.step),
|
|
483
486
|
}
|
|
484
487
|
var past: String = _past_length(anim, animation_name)
|
|
485
488
|
if not past.is_empty():
|
|
@@ -444,7 +444,7 @@ static func _keys_at(anim: Animation, calling: Array, times: Array) -> Array:
|
|
|
444
444
|
if pair[1] >= 0 and key != pair[1]:
|
|
445
445
|
continue
|
|
446
446
|
var time: float = anim.track_get_key_time(index, key)
|
|
447
|
-
var _listed: bool = held.append(str(GdhAnimationReads.
|
|
447
|
+
var _listed: bool = held.append(str(GdhAnimationReads.held_seconds(time)))
|
|
448
448
|
for given: float in times:
|
|
449
449
|
if GdhAnimationReads.same_time(time, given):
|
|
450
450
|
at.append([index, key])
|
|
@@ -61,9 +61,11 @@ func _init(host: Node, values: GdhValues) -> void:
|
|
|
61
61
|
## pointer when the game is on one.
|
|
62
62
|
##
|
|
63
63
|
## For the root window the engine asks Windows where the pointer is, and GetCursorPos refuses a
|
|
64
|
-
## process on a desktop that is not in use:
|
|
65
|
-
## with access denied
|
|
66
|
-
##
|
|
64
|
+
## process on a desktop that is not in use: on one of gdharness's own it answers false and leaves the
|
|
65
|
+
## point unwritten, with access denied in the user's window station and with
|
|
66
|
+
## ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION in the one gdharness's desktop is in. The engine does
|
|
67
|
+
## not check, so a game driven there read get_mouse_position() as x 836852416 after a motion to
|
|
68
|
+
## 1310, 314. Nothing here can
|
|
67
69
|
## put a pointer on that desktop: Windows moves one only on the desktop in use, and making this one
|
|
68
70
|
## that would take the screen. What reads the injected event is right, tooltips included, since the
|
|
69
71
|
## engine places a tooltip from the motion that reached the control.
|
|
@@ -125,15 +125,16 @@ static func no_animation(player: AnimationPlayer, called: String, animation_name
|
|
|
125
125
|
|
|
126
126
|
static func _summary(anim: Animation) -> Dictionary:
|
|
127
127
|
return {
|
|
128
|
-
"length": anim.length,
|
|
128
|
+
"length": held_seconds(anim.length),
|
|
129
129
|
"loopMode": LOOP_MODE_NAMES.get(anim.loop_mode, str(anim.loop_mode)),
|
|
130
|
-
"step": anim.step,
|
|
130
|
+
"step": held_seconds(anim.step),
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
|
|
134
|
-
## [param seconds], a key's time, as the shortest decimal holding
|
|
135
|
-
##
|
|
136
|
-
|
|
134
|
+
## [param seconds], a key's time or an animation's length or step, as the shortest decimal holding
|
|
135
|
+
## the same 32-bit float. An animation keeps all three as 32-bit floats, so a key put at 0.7 read
|
|
136
|
+
## back as 0.699999988079071 and a step of 0.1 as 0.100000001490116.
|
|
137
|
+
static func held_seconds(seconds: float) -> float:
|
|
137
138
|
var held: float = PackedFloat32Array([seconds])[0]
|
|
138
139
|
for places: int in range(0, 10):
|
|
139
140
|
var shorter: float = String.num(held, places).to_float()
|
|
@@ -159,7 +160,7 @@ static func _track(anim: Animation, track: int, values: GdhSerialisation) -> Dic
|
|
|
159
160
|
var keyframes: Array[Dictionary] = []
|
|
160
161
|
var methods: Dictionary = {}
|
|
161
162
|
for key: int in anim.track_get_key_count(track):
|
|
162
|
-
var keyframe: Dictionary = {"time":
|
|
163
|
+
var keyframe: Dictionary = {"time": held_seconds(anim.track_get_key_time(track, key))}
|
|
163
164
|
if kind == Animation.TYPE_METHOD:
|
|
164
165
|
var method: String = str(anim.method_track_get_name(track, key))
|
|
165
166
|
methods[method] = true
|
package/build/keeper.js
CHANGED
|
@@ -7053,7 +7053,18 @@ public static class GdharnessDesktop {
|
|
|
7053
7053
|
}
|
|
7054
7054
|
|
|
7055
7055
|
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
7056
|
-
static extern IntPtr
|
|
7056
|
+
static extern IntPtr CreateDesktopExW(string name, IntPtr device, IntPtr mode, uint flags, uint access, IntPtr attributes, uint heapKilobytes, IntPtr reserved);
|
|
7057
|
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
7058
|
+
static extern IntPtr CreateWindowStationW(string name, uint flags, uint access, IntPtr attributes);
|
|
7059
|
+
[DllImport("user32.dll", SetLastError = true)] static extern IntPtr GetProcessWindowStation();
|
|
7060
|
+
[DllImport("user32.dll", SetLastError = true)] static extern bool SetProcessWindowStation(IntPtr station);
|
|
7061
|
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
7062
|
+
static extern bool GetUserObjectInformationW(IntPtr handle, int index, StringBuilder into, int size, out int needed);
|
|
7063
|
+
const uint WindowStationAllAccess = 0x37F;
|
|
7064
|
+
const int ObjectName = 2;
|
|
7065
|
+
// What Windows gives an interactive desktop. A non-interactive station's desktops get a small
|
|
7066
|
+
// fraction of that by default, and one desktop here holds every engine a run starts.
|
|
7067
|
+
const uint DesktopHeapKilobytes = 20480;
|
|
7057
7068
|
|
|
7058
7069
|
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
|
|
7059
7070
|
static extern bool CreateProcessW(string application, StringBuilder commandLine, IntPtr processAttributes, IntPtr threadAttributes, bool inherit, uint flags, IntPtr environment, string directory, ref StartupInfo startup, out Started started);
|
|
@@ -7144,12 +7155,34 @@ public static class GdharnessDesktop {
|
|
|
7144
7155
|
StartupInfo startup = new StartupInfo();
|
|
7145
7156
|
startup.cb = Marshal.SizeOf(typeof(StartupInfo));
|
|
7146
7157
|
if (desktop.Length > 0) {
|
|
7147
|
-
Step(stepFile, "
|
|
7148
|
-
IntPtr
|
|
7158
|
+
Step(stepFile, "opening the session's own window station");
|
|
7159
|
+
IntPtr station = CreateWindowStationW(null, 0, WindowStationAllAccess, IntPtr.Zero);
|
|
7160
|
+
if (station == IntPtr.Zero) {
|
|
7161
|
+
throw new Win32Exception(Marshal.GetLastWin32Error(), "CreateWindowStationW");
|
|
7162
|
+
}
|
|
7163
|
+
StringBuilder stationName = new StringBuilder(256);
|
|
7164
|
+
int needed;
|
|
7165
|
+
if (!GetUserObjectInformationW(station, ObjectName, stationName, stationName.Capacity * 2, out needed)) {
|
|
7166
|
+
throw new Win32Exception(Marshal.GetLastWin32Error(), "GetUserObjectInformationW");
|
|
7167
|
+
}
|
|
7168
|
+
// A desktop is made in the calling process's station, so this one is the caller's while it is.
|
|
7169
|
+
IntPtr own = GetProcessWindowStation();
|
|
7170
|
+
if (!SetProcessWindowStation(station)) {
|
|
7171
|
+
throw new Win32Exception(Marshal.GetLastWin32Error(), "SetProcessWindowStation");
|
|
7172
|
+
}
|
|
7173
|
+
IntPtr held;
|
|
7174
|
+
int made;
|
|
7175
|
+
try {
|
|
7176
|
+
Step(stepFile, "creating the " + desktop + " desktop");
|
|
7177
|
+
held = CreateDesktopExW(desktop, IntPtr.Zero, IntPtr.Zero, 0, GenericAll, IntPtr.Zero, DesktopHeapKilobytes, IntPtr.Zero);
|
|
7178
|
+
made = Marshal.GetLastWin32Error();
|
|
7179
|
+
} finally {
|
|
7180
|
+
SetProcessWindowStation(own);
|
|
7181
|
+
}
|
|
7149
7182
|
if (held == IntPtr.Zero) {
|
|
7150
|
-
throw new Win32Exception(
|
|
7183
|
+
throw new Win32Exception(made, "CreateDesktopExW " + stationName + "\\\\" + desktop);
|
|
7151
7184
|
}
|
|
7152
|
-
startup.desktop = desktop;
|
|
7185
|
+
startup.desktop = stationName + "\\\\" + desktop;
|
|
7153
7186
|
}
|
|
7154
7187
|
startup.flags = UseStdHandles;
|
|
7155
7188
|
if (noActivate) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.40",
|
|
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",
|