Mineflayerの落とし穴:成功・停止・到達をどう確かめるか

2026-10-16 JST。Mineflayerを使う開発者向けの技術ノート。Minecraftの版、Mineflayerの版、追加plugin、patchの適用状況を別々に固定する。Minecraftの版を下げるだけでは、非同期処理や到達判定の問題はなくならない。

本文の「試験済み」は過去に保存した観測の要約。「読んだだけ」は実装・報告の読解であり、今回その症状を再現してはいない。公開用の編集ではゲームサーバを起動していない。元の運用ログとworldは同梱していないため、以下の数字は第三者がこの配布物だけで再集計できるデータセットではない。

比較の範囲

Vanilla・Easy・通常地形で、26.1、1.21.11、1.20.4、1.16.5を比較した。共通構成はNode 22.20.0、Mineflayer 4.39.0、minecraft-protocol 1.68.0、minecraft-data 3.117.0、pathfinder 2.4.5、collectblock 1.6.0。1.21.11ではMineflayer 4.37.1未修正と、同じ基準commitへ17本の修正を当てた構成も別に測った。後者はこの配布物の1本のpatchとは異なる。

各操作10枠。各試行でbotプロセスを終了する一方、world・位置・在庫は引き継いだ。準備品をコマンドで与え、実操作は通常client操作、判定はサーバ側のブロック・在庫・位置・次元と照合した。自然地形、初期位置、天候等は一致していない。同じseedの4.37.1比較でも開始位置は違う。10/10でも無故障の保証ではなく、連続接続、独立した10地形、序盤の自力調達、長時間攻略の検証ではない。

移動は掘削・柱積み・parkour・足場材なし、上限60秒。水・ポータルには測定手順の訂正があり、準備失敗と操作失敗を分けた。26.1は比較対象で、調査時点の最新安定版26.3は未試験。

1. timeoutは非同期処理の取消ではない

版と条件:JavaScriptのPromise.raceでtimeoutを返し、移動・採掘・timerを残したまま次の操作を始める場合。特定のMinecraft版だけの問題ではない。

対処:botごとに操作の所有者を1つにする。実行世代を更新し、各await後と副作用の直前に世代を確認する。所有するtimer/listenerを解除し、pluginの取消hookと元Promiseの終了を待つ。世代番号だけでplugin内部は止まらない。

証拠:取消処理の実装・過去報告を読んだ。4版比較ではtimeoutと終了時状態を記録したが、試行ごとにプロセスを終了しており、同一接続での遅いcallbackの完全停止は全版で未確認。

2. goal・押下bit・予約済みjumpは別の状態

版と条件:1.21.11向け4.37.1修正系で、jumpを押してからphysics tick前にclearControlStates()する場合。次元移動・死亡後に古いgoalやforwardが残る場合も危険。1.20.4・1.16.5・26.1での同じ遅延jump症状は未確認。

対処:移動の所有を失効させ、goalと全controlを解除し、新しい次元・spawn・chunk・床を確認する。押下bitだけでなくjumpQueuedとjumpTicksも解除する候補を単独patchとして付けた。適用基準はMineflayer 4.37.1、commit 03eba44f3e9cb93a0f0bf69a75938246e174dc6f。このpatchは次元境界のすべての非同期処理を止めるものではない。

証拠:既存修正とqueued jumpを消す単体試験を読んだ。今回の切り出しでは基準ソースへの適用確認のみで、実ゲーム未試験。比較のportal境界では26.1/4.39.0が2件、1.21.11/4.39.0が19件、1.20.4/4.39.0が20件のrespawn観測で7bitとgoalの残留0。1.16.5と4.37.1の両構成は境界観測0件で評価できない。内部jumpや全callbackがない証明ではない。

bot.controlStateをspreadするとgetterが列挙されず空になる実装がある。getControlStateでforward/back/left/right/jump/sprint/sneakを個別に読む。空の観測を「停止済み」にしない。

3. craftのresolveと在庫の確定を分ける

版と条件:craft後の数量・cursor・windowが同期していない場合。1.21.11向け4.38隔離候補ではset_player_inventoryのhotbar indexをwindow-0へ誤適用する問題があった。1.20.4の予備連続試験でも数量不一致とslot更新timeoutがあったが、同じ原因とは確認していない。

対処:全stackの材料減・完成品増、cursor、craft grid、windowを照合する。同時操作を避け、空きslotを用意し、server更新を待つ。不整合のまま再craftしない。hotbar indexの変換は採用版のpacket schemaに合わせる。ここにはinventory修正の差分を同梱しない。

証拠:4.38候補の10 fresh worldでの収束は過去報告を読んだもの。4版比較の作業台・かまどは4.39.0で各版10/10、1.21.11の4.37.1未修正0/10、17修正構成10/10。全レシピ・全cursor状態や特定patchの効果を証明しない。

4. collectのtimeout・取消完了・拾得成功は別

版と条件:collectblockの作業中にtimeoutし、goalだけ解除する場合。4.37.1を基にした収集wrapperでは、cancelTask()自体が終了しないと取消待ちも無期限になった。他版での同じwrapperの試験はない。

対処:cancel hookと元collect Promiseの終了を監視し、共通のcleanup期限を設ける。取消hook完了と元Promise終了を別々に返す。期限切れ・未終了なら新操作を重ねず、所有処理を隔離または終了し、実在庫を照合する。収集成功はブロック消失と拾得増で判定する。

証拠:既存CPU単体試験報告を読んだ。両Promiseを永久pendingにした条件で修正前は独立watchdogまで返らず、修正後は約61msで未終了を返した。実collectblock+Paperの全副作用停止は未確認。比較のcollectは4.39.0で26.1・1.21.11・1.20.4各10/10、1.16.5は8/10。1.21.11の4.37.1未修正0/10、17修正構成10/10。これは取消試験ではない。

5. entitiesにない対象を「存在しない」としない

版と条件:追跡範囲外の敵・エンドクリスタルをbot.entitiesだけで判定する場合。追跡距離はchunkのview-distanceと別で、一律32ブロックではない。具体的な旧失敗報告は1.21.8で、4版への距離値の一般化はしない。

対処:安全に近づいて再観測する。対象ごとに未確認・存在・破壊確認を記録し、攻撃対象と射線を照合する。

証拠:過去報告を読んだだけ。今回の比較は近距離ゾンビ1体で、遠距離entityの可視範囲は全版で未試験。

6. 空のpartialを到達に数えない

版と条件:pathfinder 2.4.5のlib/goto.jsは、status判定より先にresults.path.length === 0で成功終了する。空のpartial・noPath・timeoutが渡る条件を区別できない。4版比較で共通のコードなので、Minecraft版だけを下げても直らない。

対処:noPathとtimeoutを先にrejectし、空partialは次の更新を待つ。空successでも独立した位置・goal条件を確かめる。取消をresolveする独自拡張があっても取消と到達を分ける。pathfinder修正差分はここには含めない。

証拠:2.4.5導入ソースを読んだ。過去Paper 1.21.11では移動0・goal未達で成功となり、修正後NoPathになった報告がある。今回の4版比較は全構成で空partial観測0件で、強制再現していない。0件は修正済みの証拠ではない。公開報告 #273は2022年の版非特定報告。

比較の50ブロックは4.39.0で26.1から順に10/10、7/10、8/10、10/10。200ブロックは全構成0/10だった。1.16.5の200ブロックだけ開始次元がNether。地形・制限・時間切れを含むため、これを空partialの発生率や版の優劣としない。

7. 結果不明の操作をそのまま再送しない

版と条件:全版共通の設計上の問題。timeout・切断・例外の前に、サーバ側では操作が済んでいることがある。

対処:操作ID、対象、開始前状態、送信有無、終端を記録する。旧処理を終了させ、server由来のブロック・数量と照合し、成功・未実施・部分完了・不明を分ける。不明なら再送せず、確定した不足分だけを新操作にする。

証拠:比較では前後状態・例外・準備と実操作を保存した。通信断後の一般的なtransaction再開の安全性は全版で未試験。設計指針として示す。

8. 1.20.4で報告された操作の注意

以下は公開報告を読んだもので、同じ原因を再現した保証ではない。issueの状態は2026-10-16 JSTの読解時点。

版・再現条件 対処 証拠と比較で分かった範囲
4.20.1/Vanilla 1.20.4、activateBlockで水・レバー等が反映しない バケツは視線を送りactivateItemを使う。水源とバケツの変化を確認。移動が照準を変える場合もある #3428、open報告の読解。比較の水は訂正後1.20.4/4.39.0で10/10。レバーは未試験
4.20.1/Paper 1.20.4、容器のwindowOpenが20秒timeout 接近・射線・権限・保護範囲を確認し、利用を許された容器へ。権限昇格を回避策にしない #3459、closed報告の読解。作者は権限原因と追記。比較のチェストは4.39.0で4版とも10/10だが権限制限を再現していない
collectblock/1.20.4、未掘削なのに掘ったと認識 採用commitを固定し、実ブロック消失と拾得増を照合 #137、open報告の読解。「GitHub修正済み・npm未公開」は投稿時の主張。比較の成功はこの報告の修正確認ではない
pathfinder、forward+jumpでも進まず再計算を反復。ゲーム版は不明 実座標で進捗を判定し、短い迂回や段差処理へ切り替える #339の読解。physics側へ誘導。1.20.4固有とせず、比較の移動失敗とも原因を同一視しない

水の準備失敗をAPI失敗に混ぜない。4.39.0の訂正版では26.1と1.16.5は10枠中7枠が前提未成立、残りも未完遂。1.21.11は0/10、4.37.1未修正0/10、17修正構成10/10だったが、自然のくぼみと作った水槽、初期地形の差があり、patchの因果効果とは断定できない。

9. 1.21.11でのpacketとphysicsの注意

版・再現条件 対処 証拠の種類と限界
4.37.1/1.21.11、spawn直後の操作無視、掘削・設置の見かけの成功 player_loaded、dig/placeの共有sequence、server block updateによる掘削完了を確認。chunkと着地も確認 修正実装を読んだ。比較の掘る・置くは未修正1/10、17修正構成10/10、4.39.0も10/10。単独packet修正の寄与は分離していない
1.21.6+の入力bit不一致、1.21.11で足元air時にphysics停止 対象版のplayer_input schemaとfeature判定を一致させる。足元airだけでなく直下のloaded blockを確認。未知chunkを安全なairと扱わない 修正実装を読んだ。比較の移動は関連する操作試験のみで、bit単体・chunk単体のconformanceではない。1.20.4へ新版専用packetを送らない
1.21.9+の圧縮速度、1.21.11でNaN/Infinity速度・spawn速度0 lpVec3とlegacy fieldを版に応じて分岐し、finite guardとregistry fallback、spawn初速の取込みを行う 過去のpacket対照報告を読んだ。修正前149件中127件不一致、修正後133件中0件。今回の比較で速度packet単体は未試験。全physicsや描画の正常を保証しない

この節の複数patchは同梱していない。上流の最新版に同じ修正があるとも、ないとも断定しない。feature flagとpacket schemaを実際の固定依存で照合する。

配布差分とライセンス

同梱差分はclear-queued-jump.patchのみ。Mineflayer 4.37.1の上記commitに対し、git apply --checkで適用を確認してからgit applyする。別版には無条件に適用しない。既存修正から関係する処理だけを切り出し、コメントを独立した説明にしたもの。元の修正列、独自plugin、試験ファイルは含まない。切り出し後の実ゲーム試験は未実施。

差分は上流Mineflayerと同じMITライセンス。上流の著作権表示を含むLICENSEを同梱する。原典は基準commitのLICENSE。このノートの新規本文も同じMIT条件で提供する。リンク先のissue・コメントの権利はそれぞれの原著者に帰属し、この配布の許諾に含めない。