@satelliteoflove/godot-mcp 3.16.0 → 3.18.0
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/addon/commands/input_commands.gd +5 -11
- package/addon/game_bridge/joy_names.gd +68 -0
- package/addon/game_bridge/joy_names.gd.uid +1 -0
- package/addon/game_bridge/key_names.gd +133 -0
- package/addon/game_bridge/key_names.gd.uid +1 -0
- package/addon/game_bridge/mcp_game_bridge.gd +290 -79
- package/addon/plugin.cfg +1 -1
- package/dist/__tests__/tools/game-time.test.js +76 -0
- package/dist/__tests__/tools/game-time.test.js.map +1 -1
- package/dist/__tests__/tools/input.test.js +209 -2
- package/dist/__tests__/tools/input.test.js.map +1 -1
- package/dist/tools/game-time.d.ts +116 -4
- package/dist/tools/game-time.d.ts.map +1 -1
- package/dist/tools/game-time.js +13 -9
- package/dist/tools/game-time.js.map +1 -1
- package/dist/tools/input.d.ts +122 -4
- package/dist/tools/input.d.ts.map +1 -1
- package/dist/tools/input.js +163 -9
- package/dist/tools/input.js.map +1 -1
- package/package.json +1 -1
|
@@ -115,15 +115,7 @@ func _get_editor_input_map() -> Dictionary:
|
|
|
115
115
|
|
|
116
116
|
func _event_to_string(event: InputEvent) -> String:
|
|
117
117
|
if event is InputEventKey:
|
|
118
|
-
|
|
119
|
-
var key_name := OS.get_keycode_string(key_event.keycode)
|
|
120
|
-
if key_event.ctrl_pressed:
|
|
121
|
-
key_name = "Ctrl+" + key_name
|
|
122
|
-
if key_event.alt_pressed:
|
|
123
|
-
key_name = "Alt+" + key_name
|
|
124
|
-
if key_event.shift_pressed:
|
|
125
|
-
key_name = "Shift+" + key_name
|
|
126
|
-
return key_name
|
|
118
|
+
return MCPKeyNames.event_string(event as InputEventKey)
|
|
127
119
|
elif event is InputEventMouseButton:
|
|
128
120
|
var mouse_event := event as InputEventMouseButton
|
|
129
121
|
match mouse_event.button_index:
|
|
@@ -137,10 +129,12 @@ func _event_to_string(event: InputEvent) -> String:
|
|
|
137
129
|
return "Mouse Button %d" % mouse_event.button_index
|
|
138
130
|
elif event is InputEventJoypadButton:
|
|
139
131
|
var joy_event := event as InputEventJoypadButton
|
|
140
|
-
return "Joypad Button %d" % joy_event.button_index
|
|
132
|
+
return "Joypad Button %d (%s)" % [joy_event.button_index, MCPJoyNames.button_name(joy_event.button_index)]
|
|
141
133
|
elif event is InputEventJoypadMotion:
|
|
134
|
+
# The signed axis_value is the direction bit an agent needs to lift the
|
|
135
|
+
# binding straight into an injection (e.g. move_left = left_x, value -1.0).
|
|
142
136
|
var joy_motion := event as InputEventJoypadMotion
|
|
143
|
-
return "Joypad Axis %d" % joy_motion.axis
|
|
137
|
+
return "Joypad Axis %d (%s, value %+.1f)" % [joy_motion.axis, MCPJoyNames.axis_name(joy_motion.axis), joy_motion.axis_value]
|
|
144
138
|
return event.as_text()
|
|
145
139
|
|
|
146
140
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name MCPJoyNames
|
|
3
|
+
extends RefCounted
|
|
4
|
+
## Canonical joypad name<->index tables, shared by the game bridge (event
|
|
5
|
+
## injection) and the editor input commands (get_input_map display) so the
|
|
6
|
+
## wire vocabulary and the displayed names can never drift apart.
|
|
7
|
+
|
|
8
|
+
const BUTTONS := {
|
|
9
|
+
"a": JOY_BUTTON_A,
|
|
10
|
+
"b": JOY_BUTTON_B,
|
|
11
|
+
"x": JOY_BUTTON_X,
|
|
12
|
+
"y": JOY_BUTTON_Y,
|
|
13
|
+
"back": JOY_BUTTON_BACK,
|
|
14
|
+
"guide": JOY_BUTTON_GUIDE,
|
|
15
|
+
"start": JOY_BUTTON_START,
|
|
16
|
+
"left_stick": JOY_BUTTON_LEFT_STICK,
|
|
17
|
+
"right_stick": JOY_BUTTON_RIGHT_STICK,
|
|
18
|
+
"left_shoulder": JOY_BUTTON_LEFT_SHOULDER,
|
|
19
|
+
"right_shoulder": JOY_BUTTON_RIGHT_SHOULDER,
|
|
20
|
+
"dpad_up": JOY_BUTTON_DPAD_UP,
|
|
21
|
+
"dpad_down": JOY_BUTTON_DPAD_DOWN,
|
|
22
|
+
"dpad_left": JOY_BUTTON_DPAD_LEFT,
|
|
23
|
+
"dpad_right": JOY_BUTTON_DPAD_RIGHT,
|
|
24
|
+
"misc1": JOY_BUTTON_MISC1,
|
|
25
|
+
"paddle1": JOY_BUTTON_PADDLE1,
|
|
26
|
+
"paddle2": JOY_BUTTON_PADDLE2,
|
|
27
|
+
"paddle3": JOY_BUTTON_PADDLE3,
|
|
28
|
+
"paddle4": JOY_BUTTON_PADDLE4,
|
|
29
|
+
"touchpad": JOY_BUTTON_TOUCHPAD,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const AXES := {
|
|
33
|
+
"left_x": JOY_AXIS_LEFT_X,
|
|
34
|
+
"left_y": JOY_AXIS_LEFT_Y,
|
|
35
|
+
"right_x": JOY_AXIS_RIGHT_X,
|
|
36
|
+
"right_y": JOY_AXIS_RIGHT_Y,
|
|
37
|
+
"trigger_left": JOY_AXIS_TRIGGER_LEFT,
|
|
38
|
+
"trigger_right": JOY_AXIS_TRIGGER_RIGHT,
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
## Resolve a wire value (name string, or raw index as an escape hatch) to a
|
|
43
|
+
## JoyButton index. Returns -1 for anything unknown.
|
|
44
|
+
static func button_index(value: Variant) -> int:
|
|
45
|
+
if value is int or value is float:
|
|
46
|
+
var i := int(value)
|
|
47
|
+
return i if i >= 0 and i < JOY_BUTTON_SDL_MAX else -1
|
|
48
|
+
return int(BUTTONS.get(str(value), -1))
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
## Resolve a wire axis name to a JoyAxis index. Names only — the name itself
|
|
52
|
+
## is what tells an agent trigger (0..1) from stick (-1..1). Returns -1 if unknown.
|
|
53
|
+
static func axis_index(value: Variant) -> int:
|
|
54
|
+
return int(AXES.get(str(value), -1))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
static func button_name(idx: int) -> String:
|
|
58
|
+
for n in BUTTONS:
|
|
59
|
+
if int(BUTTONS[n]) == idx:
|
|
60
|
+
return n
|
|
61
|
+
return "button_%d" % idx
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
static func axis_name(idx: int) -> String:
|
|
65
|
+
for n in AXES:
|
|
66
|
+
if int(AXES[n]) == idx:
|
|
67
|
+
return n
|
|
68
|
+
return "axis_%d" % idx
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://row4hmo5gdem
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
@tool
|
|
2
|
+
class_name MCPKeyNames
|
|
3
|
+
extends RefCounted
|
|
4
|
+
## Canonical keyboard name<->keycode parsing/formatting (#290), shared by the
|
|
5
|
+
## game bridge (raw InputEventKey injection) and the editor input commands
|
|
6
|
+
## (get_input_map display) so the wire vocabulary and the displayed names can
|
|
7
|
+
## never drift apart.
|
|
8
|
+
##
|
|
9
|
+
## Why this exists rather than calling OS.find_keycode_from_string directly:
|
|
10
|
+
## that engine helper parses Ctrl/Shift/Alt prefixes but SILENTLY DROPS Meta/Cmd
|
|
11
|
+
## (verified Godot 4.6 — "Meta+Q" returns a bare KEY_Q). We split modifiers here
|
|
12
|
+
## ourselves so meta round-trips, and only hand the base token to the engine.
|
|
13
|
+
|
|
14
|
+
## Modifier token -> key mask. Case-insensitive (callers lowercase first).
|
|
15
|
+
## Aliases cover the common cross-platform spellings agents reach for.
|
|
16
|
+
const MODIFIERS := {
|
|
17
|
+
"ctrl": KEY_MASK_CTRL,
|
|
18
|
+
"control": KEY_MASK_CTRL,
|
|
19
|
+
"shift": KEY_MASK_SHIFT,
|
|
20
|
+
"alt": KEY_MASK_ALT,
|
|
21
|
+
"option": KEY_MASK_ALT,
|
|
22
|
+
"opt": KEY_MASK_ALT,
|
|
23
|
+
"meta": KEY_MASK_META,
|
|
24
|
+
"cmd": KEY_MASK_META,
|
|
25
|
+
"command": KEY_MASK_META,
|
|
26
|
+
"super": KEY_MASK_META,
|
|
27
|
+
"win": KEY_MASK_META,
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
## Key mask -> the modifier's own keycode, so a combo can press each modifier as
|
|
31
|
+
## a real key (the only way is_key_pressed(KEY_CTRL) ever reads true — the
|
|
32
|
+
## modifier FLAG on another event does not update the polled singleton).
|
|
33
|
+
const MODIFIER_KEYCODES := {
|
|
34
|
+
KEY_MASK_CTRL: KEY_CTRL,
|
|
35
|
+
KEY_MASK_SHIFT: KEY_SHIFT,
|
|
36
|
+
KEY_MASK_ALT: KEY_ALT,
|
|
37
|
+
KEY_MASK_META: KEY_META,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# The four modifier masks, in canonical display/iteration order.
|
|
41
|
+
const _MOD_ORDER: Array = [KEY_MASK_CTRL, KEY_MASK_SHIFT, KEY_MASK_ALT, KEY_MASK_META]
|
|
42
|
+
const _MOD_TOKENS := {
|
|
43
|
+
KEY_MASK_CTRL: "Ctrl",
|
|
44
|
+
KEY_MASK_SHIFT: "Shift",
|
|
45
|
+
KEY_MASK_ALT: "Alt",
|
|
46
|
+
KEY_MASK_META: "Meta",
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
## Parse a wire key value into {code:int, mask:int}. Accepts a string with
|
|
51
|
+
## optional modifier prefixes ("a", "Escape", "ctrl+s", "shift+alt+f1") or a raw
|
|
52
|
+
## Godot Key enum int (which may carry modifier mask bits). Returns code = KEY_NONE
|
|
53
|
+
## (0) for anything that does not resolve to a base key, which the caller turns
|
|
54
|
+
## into an "Unknown key" error.
|
|
55
|
+
static func parse(value: Variant) -> Dictionary:
|
|
56
|
+
if value is int or value is float:
|
|
57
|
+
var raw := int(value)
|
|
58
|
+
return {"code": raw & ~int(KEY_MODIFIER_MASK), "mask": raw & int(KEY_MODIFIER_MASK)}
|
|
59
|
+
|
|
60
|
+
var s := str(value).strip_edges()
|
|
61
|
+
if s.is_empty():
|
|
62
|
+
return {"code": KEY_NONE, "mask": 0}
|
|
63
|
+
|
|
64
|
+
var mask := 0
|
|
65
|
+
var base := ""
|
|
66
|
+
for part in s.split("+", false):
|
|
67
|
+
var token := String(part).strip_edges()
|
|
68
|
+
if token.is_empty():
|
|
69
|
+
continue
|
|
70
|
+
var lower := token.to_lower()
|
|
71
|
+
if MODIFIERS.has(lower):
|
|
72
|
+
mask |= int(MODIFIERS[lower])
|
|
73
|
+
else:
|
|
74
|
+
# The last non-modifier token wins as the base (a stray extra base
|
|
75
|
+
# would just overwrite, which is fine — a malformed combo resolves to
|
|
76
|
+
# whatever its final base token is, or KEY_NONE if none resolves).
|
|
77
|
+
base = token
|
|
78
|
+
|
|
79
|
+
if base.is_empty():
|
|
80
|
+
# A lone modifier name ("shift", "ctrl", "cmd") means the modifier KEY
|
|
81
|
+
# itself — e.g. a game binding bare Shift, or polling is_key_pressed(KEY_CTRL).
|
|
82
|
+
# Exactly one modifier resolves to its keycode; a baseless multi-modifier
|
|
83
|
+
# combo ("ctrl+shift") is ambiguous and stays unknown.
|
|
84
|
+
for m in _MOD_ORDER:
|
|
85
|
+
if mask == int(m):
|
|
86
|
+
return {"code": int(MODIFIER_KEYCODES[m]), "mask": 0}
|
|
87
|
+
return {"code": KEY_NONE, "mask": mask}
|
|
88
|
+
|
|
89
|
+
# find_keycode_from_string handles Ctrl/Shift/Alt itself; strip any mask bits
|
|
90
|
+
# it folded in so `code` is always a bare base keycode.
|
|
91
|
+
var resolved := int(OS.find_keycode_from_string(base))
|
|
92
|
+
mask |= resolved & int(KEY_MODIFIER_MASK)
|
|
93
|
+
var code := resolved & ~int(KEY_MODIFIER_MASK)
|
|
94
|
+
return {"code": code, "mask": mask}
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
## The modifier keycodes (KEY_CTRL, ...) present in a mask, in canonical order,
|
|
98
|
+
## so a combo can press each one as its own key event.
|
|
99
|
+
static func modifier_key_indices(mask: int) -> Array:
|
|
100
|
+
var out: Array = []
|
|
101
|
+
for m in _MOD_ORDER:
|
|
102
|
+
if mask & int(m):
|
|
103
|
+
out.append(int(MODIFIER_KEYCODES[m]))
|
|
104
|
+
return out
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
## Canonical display for a key binding (get_input_map). Modifier prefixes come
|
|
108
|
+
## from the event's own flags with fixed tokens (platform-stable, and re-parseable
|
|
109
|
+
## by parse() — unlike OS.get_keycode_string's mask spelling, which
|
|
110
|
+
## find_keycode_from_string cannot read back for Meta). A logical key + modifier
|
|
111
|
+
## combo round-trips through parse(); a physical-only binding (keycode unset)
|
|
112
|
+
## renders from physical_keycode with a "(physical)" marker that tells an agent to
|
|
113
|
+
## inject with physical:true — that marker is display-only, not re-parseable.
|
|
114
|
+
static func event_string(event: InputEventKey) -> String:
|
|
115
|
+
var prefix := ""
|
|
116
|
+
if event.ctrl_pressed:
|
|
117
|
+
prefix += _MOD_TOKENS[KEY_MASK_CTRL] + "+"
|
|
118
|
+
if event.shift_pressed:
|
|
119
|
+
prefix += _MOD_TOKENS[KEY_MASK_SHIFT] + "+"
|
|
120
|
+
if event.alt_pressed:
|
|
121
|
+
prefix += _MOD_TOKENS[KEY_MASK_ALT] + "+"
|
|
122
|
+
if event.meta_pressed:
|
|
123
|
+
prefix += _MOD_TOKENS[KEY_MASK_META] + "+"
|
|
124
|
+
|
|
125
|
+
if event.keycode != KEY_NONE:
|
|
126
|
+
var base := OS.get_keycode_string(event.keycode)
|
|
127
|
+
return prefix + (base if not base.is_empty() else "Key %d" % int(event.keycode))
|
|
128
|
+
if event.physical_keycode != KEY_NONE:
|
|
129
|
+
var pbase := OS.get_keycode_string(event.physical_keycode)
|
|
130
|
+
return prefix + (pbase if not pbase.is_empty() else "Key %d" % int(event.physical_keycode)) + " (physical)"
|
|
131
|
+
# Last resort: an event with neither keycode set (e.g. unicode-only).
|
|
132
|
+
var label := prefix.trim_suffix("+")
|
|
133
|
+
return label if not label.is_empty() else "(unset)"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
uid://blya2h0lff4ar
|