Add GTA city builder + background injection API for Roblox Studio
- inject_gta_city.py: Full GTA-style city with 20 buildings, roads, cars, street lights, traffic lights, trees, 15 human enemies with varied skin/clothing, and 10 COD weapons with visible gun models - inject_bg.py: Background injection using SendMessage/PostMessage Win32 API - inject_bg2.py: PostMessage approach targeting main window for WPF apps - inject_cod_final.py: Working COD game injection (7-step sequential) - cod_inject.py: Combined COD game builder with proper Studio launch - roblox-fps-p1-p6: Split Lua scripts for multi-part injection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
214
inject_bg.py
Normal file
214
inject_bg.py
Normal file
@@ -0,0 +1,214 @@
|
||||
"""
|
||||
Background injection into Roblox Studio using SendMessage/PostMessage.
|
||||
Works even when Studio is minimized or behind other windows.
|
||||
Finds the command bar child control and sends text directly to it.
|
||||
"""
|
||||
import ctypes, ctypes.wintypes, subprocess, time, sys, os
|
||||
|
||||
user32 = ctypes.windll.user32
|
||||
WM_SETTEXT = 0x000C
|
||||
WM_KEYDOWN = 0x0100
|
||||
WM_KEYUP = 0x0101
|
||||
WM_CHAR = 0x0102
|
||||
EM_GETLINECOUNT = 0x00BA
|
||||
EM_LINELENGTH = 0x00C1
|
||||
|
||||
def find_studio():
|
||||
target = [None]
|
||||
def cb(hwnd, _):
|
||||
l = user32.GetWindowTextLengthW(hwnd)
|
||||
if l > 0:
|
||||
buf = ctypes.create_unicode_buffer(l + 1)
|
||||
user32.GetWindowTextW(hwnd, buf, l + 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 find_command_bar(parent_hwnd):
|
||||
"""Find the command bar edit control inside Studio."""
|
||||
result = [None]
|
||||
WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM)
|
||||
|
||||
def enum_children(hwnd, _):
|
||||
# Check class name - Roblox command bar is typically an edit control
|
||||
buf = ctypes.create_unicode_buffer(256)
|
||||
user32.GetClassNameW(hwnd, buf, 256)
|
||||
class_name = buf.value
|
||||
|
||||
# Get control text
|
||||
l = user32.GetWindowTextLengthW(hwnd)
|
||||
if l > 0:
|
||||
tbuf = ctypes.create_unicode_buffer(l + 1)
|
||||
user32.GetWindowTextW(hwnd, tbuf, l + 1)
|
||||
|
||||
# Look for edit controls or text input controls
|
||||
# Roblox Studio uses WPF which hosts Win32 controls
|
||||
rect = ctypes.wintypes.RECT()
|
||||
user32.GetWindowRect(hwnd, ctypes.byref(rect))
|
||||
h = rect.bottom - rect.top
|
||||
|
||||
# Command bar is a thin single-line edit at the bottom
|
||||
if class_name in ("Edit", "TextBox", "WindowsForms10.Edit", "WpfTextEdit"):
|
||||
if h > 10 and h < 60:
|
||||
result[0] = hwnd
|
||||
return False
|
||||
|
||||
# Recurse into children
|
||||
user32.EnumChildWindows(hwnd, WNDENUMPROC(enum_children), 0)
|
||||
return True
|
||||
|
||||
user32.EnumChildWindows(parent_hwnd, WNDENUMPROC(enum_children), 0)
|
||||
return result[0]
|
||||
|
||||
def find_all_children(parent_hwnd):
|
||||
"""List all child windows for debugging."""
|
||||
children = []
|
||||
WNDENUMPROC = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.wintypes.HWND, ctypes.wintypes.LPARAM)
|
||||
|
||||
def enum_children(hwnd, _):
|
||||
buf = ctypes.create_unicode_buffer(256)
|
||||
user32.GetClassNameW(hwnd, buf, 256)
|
||||
class_name = buf.value
|
||||
|
||||
l = user32.GetWindowTextLengthW(hwnd)
|
||||
text = ""
|
||||
if l > 0:
|
||||
tbuf = ctypes.create_unicode_buffer(l + 1)
|
||||
user32.GetWindowTextW(hwnd, tbuf, l + 1)
|
||||
text = tbuf.value
|
||||
|
||||
rect = ctypes.wintypes.RECT()
|
||||
user32.GetWindowRect(hwnd, ctypes.byref(rect))
|
||||
w = rect.right - rect.left
|
||||
h = rect.bottom - rect.top
|
||||
|
||||
visible = user32.IsWindowVisible(hwnd)
|
||||
enabled = user32.IsWindowEnabled(hwnd)
|
||||
|
||||
children.append({
|
||||
'hwnd': hwnd,
|
||||
'class': class_name,
|
||||
'text': text[:50],
|
||||
'size': f"{w}x{h}",
|
||||
'pos': f"({rect.left},{rect.top})",
|
||||
'visible': visible,
|
||||
'enabled': enabled
|
||||
})
|
||||
return True
|
||||
|
||||
user32.EnumChildWindows(parent_hwnd, WNDENUMPROC(enum_children), 0)
|
||||
return children
|
||||
|
||||
def inject_background(code, target_hwnd):
|
||||
"""Inject Lua code into a specific child control using SendMessage."""
|
||||
# Put code on clipboard
|
||||
tmp = os.path.join(os.environ["TEMP"], "rbx.lua")
|
||||
with open(tmp, "w", encoding="utf-8") as f:
|
||||
f.write(code)
|
||||
subprocess.run(["powershell", "-Command", f"Get-Content '{tmp}' -Raw | Set-Clipboard"],
|
||||
capture_output=True, timeout=10)
|
||||
time.sleep(0.2)
|
||||
|
||||
# Send Ctrl+A (select all) then Ctrl+V (paste) then Enter via PostMessage
|
||||
# PostMessage works on background windows
|
||||
VK_CONTROL = 0x11
|
||||
VK_A = 0x41
|
||||
VK_V = 0x56
|
||||
VK_RETURN = 0x0D
|
||||
|
||||
# Select all: Ctrl+A
|
||||
user32.PostMessageW(target_hwnd, WM_KEYDOWN, VK_CONTROL, 0)
|
||||
user32.PostMessageW(target_hwnd, WM_KEYDOWN, VK_A, 0)
|
||||
time.sleep(0.02)
|
||||
user32.PostMessageW(target_hwnd, WM_KEYUP, VK_A, 0)
|
||||
user32.PostMessageW(target_hwnd, WM_KEYUP, VK_CONTROL, 0)
|
||||
time.sleep(0.05)
|
||||
|
||||
# Paste: Ctrl+V
|
||||
user32.PostMessageW(target_hwnd, WM_KEYDOWN, VK_CONTROL, 0)
|
||||
user32.PostMessageW(target_hwnd, WM_KEYDOWN, VK_V, 0)
|
||||
time.sleep(0.02)
|
||||
user32.PostMessageW(target_hwnd, WM_KEYUP, VK_V, 0)
|
||||
user32.PostMessageW(target_hwnd, WM_KEYUP, VK_CONTROL, 0)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Execute: Enter
|
||||
user32.PostMessageW(target_hwnd, WM_KEYDOWN, VK_RETURN, 0)
|
||||
time.sleep(0.02)
|
||||
user32.PostMessageW(target_hwnd, WM_KEYUP, VK_RETURN, 0)
|
||||
time.sleep(2)
|
||||
|
||||
# ═══════════════════════════════════════
|
||||
# MAIN
|
||||
# ═══════════════════════════════════════
|
||||
hwnd = find_studio()
|
||||
if not hwnd:
|
||||
print("ERROR: Roblox Studio not found!")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"Studio window: {hwnd}")
|
||||
|
||||
# List all child windows to find command bar
|
||||
print("\nSearching for command bar control...")
|
||||
children = find_all_children(hwnd)
|
||||
|
||||
# Filter for likely candidates: visible, enabled, small height (thin bar), near bottom
|
||||
print(f"Found {len(children)} child windows")
|
||||
|
||||
# Get main window rect
|
||||
main_rect = ctypes.wintypes.RECT()
|
||||
user32.GetWindowRect(hwnd, ctypes.byref(main_rect))
|
||||
main_bottom = main_rect.bottom
|
||||
main_top = main_rect.top
|
||||
|
||||
# Find candidates near bottom of window, visible, reasonable size
|
||||
candidates = []
|
||||
for c in children:
|
||||
if not c['visible']:
|
||||
continue
|
||||
try:
|
||||
h = int(c['size'].split('x')[1])
|
||||
w = int(c['size'].split('x')[0])
|
||||
except:
|
||||
continue
|
||||
if h < 5 or h > 80:
|
||||
continue
|
||||
if w < 100:
|
||||
continue
|
||||
# Parse position
|
||||
pos = c['pos'].strip('()')
|
||||
try:
|
||||
y = int(pos.split(',')[1])
|
||||
except:
|
||||
continue
|
||||
# Check if near bottom of main window
|
||||
if y > main_bottom - 100:
|
||||
candidates.append(c)
|
||||
print(f" CANDIDATE: hwnd={c['hwnd']} class={c['class']} text='{c['text']}' size={c['size']} pos={c['pos']}")
|
||||
|
||||
if not candidates:
|
||||
print("\nNo command bar candidates found. Listing ALL visible children near bottom:")
|
||||
for c in children:
|
||||
if not c['visible']:
|
||||
continue
|
||||
pos = c['pos'].strip('()')
|
||||
try:
|
||||
y = int(pos.split(',')[1])
|
||||
except:
|
||||
continue
|
||||
if y > main_bottom - 150:
|
||||
print(f" hwnd={c['hwnd']} class={c['class']} text='{c['text']}' size={c['size']} pos={c['pos']}")
|
||||
|
||||
# Try to test each candidate with a simple print
|
||||
test_code = 'print("BG_INJECT_OK")'
|
||||
print(f"\nTesting candidates with: {test_code}")
|
||||
|
||||
for c in candidates:
|
||||
target = c['hwnd']
|
||||
print(f"\n Trying hwnd={target} class={c['class']}...")
|
||||
inject_background(test_code, target)
|
||||
print(f" Sent to {target}. Check Studio Output for 'BG_INJECT_OK'")
|
||||
Reference in New Issue
Block a user