跳到正文
本頁目錄

移動與物理

本頁說明玩家移動、服務器校正、視角、疾跑、飛行、鞘翅滑翔和載具控制在 Java 版與 Bedrock 版之間的差異,以及 bedboo 的物理 引擎如何復現 Bedrock 的規則。測量環境為 BDS 1.26.51.1(protocol 2193,bedrock-protocol 3.60.1):機器人記錄了它發送的 每個 player_auth_input 和收到的每個 correct_player_move_prediction;在以 player-position-acceptance-threshold=0.001 啟動、且每個發送的位置都偏移 +5 格的服務器上,服務器從不採納客戶端位置,因此每次校正(大約每 6 tick 一次)都攜帶服務器 自身的模擬狀態。引擎經過反覆擬合,直到能離線復現這些狀態。

移動引擎

  • 影響: bot.physics(及下列所有字段)、bot.physics.simulatePlayer、bot.entity.velocity、 mineflayer-pathfinder 的模擬、bot.physics.stepHeight、negligeableVelocity、soulsandSpeed、honeyblockSpeed、 honeyblockJumpSpeed、ladderMaxSpeed、playerHeight(潛行)、slowFalling、sprintingUUID。
  • Java: prismarine-physics(Java 1.8–1.21 規則),由客戶端決定位置的數據包。
  • Bedrock: 移動由服務器決定並支持回溯:客戶端每 tick 發送一個 player_auth_input(眼睛高度的位置、移動向量、偏航、 輸入標誌、delta),BDS 模擬同樣的輸入,當位置比它自己的結果走得更遠(或偏差超過接受閾值)時進行校正。與 Java 不同的 實測規則:
    • 位置/速度為 float32;服務器從協議中的 float32 角度取偏航,並使用精確的正弦/餘弦(沒有 65536 項的查找表):斜向行走 只有用精確三角函數才能對上;
    • 移動向量會被歸一化(斜向為 (0.7071, 0.7071)),潛行時 × 0.3,再 × 0.98;
    • 一個 tick 的順序:根據上一 tick 的著地狀態計算速度 + 摩擦 → 移動向量 → 跳躍 → 攀爬 → 卡住類方塊 → 潛行邊緣保護 → 碰撞(Y、X、Z)及自動上臺階 → 踩踏減速 / 彈跳 → 重力 + 阻力 → 摩擦;上報的 delta 是 tick 結束時的速度(靜止: (0, −0.0784, 0));
    • 摩擦取自 floor(pos − 0.5) 處的方塊,否則取支撐碰撞箱的方塊,否則為 0.6——站在邊緣或走出懸崖時從不使用空氣的 0.9;
    • 自動上臺階 0.5625(Java 0.6);negligeableVelocity 為 1e-6(只有當 |v|² < 1e-12 時速度才清零);
    • 跳躍:vy = max(vy, 0.42 + 0.1 × 跳躍提升),疾跑跳 +0.2,按住時有 10 tick 延遲;蜂蜜塊上跳躍 × 0.6(Java 0.5);
    • 潛行邊緣保護把碰撞箱縮小 0.025,向下探測 1.01 × 臺階高度,並自行裁剪速度;潛行碰撞箱高 1.49(實測; lib/bedrock/constants.js 中的 SNEAK_HEIGHT,也用作潛行玩家的 height);
    • 攀爬:下落最多 0.2/tick(Java 0.15);按住跳躍以 0.2/tick 攀升(隨後受重力:delta 0.1176);朝可攀爬方塊推進時保持 0.2 作為 tick 結束時的速度(delta 0.2);沒有水平限制;潛行時會抓住不動;
    • 碰撞把小於 1e-4 的穿透視為接觸(float32 位置最終會略微嵌入其所倚靠的面);
    • 水中重力 0.005(重力 / 16),岩漿 0.02;氣泡柱按方塊逐個生效,數值與 Java 相同;
    • 靈魂沙:地面速度 × 0.544(Java:速度 × 0.4);蜂蜜塊和黏液塊:踩踏時 × (0.4 + 0.2 × |vy|)(Java 蜂蜜塊:速度 × 0.4);黏液塊只在從空中落下時彈跳;
    • 緩降只在已經下落時(vy < 0)生效——從靜止開始的第一個 tick 仍使用 0.08;
    • 使用物品:衝量被限制且疾跑停止;舉盾等同於普通潛行(見物品使用減速);
    • 傳送後 BDS 不模擬它處理的第一個輸入(見校正);
    • 創造模式飛行:見創造模式飛行。
  • bedboo: lib/physics/engine.js(prismarine-physics 1.11.1 的分支)以上游的模塊結構實現這些規則:
    • bot.physics 保留上游的 31 個字段名,取 Bedrock 的值:stepHeight 0.5625、negligeableVelocity 1e-6、 soulsandSpeed 0.544、honeyblockSpeed 0.4(踩踏減速的基數)、honeyblockJumpSpeed 0.6、ladderMaxSpeed 0.2、 waterGravity 0.005、sprintingUUID = Bedrock 的 "Sprinting speed boost" 修飾符;
    • simulatePlayer(state, world) 接受 prismarine-physics 的 PlayerState(pathfinder)以及 bedboo 自己的 (require('bedboo/lib/physics').PlayerState);
    • require('minecraft-data')(bot.version) 解析為 Bedrock 數據(上游 API 約定),因此 prismarine-physics 的 PlayerState 可以工作;
    • 在 60 s 的行走繞圈中實測:默認接受閾值下 0 次校正,閾值 0.1 時每 100 tick 最多 1 次。

服務器校正、傳送、擊退、效果

  • 影響: forcedMove、move、bot.entity.position / velocity。
  • Java: 服務器發送絕對/相對的 position 數據包;客戶端確認傳送。
  • Bedrock: 傳送用 move_player(teleport/reset),預測錯誤用 correct_player_move_prediction(眼睛高度的位置、 delta = tick 結束時的速度、on_ground、被校正輸入的 tick),擊退用 set_entity_motion。沒有相對傳送。實測: move_player.tick、set_entity_motion.tick、mob_effect.tick 和 update_attributes.tick 是服務器已處理的最後一個 輸入;它從下一個輸入起應用變化,且不模擬傳送後收到的第一個輸入。
  • bedboo:
    • 保存最近 rewind_history_size(+20)個輸入的歷史;校正會回溯到其 tick,採用服務器狀態並重放此後的輸入(觸發 forcedMove);
    • 傳送:位置設為目標,速度為 0,在下一個輸入中帶上 handled_teleport;已在途的輸入從目標位置重新模擬,否則下一個輸入 不做模擬;
    • 帶 tick 的擊退 / 效果 / 移動速度會改寫從該 tick 起已發送的輸入並重新模擬(服務器仍會對在途的輸入發送一次校正;其 狀態與 bedboo 的相同);
    • 不存在速度"相對"傳送;set_entity_motion 直接設置速度。

輸入流與暫停

  • 影響: physicsTick,未加載區塊或加載界面期間的移動數據包。
  • Java: 只在有變化時(或每秒一次)發送位置數據包;配置階段不發送數據包;沒有區塊時不運行物理。
  • Bedrock: 每個服務器 tick 一個 player_auth_input,始終如此(服務器據此驅動玩家);沒有配置階段——加載界面、維度 切換和死亡會暫停該輸入流(bot._bedrock.input.suspend)。
  • bedboo: 沒有已加載的區塊列時,機器人既不模擬也不觸發 physicsTick,但會繼續發送其不變的位置;輸入隊列暫停期間 什麼也不發送。

視角

  • 影響: bot.look、bot.lookAt、bot.physics.yawSpeed、bot.physics.pitchSpeed。
  • Java: 發送給服務器的偏航每 tick 最多轉動 yawSpeed;服務器收到最終朝向後 look 才 resolve。
  • Bedrock: 朝向是每個 player_auth_input 的一部分(偏航、俯仰、頭部偏航、交互朝向和相機方向);服務器以該偏航移動 玩家。
  • bedboo: 靈敏度取整和 force 語義與上游相同;新的偏航/俯仰完整地在下一個輸入中發出(沒有逐步轉動——該 tick 的移動 就已使用它,與服務器完全一致);該輸入發出後 look resolve(≤ 1 tick),force 時立即 resolve。yawSpeed / pitchSpeed 保留上游的值,但不再使用。

點擊前的潛行 / 疾跑

  • 影響: bot.setControlState('sneak' | 'sprint') 後立即進行的點擊(對可交互方塊的 placeBlock / activateBlock、帶疾跑擊退的 attack、useOn);bot._bedrock.physics.controlsSent()。
  • Java: mineflayer 在 setControlState 中寫出 entity_action / player_input,因此下一次點擊會以新狀態處理。
  • Bedrock: 潛行和疾跑只作為 player_auth_input 的標誌存在(start_sneaking、sneaking 等),每 tick 發送一次; 在該輸入之前排隊的交易會被 BDS 以舊狀態處理(在 BDS 1.26.51.1 上驗證:潛行點擊箱子會打開箱子,而不是放置手持方塊)。
  • bedboo: 依賴該狀態的點擊會等待攜帶它的輸入:當有待發送的潛行 / 疾跑變化時,useItemOn / useOnEntity (lib/bedrock/transactions.js)會 await bot._bedrock.physics.controlsSent();同步的 bot.attack / useOn / mount(lib/plugins/entities.js)通過 afterControlsSent(fn) 發送(見攻擊與使用實體)。 點擊最多比上游晚一個 tick(50 ms);沒有待發送的變化時同步排隊。如果輸入先被暫停(加載界面、維度切換),點擊會立即發出; end 之後點擊被丟棄(controlsSent() reject)。因此潛行後立即對箱子 useItemOn 會放置方塊,而不是打開箱子。

雙擊前進會疾跑

  • 影響: 快速切換的 bot.setControlState('forward', …)(mineflayer-pathfinder 的短距離移動、先點按再按住)、 行走時的 bot.entity.position / 校正。
  • Java: 雙擊疾跑是客戶端功能;除非設置了 sprint 控制,mineflayer 從不疾跑。
  • Bedrock: BDS 根據輸入的 up 按鍵標誌自行判定雙擊:在上一次按下後 6 tick 內再次按下前進,玩家就會疾跑(不需要 疾跑標誌),直到鬆開前進(在 1.26.51.1 上實測:兩次按下間隔 6 tick 會疾跑,7 tick 不會;不做預測時,點按 3 tick → 鬆開 1 tick → 按住,3 s 內產生 3 次校正)。up 標誌同時驅動移動,因此不能壓住不發。
  • bedboo: 物理插件像原版客戶端一樣預測它(原版客戶端在雙擊時自己就會疾跑):這樣的第二次按下會疾跑(輸入中帶 sprinting / start_sprinting,不需要疾跑鍵),條件是前進保持按住且可以疾跑(不在飛行或騎乘,不在潛行或後退,沒有在 使用物品,飽食度高於 6,沒有被牆擋住;傳送或死亡會結束它)。與所有 Bedrock 疾跑一樣它是粘滯的: 前進保持按住時,setControlState('sprint', false) 不會結束它——BDS 保持疾跑速度(若此時預測為行走,大約每 27 tick 會產生一次校正,例如在 mineflayer-pathfinder 下,它在行走時每 tick 都會鬆開疾跑控制)。嘗試過的所有切換模式都沒有 校正,服務器的位置與機器人的一致。getControlState('sprint') 保持為 false。創造模式的 flyTo 則會避免雙擊 (創造模式飛行)。

疾跑持續到鬆開前進為止

  • 影響: forward 保持按住時的 bot.setControlState('sprint', false)(mineflayer-pathfinder 的行走分支、疾跑一段 時間的用戶代碼)、機器人的速度。
  • Java: 鬆開疾跑控制會發送 stop_sprinting;玩家從下一個 tick 起行走。
  • Bedrock: 疾跑是粘滯的(在 1.26.51.1 上實測):一旦玩家開始疾跑——疾跑標誌或雙擊——只要前進保持按住,BDS 就保持 疾跑速度,即使收到 stop_sprinting 也是如此(它會清除 sprinting actor 標誌,但會重新發送帶 "Sprinting speed boost" 的移動屬性,直到鬆開前進)。原版客戶端的行為相同(鬆開疾跑鍵不會停止疾跑)。此時按 0.1 的速度行走,大約每 27 tick 會產生一次校正。
  • bedboo: 鬆開疾跑控制後,物理插件繼續疾跑(並上報 sprinting,不發 stop_sprinting),直到鬆開前進或滿足其他 結束條件:潛行、後退、撞牆、生存模式下飽食度 ≤ 6、使用物品、飛行、騎乘、傳送或死亡。getControlState('sprint') 報告 控制狀態(false)。若要以行走速度繼續前進,請鬆開 forward(如果上一次按下在 6 tick 之內,至少鬆開 7 tick,否則再次 按下會成為雙擊)。

物品使用減速與舉盾

  • 影響: bot.usingHeldItem 期間的移動(進食、拉弓、舉盾)。
  • Java: mineflayer 的物理完全不會因為使用物品而減速。
  • Bedrock: 玩家使用物品時,BDS 把移動衝量限制在 ±0.1225 並去掉疾跑修飾符——而它只在收到 click_air 交易時才進入 物品使用狀態;之後按住的疾跑鍵會再次疾跑。盾牌通過潛行舉起(不是物品使用),因此 BDS 模擬為普通潛行(實測:兩者都是 每 60 tick 3.81 格)。
  • bedboo: 只有當 bot.usingHeldItem 已設置且為該次使用發送過 click_air 時(從發出的交易中觀察;item_release / completed_using_item 結束它),引擎才應用物品使用限制。舉起的盾牌(activateItem(true))的移動與潛行完全相同,沒有 校正。另見物品使用。

創造模式飛行與 flyTo

  • 影響: bot.creative.flyTo / startFlying / stopFlying、bot.physics.gravity、 bot._bedrock.physics.setFlying。
  • Java: 客戶端自己切換 abilities.flying;startFlying 設置 bot.physics.gravity = 0,stopFlying 恢復它; 上游的 flyTo 通過直接設置 bot.entity.position,每 tick 以 0.5 格的步長移動(不經過物理)。
  • Bedrock: 飛行是服務器端的移動狀態。BDS 在收到 request_ability flying 時開始/停止飛行(僅有 auth-input 的 start_flying 標誌會被忽略),並以 update_abilities 報告;飛行在服務器端模擬:
    • 每 tick 按住跳躍 / 潛行時垂直 +0.15 / −0.22,垂直阻力 0.6,水平 0.91,沒有疾跑加成,無輸入時額外 × 0.375 的阻尼 (飛行在幾個 tick 內停下);
    • 雙擊跳躍(兩次按下相隔幾個 tick)同樣會在服務器上切換飛行;
    • 雙擊前進會使 BDS 進入疾跑飛行(約兩倍速度),且玩家觸地時 BDS 會停止飛行。
  • bedboo:
    • bot.creative.startFlying / stopFlying 調用 bot._bedrock.physics.setFlying(on),在下一個輸入之前發送 request_ability(+ start_flying / stop_flying),並且不改動 bot.physics.gravity(重力為 0 會在服務器拒絕或 結束飛行——遊戲模式變化、死亡——之後依然殘留,破壞之後所有的移動以及 pathfinder 的模擬)。沒有物理插件的飛行鉤子時, 沿用上游的重力處理;
    • 引擎跟隨 bot.abilities(從 update_abilities 解析,同時推導出遊戲模式變化);服務器端的變化不會回發請求(見 能力);
    • bot.creative.flyTo(destination) 委託給 bot._bedrock.physics.flyTo(沒有它時使用上游的步進循環),以真實輸入飛行: 每 tick 用引擎模擬每種選擇(之後空閒直到停下),從"朝目標前進 × 跳躍 / 潛行 / 不操作"中挑選;12 tick 內從不重複按下 跳躍(該保護在連續的 flyTo 調用之間保持),鬆開前進後 10 tick 內從不再次按下前進(避免雙擊疾跑飛行),且目的地高於 著陸點時從不提前降落;在距離 0.15 格以內停下後 resolve(上游:精確到達目的地),飛行停止、傳送或受阻 60 tick 時 reject;
    • 實測:15 s 懸停和多次 flyTo 飛行均無校正。真實飛行比上游的傳送式步進慢:mineflayer 蜜蜂示例的 11 段短航程約需 15.5 s(Java:約 3 s)。

鞘翅

  • 影響: bot.elytraFly、bot.entity.elytraFlying、滑翔時的煙花助推。
  • Java: entity_action start_elytra_flying。
  • Bedrock: player_auth_input 中的 start_gliding;BDS 只在同時按下跳躍時接受它(jump_pressed_raw、jumping、 start_jumping 等),並以 gliding actor 標誌確認;它可以結束滑翔(清除標誌)。滑翔本身遵循 Java 的公式。持續使用火箭時 BDS 大約每 2 tick 發射一枚(20 次使用 → 約 10 枚火箭),並大約每 6 tick 校正一次被助推的滑翔。
  • bedboo: 檢查和錯誤與上游相同(胸甲槽從 bot.inventory 讀取,沒有物品欄插件時從原始盔甲窗口讀取);從下一個輸入起 預測滑翔,當服務器清除標誌(從該 tick 回溯)或 20 tick 後仍未確認時放棄。助推的開始被記錄在該 tick 的輸入中並會重放, 因此校正不會抹掉它。火箭檢測(usedFirework、bot.fireworkRocketDuration)見煙花助推;來自 創造模式物品欄的普通 firework_rocket 會得到其默認的 Fireworks {Flight: 1} 變種 (創造模式默認值)。

載具

  • 影響: bot.moveVehicle。
  • Java: steer_vehicle / player_input。
  • Bedrock: 騎乘者的 player_auth_input 攜帶移動向量、帶載具 unique id 和朝向的 client_predicted_vehicle,以及船的 划槳標誌。
  • bedboo: moveVehicle(left, forward) 保持輸入直到被改變(上游語義),bot.vehicle 已設置時由輸入提供者每 tick 發送;騎乘時不運行玩家物理。上坐騎與鏈接見載具。

騎乘控制

  • 影響: 與 bot.mount / bot.dismount 配合使用的 bot.moveVehicle。
  • Java: 客戶端模擬所騎的載具,併發送 vehicle_move / steer_vehicle。
  • Bedrock: 騎乘者的 player_auth_input 攜帶移動向量(船還帶划槳標誌)以及預測的載具 id / 朝向;BDS 據此模擬船 / 馬 (水上的船約 7 格/s,單槳划動時轉向;馬跟隨騎乘者的偏航)。客戶端不預測載具物理,BDS 每 6 tick 用一次 correct_player_move_prediction { prediction_type: 'vehicle' } 校準載具;騎乘者不會收到玩家校正。下馬時騎乘者被傳送到 馬旁邊;下船時 BDS 可能保留騎乘者上報的位置。實測:船 3 s 內 20.8 格,馬 3 s 內 25–29 格,0 次玩家校正,載具校正間隔 恰好 6 tick。
  • bedboo: 與上游一樣,騎乘時不觸發 physicsTick。物理插件在每個 player_auth_input 中把騎乘者上報在載具的座位上, 並讓 bot.entity.position 隨之移動,因此船上的騎乘者會在船旁邊下來:見騎乘者的位置、 騎乘者的座位和下坐騎。

已知的殘餘誤差

  • 近似處理(可能出現校正;此時採用服務器狀態並重放):跳躍時落在蜂蜜塊上、黏液塊彈跳高度(誤差 0.01 格以內)、氣泡柱 表面、細雪(每 6 tick < 2 mm)、創造模式下從地面起飛(默認閾值下一次 3 cm 的校正;嚴格探測結果一致)。
  • 未建模:游泳(在水中疾跑不會開始游泳)、爬行 / 滑翔姿態(碰撞箱高度)、被推出方塊(BDS 每 tick 推 0.1)、腳手架、實體 碰撞(船、潛影貝)、創造模式下雙擊跳躍切換飛行(跟隨服務器的 update_abilities)、遠離原點時的 float32 精度。
  • 氣泡柱:當靈魂沙 / 岩漿塊被放到已有的水下時 BDS 會形成氣泡柱;之後再替換底部方塊不會使其轉換。
  • bot._bedrock.input(input_queue.js):時鐘不會因校正而重新對齊——tick 編號只用於標識輸入以供服務器回溯,且每個發送的 tick 只運行一步引擎,因此被跳過的(追趕用的)tick 在兩端都會被跳過。