# -*- coding: utf-8 -*- """ wait_wander3.py — 等待图走动·Lua真源对齐版 + 全帧日志。 Lua 真源(解密源码/mod_fgcq/stab/scripts/logic/gamePlayerController.lua): handleActionBegin: LuaSendMsg(3013/3011, point, 0, dir, lastPoint, mapID) point = Y<<16|X(当前格), lastPoint=上一格, body=MapID字符串 → 跑(CS3013, 移动2格)与走(CS3011, 移动1格)同布局; 抓包成功序列 3013×2~4→3011; combat_bot.send_run docstring: 早期等待图 22 步跑帧全通。 SC28 MOVE_FAIL(MapPos2WorldPos(p1,p2) 直接序) → ACTION_IDLE_LOCK, recog=1 可立即再走。 日志: 每发一帧(CS3013/3011 全参数)+每收一帧(SC28/SC11/SC1/SC5020/SC41 原始参数)。 用法: .venv/bin/python wait_wander3.py --zone 14504562 --name 萌新4143 \ -u longwu988 -p Longwu1988 --minutes 2 """ import argparse import json 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, pack_point, lo16, hi16 class RunBot(Q.PushBot): """全帧日志 + CS5013 寻路握手 + 跑帧走动。 Lua 真源(AutoProxy:RequestAutoMoveNotify → LuaSendMsg(5013, act, destX, destY)): act: 1=START(recog=1, p1=X, p2=Y) / 2=ABORT / 3=FINISH AutoMoveBeginCommand: SC20102(T=1 服务器驱动)到达后, 若已有路径点必发 START; AutoMoveEndCommand: 完成发 FINISH, 中断发 ABORT。未注册寻路会话 → 服务器拒走。 """ def __init__(self, *a, **kw): super().__init__(*a, **kw) self.sent_log = {} # 发帧统计 self.verbose = True # 全帧日志开关 self.auto_move_on = False # CS5013 会话已注册 def auto_move_notify(self, act, dest_x=0, dest_y=0): """CS5013: recog=act(1/2/3), p1=destX, p2=destY(仅START带)。""" self.send(5013, act, dest_x, dest_y, 0, b"") if act == 1: self.auto_move_on = True log(f" >> CS5013 START → ({dest_x},{dest_y})") else: self.auto_move_on = False log(f" >> CS5013 {'FINISH' if act == 3 else 'ABORT'}") def send(self, mid, recog, p1, p2, p3, body=b"", tail=None): if mid in (3011, 3013): self.sent_log[mid] = self.sent_log.get(mid, 0) + 1 if self.verbose: x, y = lo16(recog), hi16(recog) lx, ly = lo16(p3), hi16(p3) log(f" >> CS{mid} 起点=({x},{y}) dir={p2} last=({lx},{ly}) " f"body={body[:20]!r} #{self.sent_log[mid]}") return super().send(mid, recog, p1, p2, p3, body, tail) def _dispatch(self, mid, recog, p1, p2, p3, body): ev = super()._dispatch(mid, recog, p1, p2, p3, body) if mid == 20102: j = self._jbody(body) or {} log(f" << SC20102 ★寻路目标 {j or body[:60]!r}") self._auto_find_target = j if self.verbose and mid in (28, 11, 1, 5020, 41, 13, 800, 634, 51, 50, 5013): y2 = p2 & 0x7FF if (p2 & 0x800) else p2 raw = body[:60] if mid in (11, 13): # body=encode6(JSON), 只打印自己的 try: j = self._jbody(body) or {} uid = str(j.get("UserID", "")) mine = (uid == (self.me.get("uid") or "") or uid == (self.me.get("sid_short") or "") or uid == str(self.me.get("id") or "")) if mine: log(f" << SC{mid} ★自己移动确认 → ({p1},{y2})") except Exception: pass if mid in (1, 28, 5020, 41, 5013): log(f" << SC{mid} recog={recog} p=({p1},{y2},{p3}) body={raw!r}") elif mid in (800, 634, 51, 50): log(f" << SC{mid} p=({p1},{y2}) body={raw!r}") return ev # ---------- 跑帧步进(对齐 Lua handleActionBegin) ---------- def run_step(self, d, dist=2): """CS3013 跑 dist 格(服务器按 dir 推算落点, 真实客户端跑=2格)。""" from combat_bot import DIRS, proto_map_name dx, dy = DIRS[d] x, y = self.me["x"], self.me["y"] lx, ly = self.me.get("last_x", x), self.me.get("last_y", y) self.send(3013, pack_point(x, y), 0, d, pack_point(lx, ly), proto_map_name(self.me).encode()) self.me["last_x"], self.me["last_y"] = x, y self.me["x"], self.me["y"], self.me["dir"] = x + dx * dist, y + dy * dist, d self._step_pending_ack = (dx * dist, dy * dist, time.time() + Q.STEP_MS / 1000.0) self._last_step_t = time.time() def walk_step(self, d): """CS3011 走 1 格。""" from combat_bot import DIRS, proto_map_name dx, dy = DIRS[d] x, y = self.me["x"], self.me["y"] lx, ly = self.me.get("last_x", x), self.me.get("last_y", y) self.send(3011, pack_point(x, y), 0, d, pack_point(lx, ly), proto_map_name(self.me).encode()) self.me["last_x"], self.me["last_y"] = x, y self.me["x"], self.me["y"], self.me["dir"] = x + dx, y + dy, d self._step_pending_ack = (dx, dy, time.time() + Q.STEP_MS / 1000.0) self._last_step_t = time.time() 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 = RunBot(g, sid, rname) P.heartbeat_loop(g, sid, bot.lock) 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']} " f"uid={bot.me.get('uid')}") return g, bot def to_wait_map(bot): if bot.me.get("mapname") == Q.WAIT_MAP: return if 1 in bot.mission_bodies: body3031 = json.dumps(bot.mission_bodies[1], separators=(",", ":")).encode() else: import os 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.5) bot.send(1011, 0, 0, 0, bot.sid, json.dumps( {"Act": "@renwu_mov,1", "UserID": "999999999", "index": 999999999}, separators=(",", ":")).encode()) t0 = time.time() while time.time() - t0 < 15 and bot.alive: bot.pump(0.3) if bot.me.get("mapname") == Q.WAIT_MAP: break log(f"renwu_mov 后: {bot.me.get('mapname')} @{bot.me['x']},{bot.me['y']}") 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="萌新4143") 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: state = Q.P.enable_auto_heal(g, bot.sid, want_on=True) log(f"喝药保护: {state}") to_wait_map(bot) log("=" * 60) log("开始等待图走动实验: CS5013握手 + 跑帧, 全帧日志") log("=" * 60) # ---- 第1步: 触发任务寻路, 等 SC20102(服务器寻路目标) ---- bot.send(1011, 0, 0, 0, bot.sid, json.dumps( {"Act": "@renwu_mov,1", "UserID": "999999999", "index": 999999999}, separators=(",", ":")).encode()) t0 = time.time() while time.time() - t0 < 8 and bot.alive: bot.pump(0.1) tgt = getattr(bot, "_auto_find_target", None) if tgt and tgt.get("x") is not None: break tgt = getattr(bot, "_auto_find_target", None) if tgt and tgt.get("x") is not None: log(f"SC20102 已到: 目标=({tgt.get('x')},{tgt.get('y')}) " f"mapID={tgt.get('mapID')} T={tgt.get('T')}") # ---- 第2步: CS5013 START 注册寻路会话(Lua: LuaSendMsg(5013, 1, x, y)) ---- bot.auto_move_notify(1, tgt.get("x", 0), tgt.get("y", 0)) else: log("[!] SC20102 未到(8s) — 用 NPC 坐标兜底注册") bot.auto_move_notify(1, 10, 0) confirmed0 = getattr(bot, "confirmed_at", None) is not None t_end = time.time() + args.minutes * 60 last_step = 0.0 rep = time.time() d = 5 pulled_dirs = {} seq = 0 while time.time() < t_end and bot.alive: evs = bot.pump(0.05) bot.tick_step_ack() pulled = any(e[0] in ("pull_back", "pull_back_bogus_dropped", "pull_back_zero_ignored") for e in evs) moved = any(e[0] == "self_move" for e in evs) if pulled: pulled_dirs[bot.me.get("dir")] = pulled_dirs.get(bot.me.get("dir"), 0) + 1 # 服务器中途又推了新寻路目标 → 跟随更新 tgt2 = getattr(bot, "_auto_find_target", None) if tgt2 and tgt2.get("_seen") != seq: tgt2["_seen"] = seq log(f"[跟随] SC20102 更新 → ({tgt2.get('x')},{tgt2.get('y')})") now = time.time() if now - last_step >= Q.STEP_MS / 1000.0 + 0.05: if pulled: cands = sorted(range(8), key=lambda k: (pulled_dirs.get(k, 0), k)) d = cands[0] seq += 1 if seq <= 4 or seq % 3 == 0: bot.run_step(d) else: bot.walk_step(d) last_step = now if now - rep >= 15: rep = now log(f"[STAT] pos=({bot.me['x']},{bot.me['y']}) " f"map={bot.me.get('mapname')} " f"发帧={bot.sent_log} 被拒方向统计={pulled_dirs}") # ---- 收尾: CS5013 FINISH ---- bot.auto_move_notify(3) log("=" * 60) log(f"[结束] pos=({bot.me['x']},{bot.me['y']}) 发帧={bot.sent_log} " f"被拒方向={pulled_dirs} confirmed0={confirmed0} " f"confirmed_now={getattr(bot, 'confirmed_at', None) is not None}") fl = sorted(bot.frame_log.items(), key=lambda kv: -kv[1]) log(f"收帧Top15: {fl[:15]}") finally: g.close() if __name__ == "__main__": main()