lyizai 42a9dd6cf0 feat: 新增机器人上/下线按钮,识别「账号已在别处登录」与「等待验证码」
下线按钮
- 顶栏新增「下线 / 上线」切换:下线会断开连接、停止自动重连,并停掉定时消息与
  扩地任务;需要在游戏客户端登同一账号时必须先下线,否则会被服务端以
  「您已经连接到此代理服务器了」反复踢掉
- 接口 POST /api/bots/:id/offline 与 /online;上线时会清掉「已在别处登录」标记
  并把重连计数归零,否则重连会一直退避到最大间隔

识别两类此前「看起来像启动不了」的情况
- 「您已经连接到此代理服务器了」:服务端踢人时发的是聊天组件对象,
  原来 JSON.stringify 之后没法看,关键信息被淹没。新增 extractKickReason()
  提取纯文本,命中后给出「请先在客户端退出」的可操作提示,
  并直接把重连退避到最大间隔,不再频繁硬撞服务器
- 「请直接输入验证码进行验证」:账号绑定过之后服务器仍可能每次登录都要求验证码,
  而原来的正则只认「完成验证 / 身份验证」,漏掉了这句话,用户只看到机器人卡在大厅。
  现在补上匹配,并在控制台弹出醒目提示 + 把输入框 placeholder 换成「输入验证码后回车发送」

测试 343 -> 355,全部通过。
2026-09-28 14:21:14 +08:00

AutoStore Bot

Minecraft 自动扩地机器人 —— 由原 Fabric 客户端模组 AutoStore 重构而来: Node.js + mineflayer 实现,UI 从游戏内 GUI 改为网页控制台,并针对装有 Grim 反作弊的服务器做合规适配。

不需要开游戏客户端,一条命令启动,浏览器打开控制台即可开始自动扩地。 支持同时挂多个玩家(机器人)、开拓日志、游戏公屏完整日志与爬取服务器地图的大地图。

npm install
cp .env.example .env   # 填入服务器地址与账号密码(支持多个账号)
npm start              # 打开 http://127.0.0.1:3081

功能对照

原模组功能 新版实现 说明
远程拉取 markers.json 领土数据 ✅ 带时间戳缓存击穿 + no-cache 头,实测 24 个邦国 / 11.5 万区块 200ms 解析完成
四种扩地策略 ✅ 方形外扩 / 区域 S 形扫描 / 玩家周围 / 绕国一圈
区域扩地严格 S 形扫描 ✅ 起始边取离王城最近的边,由内而外蛇形推进
飞地(疏漏区块)检测与优先填补 ✅ 按 dynmap 奇偶填充规则解析多边形,正确识别国中孔洞
自动寻路前往目标区块 ✅ Baritone → mineflayer-pathfinder,高度偏好启发式等价移植
寻路失败重试 / 往边上走两步 / 连续超时回城 ✅ 状态机与阈值完全一致
运行时接壤校验、目标有效性校验 ✅ 每 tick 校验,避免在无效区块上白费时间
现实时间限制(默认 11:00-22:00) ✅ 改为可配置时段,并支持「到点自动开始」
监听字幕 / 聊天判定开垦结果 ✅ 兼容 title / subtitle / actionBar / Boss 条四条通道
自动进食 ✅
自动攻击敌对生物(豁免中立生物) ✅ 铁傀儡 / 僵尸猪灵永不攻击;末影人 / 蜘蛛默认不打(见下方差异说明)
耐久保护回城 ✅
自动游泳防溺水 ✅ 水中持续按住跳跃键上浮
自动用船过水 ⚠️ 默认关闭 mineflayer 无载具物理,Grim 风险高,详见「行为差异」
实时地图 GUI ✅ 网页版 Canvas 绘制,支持缩放 / 平移 / 图层开关
日志导出 ✅ 网页按钮导出到 data/
SimpCore 开放 API 第四数据源 ✅ 低频轮询 + 手动全服拉取
Grim 反作弊适配 ✅ 新增 见下方专章
多机器人同时挂机 ✅ 新增 一个进程挂多个账号,各自独立连接 / 登录 / 扩地,配置可独立覆盖
开拓日志 ✅ 新增 逐条记录开拓玩家、邦国、区块、方块坐标、时间、判定来源,落盘可导出 CSV
游戏公屏完整日志 + 聊天框 ✅ 新增 全部公屏 / 系统消息按机器人分文件留档,控制台可直接发公屏消息
大地图 ✅ 新增 爬取服务器地图站点瓦片与领土标记,一张图上看全部机器人坐标
登录验证门禁 ✅ 新增 游戏内登录 + 控制台登录双重校验,通过后才允许操作机器人与发送消息

多机器人同时挂机

配置

在 .env 里一次配好所有账号(密码只从这里读,绝不落代码与配置文件):

# 写法一:用户名:密码,逗号分隔
MC_BOTS=开拓一号:password1,开拓二号:password2

# 写法二:JSON 数组(用户名/密码含逗号冒号时用这个)
MC_BOTS=[{"username":"bot1","password":"p1"},{"username":"bot2","password":"p2","label":"二号机"}]

# 不设置 MC_BOTS 时回退到单机器人
MC_USERNAME=AutoStoreBot
MC_PASSWORD=xxxxxx

启动后控制台左栏会列出全部机器人(连接状态 / 坐标 / 任务状态 / 已开拓数), 顶栏下拉框切换当前操作的机器人。

每个机器人独立的配置

控制台右侧配置表单勾选「仅对当前机器人生效」后保存,即为该机器人写入专属配置覆盖 (存 data/bots.json,不含密码);不勾选则写入全局默认配置。未覆盖的字段一律回退到全局默认。

典型用法:给每个账号指定不同的 目标邦国 与 扩地策略,让它们分工扩张不同方向。

设计取舍

  • 共享一份领土模型:markers.json 是全服数据(约 160KB / 11.5 万区块), 没必要每个机器人各拉一遍,因此数据服务是单例、共享给全部控制器。
  • 邦国归属按机器人解析:ExpansionController 在 start() 时把识别到的邦国固化到 controller.stateName 并写回本次任务配置,规划器不再读全局 selfStateName (否则会被最后启动的机器人覆盖,导致 A 机器人按 B 的邦国规划)。
  • 「会话新占领」区块是全局共享的:控制台地图的「己方领土」显示的是最近启动的那个机器人的邦国。

开拓日志

src/core/expansionLog.js 逐条记录每一次成功开拓:

字段 说明
t 时间戳(毫秒),界面按 YYYY-MM-DD HH:mm:ss 展示
bot / botId 开拓玩家(机器人游戏名 / id)
state 开拓归属的邦国
cx / cz 区块坐标
bx / bz 方块坐标(服务器回报的精确位置,通用成功文案时为空)
source 判定来源:chat(带坐标的聊天消息)/ chat-generic(无坐标的通用文案)
sessionCount 该机器人本轮任务的第几个
  • 落盘:data/expansion-log.jsonl(追加写入,超过 16MB 轮转为 .1)
  • 内存:保留最近 5000 条供实时查看,磁盘上的历史不受此限制
  • 控制台「开拓日志」页签支持按机器人筛选、清屏、导出带 BOM 的 CSV(Excel 打开中文不乱码)

游戏公屏完整日志与聊天框

src/core/chatLog.js 把每个机器人收到的全部消息留档,不只留开垦相关的:

  • 公屏聊天(其他玩家发言,含发言人)、系统公告、动作栏消息
  • 本机器人发出的聊天与指令(便于对照「指令发出后服务器回了什么」)
  • 落盘:data/chat/<机器人名>.jsonl(每机器人一份,超过 16MB 轮转)
  • 控制台「公屏聊天」页签可按当前机器人或全部机器人查看、按关键字过滤、导出为纯文本

控制台右下角的「控制台 / 聊天框」可以选择公屏聊天或游戏指令模式发送内容。


大地图

src/core/bigMapService.js 爬取服务器地图站点(本项目部署的是 squaremap)的瓦片图片与领土标记, 把所有机器人画在同一张图上。

坐标系(已对源站实测标定)

squaremap 前端用 L.CRS.Simple + tileSize: 512,瓦片路径为 tiles/<world>/<z>/<x>_<y>.png,并且:

  • 瓦片图片固定 512×512 像素;
  • 最大原生层级 zoom.max(本站为 3)下 1 方块 = 1 像素;
  • 因此层级 z 每张瓦片覆盖 512 × 2^(maxZoom − z) 个方块, 瓦片 (x, y) 覆盖方块区间 [x·B, (x+1)·B) × [y·B, (y+1)·B)。

实测标定:本站层级 0 每张瓦片覆盖 4096 方块,全世界(±20480)为 10×10 = 100 张。 用 27 个王城坐标交叉验证——按上式换算,王城所在瓦片的平均海洋像素占比为 28.7%, 而错误假设(256 / 1024 方块每瓦片、整体偏移半张瓦片)分别为 76.0% / 47.3% / 41.6%, 即只有正确公式能让王城稳定落在陆地上。

以机器人为中心 + 可调图幅

大地图的用途是看自己机器人周边,所以默认以机器人当前位置为中心(bigMap.followBot)。 界面上的「图幅」下拉框决定半径:

图幅 层级 0 需要的瓦片数
±512 格 1
±1024 格 1~4
±4096 格(默认) 9
±8192 格 25
全世界(±20480) 100

层级 0 每张瓦片覆盖 4096 格,所以「只看周边」比「拉全世界」省一到两个数量级。 点「爬取周边」只会爬当前图幅内的瓦片;「回到机器人」把视图拉回机器人所在位置。

缓存与爬取

层级 每瓦片方块数 全世界瓦片数 策略
0 4096 10×10 = 100 启动时自动预爬取(以机器人位置为中心,只爬当前图幅内)
1 2048 20×20 = 400 按需拉取 + 永久缓存
2 1024 40×40 = 1600 按需拉取 + 永久缓存
3 512 80×80 = 6400 按需拉取 + 永久缓存
  • 磁盘缓存:data/mapcache/<world>/<z>/<x>_<y>.png,命中即不再回源
  • 瓦片由后端代理(/api/bigmap/tile/:z/:x/:y),避免浏览器直连地图站点的跨域问题
  • 领土以 markers.json 的原始多边形绘制(预编译成 Path2D,按 even-odd 填充保留国中孔洞), 叠加本次会话新占领区块、王城与全部机器人坐标(含名字标签)
  • 操作:滚轮缩放、拖拽平移、图幅切换、「爬取周边」/「回到机器人」一键定位

SimpCore 开放 API

src/core/simpApiClient.js 对接 SimpCore 开放 API (邦国崛起服务器的只读查询服务)。它既是占领判定的第四数据源,也把服务端的公开信息 完整暴露到控制台。

文档里的接口基本都接了

分类 接口
健康 GET /health(免鉴权)
玩家 /info/players、/info/players/{key}、/info/players/{key}/union
邦国 /info/unions、/info/unions/{key}、/members、/territory、/tech、/contributions
全服 /info/territory、/info/buffs、/info/diplomacy、/info/bans、/info/logs、/info/announcements

在左栏「领土数据源」点 查看 API 数据 打开浏览弹层,分五个页签:概览 / 邦国 / 玩家 / 全服 / 实时 V2。 限流按「Key × 接口」每分钟计算,所以全部按需点击加载 + 会话内缓存 + 服务端 5 分钟缓存,绝不做轮询。

API 与 API-V2 是两套独立的密钥

文档明确说明两者互不通用的:

  • API:https://bgjq.simpfun.cn/api/ —— 邦国、领土、玩家、全服数据,配置 apiKey(或环境变量 SIMP_API_KEY)
  • API-V2:https://bgjq.simpfun.cn/api-v2/ —— 服务器实时状态(在线玩家 / 装备 / 游戏时间 / 末影龙), 需要在密钥中心另外申请,配置 apiV2Key(或环境变量 SIMP_API_V2_KEY)

V2 的接口级配额很小(末影龙只有 1 次/分钟),因此 V2 一律按需查询、不做自动轮询。

几个响应字段的坑(已按实测处理)

接口 坑 处理
捐献 只有 player_uuid,没有玩家名 先用邦国成员表补全,查不到再退到全服玩家表
捐献 amount 可为负 负数是邦国支出(如「恢复民心」消耗金锭/钻石),UI 注明
公告 message 是聊天组件 JSON 字符串 componentToText() 压平成纯文本
王城 服务端可能返回 null 显式判空,避免 Number(null) 算成 0 伪装成 (0,0)
封禁 / 日志 字段名是 banned_player_name / timestamp 等 按实测字段解析
V2 /players 返回对象 {online, max, players[]} 而不是数组,且没有坐标 按对象解析
V2 /dragon 字段是 dragonAlive,没有 health / phase 按实测字段解析
V2 /time 服务端已算好 time24 / time12 / phase 直接用,不再自己用 tick 换算
V2 /equipment 物品的 damage 是已损耗值 换算成 remainingDurability 再显示
V2 限流 429 带 retryAfterSeconds 提示里直接写出还要等多少秒

登录验证门禁

「登录验证后才允许操作」在两侧都做了强制:

游戏侧(src/bot/botManager.js)

  • canOperate() = 已连接 + 已进入世界 + 已完成登录验证
  • 未通过时 sendCommand / sendChat 直接拒绝并记警告,isReady() 也不放行扩地任务
  • 登录 / 注册流程自身用 { internal: true } 绕过门禁
  • 登录确认兜底:部分离线服登录成功后不回明确提示,server.loginConfirmTimeoutMs (默认 10 秒)内既没收到成功也没收到失败提示时按「已登录」放行,避免机器人永远卡死; 收到「密码错误 / 登录失败」则标记 loginFailed 且不放行。设为 0 可禁用兜底
  • 免登录指令白名单:有些服务器的登录流程本身就是指令(/login、/register, 简幻通体系还要先 /auth),若把门禁一刀切会形成「要先登录才能发登录指令」的死锁。 server.preLoginCommands(默认 login,l,register,reg,auth)里列出的前缀在未验证时也放行
  • 外站账号绑定(如简幻通):服务器要求 /auth <UID> <验证码> 或扫码绑定时, 密码无法自动完成,程序会输出明确的中文告警(每个连接只提示一次)而不是静默死循环。 若该服务器提供固定验证码,可在配置的「账号绑定 / 二次验证模板」(server.authCommand)里填写
  • 失败信息可操作:发送失败会区分「未连接」「登录被服务器拒绝」「未通过登录验证」三种情况, 而不是笼统报一句「发送失败」

控制台侧(src/web/server.js)

  • 配置 WEB_PASSWORD(或 WEB_TOKEN)后,页面弹出登录框,POST /api/login 校验通过才解锁
  • 所有 /api 与 WebSocket 握手都要求凭证(X-Auth-Token 头 / ?token= / Cookie)
  • /api/session 与 /api/login 本身免鉴权,否则用户无法登录
  • 控制台顶栏显示登录状态,可随时退出登录

部署到服务器

本项目已在 Debian 12 上以 pm2 常驻运行验证过,关键点:

1. Node.js 版本要求 ≥ 22

mineflayer 4.39 的 engines 声明为 >=22,用 Node 18 会在运行时出问题。 若服务器上已有别的服务依赖旧版 Node,不要升级系统 Node,装一份项目私有运行时即可:

mkdir -p /opt/autostore-bot/runtime && cd /opt/autostore-bot/runtime
curl -fsSL -o node.tar.xz https://nodejs.org/dist/v24.16.0/node-v24.16.0-linux-x64.tar.xz
tar -xJf node.tar.xz && rm node.tar.xz
ln -sfn /opt/autostore-bot/runtime/node-v24.16.0-linux-x64 /opt/autostore-bot/runtime/current

2. 环境变量

对外提供控制台时,WEB_HOST 必须是 0.0.0.0,并且必须设置 WEB_PASSWORD (否则任何人访问该端口都能操作你的游戏账号):

WEB_HOST=0.0.0.0
WEB_PORT=78
WEB_PASSWORD=<强密码>

3. pm2 常驻

export PATH=/opt/autostore-bot/runtime/current/bin:$PATH
npm install --omit=dev
pm2 start ecosystem.config.js     # interpreter 指向项目私有 Node
pm2 save
pm2 startup systemd -u root --hp /root   # 开机自启

ecosystem.config.js 里把 interpreter 显式指向私有运行时,避免 pm2 用系统 Node 启动。

4. 日志分两个文件

data/pm2-out.log 是 stdout(info 级),data/pm2-error.log 是 stderr (warn 与 error 级都在这里)。排查问题时两个都要看; 控制台页面上的运行日志是内存环形缓冲,包含全部级别,不受这个拆分影响。

5. 排障经验

现象 原因 处理
任务点了「开始扩地」却说「机器人尚未就绪」 寻路插件没加载,bot.pathfinder 恒为 undefined 已在 createBot 后自动 loadPlugin;若仍出现看日志里的插件加载错误
日志被「被服务器传送」刷屏 账号未完成服务器验证,服务端每 tick 强推位置 已按 30 秒窗口聚合;看是否有「账号绑定」告警
连上 120 秒后被踢,反复循环 服务器要求外站账号绑定(如简幻通 /auth) 人工完成一次绑定;绑定后同账号即可自动登录
mineflayer 加载报错 / 语法不支持 系统 Node < 22 用项目私有运行时启动
能连上、能收指令,但走不动 客户端移动包被服务端拒绝(回弹) 用控制台的「移动测试」按钮确认位移;详见下方专章

6. 「机器人走不动」的排查方法

控制台右下角有移动测试按钮(前 / 后 / 左 / 右 / 跳),它绕开寻路器直接按控制键 2 秒并回报真实位移。 这能一眼区分两种完全不同的故障:

  • 位移正常、但任务报「目标不可达」 → 是规划问题(距离太远、被地形挡住、挖/放方块被 Grim 约束禁用)
  • 位移 ≈ 0 → 客户端移动被服务端拒绝,属于协议 / 反作弊交互问题,改规划参数没有意义

最关键的一条:mineflayer 不发 player_input

这是本项目在真实服务器上「怎么按方向键都不动」的根因,已在 GrimCompat 里自动补发:

1.21.2+ 服务端的移动预测依赖 player_input 包,原版客户端每 tick 都发; 而 mineflayer 只在骑乘 / 跳下 / 潜行时发,地面移动的 forward / jump / sprint 完全不发。 带反作弊的服务器因此判定「没有任何输入却发生了位移」,每 tick 把玩家拉回原位置。

实测对比(同一账号、同一位置、同一时段):

入站强制位置同步 按方向键 2 秒的位移
不补 player_input ~17 次/秒 0.00 格
补 player_input + tick_end 0 次 0.53 ~ 1.40 格

同时补发的还有 tick_end(原版 1.21.2+ 客户端每 tick 末尾发送,反作弊用它判定 tick 边界)。 两者可在配置里关掉(grim.sendPlayerInput / grim.sendTickEnd),但线上服务器不要关。

长距离寻路会被自动拆段

mineflayer-pathfinder 只能规划已加载区块范围(实测本服约 8 区块 ≈ 130 格), 而扩地目标常在几十个区块之外,A* 会瞬间返回 noPath。 寻路服务会把旅程拆成若干段:走到已加载范围边缘附近 → 新区块加载 → 再规划下一段。 段长由 bot.blockAt 对未加载区块返回 null 的特性二分探测得出,不依赖世界内部结构。 相关配置:pathing.maxSegmentBlocks、pathing.segmentMarginBlocks。

配套的诊断数据

在 GET /api/bots/:id/status:

字段 含义
packetStats.inboundPosition 服务端下发的强制位置同步次数(持续很高 = 被回弹)
packetStats.outboundPosition 客户端上报的位置包次数
packetStats.playerInput / tickEnd 每 tick 补发的协议包次数(应约等于 20/秒)
packetStats.onGroundFixed 出站包中 onGround 被世界数据纠正的次数
onGround 客户端当前认为自己在不在空中
gameMode / dimension 游戏模式与维度(adventure / spectator 会限制移动)
world.blockBelow / world.surroundings.exits 脚下方块与各方向「脚部 / 头部 / 地面」方块名 + 可通行判定
grim.protocolPackets 协议补包是否生效及发送计数

判读方式:

  • 入站高、出站为 0 → 客户端根本没上报位置
  • 出入都高但位移为 0 → 客户端在报,服务端不认(回弹)
  • onGround 恒为 false 而 world.blockBelowSolid 为 true → 开启 grim.reportOnGroundFromWorld
  • 寻路日志里「已探索节点数」极小(如几十个)就 noPath → 不是距离问题,是移动生成器 从起点就找不到分支:检查 world.surroundings.exits 的 walkable,或把机器人挪到开阔地

Grim 反作弊适配

这是本次重构最核心的工程问题。mineflayer 是第三方协议实现,它的物理引擎(prismarine-physics) 与行为习惯和原版客户端存在偏差,直接跑在 Grim 服务器上会被 Prediction / Reach / NoSlow / MultiActions 等检查标记。本项目用一层 GrimCompat 合规层把所有对外行为收口:

1. 旋转绝不超前(防 Prediction)

mineflayer 的 bot.look() 会立刻把 bot.entity.yaw 改到目标值,而朝向数据包是按 physics.yawSpeed(约 8.6°/tick)逐 tick 补发的。这会造成:

本地已经朝新方向走了  ← 但服务端还以为你朝旧方向

Grim 的 Prediction 检查正是逐 tick 复算玩家位置并与上报位置比对,这种分歧必然被标记。

做法:接管 bot.look / bot.lookAt,只记录目标朝向,在每个 physicsTick 里把 bot.entity.yaw/pitch 按 ≤ 8.6° 的步长推进。这样「本地朝向」永远等于「服务端已收到的朝向」, 最多差 1 tick。awaitRotationSettled() 可等待旋转真正同步完成。

2. 攻击三重校验(防 Reach / LineOfSight / Post)

attackEntity() 出手前依次确认:

  • 朝向已同步 —— 服务端认为你正看着目标(防 Post 检查)
  • 眼睛到目标碰撞箱最近点 ≤ 3.0 格 —— 与生存模式实体交互距离一致
  • 视线可达 —— 射线检测中间没有实心方块遮挡
  • 攻击冷却已好 —— 按手持武器攻速计算(剑 625ms / 斧 1000-1250ms / 空手 250ms)
  • 不在骑乘 / 进食 / 使用物品状态

3. 动作互斥(防 MultiActions)

原版客户端不可能出现的组合一律拦下,并计入「已拦下动作」计数供控制台观察:

  • 疾跑 + 潜行
  • 疾跑 + 使用物品(进食 / 拉弓 / 举盾)
  • 疾跑 + 跳跃(默认放行 —— 这是原版合法动作,Grim 预测支持;实测被标记时可开启 grim.blockSprintJump 退化为普通跳跃)

4. 物理参数只读校验(防「改物理常数绕过预测」)

audit() 会核对 bot.physics 的 gravity / playerSpeed / sprintSpeed / stepHeight / airborneInertia / waterInertia 是否仍为原版值,并检查 physicsEnabled、游戏模式、鞘翅飞行状态。 任何一项异常都会在控制台的「Grim 合规」面板里报警。本项目从不修改物理常量,也从不直接写坐标。

5. 寻路约束

约束 默认 原因
canDig 关闭 Grim 的 FastBreak 检查极严
放置方块(scafoldingBlocks) 关闭 Grim 的 place 类检查极严
allowParkour 关闭 mineflayer 起跳时机与原版有偏差
allow1by1towers 关闭 搭塔过程几乎必被标记
maxDropDown 3 格 避免长距离坠落
liquidCost 200(avoid 模式) mineflayer 液体物理与原版差异最大,优先绕开水域
allowFreeMotion 关闭 禁止自由运动

与原模组的行为差异(重要)

重构过程中有几处有意为之的取舍,请务必了解:

1. 自动用船默认关闭

mineflayer 使用的 prismarine-physics 没有实现载具物理。上船后 mineflayer 会关闭本地物理预测 (bot.on('mount') → shouldUsePhysics = false),机器人位置完全由服务端下发的船实体位置决定, 只能靠 bot.moveVehicle() 驱动船前进。这意味着无法本地预测船的轨迹,Grim 的 Vehicle 类检查 逐 tick 复算载具运动时极易分歧。

因此 waterMode 默认为 avoid(绕开水域),autoBoat 默认为 false。 用船逻辑已完整实现(扫描水域 → 装备船 → 放船 → 登船 → 航行 → 下船 → 打掉回收), 仅在你实测确认该服务器对船宽容时才建议开启(waterMode: 'boat' + autoBoat: true)。

2. 末影人与蜘蛛默认不打

原模组按原版 AI 条件判断(末影人仅被激怒时、蜘蛛仅黑暗中才反击)。mineflayer 拿不到可靠的 「是否被激怒」与「环境亮度」信息,因此默认一律不打(宁可漏打也不误打)。 需要时可用 combat.attackEnderman / combat.attackSpiders 放开。

3. 新增高危生物跳过

监守者 / 末影龙 / 凋灵默认跳过(combat.avoidDangerousMobs)—— 原模组会攻击它们, 但那基本等于自杀。

4. 开垦时段可配置

原模组硬编码 11:00-22:00,现改为 enforceClaimTime + claimStartHour / claimEndHour。

5. 模糊匹配算法修正

原 Java 版的 containsFuzzy 只取固定长度前缀的末行距离,导致 containsFuzzy('开垦中 50%', '开垦中', 0) 返回 false(连完全相同的子串都匹配不上)。本版按注释所述的语义修正为 「存在编辑距离 ≤ maxEdits 的子串」,并补了回归测试。


安装与运行

依赖

  • Node.js 18+(开发与验证使用 Node 24)
  • 无需 Java、无需游戏客户端

步骤

npm install
cp .env.example .env
# 编辑 .env 填入服务器地址、账号、密码
npm start

启动后控制台会打印访问地址(默认 http://127.0.0.1:3081),浏览器打开即可。

环境变量

所有敏感信息只放在 .env(已被 .gitignore 忽略,绝不要提交):

变量 说明
MC_HOST / MC_PORT 服务器地址与端口
MC_VERSION 协议版本,默认 1.21.4
MC_BOTS 多机器人账号列表:用户名:密码 逗号分隔,或 JSON 数组写法
MC_USERNAME / MC_PASSWORD 单机器人兜底(未设置 MC_BOTS 时生效)
MC_AUTH offline(离线服)或 microsoft(正版)
WEB_HOST / WEB_PORT 控制台监听地址与端口
WEB_TOKEN 控制台令牌;非本机访问必须设置(页面静态资源不校验,/api 与 WebSocket 握手校验)
WEB_PASSWORD 控制台登录密码:配置后页面弹出登录框,验证通过才允许操作机器人的控制台 / 聊天框
DYNMAP_BASE / DYNMAP_WORLD 地图站点根地址与世界目录名(领土数据 + 大地图瓦片)
SIMP_API_KEY SimpCore 开放 API Key(可选)

网页控制台

打开控制台后:

  • 顶栏:机器人下拉选择、连接 / 任务徽标、控制台登录状态、开始扩地 / 停止 / 重连
  • 左栏:机器人列表、连接、任务、定时消息、领土数据源、Grim 合规
  • 中栏:
    • 地图页签 —— 大地图(瓦片 + 领土多边形 + 全部机器人坐标)与 领土地图(区块级)
    • 日志页签 —— 运行日志 / 公屏聊天 / 开拓日志
  • 右栏:控制台(统一输入框 + 快捷发送 + 移动测试)+ 8 个标签页的配置表单

控制台输入框:聊天与指令是同一个入口

不需要切换「聊天 / 指令」模式 —— 以 / 开头就按游戏指令发送,否则按公屏消息发送。 服务端 /api/bots/:id/chat 就是这么分流的,返回里带 kind 字段说明实际走的是哪条路。

另外提供:

  • 快捷发送:/u return、/u claimmode、/u restore confirm、/spawn、/list 一键发出
  • 移动测试:前 / 后 / 左 / 右 / 跳,绕开寻路器直接按控制键 2 秒并回报真实位移

响应式与移动端

宽度 布局
≥1200px 三栏(状态 / 地图日志 / 配置)
≤1200px 两栏,配置栏落到下方并自动分列
≤820px 单栏 + 底部导航(状态 / 地图 / 日志 / 配置一键定位)

移动端额外处理:触控目标放大到 ≥36px、地图与日志高度按视口自适应、 避让底部安全区(env(safe-area-inset-bottom))、横屏时压矮地图高度、 尊重系统「减少动效」偏好。

定时重复发送消息

挂机时常需要周期性发同一条指令(例如每 300 秒 /u restore confirm 维持疆土民心)。 左栏「定时消息」卡片里点「+ 新增」,填内容与间隔即可;以 / 开头按游戏指令发送,否则按公屏消息发送。

实现上刻意不用朴素的 setInterval:

  • 只在「已连接 + 已通过登录验证」时才发送;
  • 未就绪的周期直接顺延,不补发 —— 否则重连后会把漏掉的几条一次性连发出去;
  • 与指令节流队列共用,不会绕过 server.commandCooldownMs 变成刷屏;
  • 间隔下限 5 秒、单个机器人最多 20 条,防止误配置把账号玩进黑名单。


REST / WebSocket 接口

方法 路径 说明
GET /api/session 控制台登录状态(免鉴权,用于决定是否弹登录框)
POST /api/login 控制台登录(免鉴权)
GET /api/status 机器人池 / 任务 / 数据源 / 日志 / 大地图完整状态
GET /api/bots 全部机器人状态列表
GET /api/bots/:id/status 单个机器人状态
GET /api/bots/:id/settings 单个机器人的设置与生效配置
POST /api/bots/:id/settings 保存备注名 / 启用 / 自动开始 / 配置覆盖
GET /api/bots/:id/schedules 该机器人的定时消息列表(含距下次发送的秒数)
POST /api/bots/:id/schedules 整体替换该机器人的定时消息列表
POST /api/bots/:id/task/start 启动该机器人的扩地(body 为配置覆盖)
POST /api/bots/:id/task/stop 停止该机器人的扩地
POST /api/bots/:id/reconnect 重连该机器人
POST /api/bots/:id/move 手动移动测试(direction / seconds),返回真实位移
POST /api/bots 控制台新增玩家(username / password / label)
DELETE /api/bots/:id 移除控制台新增的玩家(.env 里的会返回 400)
POST /api/bots/:id/chat 发送内容:以 / 开头按指令发送,否则按公屏消息发送(要求已登录验证)
POST /api/bots/:id/command 同上,但强制按指令发送(等价于给内容加 / 前缀)
GET /api/bots/:id/chat 该机器人的公屏日志(limit / since / kind / keyword)
POST /api/bots/:id/chat/export 导出该机器人的公屏日志到 data/
GET /api/expansion-log 开拓日志(limit / botId / state / since)+ 统计
POST /api/expansion-log/export 导出开拓日志 CSV 到 data/
POST /api/expansion-log/clear 清空内存中的开拓日志(磁盘保留)
GET /api/bigmap 大地图元信息(坐标系、图幅、瓦片范围、缓存进度、机器人坐标、领土标记);支持 rangeBlocks / bot 参数
GET /api/bigmap/markers 大地图领土标记(多边形 + 王城)
GET /api/bigmap/tile/:z/:x/:y 瓦片代理(缓存优先,避免跨域)
POST /api/bigmap/crawl 爬取瓦片;默认爬机器人周边,支持 centerX / centerZ / rangeBlocks
POST /api/bigmap/cache/clear 清空瓦片缓存
GET /api/config 全局配置
POST /api/config 保存并立即应用全局配置
GET /api/map?full=1&bot=<id> 领土地图数据(full=1 附带全部邦国区块,bot 指定视角机器人)
GET /api/logs 运行日志
POST /api/logs/export 导出运行日志到 data/
POST /api/map/refresh 手动刷新地图数据
POST /api/api/fetch-all 手动拉取全服领土(重接口)
POST /api/task/start、/api/task/stop、/api/command、/api/bot/reconnect 默认机器人的等价别名(兼容单机器人用法)
WS /ws 实时推送:status(1s)、log、chat、expansion

配置项

配置保存在 data/autostore.json(全局默认),可通过控制台表单修改;勾选「仅对当前机器人生效」时 写入 data/bots.json 作为该机器人的配置覆盖。完整字段见 src/config/expansionConfig.js,分组如下:

分组 内容
策略 扩地策略、目标邦国、目标数量、扩圈上限、区域矩形
超时 寻路 / 等待超时、重试次数、回城阈值、走两步轮数、开垦时段
寻路 疾跑 / 游泳 / 跳跃 / 挖方块 / 放方块、高度偏好、跑酷、搭塔、下落高度、液体代价
生存 自动进食、自动攻击、耐久保护、战斗筛选、水域策略、自动用船
数据 地图刷新间隔、SimpCore API
大地图 开关、世界名、预爬取层级、最大层级、图幅半径、是否跟随机器人、启动预爬取、下载并发数
Grim 合规层全部阈值 + 协议补包开关(见上)
服务器 开垦模式指令、登录 / 注册模板、登录确认超时、指令节流、重连

开发

npm test        # 运行测试(Node 内置 test runner,无需额外依赖)
npm run check   # 语法自检(require 全部源码,捕获语法与加载错误)
npm run dev     # 文件变更自动重启

测试覆盖

242 个测试用例,覆盖:

  • 区块坐标编解码(含负数与边界)
  • 领土模型:飞地识别、会话占领、按邦国名取飞地与己方区块、API 数据合并、多边形保留
  • 规划器:四种策略、S 形顺序、接壤只算己方领土(历史 bug 回归)、他国区块切断连通、 按 config.stateName 解析己方邦国
  • markers.json 解析:奇偶填充孔洞、王城换算、颜色解析、多边形扁平化
  • 字幕 / 聊天解析:进度、带坐标成功、通用成功、模糊匹配
  • Grim 合规层:旋转限速、动作互斥、攻击冷却表、距离 / 视线校验、物理自检
  • 战斗筛选:中立生物豁免、条件敌对、猪灵金甲判定、耐久保护
  • 寻路服务:Grim 约束是否真正施加到 Movements、高度偏好目标
  • 控制器状态机:完整扩地流程、超时重试、走两步、回城、耐久保护停止、完成、 多机器人邦国归属隔离、开拓日志写入与去重
  • 多机器人:凭据解析(JSON / 逗号分隔 / 兜底)、机器人池组装、配置覆盖与持久化、 密码不落配置文件、未登录时拒绝发送、全局配置下发保留各自覆盖
  • 机器人登录门禁:就绪判定、登录成功 / 失败 / 已登录提示、注册与登录自动补发、 确认兜底超时、公屏消息类型映射
  • 开拓日志:字段记录、筛选、统计、JSONL 落盘与重启恢复、损坏行容错、环形缓冲、CSV 导出
  • 公屏日志:按机器人隔离、类型 / 关键字过滤、分文件落盘、文件名安全化、文本导出
  • 大地图:坐标系换算(含对源站实测标定的王城用例)、以机器人为中心的图幅换算、瓦片范围、缓存命中、批量爬取、缓存清理
  • 定时消息:内容归一化与间隔钳制、到点发送与按周期重复、未就绪时顺延不补发、多任务独立计时、整体替换重置计时
  • Web 控制台:地图负载、单 / 多机器人接口、聊天与开拓日志接口、定时任务接口、 图幅与中心计算、瓦片代理、令牌与登录鉴权、WebSocket 握手鉴权

目录结构

src/
├── index.js                     入口:装配模块、启动 Web 控制台、驱动多机器人 tick
├── config/
│   ├── expansionConfig.js       配置默认值 / 校验 / 归一化
│   ├── configStore.js           全局配置 JSON 持久化
│   ├── botsConfig.js            从 .env 解析多机器人凭据(密码只在这里读)
│   └── botStore.js              机器人设置持久化(不含密码)
├── core/
│   ├── chunkPos.js              区块坐标编解码(整数键,零临时对象)
│   ├── territoryModel.js        领土模型:区块集合、飞地、会话占领、API 合并、多边形
│   ├── territoryDataService.js  markers.json 拉取与解析
│   ├── simpApiClient.js         SimpCore 开放 API 客户端
│   ├── expansionPlanner.js      四种扩地策略
│   ├── subtitleChatMonitor.js   字幕 / 聊天解析与模糊匹配
│   ├── expansionLog.js          ★ 开拓日志(玩家 / 邦国 / 区块 / 时间 / 来源)
│   ├── chatLog.js               ★ 游戏公屏完整日志(按机器人分文件)
│   └── bigMapService.js         ★ 大地图瓦片爬取与缓存
├── bot/
│   ├── botPool.js               ★ 机器人池:同时挂多个玩家
│   ├── botManager.js            连接、登录验证、公屏留档、指令 / 聊天发送
│   ├── scheduledMessenger.js    ★ 定时重复发送消息 / 指令
│   ├── grimCompat.js            ★ Grim 反作弊合规层 + 协议补包(player_input / tick_end)
│   ├── pathingService.js        寻路服务(替代 Baritone)
│   ├── autoCombat.js            战斗筛选与耐久检查
│   ├── autoEat.js               自动进食
│   ├── autoBoat.js              自动用船(默认关闭)
│   └── durabilityGuard.js       耐久保护
├── controller/
│   └── expansionController.js   扩地任务状态机(按机器人实例化)
├── web/
│   ├── server.js                Express + WebSocket + 登录鉴权
│   └── public/                  控制台前端(原生 HTML/CSS/JS,无构建步骤)
└── util/
    └── logger.js                环形日志缓冲

data/                            (已 gitignore,运行时生成)
├── autostore.json               全局配置
├── bots.json                    机器人设置(启用 / 备注 / 自动开始 / 配置覆盖,不含密码)
├── expansion-log.jsonl          开拓日志
├── chat/<机器人>.jsonl          游戏公屏日志
└── mapcache/<world>/<z>/        大地图瓦片缓存

许可证

MIT

S
描述
无详细信息
自述文档
2.1 MiB
语言
JavaScript 96.1%
CSS 2.1%
HTML 1.8%