跳到正文
本頁目錄

BDS 筆記

在基岩版專用服務器 1.26.51.1 上運行機器人時值得了解的行為:服務器打印什麼、發送什麼、何時發送。bedboo 已經處理了所有這些情況; 這些筆記在你閱讀數據包日誌或編寫服務器控制台腳本時會有幫助。

啟動與端口

  • 就緒行: [… INFO] Server started.;當 transport=raknet 時,其後緊跟一個多行的 TRANSPORT TYPE ERROR 信息塊 ("Your current connection type is not set to NetherNet … Players will not be able to connect")。它是無害的:RakNet 客戶端可以正常連接。BDS 還會輸出 Signed in to signaling service successfully。一個全新的超平坦世界大約在進程啟動後 1 秒 就緒。
  • 端口被佔用時會打印 ERROR] Port [<port>] may be in use by another process. Free up port and re-run program …, 然後是 Exiting program 和一份崩潰轉儲;服務器會在一秒內退出。
  • NetherNet: BDS 在 TCP server-port 上打開信令監聽,並輸出 Accepting clients on [::]:<port>。如果該 TCP 端口 仍被佔用(例如上一個服務器留下的 TIME_WAIT 連接,約持續一分鐘),服務器會在沒有監聽的情況下運行且不輸出任何信息;客戶端隨後 會得到 ECONNREFUSED。
  • Ping: 在 enable-lan-visibility=false 時 BDS 不響應未連接 ping,因此 bedboo 默認 skipPing: true。

傳輸層與後端

  • 基於 raknet-native 的 RakNet 可以工作(加入 ≈0.3 秒,start_game ≈0.5 秒,spawn ≈1.0 秒)。raknet-native 1.2.3 只捆綁了 x64 預編譯包(linux-5-x64、darwin-21-x64、win32-10-x64),且禁用了源碼構建,因此 arm64 主機(Apple Silicon、 linux-arm64)沒有 RakNet 後端。工作區補丁讓綁定能在較新的內核(linux-6)上找到捆綁的預編譯包;該預編譯包需要 glibc ≥ 2.34。
  • raknetBackend: 'jsp-raknet' 會失敗:BDS 對 open-connection 請求的應答是 ID_INCOMPATIBLE_PROTOCOL_VERSION (服務器的 RakNet 協議為 11;jsp-raknet 使用 10)。
  • 在服務器端口上使用 HTTP 信令的 NetherNet 可以工作(加入 ≈1.7 秒,spawn ≈2.6 秒)。bedrock-protocol 和 node-nethernet 的 HTTP 信令通過工作區補丁回移。

加入流程

  • start_game.player_position 為 (0.5, 32769.62, 0.5)(出生點搜索高度),隨後是三個 respawn 數據包: 32769.62 處的 state 0、32768 處的 state 0,以及位於真實眼睛高度出生點的 state 1(超平坦世界上為 (0.5, -58.38, 0.5), 腳部 y = -60)。客戶端以 respawn { state: 2, runtime_entity_id } 回應 state 1。然後是區塊和 play_status player_spawn。
  • 加載界面閘門: 生成之後,BDS 不會模擬玩家(忽略 player_auth_input 中的位置和移動向量,也沒有 correct_player_move_prediction),直到客戶端發送 serverbound_loading_screen { type: 1 } 和 { type: 2 }。此後行走輸入 才會移動玩家,不可能的移動會被糾正。
  • start_game:rewind_history_size = 40、server_authoritative_block_breaking = true、 server_authoritative_inventory = true、block_network_ids_are_hashes = true;player_gamemode 為 fallback,因此 遊戲模式保存在 world_gamemode 中。默認 server.properties:server-authoritative-movement-strict=false、 player-position-acceptance-threshold=0.5。
  • player_list 以 records: [{ type: 'add', username, … }] 的形式到達,此時玩家尚未生成,也無法被選中。

區塊與保存

  • level_chunk 總是以子區塊請求模式到達(sub_chunk_count: 0 且帶有 highest_subchunk_count),從不內聯子區塊。 subchunk_request { dimension, origin, requests: [{ x: 0, y: -4..19, z: 0 }] } 返回一個 subchunk,其中每個請求對應一個條目 (帶負載的 success,或 success_all_air)。request_chunk_radius 10 之後,network_chunk_publisher_update 的半徑從 64 變為 160 格。
  • 附近沒有玩家時區塊不會加載:setblock / testforblock 會回應 Cannot place block outside of the world。
  • 修改過的區塊每 5–10 秒才寫入一次 LevelDB,而且 save hold 不會刷新它們,因此在線備份可能遺漏最近的改動。正常停止服務器 會保存所有內容。

控制台命令

  • say / tellraw 在控制台上不打印任何內容;命令錯誤是 ERROR 行(Unknown command: <cmd>. Please check that the command exists …、No targets matched selector、Syntax error: …)。
  • list 打印 There are n/max players online: 以及一行玩家名;querytarget 打印一個 JSON 塊(y 為眼睛高度); gamerule(不帶參數)在一行中打印所有規則;testforblock 打印 Successfully found the block at x,y,z. / The block at x,y,z is Air (expected: …)。
  • gamemode <m> <player> 發送 update_player_game_type { gamemode, player_unique_id }(而不是 set_player_game_type);玩家已處於該模式時不發送任何內容。遊戲模式命令對已死亡的玩家無效 (No targets matched selector)。
  • tp <p> 10 -60 10 會將整數 x/z 居中(Teleported p to 10.50, -60.00, 10.50),並在眼睛高度發送 move_player { mode: 'teleport', teleport: { cause: 'command' } }。
  • setblock 打印 Block placed 併發送 update_block;再次放置同一方塊會打印 The block couldn't be placed 且不發送任何內容。
  • summon 打印 Object successfully summoned 併發送 add_entity(腳部位置,已居中);kill @e[type=pig] 打印 Killed Pig, 並依次發送 entity_event death_animation、掉落物(add_item_entity),然後是 remove_entity。
  • 創造模式玩家無法被殺死(Players cannot be killed while they are in Creative mode.),即使使用 /damage … void 或掉入 虛空也不行。

值得了解的數據包

  • 客戶端發出的命令: command_request { command, origin: { type: 'player', uuid, request_id: '' }, … } 由 command_output 應答,其 origin 回顯的是 uuid(而不是 request_id),另外還有一個 event { event_type: 'command_executed' }。
  • 聊天: 客戶端的 text 聊天(type: 'chat'、category: 'authored')會以 source_name: '<name>§r' 回顯給所有人。
  • 物品欄: /give、/replaceitem(快捷欄 / 物品欄)和 /clear(即使沒有清除任何東西)會發送所有窗口的 inventory_content(物品欄 36、盔甲 5、ui 54、副手 1);針對盔甲的 replaceitem 發送 inventory_slot;/give 還會發送一個 inventory_transaction。玩家的物品帶有 stack_id。
  • 物品欄重發: inventory_transaction { transaction_type: 'inventory_mismatch' } 會讓 BDS 重新發送玩家的物品欄 (inventory_content × 4 + player_hotbar);被拒絕的物品堆疊請求或重新打開物品欄界面都不會觸發重發。
  • 死亡: 自身的 entity_event death_animation、death_info、text(death.attack.generic)、死亡位置處的 respawn state 0。重生:客戶端 respawn state 2 → 服務端 respawn state 0(y 32767)+ state 1(眼睛高度的出生點)→ 客戶端 player_action respawn → entity_event respawn + set_health 20。
  • 時間: 沒有 set_time 數據包,即使是 /time set 也沒有;時間通過 sync_world_clocks 傳遞。
  • 下落方塊: 方塊開始下落以及下落方塊落地都只以 update_block_synced 到達,從不以 update_block 到達 (下落方塊)。
  • 生物: 超平坦世界會持續生成被動生物:每分鐘數百個 move_entity_delta。