update: enhance bot functionality with new item handling and environment variable support
This commit is contained in:
parent
7976d46d05
commit
2241108f97
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
/node_modules
|
||||
package-lock.json
|
||||
package-lock.json
|
||||
.env
|
||||
218
index.js
218
index.js
@ -3,12 +3,22 @@ const fs = require("fs");
|
||||
const path = require("path");
|
||||
const mineflayer = require("mineflayer");
|
||||
const gui = require("mineflayer-gui");
|
||||
const Vec3 = require("vec3");
|
||||
|
||||
require("dotenv").config();
|
||||
|
||||
const logs = [];
|
||||
const MAX_LOGS = 200;
|
||||
const LOOP_DELAY_MS = 30_000;
|
||||
const LOOP_DELAY_MS = 10_000;
|
||||
const MAX_ALLOWED_MOVE_BLOCKS = 10;
|
||||
const REJOIN_DELAY_MS = 60_000;
|
||||
const BULB_VERIFY_RETRIES = 3;
|
||||
|
||||
const ITEM_MODE = normalizeItemMode(
|
||||
process.env.ITEM_MODE || process.env.SPAWNER_MODE || "bones",
|
||||
);
|
||||
const BUTTON_POSITION = parseEnvPosition("BUTTON");
|
||||
const BULB_POSITION = parseEnvPosition("BULB");
|
||||
|
||||
let spawnerLoopRunning = false;
|
||||
let joinPosition = null;
|
||||
@ -16,6 +26,102 @@ let hasJoined = false;
|
||||
let spawnerPosition = null;
|
||||
let reconnectTimer = null;
|
||||
let bot = null;
|
||||
let bothModeStep = 0;
|
||||
|
||||
function normalizeItemMode(value) {
|
||||
const input = String(value || "")
|
||||
.trim()
|
||||
.toLowerCase();
|
||||
if (input === "both") return "both";
|
||||
if (input === "arrow" || input === "arrows") return "arrows";
|
||||
return "bones";
|
||||
}
|
||||
|
||||
function parseEnvPosition(prefix) {
|
||||
const compact = process.env[`${prefix}_POS`];
|
||||
if (compact) {
|
||||
const parsed = compact
|
||||
.split(/[\s,]+/)
|
||||
.filter(Boolean)
|
||||
.map((v) => Number(v));
|
||||
if (parsed.length === 3 && parsed.every(Number.isFinite)) {
|
||||
return new Vec3(parsed[0], parsed[1], parsed[2]);
|
||||
}
|
||||
}
|
||||
|
||||
const x = Number(process.env[`${prefix}_X`]);
|
||||
const y = Number(process.env[`${prefix}_Y`]);
|
||||
const z = Number(process.env[`${prefix}_Z`]);
|
||||
if ([x, y, z].every(Number.isFinite)) {
|
||||
return new Vec3(x, y, z);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function isBulbOn(block) {
|
||||
if (!block) return null;
|
||||
|
||||
const props =
|
||||
typeof block.getProperties === "function" ? block.getProperties() : null;
|
||||
if (props && typeof props.lit === "boolean") return props.lit;
|
||||
if (props && typeof props.powered === "boolean") return props.powered;
|
||||
|
||||
const name = String(block.name || "").toLowerCase();
|
||||
if (name.includes("lit")) return true;
|
||||
if (name.includes("unlit")) return false;
|
||||
if (name.includes("redstone_lamp")) return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
async function ensureBulbModeBeforeOpen(mode) {
|
||||
if (!BULB_POSITION || !BUTTON_POSITION) {
|
||||
pushLog("missing BULB_POS/BUTTON_POS in .env; skipping mode check");
|
||||
return false;
|
||||
}
|
||||
|
||||
const desiredBulbOn = mode === "arrows";
|
||||
let bulbBlock = bot.blockAt(BULB_POSITION);
|
||||
if (!bulbBlock || bulbBlock.name === "air") {
|
||||
pushLog("bulb block not found at BULB_POS");
|
||||
return false;
|
||||
}
|
||||
|
||||
let currentBulbOn = isBulbOn(bulbBlock);
|
||||
if (currentBulbOn === desiredBulbOn) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const buttonBlock = bot.blockAt(BUTTON_POSITION);
|
||||
if (!buttonBlock || buttonBlock.name === "air") {
|
||||
pushLog("button block not found at BUTTON_POS");
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let attempt = 1; attempt <= BULB_VERIFY_RETRIES; attempt += 1) {
|
||||
try {
|
||||
await bot.lookAt(buttonBlock.position.offset(0.5, 0.5, 0.5), true);
|
||||
await bot.activateBlock(buttonBlock);
|
||||
pushLog(
|
||||
`clicked button to set bulb ${desiredBulbOn ? "on" : "off"} for ${mode} (attempt ${attempt}/${BULB_VERIFY_RETRIES})`,
|
||||
);
|
||||
await sleep(900);
|
||||
|
||||
bulbBlock = bot.blockAt(BULB_POSITION);
|
||||
currentBulbOn = isBulbOn(bulbBlock);
|
||||
if (currentBulbOn === desiredBulbOn) {
|
||||
return true;
|
||||
}
|
||||
} catch (err) {
|
||||
pushLog(`failed to click button: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
pushLog(
|
||||
`bulb state still incorrect after ${BULB_VERIFY_RETRIES} attempts (wanted ${desiredBulbOn ? "on" : "off"})`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
function createBotInstance() {
|
||||
hasJoined = false;
|
||||
@ -237,23 +343,32 @@ function waitForWindowOpen(timeoutMs = 5000) {
|
||||
});
|
||||
}
|
||||
|
||||
async function openSpawnerGui() {
|
||||
const targetBlock = await rotateToSpawner();
|
||||
if (!targetBlock || targetBlock.name === "air") {
|
||||
pushLog("unable to target spawner block");
|
||||
return;
|
||||
async function openSpawnerGui(maxAttempts = 2) {
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
||||
const targetBlock = await rotateToSpawner();
|
||||
if (!targetBlock || targetBlock.name === "air") {
|
||||
pushLog("unable to target spawner block");
|
||||
return false;
|
||||
}
|
||||
|
||||
const openPromise = waitForWindowOpen(5000);
|
||||
closeCurrentWindow();
|
||||
await bot.activateBlock(targetBlock);
|
||||
const opened = await openPromise;
|
||||
|
||||
if (opened) {
|
||||
return true;
|
||||
}
|
||||
|
||||
pushLog(
|
||||
`no gui opened from block '${targetBlock.name}' (attempt ${attempt}/${maxAttempts})`,
|
||||
);
|
||||
if (attempt < maxAttempts) {
|
||||
await sleep(700);
|
||||
}
|
||||
}
|
||||
|
||||
const openPromise = waitForWindowOpen(5000);
|
||||
await bot.activateBlock(targetBlock);
|
||||
const opened = await openPromise;
|
||||
|
||||
if (!opened) {
|
||||
pushLog(`no gui opened from block '${targetBlock.name}'`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
function getWindowItems() {
|
||||
@ -302,6 +417,43 @@ async function dropAllBonesFromCurrentWindow() {
|
||||
pushLog(`dropped bones total: ${dropped}`);
|
||||
}
|
||||
|
||||
async function dropAllArrowsFromCurrentWindow() {
|
||||
if (!bot.currentWindow) {
|
||||
pushLog("no window open to drop arrows");
|
||||
return;
|
||||
}
|
||||
|
||||
let dropped = 0;
|
||||
while (true) {
|
||||
const arrowStacks = getWindowItems().filter(
|
||||
(item) => item && String(item.name || "").toLowerCase() === "arrow",
|
||||
);
|
||||
const arrowCount = arrowStacks.reduce(
|
||||
(sum, item) => sum + (item.count || 0),
|
||||
0,
|
||||
);
|
||||
|
||||
if (arrowCount === 0) break;
|
||||
|
||||
const ok = await bot.gui
|
||||
.Query()
|
||||
.Window(comparator)
|
||||
.Drop("Arrow", 64)
|
||||
.end()
|
||||
.run();
|
||||
|
||||
if (!ok) {
|
||||
pushLog("failed to drop arrows from window");
|
||||
break;
|
||||
}
|
||||
|
||||
dropped += Math.min(64, arrowCount);
|
||||
await sleep(150);
|
||||
}
|
||||
|
||||
pushLog(`dropped arrows total: ${dropped}`);
|
||||
}
|
||||
|
||||
function closeCurrentWindow() {
|
||||
if (!bot.currentWindow) return;
|
||||
if (typeof bot.closeWindow === "function") {
|
||||
@ -313,13 +465,32 @@ function closeCurrentWindow() {
|
||||
}
|
||||
}
|
||||
|
||||
async function runSpawnerCycle() {
|
||||
async function runSpawnerPhase(mode) {
|
||||
const toggled = await ensureBulbModeBeforeOpen(mode);
|
||||
await sleep(toggled ? 1300 : 500);
|
||||
const opened = await openSpawnerGui();
|
||||
if (!opened) return;
|
||||
await dropAllBonesFromCurrentWindow();
|
||||
|
||||
if (mode === "arrows") {
|
||||
await dropAllArrowsFromCurrentWindow();
|
||||
} else {
|
||||
await dropAllBonesFromCurrentWindow();
|
||||
}
|
||||
|
||||
closeCurrentWindow();
|
||||
}
|
||||
|
||||
async function runSpawnerCycle() {
|
||||
if (ITEM_MODE === "both") {
|
||||
const phase = bothModeStep < 2 ? "bones" : "arrows";
|
||||
await runSpawnerPhase(phase);
|
||||
bothModeStep = (bothModeStep + 1) % 4;
|
||||
return;
|
||||
}
|
||||
|
||||
await runSpawnerPhase(ITEM_MODE);
|
||||
}
|
||||
|
||||
function startSpawnerLoop() {
|
||||
if (spawnerLoopRunning) {
|
||||
pushLog("spawner loop already running");
|
||||
@ -332,6 +503,7 @@ function startSpawnerLoop() {
|
||||
}
|
||||
|
||||
spawnerLoopRunning = true;
|
||||
bothModeStep = 0;
|
||||
pushLog("spawner loop started");
|
||||
|
||||
(async () => {
|
||||
@ -343,7 +515,7 @@ function startSpawnerLoop() {
|
||||
}
|
||||
|
||||
if (!spawnerLoopRunning) break;
|
||||
pushLog("waiting 30s for next cycle");
|
||||
pushLog("waiting 10s for next cycle");
|
||||
await sleep(LOOP_DELAY_MS);
|
||||
}
|
||||
})();
|
||||
@ -398,3 +570,11 @@ function itemToString(item) {
|
||||
}
|
||||
|
||||
createBotInstance();
|
||||
|
||||
if (BULB_POSITION && BUTTON_POSITION) {
|
||||
pushLog(
|
||||
`mode=${ITEM_MODE}, bulb=${BULB_POSITION.x},${BULB_POSITION.y},${BULB_POSITION.z}, button=${BUTTON_POSITION.x},${BUTTON_POSITION.y},${BUTTON_POSITION.z}`,
|
||||
);
|
||||
} else {
|
||||
pushLog("set BULB_POS and BUTTON_POS in .env to enable mode checking");
|
||||
}
|
||||
|
||||
@ -10,7 +10,9 @@
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"dependencies": {
|
||||
"dotenv": "^17.3.1",
|
||||
"mineflayer": "^4.35.0",
|
||||
"mineflayer-gui": "^4.0.2"
|
||||
"mineflayer-gui": "^4.0.2",
|
||||
"vec3": "^0.1.10"
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user