# -*- coding: utf-8 -*- """ wait_wander6.py — 等待图走动·终版(2026-09-23 抓包定案)。 真实客户端抓包(/tmp/cap_wait.jsonl, 996盒子在等待图滞留带): 走一步 = CS3011 + 50ms后 CS1, 每550ms重复: CS3011: recog=0, p1=dir(0-7), p2=递增计数(等待图段 1638421≈25<<16 起, 每步 +65535 附近抖动), p3=95(固定), body="等待地图"(UTF-8 12B) CS1: recog=X, p1=Y, p2=dir, p3=32368232(固定), tail=递增, body=encode6("+GOOD/<大毫秒>") — 明文即 +GOOD/98996961125 量级, 步进 ~750-1000ms; 坐标(X,Y)即步后位置(每步减/加1) → 角色确实在滞留带移动(轨迹 (20,26)→(19,27)→(18,26)...)! → 同一 3011 在比奇布局不同: p1=dir, p2=pack_point(起点) — quest_bot3 已实证。 之前全拒的根因: 布局错(发成比奇布局 recog=pack_point) + 缺紧跟的 CS1。 用法: .venv/bin/python wait_wander6.py --zone 14504562 --name 萌新8195 \ -u longwu988 -p Longwu1988 --minutes 2 """ import argparse import json import random import time import requests as rq import urllib3 urllib3.disable_warnings() import login_996pc as L import quest_bot3 as Q from combat_bot import log, DIRS, lo16, hi16 from encode6bitbuf_final import encode6 class TrueWalkBot(Q.PushBot): """抓包同构: 3011(等待图布局)+50ms CS1, 550ms/步。""" def __init__(self, *a, **kw): super().__init__(*a, **kw) self.good_ms = 98996961125 + random.randint(0, 5000) # 抓包同量级 self.wait_seq = random.randint(20, 30) << 16 # p2 计数初值 self.tail_seq = 90 # CS1 tail self.move_confirmed = 0 self.move_rejected = 0 def _send_good(self, x, y, d): """CS1: recog=X p1=Y p2=dir p3=32368232 body=encode6('+GOOD/ms')。 tail 走主链(抓包实证 CS1 tail 恒 817 是引擎会话参数; 主链唯一递增即可)。""" self.good_ms += random.randint(750, 1000) body = encode6(f"+GOOD/{self.good_ms}".encode()) self.send(1, x & 0xFFFF, y & 0xFFFF, d, 32368232, body) def step_true(self, d): """走一步 = 三帧组, 严格链式(黄金抓包 cap_gold 会话4 定案): ①CS3011: recog=0, p1=dir, p2=pack_point(起点), p3=sid, body=图名 起点 = 上一组 CS1 的落点(链式咬合, 断链→SC28拉回锚点) ②CS1(+70ms): recog=落点X, p1=落点Y, p2=dir, p3=32368232, body=+GOOD/ms ③CS28(+20ms): recog=锚点X, p1=锚点Y, p2=0, p3=32368232, body=空 锚点=服务器权威传送落点(每步恒定重复申报)""" from combat_bot import pack_point if getattr(self, "chain_pos", None) is None: self.chain_pos = (self.me["x"], self.me["y"]) if getattr(self, "anchor_pos", None) is None: self.anchor_pos = (self.me["x"], self.me["y"]) dx, dy = DIRS[d] x, y = self.chain_pos nx, ny = x + dx, y + dy self.send(3011, 0, d, pack_point(x, y), self.sid, Q.WAIT_MAP.encode()) time.sleep(0.07) self._send_good(nx, ny, d) time.sleep(0.02) ax, ay = self.anchor_pos self.send(28, ax & 0xFFFF, ay & 0xFFFF, 0, 32368232, b"") self.chain_pos = (nx, ny) self.me["x"], self.me["y"], self.me["dir"] = nx, ny, d def _dispatch(self, mid, recog, p1, p2, p3, body): ev = super()._dispatch(mid, recog, p1, p2, p3, body) if mid in (11, 13): j = self._jbody(body) or {} uid = str(j.get("UserID", "")) if uid in ((self.me.get("uid") or ""), (self.me.get("sid_short") or ""), str(self.me.get("id") or "")): self.move_confirmed += 1 y2 = p2 & 0x7FF if (p2 & 0x800) else p2 self.me["x"], self.me["y"] = p1, y2 self.me["last_x"], self.me["last_y"] = p1, y2 log(f" << SC{mid} ★★★服务器确认移动 → ({p1},{y2})") elif mid == 28: y2 = p2 & 0x7FF if (p2 & 0x800) else p2 self.move_rejected += 1 if self.move_rejected <= 10: log(f" << SC28 拒绝 → ({p1},{y2}) recog={recog}") return ev def enter(args): j = None for a in range(5): try: s = rq.Session() j, _ = L.do_login(s, args.user, args.password) if j.get("code") == 200: break except Exception as e: log(f"登录重试{a + 1}: {str(e)[:60]}") time.sleep(12) if not j or j.get("code") != 200: raise SystemExit(f"[!] 登录失败: {(j or {}).get('msg', '网络异常')}") jwt = j["data"]["token"] uid = str(j["data"]["userInfo"]["id"]) import cq2_pure_enter as P from combat_bot import send_init_frames for a in range(4): try: P.enter_game(s, jwt, args.zone) break except Exception: if a == 3: raise time.sleep(12) got = None for pt in ((args.port,) if args.port else (8021, 8061, 8001, 8041)): try: r = P.login_gate(args.zone, jwt, args.name, pt, uid) if r[0]: got = r break except SystemExit as e: log(f" 端口{pt}: {e}") except Exception as e: log(f" 端口{pt}: {str(e)[:60]}") if not got: raise SystemExit("[!] 登录服失败") key32, sid, rname, tkey, gp = got g = P.enter_game_gate(args.zone, rname, tkey, sid, gp, uid) bot = TrueWalkBot(g, sid, rname) # 心跳经 bot.send: tail 与主链共用递增(抓包实证全序连续) P.heartbeat_loop(g, sid, sender=bot.send) t0 = time.time() while time.time() - t0 < 10: bot.pump(0.3) if 658 in bot.frame_log: break if 658 not in bot.frame_log: g.close() raise SystemExit("[!] SC658 超时") send_init_frames(bot, uid) for _ in range(24): bot.pump(0.5) if bot.me["id"] and bot.me.get("mapname"): break log(f"进世界: map={bot.me.get('mapname')} @{bot.me['x']},{bot.me['y']}") return g, bot def npc_talk_to_biqi(bot): """滞留带 → 点NPC → 传比奇(quest_bot3 实证链)。""" t0 = time.time() while time.time() - t0 < 8 and not bot.npcs: bot.pump(0.4) npc = bot.find_guide_npc() if not npc: return bot.me.get("mapname") == "比奇大城" aid = npc["aid"] for idx in [npc["index"], 35, 34]: if idx == npc["index"] and idx == 35: pass bot.pending_map_ack = "比奇大城" bot.send(1010, 0, 0, 0, bot.sid, json.dumps( {"UserID": aid, "index": idx}, separators=(",", ":")).encode()) bot.send(5012, 0, 0, 0, bot.sid, b"0") bot.send(5027, 0, 0, 0, bot.sid, b"0") t1 = time.time() while time.time() - t1 < 8 and bot.alive: evs = bot.pump(0.3) if bot.me.get("mapname") == "比奇大城": bot.pending_map_ack = None return True talk = next((e[1] for e in evs if e[0] == "npc_talk"), None) if talk: acts = bot.parse_sui_acts(str(talk.get("sMsg", ""))) act = next((a for a in acts if a not in ("exit", "close")), None) if act: bot.npc_click_act(talk, act) bot.pending_map_ack = None if bot.me.get("mapname") == "比奇大城": return True return bot.me.get("mapname") == "比奇大城" def renwu_back_to_wait(bot): """比奇 → @renwu_mov → 等待图可走区(黄金抓包 cap_gold t=121-146 定案): 1011 → 服务器把权威坐标传到 (24,27)(可走区) → 客户端 100ms 后 5012/5027+3013(起点=服务器新位置) → CS1 申报 → 连续走。 bot 要点: 等 SC634/SC51 拿权威落点后再起步, 不从旧坐标走。""" import os from combat_bot import pack_point if 1 in bot.mission_bodies: body3031 = json.dumps(bot.mission_bodies[1], separators=(",", ":")).encode() else: body3031 = open(os.path.join(Q.HERE, "docs", "cs3031_template.txt"), "rb").read() bot.send(3031, 0, 0, 0, bot.sid, body3031) bot.pump(0.3) bot.send(1011, 0, 0, 0, bot.sid, json.dumps( {"Act": "@renwu_mov,1", "UserID": "999999999", "index": 999999999}, separators=(",", ":")).encode()) # 黄金时序(cap_gold 会话4 t=750.87-751.2): 1011后 ~200ms: # 5012/5027+cTh ×2组 → 首步3013(p2=服务器权威落点!) → CS1(落点) → CS28(落点) # 等 SC634 权威落点(修正解析后: recog=X, p1=Y) — y>4 才算传送到可走区 t0 = time.time() while time.time() - t0 < 6 and bot.alive: bot.pump(0.1) if bot.me.get("mapname") == Q.WAIT_MAP and bot.me["y"] > 4: break x0, y0 = bot.me["x"], bot.me["y"] if y0 < 20: x0, y0 = 24, 27 # 兜底: 权威落点(黄金抓包) for _ in range(2): for mid in (5012, 5027): bot.send(mid, 0, 0, 0, bot.sid, Q.WAIT_MAP.encode()) bot.send(5012, 0, 0, 0, 32611848, b"cTh") # 首步: 3013 起点必须=权威落点(黄金会话4: p2=(24,27)) bot.send(3013, 0, 5, pack_point(x0, y0), bot.sid, Q.WAIT_MAP.encode()) nx, ny = x0 - 2, y0 + 2 # 跑dir5两格 bot._send_good(nx, ny, 5) time.sleep(0.02) bot.send(28, nx & 0xFFFF, ny & 0xFFFF, 0, 32368232, b"") bot.chain_pos = (nx, ny) # 链式起点 bot.anchor_pos = (x0, y0) # CS28 锚点=权威落点 bot.me["x"], bot.me["y"] = nx, ny bot.me["last_x"], bot.me["last_y"] = x0, y0 time.sleep(0.5) bot.pump(0.5) n28 = bot.frame_log.get(28, 0) log(f"链式起步 权威=({x0},{y0}) 链位=({nx},{ny}) SC28={n28}") return True def main(): ap = argparse.ArgumentParser() ap.add_argument("-u", "--user", default="longwu988") ap.add_argument("-p", "--password", default="Longwu1988") ap.add_argument("--zone", type=int, default=14504562) ap.add_argument("--name", default="萌新8195") ap.add_argument("--port", type=int, default=0) ap.add_argument("--minutes", type=float, default=2) args = ap.parse_args() args.minutes = min(args.minutes, 2) g, bot = enter(args) try: Q.P.enable_auto_heal(g, bot.sid, want_on=True) log("喝药保护: on") if bot.me.get("mapname") == Q.WAIT_MAP and bot.me["y"] <= 4: log("[1] 滞留带 → NPC传比奇") if not npc_talk_to_biqi(bot): log("[!] 未传比奇, 中止") return log("[1b] 比奇下线(回归态落盘) → 15s后重连") g.close() time.sleep(15) g, bot = enter(args) # 回归角色应直接落可走区 Q.P.enable_auto_heal(g, bot.sid, want_on=True) if bot.me.get("mapname") != Q.WAIT_MAP: log("[2] 比奇 → @renwu_mov 回等待图可走区") if not renwu_back_to_wait(bot): log("[!] 未回可走区, 中止") return log("=" * 60) log(f"[3] 可走区真实布局走动 @{bot.me['x']},{bot.me['y']}") log("=" * 60) t_end = time.time() + args.minutes * 60 last_step = 0.0 rep = time.time() heading = 5 while time.time() < t_end and bot.alive: bot.pump(0.05) now = time.time() if now - last_step >= 0.55: # 换向策略: 确认多→保持, 拒绝多→随机换向 if bot.move_rejected > bot.move_confirmed: heading = random.choice([h for h in range(8) if h != heading]) bot.move_rejected = 0 bot.move_confirmed = 0 bot.step_true(heading) last_step = now if now - rep >= 15: rep = now log(f"[STAT] pos=({bot.me['x']},{bot.me['y']}) " f"确认={bot.move_confirmed} 拒绝={bot.move_rejected}") log("=" * 60) log(f"[结束] pos=({bot.me['x']},{bot.me['y']}) " f"确认={bot.move_confirmed} 拒绝={bot.move_rejected}") fl = sorted(bot.frame_log.items(), key=lambda kv: -kv[1]) log(f"收帧Top10: {fl[:10]}") finally: g.close() if __name__ == "__main__": main()