| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256 |
- """Correct game launch: use the KNOWN enterGameClient HTTP path from our SDK
- (same as the real client's 启动 button) targeted at gameId=206923 + a real
- 玖扬 serverid, PLUS CDP-click verification. But first: find the true 玖扬
- game detail page via the search flow (click suggestion properly this time),
- because EnterGameClient only records the launch — cq.exe is spawned by the
- Electron side via the game page's IPC.
- Robust plan:
- 1. CDP: focus search input, select-all, type 玖扬复古176 with REAL key events
- 2. Wait for suggestion dropdown; click the suggestion whose text EXACTLY
- starts with 玖扬 (verify its gameId in its attributes/parent)
- 3. On the detail page: verify the game name shows 玖扬复古176 before clicking 启动
- 4. Background capture running the whole time
- """
- import asyncio
- import base64
- import json
- import subprocess
- import sys
- import time
- import httpx
- import websockets
- CDP = "http://127.0.0.1:9222"
- GAME = "玖扬复古176"
- async def get_targets():
- async with httpx.AsyncClient(timeout=5) as c:
- r = await c.get(f"{CDP}/json/list")
- return r.json()
- class Page:
- def __init__(self, ws):
- self.ws = ws
- self.mid = 0
- async def call(self, method, params=None):
- self.mid += 1
- await self.ws.send(json.dumps({"id": self.mid, "method": method, "params": params or {}}))
- while True:
- m = json.loads(await self.ws.recv())
- if m.get("id") == self.mid:
- return m.get("result", {})
- async def eval(self, expr):
- r = await self.call("Runtime.evaluate", {"expression": expr, "returnByValue": True})
- return r.get("result", {}).get("value")
- async def click(self, x, y):
- await self.call("Input.dispatchMouseEvent", {"type": "mousePressed", "x": x, "y": y, "button": "left", "clickCount": 1, "buttons": 1})
- await asyncio.sleep(0.06)
- await self.call("Input.dispatchMouseEvent", {"type": "mouseReleased", "x": x, "y": y, "button": "left", "clickCount": 1, "buttons": 0})
- async def key(self, key, code, vk, text=None):
- p = {"type": "keyDown", "key": key, "code": code, "windowsVirtualKeyCode": vk}
- if text:
- p["text"] = text
- await self.call("Input.dispatchKeyEvent", p)
- await asyncio.sleep(0.03)
- p["type"] = "keyUp"
- await self.call("Input.dispatchKeyEvent", p)
- async def shot(self, name):
- r = await self.call("Page.captureScreenshot", {"format": "png"})
- if r.get("data"):
- with open(name, "wb") as f:
- f.write(base64.b64decode(r["data"]))
- def poll():
- try:
- out = subprocess.run(
- ["powershell", "-NoProfile", "-Command",
- "$p = Get-Process | Where-Object {$_.Name -match '^cq'} | ForEach-Object { $_.Name + ':' + $_.Id };"
- "$g = (netstat -ano | Select-String ':12000' | Measure-Object).Count;"
- "$c = (Get-Content 'D:\\传奇\\cq2_sdk\\real_login.jsonl' -ErrorAction SilentlyContinue | Measure-Object).Count;"
- "ConvertTo-Json @{procs=$p; gw=$g; cap=$c}"],
- capture_output=True, text=True, timeout=8)
- return json.loads(out.stdout or "{}")
- except Exception:
- return {}
- async def main():
- targets = await get_targets()
- home = next((t for t in targets if "homePage" in t.get("url", "")), None)
- if not home:
- print("no home page")
- return 1
- async with websockets.connect(home["webSocketDebuggerUrl"], max_size=10**7) as ws:
- page = Page(ws)
- # ---- 1. search flow ----
- inp = await page.eval("""
- (function() {
- const inps = document.querySelectorAll('input');
- for (const i of inps) {
- const r = i.getBoundingClientRect();
- if (r.width > 100 && r.y < 150 && i.offsetParent) {
- return JSON.stringify({x: r.x + 30, y: r.y + r.height/2});
- }
- }
- return null;
- })()
- """)
- if not inp:
- print("no search input")
- return 1
- pos = json.loads(inp)
- print(f"search input @ ({pos['x']:.0f},{pos['y']:.0f})")
- # focus + select all + backspace (clear old text)
- await page.click(pos["x"], pos["y"])
- await asyncio.sleep(0.3)
- await page.key("a", "KeyA", 65)
- await asyncio.sleep(0.1)
- await page.key("Backspace", "Backspace", 8)
- await asyncio.sleep(0.3)
- # type via IME-commit style: set value + input event per char
- # (CDP can't do real IME, but the page's input handler reads .value)
- for ch in GAME:
- await page.eval(f"""
- (function() {{
- const i = document.activeElement;
- if (i && i.tagName === 'INPUT') {{
- const set = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set;
- set.call(i, i.value + '{ch}');
- i.dispatchEvent(new Event('input', {{bubbles: true}}));
- i.dispatchEvent(new KeyboardEvent('keyup', {{key: '{ch}', bubbles: true}}));
- }}
- }})()
- """)
- await asyncio.sleep(0.12)
- val = await page.eval("document.activeElement && document.activeElement.value")
- print(f"typed: {val}")
- # press Enter to commit search
- await page.key("Enter", "Enter", 13, text="\r")
- await asyncio.sleep(4)
- url = await page.eval("location.href")
- print(f"url after Enter: {url[:80]}")
- await page.shot("search_commit.png")
- # ---- 2. find the EXACT game card ----
- card = await page.eval(r"""
- (function() {
- // search-results page cards; match text EXACTLY on 玖扬复古176
- const els = document.querySelectorAll('*');
- let best = null;
- for (const el of els) {
- if (el.children.length <= 4 && el.offsetParent) {
- const t = (el.innerText || '').trim();
- if (t.replace(/\s/g, '') === '玖扬复古176') {
- const r = el.getBoundingClientRect();
- if (r.width > 60 && r.height > 30 && (!best || r.width > best.w)) {
- best = {x: r.x + r.width/2, y: r.y + r.height/2, w: r.width, h: r.height};
- }
- }
- }
- }
- return best ? JSON.stringify(best) : null;
- })()
- """)
- if not card:
- # maybe still on home; check any exact-match anywhere
- print("no exact card; screenshot for diagnosis")
- body = await page.eval("document.body.innerText.slice(0, 400)")
- print(f"body head: {body}")
- return 1
- c = json.loads(card)
- print(f"*** EXACT 玖扬复古176 card @ ({c['x']:.0f},{c['y']:.0f}) ***")
- await page.shot("target_card.png")
- # ---- 3. click card -> detail page ----
- await page.click(c["x"], c["y"])
- await asyncio.sleep(4)
- url2 = await page.eval("location.href")
- title = await page.eval("document.title")
- print(f"after card click: url={url2[:80]} title={title}")
- # verify we're on 玖扬's page
- verify = await page.eval("""
- document.body.innerText.includes('玖扬复古176') ? 'YES' : document.body.innerText.slice(0,200)
- """)
- print(f"page contains 玖扬复古176: {verify if verify == 'YES' else str(verify)[:150]}")
- if verify != "YES":
- print("WRONG PAGE — aborting to avoid another misfire")
- await page.shot("wrong_page.png")
- return 1
- # ---- 4. find + click 启动 on THE GAME'S OWN PAGE ----
- for attempt in range(4):
- btn = await page.eval(r"""
- (function() {
- const btns = document.querySelectorAll('button, [class*=btn]');
- for (const b of btns) {
- const t = (b.innerText || '').trim();
- if (/^(启动|开始游戏|进入游戏)$/.test(t) && b.offsetParent) {
- const r = b.getBoundingClientRect();
- return JSON.stringify({text: t, x: r.x + r.width/2, y: r.y + r.height/2});
- }
- }
- return null;
- })()
- """)
- if btn:
- b = json.loads(btn)
- print(f"启动 button: '{b['text']}' @ ({b['x']:.0f},{b['y']:.0f})")
- await page.shot("before_launch_target.png")
- await page.click(b["x"], b["y"])
- print("*** 启动 CLICKED (on 玖扬复古176's page) ***")
- break
- await asyncio.sleep(2.5)
- else:
- print("no 启动 button on detail page")
- await page.shot("no_launch_on_detail.png")
- return 1
- # ---- 5. monitor ----
- print("\n=== monitoring 240s ===")
- t0 = time.time()
- ok = False
- while time.time() - t0 < 240:
- await asyncio.sleep(5)
- el = int(time.time() - t0)
- st = poll()
- procs = st.get("procs", [])
- gw = st.get("gw", 0)
- cap = st.get("cap", 0)
- flags = []
- if procs:
- flags.append("procs=" + ",".join(p.split(":")[0] for p in (procs if isinstance(procs, list) else [procs])))
- if gw:
- flags.append(f"gw={gw}")
- if cap:
- flags.append(f"cap={cap}")
- print(f" t+{el:>3}s {' '.join(flags) or '...'}")
- if cap >= 2:
- ok = True
- print("\n*** CAPTURED REAL 玖扬 LOGIN TRAFFIC ***")
- await asyncio.sleep(20)
- break
- if procs and gw and el > 60:
- ok = True
- print("\n*** 玖扬 game connected to gateway ***")
- await asyncio.sleep(30)
- break
- return 0 if ok else 1
- sys.exit(asyncio.run(main()))
|