世界與方塊
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_eventstart_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_eventblock_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: 庫中無需處理;這樣放置的箱子,其窗口與手動放置的箱子行為相同。