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>
This commit is contained in:
176
examples/inject-all-parts.py
Normal file
176
examples/inject-all-parts.py
Normal file
@@ -0,0 +1,176 @@
|
||||
"""
|
||||
Inject all 5 FPS game parts into Roblox Studio command bar sequentially.
|
||||
"""
|
||||
import ctypes
|
||||
import ctypes.wintypes
|
||||
import subprocess
|
||||
import time
|
||||
import sys
|
||||
import os
|
||||
|
||||
user32 = ctypes.windll.user32
|
||||
|
||||
def find_studio():
|
||||
target = [None]
|
||||
def cb(hwnd, _):
|
||||
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:
|
||||
target[0] = hwnd
|
||||
return False
|
||||
return True
|
||||
WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM)
|
||||
user32.EnumWindows(WNDENUMPROC(cb), 0)
|
||||
return target[0]
|
||||
|
||||
def set_foreground(hwnd):
|
||||
SW_RESTORE = 9
|
||||
user32.ShowWindow(hwnd, SW_RESTORE)
|
||||
time.sleep(0.3)
|
||||
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 set_clipboard(text):
|
||||
# Use PowerShell for reliable clipboard
|
||||
# Write to temp file first to avoid escaping issues
|
||||
tmp = os.path.join(os.environ["TEMP"], "roblox_clipboard.lua")
|
||||
with open(tmp, "w", encoding="utf-8") as f:
|
||||
f.write(text)
|
||||
result = subprocess.run(
|
||||
["powershell", "-Command",
|
||||
f"Get-Content '{tmp}' -Raw | Set-Clipboard"],
|
||||
capture_output=True, text=True, timeout=10
|
||||
)
|
||||
return result.returncode == 0
|
||||
|
||||
def press_key(vk):
|
||||
user32.keybd_event(vk, 0, 0, 0)
|
||||
time.sleep(0.03)
|
||||
user32.keybd_event(vk, 0, 2, 0)
|
||||
time.sleep(0.05)
|
||||
|
||||
def ctrl_v():
|
||||
user32.keybd_event(0x11, 0, 0, 0) # Ctrl down
|
||||
time.sleep(0.02)
|
||||
user32.keybd_event(0x56, 0, 0, 0) # V down
|
||||
time.sleep(0.03)
|
||||
user32.keybd_event(0x56, 0, 2, 0) # V up
|
||||
time.sleep(0.02)
|
||||
user32.keybd_event(0x11, 0, 2, 0) # Ctrl up
|
||||
time.sleep(0.1)
|
||||
|
||||
def click_at(x, y):
|
||||
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(0x8001, nx, ny, 0, 0) # Move
|
||||
time.sleep(0.02)
|
||||
user32.mouse_event(0x8002, nx, ny, 0, 0) # Down
|
||||
time.sleep(0.03)
|
||||
user32.mouse_event(0x8004, nx, ny, 0, 0) # Up
|
||||
time.sleep(0.05)
|
||||
|
||||
def inject_script(lua_code, part_num, total):
|
||||
print(f"\n [{part_num}/{total}] Injecting {len(lua_code)} bytes...")
|
||||
|
||||
if not set_clipboard(lua_code):
|
||||
print(f" ERROR: Clipboard failed for part {part_num}")
|
||||
return False
|
||||
|
||||
time.sleep(0.3)
|
||||
|
||||
# Press Escape to clear any selection
|
||||
press_key(0x1B)
|
||||
time.sleep(0.2)
|
||||
|
||||
# Click in command bar area
|
||||
hwnd = find_studio()
|
||||
if not hwnd:
|
||||
print(" ERROR: Studio window lost!")
|
||||
return False
|
||||
|
||||
rect = ctypes.wintypes.RECT()
|
||||
user32.GetWindowRect(hwnd, ctypes.byref(rect))
|
||||
w = rect.right - rect.left
|
||||
h = rect.bottom - rect.top
|
||||
cmd_x = rect.left + w // 2
|
||||
cmd_y = rect.bottom - 50
|
||||
|
||||
click_at(cmd_x, cmd_y)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Select all + delete existing text
|
||||
user32.keybd_event(0x11, 0, 0, 0) # Ctrl
|
||||
press_key(0x41) # A
|
||||
user32.keybd_event(0x11, 0, 2, 0) # Ctrl up
|
||||
time.sleep(0.1)
|
||||
press_key(0x2E) # Delete
|
||||
time.sleep(0.1)
|
||||
|
||||
# Paste
|
||||
ctrl_v()
|
||||
time.sleep(0.5)
|
||||
|
||||
# Execute
|
||||
press_key(0x0D) # Enter
|
||||
time.sleep(1.5) # Wait for execution
|
||||
|
||||
print(f" [{part_num}/{total}] Done.")
|
||||
return True
|
||||
|
||||
def main():
|
||||
parts = [
|
||||
r"C:\Users\Admin\ClaudeCode-Roblox-Studio-MCP\examples\fps-game\part1_map.lua",
|
||||
r"C:\Users\Admin\ClaudeCode-Roblox-Studio-MCP\examples\fps-game\part2_weapons.lua",
|
||||
r"C:\Users\Admin\ClaudeCode-Roblox-Studio-MCP\examples\fps-game\part3_ai.lua",
|
||||
r"C:\Users\Admin\ClaudeCode-Roblox-Studio-MCP\examples\fps-game\part4_hud.lua",
|
||||
r"C:\Users\Admin\ClaudeCode-Roblox-Studio-MCP\examples\fps-game\part5_client.lua",
|
||||
]
|
||||
total = len(parts)
|
||||
|
||||
print("=" * 50)
|
||||
print(" MINI CALL OF DUTY - Injecting into Roblox Studio")
|
||||
print("=" * 50)
|
||||
|
||||
# Find and focus Studio
|
||||
hwnd = find_studio()
|
||||
if not hwnd:
|
||||
print("ERROR: Roblox Studio not found!")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"\n Studio found: HWND={hwnd}")
|
||||
set_foreground(hwnd)
|
||||
time.sleep(1)
|
||||
|
||||
for i, path in enumerate(parts, 1):
|
||||
if not os.path.exists(path):
|
||||
print(f"\n WARNING: {path} not found. Skipping.")
|
||||
continue
|
||||
|
||||
with open(path, "r", encoding="utf-8") as f:
|
||||
lua_code = f.read()
|
||||
|
||||
if not inject_script(lua_code, i, total):
|
||||
print(f"\n FATAL: Part {i} failed. Stopping.")
|
||||
sys.exit(1)
|
||||
|
||||
# Re-focus between injections
|
||||
set_foreground(hwnd)
|
||||
time.sleep(1)
|
||||
|
||||
print("\n" + "=" * 50)
|
||||
print(" ALL PARTS INJECTED SUCCESSFULLY!")
|
||||
print(" Press PLAY in Roblox Studio to start the game.")
|
||||
print("=" * 50)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user