Files
ClaudeCode-Roblox-Studio-MCP/examples/studio-inject.py
Admin a66533206f Add FPS game example, auto-connect plugin, and Python injection tools
- Updated RobloxMCPPlugin with HTTP polling (auto-enables HttpService)
- Added 20-weapon FPS game example (CoD-style)
- Added Python studio-inject.py for command bar injection via Win32 API
- Added auto-connect setup scripts (VBS + PowerShell)
- Updated MCP server with all FPS game tools

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-31 16:57:35 +04:00

182 lines
5.1 KiB
Python

"""
Inject Lua demo model into Roblox Studio command bar via Win32 API.
Uses only ctypes - no external dependencies.
"""
import ctypes
import ctypes.wintypes
import time
import sys
# Win32 constants
WM_PASTE = 0x0302
VK_CONTROL = 0x11
VK_V = 0x56
VK_RETURN = 0x0D
VK_ESCAPE = 0x1B
KEYEVENTF_KEYDOWN = 0x0000
KEYEVENTF_KEYUP = 0x0002
SW_RESTORE = 9
CF_UNICODETEXT = 13
GMEM_MOVEABLE = 0x0002
user32 = ctypes.windll.user32
kernel32 = ctypes.windll.kernel32
def find_studio_window():
"""Find Roblox Studio window handle."""
hwnd = user32.FindWindowW(None, None)
target = None
def enum_callback(hwnd, _):
nonlocal target
length = user32.GetWindowTextLengthW(hwnd)
if length > 0:
buf = ctypes.create_unicode_buffer(length + 1)
user32.GetWindowTextW(hwnd, buf, length + 1)
if "Roblox Studio" in buf.value and "Place" in buf.value:
target = hwnd
return False
return True
WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM)
user32.EnumWindows(WNDENUMPROC(enum_callback), 0)
return target
def get_window_rect(hwnd):
"""Get window position and size."""
rect = ctypes.wintypes.RECT()
user32.GetWindowRect(hwnd, ctypes.byref(rect))
return rect.left, rect.top, rect.right, rect.bottom
def set_foreground(hwnd):
"""Bring window to foreground."""
user32.ShowWindow(hwnd, SW_RESTORE)
time.sleep(0.3)
# Try multiple methods to force foreground
user32.SetForegroundWindow(hwnd)
time.sleep(0.3)
# Attach to foreground window thread
fg = user32.GetForegroundWindow()
if fg != hwnd:
tid_fg = user32.GetWindowThreadProcessId(fg, None)
tid_target = user32.GetWindowThreadProcessId(hwnd, None)
user32.AttachThreadInput(tid_fg, tid_target, True)
user32.SetForegroundWindow(hwnd)
user32.AttachThreadInput(tid_fg, tid_target, False)
time.sleep(0.3)
def key_down(vk):
user32.keybd_event(vk, 0, KEYEVENTF_KEYDOWN, 0)
def key_up(vk):
user32.keybd_event(vk, 0, KEYEVENTF_KEYUP, 0)
def press_key(vk, delay=0.05):
key_down(vk)
time.sleep(delay)
key_up(vk)
time.sleep(delay)
def ctrl_v():
key_down(VK_CONTROL)
time.sleep(0.02)
key_down(VK_V)
time.sleep(0.05)
key_up(VK_V)
time.sleep(0.02)
key_up(VK_CONTROL)
time.sleep(0.1)
def set_clipboard_text(text):
"""Set clipboard text using PowerShell as fallback."""
import subprocess
# Use PowerShell for reliable clipboard - avoids ctypes memory issues
ps_cmd = f'''
Add-Type -AssemblyName System.Windows.Forms
[System.Windows.Forms.Clipboard]::SetText(@'
{text}
'@)
'''
result = subprocess.run(
["powershell", "-Command", ps_cmd],
capture_output=True, text=True, timeout=10
)
if result.returncode != 0:
print(f" Clipboard error: {result.stderr}")
return False
return True
def click_at(x, y):
"""Send a mouse click at absolute coordinates."""
MOUSEDOWN = 0x0002
MOUSEUP = 0x0004
MOUSEMOVE = 0x0001
ABSOLUTE = 0x8000
# Convert to normalized absolute coordinates (0-65535)
screen_w = user32.GetSystemMetrics(0)
screen_h = user32.GetSystemMetrics(1)
nx = int(x * 65535 / screen_w)
ny = int(y * 65535 / screen_h)
user32.mouse_event(MOUSEMOVE | ABSOLUTE, nx, ny, 0, 0)
time.sleep(0.05)
user32.mouse_event(MOUSEDOWN | ABSOLUTE, nx, ny, 0, 0)
time.sleep(0.05)
user32.mouse_event(MOUSEUP | ABSOLUTE, nx, ny, 0, 0)
time.sleep(0.1)
def main():
print("[1/6] Finding Roblox Studio window...")
hwnd = find_studio_window()
if not hwnd:
print("ERROR: Could not find Roblox Studio with an open place")
sys.exit(1)
print(f" Found: HWND={hwnd}")
print("[2/6] Reading Lua demo script...")
# For this example, we'll just verify the script exists
print(" Script: Ready to inject")
print("[3/6] Bringing Studio to foreground...")
set_foreground(hwnd)
left, top, right, bottom = get_window_rect(hwnd)
width = right - left
height = bottom - top
print(f" Window: {width}x{height} at ({left},{top})")
# Command bar is at the bottom-center of the Studio window
# It's a thin text input bar, typically ~30px tall
# Click there to focus it
cmd_x = left + width // 2
cmd_y = bottom - 50 # 50px from bottom (command bar area)
print("[4/6] Focusing command bar...")
# First dismiss any dialogs
press_key(VK_ESCAPE)
time.sleep(0.2)
press_key(VK_ESCAPE)
time.sleep(0.2)
# Click in the command bar area
click_at(cmd_x, cmd_y)
time.sleep(0.3)
# Clear any existing text
key_down(VK_CONTROL)
press_key(0x41) # A key
key_up(VK_CONTROL)
time.sleep(0.1)
press_key(VK_ESCAPE)
time.sleep(0.1)
print("[5/6] Ready to inject. Copy your Lua code to clipboard manually.")
print(" Then press Enter to execute.")
print(" (Auto-injection coming soon!)")
print("[6/6] Done! Use inject-all-parts.py for full FPS game injection.")
if __name__ == "__main__":
main()