2025年8月12日火曜日

Mineflayer で移動する際にブロックを置いたり壊したりして指定の座標までかならず来るようにする方法

Mineflayer で移動する際にブロックを置いたり壊したりして指定の座標までかならず来るようにする方法

概要

前回 mineflayer-pathfinder を使って指定の座標にボットが移動するような処理を追加しました
ただその際にZ座標などが高い場所だとボットがその下までしか来ないような状態でした
今回はボットにブロックを持たせて高いところでも足場を自分で配置して登ってくるような機能を追加してみます

環境

  • macOS 15.6
  • Minecraft 1.21.4
  • nodejs 22.15.1
  • mineflayer 4.31.0

lib/command/parser.js

comeoninv がチャットで使えるように追加します

class CommandParser {
  constructor(message) {
    this.message = message.trim();
    this.parts = this.message.split(/\s+/);
  }

  isMoveCommand() {
    return this.parts[0].toLowerCase() === "move";
  }

  isComeOnCommand() {
    return this.parts[0].toLowerCase() === "comeon";
  }

  isInventoryCommand() {
    return this.parts[0].toLowerCase() === "inv";
  }

  getArgs() {
    return this.parts.slice(1);
  }
}

module.exports = CommandParser;

lib/command/commands.js

comeOnCmd と inventoryCmd を追加しています

前者はプレイヤーの座標に移動してくれる新機能でこのコマンドはたとえプレイヤーが高い場所にいても自分で足場ブロックを配置したり壊したりして移動してきてくれます

後者の inventoryCmd はボットのインベントリの内容を確認することができるデバッグ用のコマンドです

ポイントは movements.allowParkour などの Movements の設定でこれを true にすることでパルクールしてくれたりジャンプして1マスしたにブロックを置いてどんどん上に上がってきたりする機能を追加することができます
他の機能は公式のドキュメントを参照してください

また足場ブロックは何でも OK ですが今回は3種類でフィルタしています
これはなくても OK ですが念の為入れています
足場ブロックとして許可しているブロックは「土」「丸石」「オークの原木」になります

const { goals, Movements } = require("mineflayer-pathfinder");
const mcDataLoader = require("minecraft-data");

function moveCmd(bot, username, args) {
  if (args.length < 3) {
    bot.chat(`Usage: move <x> <y> <z>`);
    return;
  }

  const [x, y, z] = args.map(Number);
  if ([x, y, z].some(isNaN)) {
    bot.chat("座標は数値で指定してください");
    return;
  }

  bot.chat(`${username} の指示で (${x}, ${y}, ${z}) に移動します`);
  bot.pathfinder.setGoal(new goals.GoalBlock(x, y, z));
}

function comeOnCmd(bot, username) {
  const player = bot.players[username]?.entity;
  if (!player) {
    bot.chat(`プレイヤー ${username} が見つかりません`);
    return;
  }

  const mcData = mcDataLoader(bot.version);
  const movements = new Movements(bot, mcData);

  // 足場設置を許可(ジャンプしてブロックを置く)
  movements.allowParkour = true; // ジャンプ移動許可
  movements.allow1by1towers = true; // 垂直ジャンプブロック置き許可
  movements.canDig = true; // 必要なら掘削許可
  movements.scafoldingBlocks = getScaffoldBlocks(bot, mcData);

  bot.pathfinder.setMovements(movements);

  // プレイヤーの目の前をゴールに
  const pos = player.position.clone();
  const dx = Math.round(Math.cos(player.yaw));
  const dz = Math.round(Math.sin(player.yaw));
  const targetPos = pos.offset(dx, 0, dz);

  bot.chat(`${username} の所に行きます!`);
  bot.pathfinder.setGoal(
    new goals.GoalBlock(targetPos.x, targetPos.y, targetPos.z),
  );
}

// 手持ちの足場ブロックを取得
function getScaffoldBlocks(bot, mcData) {
  const usableItemIds = [
    mcData.itemsByName.dirt.id,
    mcData.itemsByName.cobblestone.id,
    mcData.itemsByName.sand.id,
  ];
  // console.log("usableItemIds:", usableItemIds);

  return bot.inventory
    .items()
    .filter((item) => usableItemIds.includes(item.type))
    .map((item) => item.type);
}

function inventoryCmd(bot, username) {
  const items = bot.inventory.items();
  if (items.length === 0) {
    bot.chat(`${username}、インベントリは空です。`);
    return;
  }
  const itemList = items
    .map((item) => `${item.count}x${item.name}(${item.type})`)
    .join(", ");
  bot.chat(`${username} の所持品: ${itemList}`);
}

module.exports = { moveCmd, comeOnCmd, inventoryCmd };

index.js

メイン部分です
comeOnCmd と inventoryCmd の処理を追加しています

const mineflayer = require("mineflayer");
const { pathfinder, Movements } = require("mineflayer-pathfinder");
const CommandParser = require("./lib/command/parser");
const { moveCmd, comeOnCmd, inventoryCmd } = require("./lib/command/commands");

class MinecraftBot {
  constructor(host, port, username) {
    this.bot = mineflayer.createBot({
      host,
      port,
      username,
      version: false,
    });

    this.bot.loadPlugin(pathfinder);
    this.registerEvents();
  }

  registerEvents() {
    this.bot.once("spawn", () => this.onSpawn());
    this.bot.on("chat", (username, message) => this.onChat(username, message));
    this.bot.on("kicked", (reason) =>
      console.error("キックされました:", reason),
    );
    this.bot.on("error", (err) => console.error("エラー:", err));
    this.bot.on("end", () => console.log("Bot が切断されました"));
  }

  onSpawn() {
    console.log("Bot がログインしました!");
    this.bot.chat("こんにちは!Botクラスです!");

    const defaultMovements = new Movements(this.bot, this.bot.registry);
    this.bot.pathfinder.setMovements(defaultMovements);
  }

  onChat(username, message) {
    if (username === this.bot.username) return;
    console.log(`[${username}]: ${message}`);

    const parser = new CommandParser(message);

    if (parser.isMoveCommand()) {
      moveCmd(this.bot, username, parser.getArgs());
    } else if (parser.isComeOnCommand()) {
      comeOnCmd(this.bot, username);
    } else if (parser.isInventoryCommand()) {
      inventoryCmd(this.bot, username);
    }
  }

  sendChat(text) {
    this.bot.chat(text);
  }
}

const myBot = new MinecraftBot("localhost", 25565, "bot");

動作確認

ボットを参加させチャットに comeon と入力してみましょう
動きを見るとわかりますが垂直ブロック置きしていることがわかります
しかし安定はしないようでブロックが置けていないときがあり何度かリトライしているようです
この辺り安定するように修正が必要そうです

ボットが足場ブロックを持っていないといけないので持っていない場合はコマンドなどで持たせましょう

  • /give bot minecraft:dirt 64

最後に

mineflayer を使ってボットを移動する際に足場ブロックを使って自力で指定の場所に移動するようにしてみました
ブロックを壊すアイテム(ピッケルやシャベル)を持たせると来る途中にあるブロックも壊して来てくれます

参考サイト

0 件のコメント:

コメントを投稿