import sys
import subprocess
# ============================================================
# DEPENDENCY INJECTOR - Forces install into active runtime
# ============================================================
def force_install_dependencies():
required = {
"vgamepad": "vgamepad",
"pygame": "pygame",
"pywinusb": "pywinusb",
"inputs": "inputs",
}
for pkg, import_name in required.items():
try:
__import__(import_name)
except ImportError:
print(f"[!] Missing: {pkg}")
print(f"[+] Installing into: {sys.executable}")
try:
subprocess.check_call(
[sys.executable, "-m", "pip", "install", pkg],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
print(f"[+] {pkg} installed successfully.")
except Exception as e:
print(f"[-] Failed to install {pkg}: {e}")
print(f"\n[!] Run manually:")
print(f' "{sys.executable}" -m pip install {pkg}')
sys.exit(1)
force_install_dependencies()
# ============================================================
# CORE IMPORTS
# ============================================================
import threading
import time
import ctypes
import ctypes.wintypes
import tkinter as tk
from tkinter import ttk
import vgamepad as vg
# ============================================================
# XINPUT DIRECT BYPASS ENGINE
# Reads controller state via XInput API directly.
# This bypasses HidHide because XInput talks to the
# kernel-level driver stack, not the raw HID device.
# ============================================================
# XInput button bitmasks
XINPUT_BUTTONS = {
"DPAD_UP": 0x0001,
"DPAD_DOWN": 0x0002,
"DPAD_LEFT": 0x0004,
"DPAD_RIGHT": 0x0008,
"START": 0x0010,
"BACK": 0x0020,
"LEFT_THUMB": 0x0040,
"RIGHT_THUMB": 0x0080,
"LEFT_SHOULDER": 0x0100,
"RIGHT_SHOULDER": 0x0200,
"A": 0x1000,
"B": 0x2000,
"X": 0x4000,
"Y": 0x8000,
}
class XINPUT_GAMEPAD(ctypes.Structure):
_fields_ = [
("wButtons", ctypes.wintypes.WORD),
("bLeftTrigger", ctypes.wintypes.BYTE),
("bRightTrigger", ctypes.wintypes.BYTE),
("sThumbLX", ctypes.wintypes.SHORT),
("sThumbLY", ctypes.wintypes.SHORT),
("sThumbRX", ctypes.wintypes.SHORT),
("sThumbRY", ctypes.wintypes.SHORT),
]
class XINPUT_STATE(ctypes.Structure):
_fields_ = [
("dwPacketNumber", ctypes.wintypes.DWORD),
("Gamepad", XINPUT_GAMEPAD),
]
class XInputEngine:
"""
Direct XInput reader. HidHide does NOT block XInput.
XInput talks through xinput driver -> kernel, not HID path.
Supports up to 4 controllers, auto-detects first active one.
"""
def __init__(self):
try:
self.xinput = ctypes.windll.xinput1_4
except Exception:
try:
self.xinput = ctypes.windll.xinput1_3
except Exception:
self.xinput = ctypes.windll.xinput9_1_0
self.active_port = -1
self.state = XINPUT_STATE()
self._deadzone_left = 7849 # XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE
self._deadzone_right = 8689 # XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE
self._trigger_threshold = 30 # XINPUT_GAMEPAD_TRIGGER_THRESHOLD
def scan_for_controller(self):
"""Find first connected XInput controller port."""
for port in range(4):
state = XINPUT_STATE()
result = self.xinput.XInputGetState(port, ctypes.byref(state))
if result == 0: # ERROR_SUCCESS
self.active_port = port
return True
self.active_port = -1
return False
def get_state(self):
"""
Returns dict with all controller values, or None if disconnected.
All stick values normalized to -1.0 -> 1.0
Triggers normalized to 0.0 -> 1.0
"""
if self.active_port == -1:
return None
result = self.xinput.XInputGetState(
self.active_port, ctypes.byref(self.state)
)
if result != 0:
self.active_port = -1
return None
gp = self.state.Gamepad
return {
"buttons": gp.wButtons,
"lt": gp.bLeftTrigger / 255.0,
"rt": gp.bRightTrigger / 255.0,
"lx": self._normalize_stick(gp.sThumbLX, self._deadzone_left),
"ly": self._normalize_stick(gp.sThumbLY, self._deadzone_left),
"rx": self._normalize_stick(gp.sThumbRX, self._deadzone_right),
"ry": self._normalize_stick(gp.sThumbRY, self._deadzone_right),
}
def _normalize_stick(self, raw, deadzone):
"""Normalize raw XInput SHORT value with deadzone to -1.0 -> 1.0"""
if abs(raw) < deadzone:
return 0.0
if raw > 0:
return min(1.0, (raw - deadzone) / (32767.0 - deadzone))
else:
return max(-1.0, (raw + deadzone) / (32768.0 - deadzone))
def is_button_pressed(self, state, button_name):
if state is None:
return False
mask = XINPUT_BUTTONS.get(button_name, 0)
return bool(state["buttons"] & mask)
# ============================================================
# VIRTUAL CONTROLLER ASSISTANT - FULL REWRITE
# ============================================================
class VirtualControllerAssistant:
POLL_RATE_HZ = 500 # Input polling rate
POLL_INTERVAL = 1.0 / 500 # Seconds per poll cycle
def __init__(self):
# --- State ---
self.running = True
self.controller_found = False
self.port_label = "None"
# --- XInput Engine (bypasses HidHide) ---
self.xinput = XInputEngine()
# --- Virtual Gamepad ---
try:
self.vpad = vg.VX360Gamepad()
self.vpad_status = "Active"
except Exception:
self.vpad = None
self.vpad_status = "ViGEmBus Driver Missing"
# --- Shoot Timing State ---
self._rs_down_time = 0.0
self._rs_tracking = False
self._last_ry = 0.0
# --- GUI Variables ---
self.shooting_assist = tk.BooleanVar(value=True)
self.dribble_assist = tk.BooleanVar(value=True)
self.defense_assist = tk.BooleanVar(value=True)
self.auto_combo = tk.BooleanVar(value=False)
self.tempo_release = tk.DoubleVar(value=0.45)
self.dribble_speed = tk.DoubleVar(value=1.0)
self.defense_intensity = tk.DoubleVar(value=0.8)
self.sensitivity_var = tk.DoubleVar(value=1.1)
self.curve_var = tk.DoubleVar(value=1.4)
# --- Build GUI ---
self.root = tk.Tk()
self.root.title("2K Virtual Controller Assistant")
self.root.geometry("580x780")
self.root.configure(bg="#0a0b0d")
self.root.resizable(False, False)
self._build_gui()
self._bind_hotkeys()
# --- Start Input Thread ---
self._input_thread = threading.Thread(
target=self._input_loop, daemon=True
)
self._input_thread.start()
self.root.protocol("WM_DELETE_WINDOW", self._on_close)
self.root.mainloop()
# ==========================================================
# GUI CONSTRUCTION
# ==========================================================
def _build_gui(self):
style = ttk.Style()
style.theme_use("clam")
style.configure("TFrame", background="#0a0b0d")
style.configure("TLabel", background="#0a0b0d", foreground="#ffffff",
font=("Segoe UI", 10))
style.configure("TCheckbutton",background="#0a0b0d", foreground="#ffffff",
font=("Segoe UI", 10))
style.map("TCheckbutton",
background=[("active", "#0a0b0d")],
foreground=[("active", "#00d2ff")])
style.configure("Horizontal.TScale",
background="#0a0b0d",
troughcolor="#1a1c23",
slidercolor="#00d2ff")
# ---- HEADER ----
hdr = tk.Frame(self.root, bg="#0a0b0d")
hdr.pack(fill="x", padx=25, pady=18)
tk.Label(hdr, text="2K VIRTUAL CONTROLLER",
font=("Segoe UI", 16, "bold"),
fg="#00d2ff", bg="#0a0b0d").pack(side="left")
self._anim_canvas = tk.Canvas(hdr, width=14, height=14,
bg="#0a0b0d", highlightthickness=0)
self._anim_canvas.pack(side="right", padx=(6, 0))
self._dot = self._anim_canvas.create_oval(2, 2, 12, 12,
fill="#ff3b30", outline="")
self._status_lbl = tk.Label(hdr, text="SCANNING...",
font=("Segoe UI", 9, "bold"),
fg="#ff3b30", bg="#0a0b0d")
self._status_lbl.pack(side="right")
# ---- MAIN SCROLL CONTAINER ----
container = tk.Frame(self.root, bg="#0a0b0d")
container.pack(fill="both", expand=True, padx=25, pady=5)
# ---- ASSIST MODULES ----
self._section(container, "ASSIST MODULES")
mod = tk.Frame(container, bg="#0a0b0d")
mod.pack(fill="x", pady=8)
checks = [
("Tempo Shooting Assist [F1]", self.shooting_assist),
("Dribble Combo Engine [F2]", self.dribble_assist),
("Lateral Defense Assist [F3]", self.defense_assist),
("Macro Auto-Combo Chain", self.auto_combo),
]
for txt, var in checks:
ttk.Checkbutton(mod, text=txt, variable=var).pack(anchor="w", pady=5)
# ---- TUNING ----
self._section(container, "TUNING CONTROLS")
tun = tk.Frame(container, bg="#0a0b0d")
tun.pack(fill="x", pady=8)
sliders = [
("Tempo Shot Release Window (sec)", self.tempo_release, 0.20, 0.80),
("Dribble Scale Factor", self.dribble_speed, 0.50, 1.50),
("Defense Stick Response", self.defense_intensity, 0.50, 1.50),
("Stick Sensitivity Multiplier", self.sensitivity_var, 0.80, 2.00),
("Stick Response Curve Exponent", self.curve_var, 0.80, 2.50),
]
for lbl_txt, var, lo, hi in sliders:
tk.Label(tun, text=lbl_txt,
bg="#0a0b0d", fg="#a0a5b5",
font=("Segoe UI", 9)).pack(anchor="w")
row = tk.Frame(tun, bg="#0a0b0d")
row.pack(fill="x", pady=(2, 10))
ttk.Scale(row, from_=lo, to=hi, orient="horizontal",
variable=var,
style="Horizontal.TScale").pack(side="left", fill="x",
expand=True)
val_lbl = tk.Label(row, textvariable=var, width=5,
bg="#0a0b0d", fg="#00d2ff",
font=("Segoe UI", 9))
val_lbl.pack(side="right")
# ---- HARDWARE MATRIX ----
self._section(container, "HARDWARE MATRIX")
matrix = tk.Frame(container, bg="#111318", bd=1, relief="flat")
matrix.pack(fill="x", pady=10, ipady=8)
self._lbl_xinput = tk.Label(
matrix,
text="XInput Engine: Scanning ports 0-3...",
bg="#111318", fg="#ffffff", font=("Segoe UI", 9)
)
self._lbl_xinput.pack(anchor="w", padx=15, pady=3)
self._lbl_hidhide = tk.Label(
matrix,
text="HidHide Bypass: XInput API (kernel-level, always active)",
bg="#111318", fg="#00ff66", font=("Segoe UI", 9)
)
self._lbl_hidhide.pack(anchor="w", padx=15, pady=3)
self._lbl_vpad = tk.Label(
matrix,
text=f"ViGEmBus Virtual Stack: {self.vpad_status}",
bg="#111318", fg="#ffffff", font=("Segoe UI", 9)
)
self._lbl_vpad.pack(anchor="w", padx=15, pady=3)
self._lbl_poll = tk.Label(
matrix,
text=f"Poll Rate: {self.POLL_RATE_HZ} Hz",
bg="#111318", fg="#a0a5b5", font=("Segoe UI", 9)
)
self._lbl_poll.pack(anchor="w", padx=15, pady=3)
# ---- LIVE STICK VISUALIZER ----
self._section(container, "LIVE STICK MONITOR")
vis = tk.Frame(container, bg="#111318", bd=1, relief="flat")
vis.pack(fill="x", pady=10, ipady=8)
stick_row = tk.Frame(vis, bg="#111318")
stick_row.pack(padx=15, pady=5)
self._ls_canvas = self._make_stick_canvas(stick_row, "L-STICK")
tk.Frame(stick_row, bg="#111318", width=30).pack(side="left")
self._rs_canvas = self._make_stick_canvas(stick_row, "R-STICK")
# ---- FOOTER ----
footer = tk.Frame(self.root, bg="#0a0b0d")
footer.pack(fill="x", side="bottom", padx=25, pady=12)
tk.Label(footer, text="F4 - Emergency Kill All Assists",
bg="#0a0b0d", fg="#ff3b30",
font=("Segoe UI", 9, "bold")).pack(side="left")
tk.Label(footer, text="v3.0.0-XInputBypass",
bg="#0a0b0d", fg="#4a4d5a",
font=("Segoe UI", 9)).pack(side="right")
self._animate_status()
def _make_stick_canvas(self, parent, label):
frame = tk.Frame(parent, bg="#111318")
frame.pack(side="left")
tk.Label(frame, text=label, bg="#111318", fg="#a0a5b5",
font=("Segoe UI", 8)).pack()
c = tk.Canvas(frame, width=80, height=80,
bg="#1a1c23", highlightthickness=1,
highlightbackground="#2a2d3a")
c.pack()
# Background crosshair
c.create_line(40, 0, 40, 80, fill="#2a2d3a")
c.create_line(0, 40, 80, 40, fill="#2a2d3a")
c.create_oval(5, 5, 75, 75, outline="#2a2d3a", fill="")
# Dot
c._dot = c.create_oval(36, 36, 44, 44, fill="#00d2ff", outline="")
return c
def _update_stick_visual(self, canvas, x_norm, y_norm):
"""x_norm, y_norm in -1.0 -> 1.0"""
cx = 40 + x_norm * 34
cy = 40 - y_norm * 34
canvas.coords(canvas._dot, cx - 4, cy - 4, cx + 4, cy + 4)
def _section(self, parent, text):
f = tk.Frame(parent, bg="#0a0b0d")
f.pack(fill="x", pady=(14, 2))
tk.Label(f, text=text, font=("Segoe UI", 8, "bold"),
fg="#00d2ff", bg="#0a0b0d").pack(side="left")
tk.Frame(f, bg="#1a1c23", height=1).pack(
side="left", fill="x", expand=True, padx=(10, 0), pady=5
)
def _bind_hotkeys(self):
self.root.bind("<F1>", lambda e: self.shooting_assist.set(
not self.shooting_assist.get()))
self.root.bind("<F2>", lambda e: self.dribble_assist.set(
not self.dribble_assist.get()))
self.root.bind("<F3>", lambda e: self.defense_assist.set(
not self.defense_assist.get()))
self.root.bind("<F4>", lambda e: self._emergency_kill())
def _emergency_kill(self):
self.shooting_assist.set(False)
self.dribble_assist.set(False)
self.defense_assist.set(False)
self.auto_combo.set(False)
if self.vpad:
self.vpad.reset()
self.vpad.update()
def _animate_status(self):
if not self.running:
return
cur = self._anim_canvas.itemcget(self._dot, "fill")
if self.controller_found and self.vpad_status == "Active":
self._status_lbl.config(text="SYSTEM OPERATIONAL", fg="#00ff66")
nxt = "#00ff66" if cur != "#00ff66" else "#004d1f"
else:
self._status_lbl.config(text="NO CONTROLLER DETECTED", fg="#ff3b30")
nxt = "#ff3b30" if cur != "#ff3b30" else "#4d0f0f"
self._anim_canvas.itemconfig(self._dot, fill=nxt)
self.root.after(500, self._animate_status)
# ==========================================================
# STICK PROCESSING
# ==========================================================
def _process_stick(self, val):
"""
XInput engine already applies deadzone in normalization.
This adds sensitivity curve on top.
"""
if val == 0.0:
return 0.0
sign = 1.0 if val > 0 else -1.0
curved = pow(abs(val), self.curve_var.get()) * self.sensitivity_var.get()
return max(-1.0, min(1.0, sign * curved))
# ==========================================================
# INPUT LOOP (runs on background thread, 500 Hz)
# ==========================================================
def _input_loop(self):
last_scan = 0.0
scan_every = 2.0 # Re-scan for controller every 2 seconds
while self.running:
t_start = time.perf_counter()
# --- Controller discovery ---
now = time.perf_counter()
if not self.controller_found or (now - last_scan) > scan_every:
if self.xinput.scan_for_controller():
self.controller_found = True
port = self.xinput.active_port
self._safe_gui(
self._lbl_xinput,
f"XInput Engine: Controller on Port {port} "
f"[XInput direct bypass ACTIVE]"
)
else:
self.controller_found = False
self._safe_gui(
self._lbl_xinput,
"XInput Engine: No controller found (ports 0-3)"
)
last_scan = now
if not self.controller_found:
if self.vpad:
self.vpad.reset()
self.vpad.update()
time.sleep(0.5)
continue
if not self.vpad:
time.sleep(0.1)
continue
# --- Read state ---
state = self.xinput.get_state()
if state is None:
self.controller_found = False
continue
gp = state # shorthand
# ---- BUTTONS ----
btn_map = {
"A": vg.XUSB_BUTTON.XUSB_GAMEPAD_A,
"B": vg.XUSB_BUTTON.XUSB_GAMEPAD_B,
"X": vg.XUSB_BUTTON.XUSB_GAMEPAD_X,
"Y": vg.XUSB_BUTTON.XUSB_GAMEPAD_Y,
"LEFT_SHOULDER": vg.XUSB_BUTTON.XUSB_GAMEPAD_LEFT_SHOULDER,
"RIGHT_SHOULDER": vg.XUSB_BUTTON.XUSB_GAMEPAD_RIGHT_SHOULDER,
"BACK": vg.XUSB_BUTTON.XUSB_GAMEPAD_BACK,
"START": vg.XUSB_BUTTON.XUSB_GAMEPAD_START,
"LEFT_THUMB": vg.XUSB_BUTTON.XUSB_GAMEPAD_LEFT_THUMB,
"RIGHT_THUMB": vg.XUSB_BUTTON.XUSB_GAMEPAD_RIGHT_THUMB,
"DPAD_UP": vg.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_UP,
"DPAD_DOWN": vg.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_DOWN,
"DPAD_LEFT": vg.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_LEFT,
"DPAD_RIGHT": vg.XUSB_BUTTON.XUSB_GAMEPAD_DPAD_RIGHT,
}
for name, vbtn in btn_map.items():
if self.xinput.is_button_pressed(gp, name):
self.vpad.press_button(vbtn)
else:
self.vpad.release_button(vbtn)
# ---- TRIGGERS ----
lt_raw = gp["lt"]
rt_raw = gp["rt"]
self.vpad.left_trigger( value=int(lt_raw * 255))
self.vpad.right_trigger(value=int(rt_raw * 255))
# ---- STICKS - raw normalized values ----
lx = self._process_stick(gp["lx"])
ly = self._process_stick(gp["ly"])
rx = self._process_stick(gp["rx"])
ry = self._process_stick(gp["ry"])
# ---- ASSIST: DEFENSE ----
# Scales left stick when LB is held
if self.defense_assist.get() and \
self.xinput.is_button_pressed(gp, "LEFT_SHOULDER"):
scale = self.defense_intensity.get()
lx = max(-1.0, min(1.0, lx * scale))
ly = max(-1.0, min(1.0, ly * scale))
# ---- ASSIST: AUTO DRIBBLE COMBO ----
# When left stick is pushed hard sideways, nudge right stick
if self.dribble_assist.get() and self.auto_combo.get():
if abs(lx) > 0.7:
nudge = 0.3 * self.dribble_speed.get()
direction = 1.0 if lx > 0 else -1.0
rx = max(-1.0, min(1.0, rx + nudge * direction))
# ---- ASSIST: SHOOTING TEMPO ----
# Detects downward pull on right stick then applies
# timed release window delay for green window consistency
if self.shooting_assist.get():
if ry > 0.7 and self._last_ry <= 0.7:
self._rs_down_time = time.perf_counter()
self._rs_tracking = True
if self._rs_tracking and ry < -0.4 and self._last_ry >= -0.4:
elapsed = time.perf_counter() - self._rs_down_time
delay = self.tempo_release.get()
if elapsed < delay:
time.sleep(delay - elapsed)
ry = 0.0
rx = 0.0
self._rs_tracking = False
self._last_ry = ry
# ---- WRITE TO VIRTUAL PAD ----
self.vpad.left_joystick(
x_value=int(lx * 32767),
y_value=int(ly * 32767) # XInput Y is already correct axis
)
self.vpad.right_joystick(
x_value=int(rx * 32767),
y_value=int(ry * 32767)
)
self.vpad.update()
# ---- UPDATE STICK VISUALIZER (throttled to ~30fps) ----
if int(time.perf_counter() * 30) % 1 == 0:
self.root.after(0, self._update_stick_visual,
self._ls_canvas, lx, ly)
self.root.after(0, self._update_stick_visual,
self._rs_canvas, rx, ry)
# ---- PRECISE TIMING ----
elapsed = time.perf_counter() - t_start
sleep_t = self.POLL_INTERVAL - elapsed
if sleep_t > 0:
time.sleep(sleep_t)
# ==========================================================
# THREAD-SAFE GUI UPDATE
# ==========================================================
def _safe_gui(self, widget, text):
try:
self.root.after(0, widget.config, {"text": text})
except Exception:
pass
# ==========================================================
# CLEAN SHUTDOWN
# ==========================================================
def _on_close(self):
self.running = False
if self.vpad:
self.vpad.reset()
self.vpad.update()
self.root.destroy()
# ============================================================
# ENTRY POINT
# ============================================================
if __name__ == "__main__":
app = VirtualControllerAssistant()1 views