连接与版本
bedboo 如何连接 Bedrock 服务器、支持哪些版本,以及加入、出生和维度切换如何对应到 mineflayer 的事件。观察环境为 BDS 1.26.51.1(protocol 2193)与 bedrock-protocol 3.60.1。
版本
- 影响:
bot.version、bot.majorVersion、bot.protocolVersion,导出项testedVersions、latestSupportedVersion、oldestSupportedVersion、supportFeature(feature, version)。 - Java: Java 版本号('1.21.4',protocol 769),同时支持多个版本。
- Bedrock: Bedrock 版本号;bedboo 只支持最新的一个。
- bedboo:
bot.version = '1.26.51'、majorVersion = '1.26'、protocolVersion = 2193;testedVersions = ['1.26.51']。createBot({ version })接受'1.26.51'或false(自动),其他值会像 mineflayer 一样抛出异常。模块级的supportFeature(feature, version)在版本为 1.26.51、'bedrock_1.26.51' 或未指定时,直接查 Bedrock 特性表,无需构建 registry;其他版本则与上游一样查 minecraft-data。
协议客户端与 connect
- 影响:
bot._client、connect事件,createBot选项auth、host、port、profilesFolder、onMsaCode。 - Java: minecraft-protocol 客户端;TCP socket 连上时触发
connect。 - Bedrock: bedrock-protocol 的
Client(RakNet)。没有connect事件:RakNet 连接和 network settings 完成、 发送 login 包时触发loggingIn。 - bedboo:
bot._client就是 bedrock-protocol 客户端(on、queue、write);connect在loggingIn时触发一次。 默认值:host 127.0.0.1、port 19132、RakNet(raknet-native)、skipPing、conLog: null。默认auth: 'offline';auth: 'microsoft'是通过 prismarine-auth 登录 Xbox Live(透传profilesFolder、onMsaCode、authTitle、flow);auth: 'mojang'会抛出异常(Bedrock 没有 Mojang 账号)。显式传入的 bedrock-protocoloffline选项优先。参见 createBot 选项。
传输层:默认 RakNet,可选 NetherNet
-
影响:
createBot选项transport、nethernet;bot._client.connection。 -
Java: 只有一种传输层(TCP,minecraft-protocol)。
-
Bedrock: BDS 1.26.51 提供两种:
- NetherNet(BDS 默认):WebRTC data channel,信令可走
server-portTCP 上的 HTTP (POST /v1/join/<network id>携带 SDP offer,返回签名的 SDP answer)、UDP 7551 上的局域网发现,或 Xbox 服务。 - RakNet(
transport=raknet):迄今所有 Bedrock 版本使用的传输层。选用它时 BDS 会打印TRANSPORT TYPE ERROR横幅,Mojang 已宣布将移除它,可能在 BDS 26.60。
NetherNet 服务器不会打开 RakNet 端口,反之亦然。
- NetherNet(BDS 默认):WebRTC data channel,信令可走
-
bedboo: 默认
transport: 'raknet'(raknet-native)。transport: 'nethernet'通过host:port上的 HTTP 信令加入, 除非nethernet.signalling指定'lan'/'services'。bedrock-protocol#831 和 node-nethernet#32(尚未发布)以patches/bedrock-protocol@3.61.0.patch与patches/nethernet@1.1.2.patch的形式回移(另加一条packageExtensions,为 nethernet 新增的 jsonwebtoken 依赖)。服务器用身份密钥对 answer 签名;纯 HTTP 无法担保该密钥, 因此默认接受,除非用nethernet.serverKey固定它('sha256:<hex of the DER SPKI>',不匹配时以Untrusted Nethernet server key …拒绝),或由nethernet.onServerKey(fingerprint, url)决定。这与 RakNet 的信任程度 相同,RakNet 的握手密钥同样没有固定。传输层之上的一切(数据包、插件、事件)完全相同。
NetherNet:服务器端口的 TCP 也必须空闲
- 影响: 以
transport=nethernet运行的服务器。 - Java: 不适用。
- Bedrock: 使用
transport=nethernet时,BDS 在 TCPserver-port上打开 HTTP 信令监听,并输出Accepting clients on [::]:<port>。如果该 TCP 端口仍被占用(例如同一端口上一台服务器遗留的TIME_WAIT连接, 约持续一分钟),BDS 会在没有监听的情况下启动,且不报任何错误;之后所有客户端都会得到ECONNREFUSED。 - bedboo: 库中无需处理。若 NetherNet 加入因
ECONNREFUSED失败,请检查服务器日志中是否有Accepting clients这一行,以及 BDS 启动时该 TCP 端口是否空闲。
踢出原因
- 影响:
kicked事件。 - Java:
reason是 JSON 聊天组件(字符串,1.20.3 起也可以是对象);loggedIn区分游戏状态与登录状态。 - Bedrock:
disconnect { reason: <code>, hide_disconnect_reason, message };message 是自由文本或翻译键 (disconnectionScreen.serverFull),经常为空(此时只有 code 说明原因,例如server_id_conflict、missing_client)。 - bedboo:
reason= message;message 为空或被隐藏时取 reason code 的名字;loggedIn= 已收到 start_game。 被踢出后 bedrock-protocol 会关闭连接,随后触发end。
心跳
- 影响: 连接心跳(无 API)。
- Java:
ping→pong,keep-alive 包。 - Bedrock:
network_stack_latency { timestamp, needs_response }。 - bedboo: bedrock-protocol 用相同的 timestamp 回应
network_stack_latency;game 插件无需处理。
资源包
- 影响:
resourcePack事件、bot.acceptResourcePack()、bot.denyResourcePack()。 - Java: 每个
resource_pack_send/add_resource_pack一个包(url + hash 或 uuid);客户端回应 ACCEPTED + SUCCESSFULLY_LOADED 或 DECLINED;拒绝意味着什么由服务器决定。 - Bedrock: 所有资源包在登录期间(start_game 之前)通过
resource_packs_info一次发来,带 uuid、版本和可选的 CDN url, 没有 hash。客户端以resource_pack_client_response回应,BDS 检查其中的状态名:- 'completed':加入;
- 'have_all_packs':不下载直接加入(BDS 发送
resource_pack_stack,再回应 'completed'); - 'refused' / 'cancel':BDS 断开连接(
missing_client),可选包也是如此。
- bedboo: 每个包触发一次
resourcePack(url, hash, uuid),url = CDN url 或 '',hash = Bedrock 包 id<uuid>_<version>。没有监听器时立即接受所有包;有监听器时等待acceptResourcePack()('completed')或denyResourcePack()(可选包回 'have_all_packs' = 不带这些包加入;必需包回 'refused' = 被 BDS 踢出,kicked('missing_client', false))。资源包从不下载。此功能依赖 bedrock-protocol 补丁 (client.holdResourcePacks/client.respondToResourcePacks)。
客户端设置
- 影响:
bot.settings.*、bot.setSettings()、bot.game.serverViewDistance。 - Java: 一个
settings包(语言、视距、聊天模式、颜色、皮肤部件、主手、过滤、服务器列表、粒子)。 - Bedrock: 只协商区块半径(
request_chunk_radius→chunk_radius_update);皮肤部件、主手等属于登录时的皮肤数据, 或者不可配置。 - bedboo:
bot.settings对象、默认值和校验错误与上游相同。只有viewDistance('far' 12、'normal' 10、'short' 8、 'tiny' 6 个区块,或一个数字)会被发送:加入时的请求由 bedrock-protocol 发出(它读取保持同步的client.viewDistance), 之后的setSettings调用发送request_chunk_radius { chunk_radius, max_radius }。bot.game.serverViewDistance是 服务器批准的半径(chunk_radius_update)。其他设置只保存在本地。
服务器品牌
- 影响:
bot.game.serverBrand。 - Java:
minecraft:brand插件频道('vanilla'、'Paper' 等)。 - Bedrock: 没有品牌频道;start_game 有一个可选的
server_identifier(BDS 上为空)。 - bedboo: 有
server_identifier时取其值,否则为'vanilla'(BDS 即原版 Bedrock 服务器)。
客户端拿不到玩家上限
- 影响:
bot.game.maxPlayers。 - Java: login 包携带服务器的最大玩家数。
- Bedrock: start_game 不含玩家上限;它只在 unconnected pong 中,而 BDS 在
enable-lan-visibility=false时不发送 pong (且机器人连接时不 ping)。 - bedboo:
bot.game.maxPlayers保持undefined。
世界类型
- 影响:
bot.game.levelType。 - Java:
'default'、'flat'、'largeBiomes'、'amplified'等。 - Bedrock:
start_game.generator:0 legacy、1 infinite、2 flat、3 nether、4 the end、5 void。 - bedboo: 1 →
'default'、2 →'flat'、0 →'legacy'、5 →'void'、3 / 4 →'nether'/'the_end'。
登录时的实体 id
- 影响:
bot.entity.id、bot.entity.uniqueId。 - Java: 代理可能发送第二个 login 包,带新的实体 id。
- Bedrock: 每个连接只发送一次 start_game;服务器转移是一个新连接。
- bedboo:
bot.entity.id = Number(start_game.runtime_entity_id),bot.entity.uniqueId = start_game.entity_id;同一连接上以新实体 id 重新登录的情况不会发生。
离线玩家保留存档数据
- 影响:
createBot({ auth: 'offline' }):玩家的物品栏、位置、出生点等在重连和服务器重启后是否保留。 - Java: 离线模式玩家的 UUID 是
nameUUIDFromBytes("OfflinePlayer:" + name):重新加入仍是同一玩家,服务器恢复其存档数据。 - Bedrock: BDS 以登录中的
SelfSignedId作为离线(xuid 0)玩家存档数据的键,而 bedrock-protocol 每次连接都会随机生成它。 在 BDS 1.26.51.1 上实测:给予物品、退出、重新加入(同一服务器,或从世界快照启动的服务器)→ 物品栏为空,位于世界出生点。 使用固定的SelfSignedId重新加入则能取回物品栏和位置。 - bedboo: 离线认证时
SelfSignedId默认取用户名对应的 Java 离线模式 UUID(offlineSelfSignedId,lib/client.js); 可用skinData.SelfSignedId覆盖;Xbox Live 登录不受影响。
出生
- 影响:
spawn事件。 - Java: 在登录后 / 重生后的第一次生命值更新时触发。
- Bedrock: 加入流程为 start_game(出生搜索高度,y 32769.62)→
respawnstate 0 ×2 →respawnstate 1(真实位置)→ 客户端respawnstate 2 →play_status player_spawn→ 客户端serverbound_loading_screen1 和 2(没有它们,BDS 不会模拟该玩家)。 - bedboo:
spawn由生命周期模块(lib/bedrock/lifecycle.js,唯一的触发者)在真实位置已知且加载界面已确认后触发, 顺序在login和game之后,与 mineflayer 一致。维度切换和重生之后会再次触发。
spawn 与出生搜索高度
- 影响: 经末地出口传送门之后、以及重生到另一维度之后的
spawn;默认(非超平坦)世界上的bot.spawnPoint。 - Java: 服务器先发送位置,再发送使 mineflayer 触发
spawn的生命值更新;出生点数据包携带真实的出生方块。 - Bedrock: 服务器尚未找到真实高度时,Bedrock 使用一个占位高度(出生搜索高度):
- 末地出口的
change_dimension携带 y 32767;BDS 立即回应维度确认,约 50 ms 后才在respawnstate 1 中发送真实位置。 - 重生到另一维度时,BDS 经常把正在重生的玩家修正到搜索高度(
correct_player_move_predictiony 32768.62,在真实的respawnstate 1 之前或之后;下界中 6 次死亡里见到 4 次),约 300 ms 后再修正到真实位置。 - 在服务器确定高度之前,
start_game.spawn_position的 y = 32767(有玩家在那里出生时,BDS 会搜索最高的安全方块)。 以该 y 为目标的寻路会让机器人一直向上搭柱子。
- 末地出口的
- bedboo:
- 不应用
change_dimension/respawn中的搜索高度(这里不触发move/forcedMove)。当机器人最后的服务器位置是 搜索高度时,spawn会等待真实位置(下一个低于它的forcedMove,先于spawn触发),最多 2 s;在此期间输入时钟保持 暂停(末地出口),或机器人保持在重生中状态。 bot.spawnPoint(lib/plugins/spawn_point.js):一旦出生点所在的主世界区块列加载完成(chunkColumnLoad/spawn), 32767 的高度就被替换为该列最高实心方块 + 1;x 和 z 保持不变,真实高度(/setworldspawn)不会被改动,机器人在其他维度时 不做解析。加入期间的解析(出生点区块列在spawn之前加载)不单独触发game:它属于 start_game 的出生点位置,由登录时的game涵盖(mineflayer 的顺序为 connect → login → game → spawn)。之后的解析会像上游出生点变化时一样触发game。 另见出生点。
- 不应用
维度
- 影响:
bot.game.dimension、bot.game.minY、bot.game.height、bot._getDimensionName()。 - Java: 维度类型和高度来自 registry codec;世界名为
minecraft:overworld、minecraft:the_nether、minecraft:the_end;respawn包切换维度。 - Bedrock: 没有 codec:维度 id 为 0 / 1 / 2(start_game 将其映射为 'overworld' / 'nether' / 'end'),高度固定
(主世界 -64..319、下界 0..127、末地 0..255)。维度切换为
change_dimension { dimension, position (FEET level), loading_screen_id };客户端以player_action dimension_change_ack确认;新维度 就绪后 BDS 回以它自己的dimension_change_ack(没有 play_status),随后客户端发送serverbound_loading_screen1 和 2。 - bedboo:
bot.game.dimension使用 mineflayer 的名字('overworld'、'the_nether'、'the_end'),minY / height 来自固定表,_getDimensionName()返回minecraft:<dimension>。收到change_dimension时重置世界(卸载所有区块列 →chunkColumnUnload, 按位置注册的blockUpdate:监听器以null, null调用后移除,与上游的 switchWorld 相同),触发game,并在服务器确认后由 生命周期再次触发spawn(mineflayer 在维度切换后也会通过 health 触发spawn)。维度流程的其余部分(respawn事件、 末地制作人员名单、传送门)见维度切换。