"""quest_probe.py — 激活任务态探测: 走位活动 → CS3031 → dump SC643 真实按钮。 背景(2026-09-29): - 凌风踏雪 CS1010(传送员)/CS1011@act_move,2 全部无响应 — 任务态挂起 - 用户真客户端弹窗证实: 传送员列表无"地牢一层东"(任务图, 只能走任务按钮) → 本脚本: 登录后先走位活动(证明会话健康) → 发 CS3031 → 长窗口收集 SC643 → dump 弹窗全部按钮 Act(不盲点) → 按用户确认的 Act 传送。 """ from __future__ import annotations import importlib.util import json import logging import re import sys import time from pathlib import Path ROOT = Path(__file__).resolve().parents[2] sys.path.insert(0, str(ROOT / "cpp_reversed")) sys.path.insert(0, str(ROOT / "protocol_bot" / "src")) import requests # noqa: E402 from pbot.config import Account # noqa: E402 from pbot.game.session import enter_world # noqa: E402 from pbot.login.box_client import enter_game_client, login # noqa: E402 from pbot.transport.gateway import login_gate_with_retry # noqa: E402 from pbot.world.update import WorldUpdater # noqa: E402 from map_parser import load as load_map # noqa: E402 _spec = importlib.util.spec_from_file_location( "wt", ROOT / "protocol_bot" / "scripts" / "watch_tp_pathfind.py") wt = importlib.util.module_from_spec(_spec) sys.modules["wt"] = wt _spec.loader.exec_module(wt) _LOG = logging.getLogger("quest_probe") LINK_RE = re.compile(r"link=@([^|<>]+)") def main() -> int: http = requests.Session() http.trust_env = False http.verify = False jwt, uid = login(Account(username="longwu988", password="Longwu1988")) _ = enter_game_client(http, jwt, 14558825) key32, sid, rname, tkey, gport = login_gate_with_retry( 14558825, jwt, uid, "凌风踏雪", wt.PORTS) stop = [False] conn, world, updater = enter_world(14558825, rname, tkey, sid, gport, uid, stop=stop) _LOG.info("[出生] %s @(%d,%d)", world.me.map_name, world.me.x, world.me.y) try: time.sleep(8.0) wt.pump(conn, world, updater, 1.0) # ---- 1. 走位活动: 出墙格 + 走到传送员旁(证明会话健康, 顺便对话) ---- m3 = load_map(str(ROOT / "protocol_bot" / "assets" / "maps" / "3.map")) me = (world.me.x, world.me.y) if not m3.get_cell(me[0], me[1]).is_walkable: for rr in range(1, 6): near = next(((me[0] + dx, me[1] + dy) for dx in range(-rr, rr + 1) for dy in range(-rr, rr + 1) if max(abs(dx), abs(dy)) == rr and 0 <= me[0] + dx < m3.width and 0 <= me[1] + dy < m3.height and m3.get_cell(me[0] + dx, me[1] + dy).is_walkable), None) if near: _ = wt.goto(conn, sid, world, updater, m3, near) _LOG.info("[出墙] %s → %s", me, near) break _ = wt.goto(conn, sid, world, updater, m3, (340, 338)) _LOG.info("[活动] 走位完成 @(%d,%d)", world.me.x, world.me.y) time.sleep(3.0) # ---- 2. CS3031 激活任务态 ---- dialogs: list[dict] = [] _ = conn.send_frame( 3031, 0, 0, 0, sid, (ROOT / "protocol_bot" / "assets" / "cs3031_full.bin").read_bytes()) _LOG.info("[CS3031] 已发, 3s 后发 1011@renwu_mov,1") time.sleep(3.0) wt.pump(conn, world, updater, 0.5) _ = conn.send_frame( 1011, 0, 0, 0, sid, b'{"UserID":"999999999","Act":"@renwu_mov,1","index":999999999}') _LOG.info("[CS1011] @renwu_mov,1 已发 — 长窗口 45s 收弹窗") # ---- 2.5 传送员对话(蜈蚣洞穴=地牢一层东, 用户实证) ---- time.sleep(6.0) wt.pump(conn, world, updater, 0.5) body = json.dumps({"UserID": wt.TELEPORTER_AID, "index": 2}, separators=(",", ":")).encode() for i in range(3): _ = conn.send_frame(1010, 0, 0, 0, sid, body) _LOG.info("[传送员] CS1010 第%d次", i + 1) t0 = time.time() while time.time() - t0 < 5: wt.pump(conn, world, updater, 0.2) if world.npc_dialog: dialogs.append(dict(world.npc_dialog)) _LOG.info("[传送员弹窗✓] 到达") break if dialogs: break time.sleep(2.0) # ---- 3. 长窗口收集 SC643 ---- t0 = time.time() while time.time() - t0 < 45: for f in conn.recv_frames(0.3): pre = world.npc_dialog updater.apply(world, f) if f.msg_id == 643 and world.npc_dialog and \ world.npc_dialog is not pre: dialogs.append(dict(world.npc_dialog)) _LOG.info("[SC643 #%d] 到达", len(dialogs)) if f.msg_id in (634, 800): _LOG.info("[传送帧] SC%d — 落地 %s", f.msg_id, world.me.map_name) if dialogs and time.time() - t0 > 15: break _LOG.info("[终态] %s @(%d,%d)", world.me.map_name, world.me.x, world.me.y) if not dialogs: _LOG.error("[结论] 45s 无 SC643 — 任务态仍挂起") return 1 for i, d in enumerate(dialogs): smsg = str(d.get("sMsg", "")) links = LINK_RE.findall(smsg) texts = re.findall(r"text=([^|<>]+)", smsg) _LOG.info("[弹窗%d] UserID=%s index=%s", i + 1, d.get("UserID"), d.get("index")) _LOG.info(" 按钮: %s", links[:20]) _LOG.info(" 文本: %s", [t.strip()[:30] for t in texts[:10]]) return 0 finally: stop[0] = True conn.close() if __name__ == "__main__": sys.exit(main())