quest_probe.py 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. """quest_probe.py — 激活任务态探测: 走位活动 → CS3031 → dump SC643 真实按钮。
  2. 背景(2026-09-29):
  3. - 凌风踏雪 CS1010(传送员)/CS1011@act_move,2 全部无响应 — 任务态挂起
  4. - 用户真客户端弹窗证实: 传送员列表无"地牢一层东"(任务图, 只能走任务按钮)
  5. → 本脚本: 登录后先走位活动(证明会话健康) → 发 CS3031 → 长窗口收集 SC643
  6. → dump 弹窗全部按钮 Act(不盲点) → 按用户确认的 Act 传送。
  7. """
  8. from __future__ import annotations
  9. import importlib.util
  10. import json
  11. import logging
  12. import re
  13. import sys
  14. import time
  15. from pathlib import Path
  16. ROOT = Path(__file__).resolve().parents[2]
  17. sys.path.insert(0, str(ROOT / "cpp_reversed"))
  18. sys.path.insert(0, str(ROOT / "protocol_bot" / "src"))
  19. import requests # noqa: E402
  20. from pbot.config import Account # noqa: E402
  21. from pbot.game.session import enter_world # noqa: E402
  22. from pbot.login.box_client import enter_game_client, login # noqa: E402
  23. from pbot.transport.gateway import login_gate_with_retry # noqa: E402
  24. from pbot.world.update import WorldUpdater # noqa: E402
  25. from map_parser import load as load_map # noqa: E402
  26. _spec = importlib.util.spec_from_file_location(
  27. "wt", ROOT / "protocol_bot" / "scripts" / "watch_tp_pathfind.py")
  28. wt = importlib.util.module_from_spec(_spec)
  29. sys.modules["wt"] = wt
  30. _spec.loader.exec_module(wt)
  31. _LOG = logging.getLogger("quest_probe")
  32. LINK_RE = re.compile(r"link=@([^|<>]+)")
  33. def main() -> int:
  34. http = requests.Session()
  35. http.trust_env = False
  36. http.verify = False
  37. jwt, uid = login(Account(username="longwu988", password="Longwu1988"))
  38. _ = enter_game_client(http, jwt, 14558825)
  39. key32, sid, rname, tkey, gport = login_gate_with_retry(
  40. 14558825, jwt, uid, "凌风踏雪", wt.PORTS)
  41. stop = [False]
  42. conn, world, updater = enter_world(14558825, rname, tkey, sid, gport, uid,
  43. stop=stop)
  44. _LOG.info("[出生] %s @(%d,%d)", world.me.map_name, world.me.x, world.me.y)
  45. try:
  46. time.sleep(8.0)
  47. wt.pump(conn, world, updater, 1.0)
  48. # ---- 1. 走位活动: 出墙格 + 走到传送员旁(证明会话健康, 顺便对话) ----
  49. m3 = load_map(str(ROOT / "protocol_bot" / "assets" / "maps" / "3.map"))
  50. me = (world.me.x, world.me.y)
  51. if not m3.get_cell(me[0], me[1]).is_walkable:
  52. for rr in range(1, 6):
  53. near = next(((me[0] + dx, me[1] + dy)
  54. for dx in range(-rr, rr + 1)
  55. for dy in range(-rr, rr + 1)
  56. if max(abs(dx), abs(dy)) == rr
  57. and 0 <= me[0] + dx < m3.width
  58. and 0 <= me[1] + dy < m3.height
  59. and m3.get_cell(me[0] + dx,
  60. me[1] + dy).is_walkable), None)
  61. if near:
  62. _ = wt.goto(conn, sid, world, updater, m3, near)
  63. _LOG.info("[出墙] %s → %s", me, near)
  64. break
  65. _ = wt.goto(conn, sid, world, updater, m3, (340, 338))
  66. _LOG.info("[活动] 走位完成 @(%d,%d)", world.me.x, world.me.y)
  67. time.sleep(3.0)
  68. # ---- 2. CS3031 激活任务态 ----
  69. dialogs: list[dict] = []
  70. _ = conn.send_frame(
  71. 3031, 0, 0, 0, sid,
  72. (ROOT / "protocol_bot" / "assets" / "cs3031_full.bin").read_bytes())
  73. _LOG.info("[CS3031] 已发, 3s 后发 1011@renwu_mov,1")
  74. time.sleep(3.0)
  75. wt.pump(conn, world, updater, 0.5)
  76. _ = conn.send_frame(
  77. 1011, 0, 0, 0, sid,
  78. b'{"UserID":"999999999","Act":"@renwu_mov,1","index":999999999}')
  79. _LOG.info("[CS1011] @renwu_mov,1 已发 — 长窗口 45s 收弹窗")
  80. # ---- 2.5 传送员对话(蜈蚣洞穴=地牢一层东, 用户实证) ----
  81. time.sleep(6.0)
  82. wt.pump(conn, world, updater, 0.5)
  83. body = json.dumps({"UserID": wt.TELEPORTER_AID, "index": 2},
  84. separators=(",", ":")).encode()
  85. for i in range(3):
  86. _ = conn.send_frame(1010, 0, 0, 0, sid, body)
  87. _LOG.info("[传送员] CS1010 第%d次", i + 1)
  88. t0 = time.time()
  89. while time.time() - t0 < 5:
  90. wt.pump(conn, world, updater, 0.2)
  91. if world.npc_dialog:
  92. dialogs.append(dict(world.npc_dialog))
  93. _LOG.info("[传送员弹窗✓] 到达")
  94. break
  95. if dialogs:
  96. break
  97. time.sleep(2.0)
  98. # ---- 3. 长窗口收集 SC643 ----
  99. t0 = time.time()
  100. while time.time() - t0 < 45:
  101. for f in conn.recv_frames(0.3):
  102. pre = world.npc_dialog
  103. updater.apply(world, f)
  104. if f.msg_id == 643 and world.npc_dialog and \
  105. world.npc_dialog is not pre:
  106. dialogs.append(dict(world.npc_dialog))
  107. _LOG.info("[SC643 #%d] 到达", len(dialogs))
  108. if f.msg_id in (634, 800):
  109. _LOG.info("[传送帧] SC%d — 落地 %s", f.msg_id,
  110. world.me.map_name)
  111. if dialogs and time.time() - t0 > 15:
  112. break
  113. _LOG.info("[终态] %s @(%d,%d)", world.me.map_name, world.me.x,
  114. world.me.y)
  115. if not dialogs:
  116. _LOG.error("[结论] 45s 无 SC643 — 任务态仍挂起")
  117. return 1
  118. for i, d in enumerate(dialogs):
  119. smsg = str(d.get("sMsg", ""))
  120. links = LINK_RE.findall(smsg)
  121. texts = re.findall(r"text=([^|<>]+)", smsg)
  122. _LOG.info("[弹窗%d] UserID=%s index=%s", i + 1, d.get("UserID"),
  123. d.get("index"))
  124. _LOG.info(" 按钮: %s", links[:20])
  125. _LOG.info(" 文本: %s", [t.strip()[:30] for t in texts[:10]])
  126. return 0
  127. finally:
  128. stop[0] = True
  129. conn.close()
  130. if __name__ == "__main__":
  131. sys.exit(main())