"""Precise launch: find the ready_start button's REAL clickable sub-element (the '启动' text node inside it), verify game context, click, and watch the Electron ipc 'openApp' via CDP console + process spawn. """ import asyncio import base64 import json import subprocess import time import httpx import websockets async def main(): async with httpx.AsyncClient(timeout=5) as c: r = await c.get("http://127.0.0.1:9222/json/list") det = next((t for t in r.json() if "gameDetail" in t.get("url", "")), None) if not det: print("no gameDetail target") return 1 async with websockets.connect(det["webSocketDebuggerUrl"], max_size=10**7) as ws: mid = 0 async def ev(expr, wait=True): nonlocal mid mid += 1 await ws.send(json.dumps({"id": mid, "method": "Runtime.evaluate", "params": {"expression": expr, "returnByValue": True}})) if not wait: return None while True: m = json.loads(await ws.recv()) if m.get("id") == mid: return m.get("result", {}).get("result", {}).get("value") async def click(x, y): nonlocal mid mid += 1 await ws.send(json.dumps({"id": mid, "method": "Input.dispatchMouseEvent", "params": {"type": "mousePressed", "x": x, "y": y, "button": "left", "clickCount": 1, "buttons": 1}})) await asyncio.sleep(0.07) mid += 1 await ws.send(json.dumps({"id": mid, "method": "Input.dispatchMouseEvent", "params": {"type": "mouseReleased", "x": x, "y": y, "button": "left", "clickCount": 1, "buttons": 0}})) # drain while True: m = json.loads(await ws.recv()) if m.get("id") == mid: return # 1. Full anatomy of ready_start and its children anatomy = await ev(r""" (function() { const btn = document.querySelector('.ready_start'); if (!btn) return JSON.stringify({found: false}); const r = btn.getBoundingClientRect(); const kids = []; btn.querySelectorAll('*').forEach(k => { const kr = k.getBoundingClientRect(); if (kr.width > 20) { kids.push({ tag: k.tagName, cls: (k.className || '').toString().slice(0, 35), text: (k.innerText || '').trim().slice(0, 20), x: Math.round(kr.x + kr.width/2), y: Math.round(kr.y + kr.height/2), w: Math.round(kr.width), h: Math.round(kr.height) }); } }); // also check Vue handlers const vueKeys = Object.keys(btn).filter(k => k.startsWith('__vue') || k.startsWith('onclick') || k.includes('click')); return JSON.stringify({ found: true, rect: {x: Math.round(r.x), y: Math.round(r.y), w: Math.round(r.width), h: Math.round(r.height)}, cx: Math.round(r.x + r.width/2), cy: Math.round(r.y + r.height/2), fullText: (btn.innerText || '').replace(/\n/g, ' | ').slice(0, 80), kids: kids.slice(0, 12), handlers: vueKeys.slice(0, 5) }, null, 1); })() """) print("=== ready_start anatomy ===") print(anatomy) # 2. Hook the window.raw IPC bridge to observe openApp calls await ev(r""" (function() { if (window.__hookInstalled) return 'already'; window.__hookInstalled = true; window.__ipcCalls = []; const raw = window.raw; if (raw && raw.openApp) { const orig = raw.openApp.bind(raw); raw.openApp = function(...args) { window.__ipcCalls.push({fn: 'openApp', args: JSON.stringify(args).slice(0, 200), t: Date.now()}); return orig(...args); }; return 'hooked window.raw.openApp'; } return 'window.raw not found — hooks may differ'; })() """) hook = await ev("window.__hookInstalled ? (window.__ipcCalls ? 'hook ready' : '?') : 'no'") print(f"\nIPC hook: {hook}") # 3. Click the CENTER of the button (or its main text child) a = json.loads(anatomy or "{}") if not a.get("found"): print("button not found!") return 1 # prefer a child whose text contains 启动 or 上一次 — else button center target = None for k in a.get("kids", []): if "启动" in k["text"] or "上一次" in k["text"]: target = k break if not target: target = {"x": a["cx"], "y": a["cy"], "text": "(center)"} print(f"\nclicking child: '{target['text']}' @ ({target['x']},{target['y']})") await click(target["x"], target["y"]) await asyncio.sleep(3) # 4. Did the IPC fire? Did anything change? calls = await ev("JSON.stringify(window.__ipcCalls || [])") print(f"\nopenApp IPC calls after click: {calls}") # 5. Fallback: call openApp DIRECTLY via the bridge (exactly what 启动 does) direct = await ev(r""" (function() { const raw = window.raw; if (!raw) return 'no raw bridge'; // the game page knows the game id; find it in state const gid = 206923; const url = 'https://h5qfdt.996box.com/'; if (raw.openApp) { const p = raw.openApp('cq', url); return 'direct openApp fired: cq + ' + url; } return 'raw.openApp missing'; })() """) print(f"direct openApp: {direct}") # 6. monitor processes + traffic print("\n=== monitoring 180s ===") t0 = time.time() while time.time() - t0 < 180: await asyncio.sleep(5) el = int(time.time() - t0) try: out = subprocess.run( ["powershell", "-NoProfile", "-Command", "$p=(Get-Process | Where-Object {$_.Name -match '^cq'} | ForEach-Object {$_.Name+':'+$_.Id}) -join ',';" "$g=(netstat -ano | Select-String ':12000' | Measure-Object).Count;" "$c=(Get-Content 'D:\\传奇\\cq2_sdk\\real_login.jsonl' -ErrorAction SilentlyContinue | Measure-Object).Count;" "$p+'|'+$g+'|'+$c"], capture_output=True, text=True, timeout=8) parts = out.stdout.strip().split("|") procs, gw, cap = parts[0], parts[1] if len(parts) > 1 else "0", parts[2] if len(parts) > 2 else "0" except Exception: procs, gw, cap = "", "0", "0" flags = [] if procs: flags.append(f"procs={procs}") if gw != "0": flags.append(f"gw={gw}") if cap != "0": flags.append(f"cap={cap}") print(f" t+{el:>3}s {' '.join(flags) or '...'}") if cap != "0" and int(cap) >= 2: print("\n*** TRAFFIC CAPTURED ***") await asyncio.sleep(20) return 0 if procs and gw != "0" and el > 50: await asyncio.sleep(30) return 0 return 1 import sys sys.exit(asyncio.run(main()))