連接與版本
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事件、 末地製作人員名單、傳送門)見維度切換。