跳到正文
本頁目錄

世界與方塊

bedboo 如何讓世界模型(bot.blockAt、blockUpdate)與 Bedrock 的方塊層和方塊更新保持同步,以及世界狀態(時間、天氣、 遊戲規則)、告示牌、命令方塊、方塊動作和傳送門有哪些不同。觀察環境為 BDS 1.26.51.1(protocol 2193)。

含水方塊(方塊層 1)

  • 影響: bot.blockAt、blockUpdate、bot.world。
  • Java: waterlogged=true 方塊狀態。
  • Bedrock: 第二個方塊存儲層:層 0 是方塊,層 1 是液體(update_block.layer、update_subchunk_blocks.extra、雙層子區塊)。
  • bedboo: blockAt() 返回層 0 的方塊;當層 1 中有水時,它帶有 block.superimposed(層 1 的 Block)、 block.computedStates.waterlogged = true 和 block.isWaterlogged = true(prismarine-chunk 的 Bedrock 表示方式;bedboo 的 補丁只疊加非空氣的層 1 條目)。層 1 的更新與層 0 一樣觸發 blockUpdate。當一個方塊替換水或含水方塊時,BDS 只發送層 0: 此時液體層遵循 Bedrock 的規則(液體層)。

被替換方塊的液體層

  • 影響: bot.blockAt(…).isWaterlogged / .superimposed / .computedStates.waterlogged、blockUpdate、物理引擎中的水 (它讀取 isWaterlogged)。
  • Java: waterlogged 是方塊狀態:每次方塊更新都攜帶它。
  • Bedrock: 含水是方塊層 1,當一個方塊替換水源或含水方塊時,BDS 只發送層 0 的變化(update_block 層 0,或 extra 為空的 update_subchunk_blocks);液體層規則由客戶端自行應用。在線觀察(以重新加入後的結果為準):放入水源中的柵欄、梯子、臺階、 地毯或牆會含水;含水柵欄被臺階 / 地毯 / 牆替換後仍含水,被石頭 / 玻璃替換後變幹,被空氣或水替換後就是普通的水;流動的水 (非水源)和岩漿永遠不會進入層 1。Bedrock 的"可容納液體"方塊集合與 Java 的 waterlogged 方塊不同(地毯可以含水),且沒有 客戶端數據列出這個集合。
  • bedboo: 層 0 更新後的新方塊若為空氣或液體,則清除層 1;同一方塊變為另一狀態(活板門開關、海帶生長)則保留其液體層; 這兩種情況是確定的。其他任何方塊替換水源或含水方塊時,先做預測(完整方塊為幹,其他方塊保留 / 吸收水;一次 blockUpdate 同時包含兩層),再進行確認:機器人重新請求該子區塊(每個子區塊同一時間只有一個 subchunk_request,5 s 無應答則重發,最多 3 次),並對這些方塊應用服務器的層 1(預測錯誤的位置再觸發一次 blockUpdate;在此期間層 0 又發生變化的方塊交給它自己的更新 處理,請求發出期間發生變化的方塊會再次請求)。

下落的方塊(update_block_synced)

  • 影響: bot.blockAt、blockUpdate / blockUpdate:(x, y, z)、bot.world,以及所有讀取它們的代碼(物理、挖掘、 mineflayer-pathfinder)。
  • Java: 方塊開始下落時,block_update 把它變為空氣(下落中的方塊是一個實體),落地時再設置到落點。
  • Bedrock: 沙礫、沙子、混凝土粉末、鐵砧等開始下落和落地都只通過 update_block_synced 發送,從不使用 update_block: 起始位置以 transition_type: 'create' 變為空氣(隨後是 add_entity minecraft:falling_block),落點以 'destroy' 變為該方塊 (約 150 ms 後是下落方塊的 remove_entity)。懸空放置的方塊(/setblock、放置操作)先以 update_block 到達,約 100 ms 後 開始下落。entity_unique_id 是下落方塊的 unique id,按無符號 varint64 寫入(bedrock-protocol 按 zigzag64 讀取:-4294967288 會讀成 9223372034707292164)。忽略這些數據包的客戶端會讓已下落的方塊留在空中、看不到落地的方塊,結果服務器讓機器人在一個 客戶端顯示為空氣的方塊裡窒息。
  • bedboo: update_block_synced 與 update_block 同樣處理(層、runtime id → state id、液體層規則、方塊 實體),並觸發 blockUpdate 事件:pathfinder 會重新規劃,對已落走的方塊的挖掘會完成(與 Java 上方塊變為空氣時一致)。

燃燒中的熔爐等"點亮"方塊是獨立的方塊

  • 影響: 對熔爐、高爐、煙燻爐、紅石礦石、紅石燈、紅石火把、中繼器、比較器、陽光探測器使用 bot.findBlock / bot.findBlocks / bot.blockAt;bot.openFurnace。
  • Java: 一個方塊帶 lit / powered / inverted 狀態(furnace[lit=true]):按方塊 id 匹配可以找到它的所有狀態。
  • Bedrock: 兩個方塊,各有自己的 id:furnace / lit_furnace、blast_furnace / lit_blast_furnace、 smoker / lit_smoker、redstone_ore / lit_redstone_ore、deepslate_redstone_ore / lit_deepslate_redstone_ore、 redstone_lamp / lit_redstone_lamp、redstone_torch / unlit_redstone_torch、unpowered_repeater / powered_repeater、unpowered_comparator / powered_comparator、daylight_detector / daylight_detector_inverted。 正在燃燒的熔爐在熄滅前一直是 lit_furnace。
  • bedboo: registry 保留 Bedrock 的方塊(bot.findBlock({ matching: furnace.id }) 找不到燃燒中的熔爐)。 bot.bedrock.blockVariants(nameOrId) 返回該組的 id(blockVariants('furnace') → [furnace, lit_furnace];其他方塊 → [id]),可用於 matching(見 Bedrock 擴展)。openFurnace 接受點亮的方塊(窗口類型和總燒煉 時間取未點亮方塊的);方塊點亮或熄滅時,熔爐窗口保持打開。

門的上半部分取下半部分的形狀

  • 影響: bot.blockAt(<门的上半部分>).shapes、門口處的物理碰撞、mineflayer-pathfinder 穿門。
  • Java: 上下兩半都帶有 open、facing 和 hinge。
  • Bedrock: 開關狀態和朝向在下半部分,鉸鏈在上半部分:BDS 從不設置上半部分的 open_bit(在 1.26.51.1 上測得,也用 testforblock 核對過),原版客戶端根據兩者繪製兩半。因此按狀態取形狀會讓打開的門的上半部分仍是關閉的門板:機器人在門口撞頭 (服務器修正),mineflayer-pathfinder 找不到穿過自己打開的門返回的路(NoPath,或在 canDig 下把門挖掉)。
  • bedboo: bot.blockAt()(lib/plugins/blocks.js)讓門的每一半都使用組合狀態的形狀(下半部分的開關狀態和朝向,上半部分 的鉸鏈);stateId 和 getProperties() 保持服務器的值(上半部分的 open_bit 仍為 0:請讀取下半部分)。

世界時間

  • 影響: bot.time.*、time 事件。
  • Java: 每 20 tick 一次 update_time(世界年齡 + 一天中的時間;26.1:按維度的時鐘)。
  • Bedrock: 完全沒有 set_time 數據包:使用 sync_world_clocks 中的 minecraft:overworld 世界時鐘(initialize_registry 帶時間標記,/time set|add 時以及偶爾發送 sync_state);晝夜循環由 doDayLightCycle 遊戲規則決定;沒有世界年齡。
  • bedboo: bot.time.time = 世界時鐘(絕對的一天中時間,所有維度通用),派生字段與上游相同;doDaylightCycle 來自遊戲規則 (變化時觸發 time);bigAge / age = 服務器 tick(start_game.current_tick + 已過去的 tick)。兩次同步之間時鐘在本地 運行(在循環開啟且時鐘未暫停時),並且像 Java 的廣播一樣每 20 個物理 tick 觸發一次 time。bot.time.clocks 以名字為鍵保存 Bedrock 時鐘(overworld):上游的 { id, totalTicks, partialTick, rate } + name、paused、timeMarkers。

天氣等級

  • 影響: bot.rainState、bot.thunderState、weatherUpdate。
  • Java: game_state_change 開始 / 停止下雨,以及服務器漸變期間的雨 / 雷暴等級變化。
  • Bedrock: level_event start_rain / stop_rain / start_thunder / stop_thunder,攜帶目標等級 × 65535;由客戶端在本地漸變。 加入時,start_game.rain_level / lightning_level 攜帶當前(漸變中的)等級,之後不再有 level_event。
  • bedboo: isRaining / rain 與上游完全一致(過零判斷,每次變化一個事件);rainState / thunderState 是 level event 的 目標等級(BDS 上為 0 或 1),加入時取 start_game 的等級;每個 level event 觸發一次 weatherUpdate。

遊戲規則

  • 影響: bot.bedrock.gameRules(一項 Bedrock 擴展)、bot.game.enableRespawnScreen。
  • Java: 客戶端沒有遊戲規則狀態(只有通過 game event 傳遞的 reducedDebugInfo / 立即重生)。
  • Bedrock: 所有遊戲規則都通過 start_game.gamerules 和 game_rules_changed 發送。
  • bedboo: bot.bedrock.gameRules({ [Bedrock name]: boolean | number },例如 doDayLightCycle、keepInventory); bot.game.enableRespawnScreen 在加入時取自 doImmediateRespawn,與上游的 login 字段一致。

告示牌

  • 影響: bot.updateSign、signOpen、block.signText。
  • Java: open_sign_entity;update_sign 帶四行文本;服務器回顯 tile_entity_data。
  • Bedrock: open_sign { position, is_front },在玩家放置告示牌或與未塗蠟的告示牌交互時發送;每次打開只能編輯一次。編輯是一個 攜帶完整告示牌複合標籤的 block_entity_data:FrontText / BackText 複合標籤,各含一個 Text 字符串(各行以 \n 連接) 和樣式字段。
  • bedboo: updateSign(block, text, back) 保留上游的校驗(4 行,45 個字符),發送該方塊當前的告示牌複合標籤並替換對應面的 Text。prismarine-block 的 Bedrock 告示牌輔助函數已打補丁,改為讀寫 FrontText / BackText(getSignText() → [front, back], 與 Java 的雙面告示牌一致;上游讀取頂層 Text 並拋出異常)。

命令方塊

  • 影響: bot.setCommandBlock。
  • Java: update_command_block(模式、標誌);服務器回應 advMode.setCommand.success。
  • Bedrock: command_block_update,使用模式名(impulse / repeat / chain;模式即方塊本身: repeating_command_block、chain_command_block)、needs_redstone、conditional、should_track_output。BDS 不發送消息: 返回的是更新後的方塊實體(Command、auto、conditionalMode、TrackOutput)。它會忽略超出玩家觸及範圍的命令方塊的更新。
  • bedboo: 簽名、選項和檢查都相同(通過 bot.game.gameMode 檢查創造模式);模式 0 / 1 / 2 → chain / repeat / impulse, alwaysActive → !needs_redstone。與上游一樣接受非對象的 options(例如 false)。應等待方塊實體更新而不是聊天消息, 並把命令方塊放在觸及範圍內。

爆炸傷害

  • 影響: bot.getExplosionDamages。
  • Java: 實體帶有 generic.armor 屬性;沒有該屬性時上游返回 null(而不是 rawDamages)。
  • Bedrock: 線上沒有護甲屬性。
  • bedboo: 內置的上游插件同時接受 Java 資源名(minecraft:armor 等)以及 bedboo 為實體合成的護甲屬性 (裝備);沒有任何護甲屬性的生物實體(玩家 / 怪物)按無護甲計算,而不是返回 null。其餘公式相同 (Bedrock 的爆炸傷害遵循同一公式)。

方塊動作

  • 影響: noteHeard、pistonMove、chestLidMove、blockBreakProgressObserved、blockBreakProgressEnd。
  • Java: block_action(音符盒、活塞、帶玩家數量的箱子)和 block_break_animation(階段 0–9,帶破壞者的實體 id)。
  • Bedrock:
    • 音符盒:block_event { type: instrument, data: pitch }(Bedrock 樂器 id,例如 6 bell);
    • 箱子:block_event { type: change_state, data: open count },大箱子通過方塊實體配對(pairx / pairz);
    • 活塞不發送事件:它的 PistonArm 方塊實體的 NewState 依次為 0 → 1 → 2 → 3 → 0;活塞的 facing_direction 為 0 下、1 上、 2 南、3 北、4 東、5 西;
    • 破壞:level_event block_start_break / block_break_speed(每 tick 進度 × 65535)/ block_stop_break,不帶破壞者和階段, 並且也會發給正在破壞的玩家本人。
  • bedboo: noteHeard(block, registry.instruments[type], pitch);chestLidMove(block, count, block2) 每次變化觸發一次, block2 = 配對的另一半;pistonMove(block, 0 | 1, Java face index) 在推 / 拉開始時觸發;破壞進度:開始時為階段 0,之後根據速度 算出的每個新階段觸發一次 blockBreakProgressObserved,停止或方塊變化時觸發 blockBreakProgressEnd;實體取 8 格內最近的其他 玩家(沒有時為 undefined);機器人自己正在挖掘的位置(bot.targetDigBlock)會被跳過。

pitch / yaw 為 0 時的射線檢測(修復上游 bug)

  • 影響: bot.blockAtEntityCursor、bot.blockAtCursor、bot.blockInSight、bot.entityAtCursor。
  • Java: 當實體的 pitch 或 yaw 恰好為 0 時,mineflayer 4.39.0 返回 null(!entity.pitch || !entity.yaw):水平視線,或 朝北(默認朝向)時永遠命中不了方塊。
  • Bedrock: 不是協議差異。
  • bedboo: 內置插件只在缺少 pitch / yaw 時返回 null;其餘不變。

在玩家體內設置的末地傳送門可能把玩家推出去

  • 影響: 在機器人位置 /setblock … end_portal 以把它送到末地。
  • Java: 末地傳送門方塊沒有碰撞;位於其中的實體會被傳送。
  • Bedrock: end_portal 有一個 0.25 高的碰撞箱。在玩家體內設置方塊(/setblock)會讓 BDS 把玩家推出該方塊;如果這次推動 在同一批 tick 中先於傳送門檢測生效,玩家就站到傳送門旁邊,什麼也不會發生(在高負載下在線追蹤到:command_output 成功,隨後 correct_player_move_prediction 把玩家移開 0.8 格,30 s 內沒有 change_dimension)。
  • bedboo: 庫中無需修改。要可靠地進入末地,把機器人腳下的方塊替換為傳送門,然後讓機器人(已停止創造飛行)落到上面,就像 玩家走進傳送門一樣。

末地傳送門方塊的碰撞

  • 影響: 走進末地傳送門。
  • Java: 末地傳送門方塊沒有碰撞箱;碰到即傳送。
  • Bedrock: 數據給 end_portal 一個 0.25 高的碰撞箱(會被推出);玩家一碰到該方塊,BDS 就會傳送玩家。
  • bedboo: 庫中無需處理。要進入要塞中的傳送門,從樓梯走上框架環,再朝環的中心走,直到維度切換。

長距離傳送後的區塊

  • 影響: 傳送後立即在機器人位置執行的命令(例如 /setblock)和方塊變更。
  • Bedrock: 傳送到 16–128 格之外後,目標區塊在服務器上可能尚未加載:此時在那裡執行 /setblock ~ ~ ~ portal 會以 commands.setblock.outOfWorld 失敗,傳送門不會出現。
  • bedboo: 庫中無需處理。在操作目標區塊之前先等待 bot.waitForChunksToLoad()(服務器只會在加載區塊後才發送它)。

用命令放置的箱子

  • 影響: 用 /setblock 組成的大箱子、用 /replaceitem 填充容器。
  • Bedrock: 兩個相鄰且朝向相同的箱子,若沿垂直於其朝向的軸相鄰,/setblock 會把它們合併成大箱子(默認朝北時沿 x 軸合併)。容器槽位用 /replaceitem block <x y z> slot.container <n> <item> <count> 填充。
  • bedboo: 庫中無需處理;這樣放置的箱子,其窗口與手動放置的箱子行為相同。