Mostly works

This commit is contained in:
2026-05-03 11:13:06 -04:00
parent 6f4519894b
commit 60663b8d4a
21 changed files with 3188 additions and 1706 deletions
+15 -1
View File
@@ -400,6 +400,20 @@ class Database {
`, [itemName]);
}
// Find chests with available slots, ordered nearest-first from a reference point.
// Returns an array of { id, pos_x, pos_y, pos_z, chest_type, empty_slots }.
async findNearestChestsWithSpace(neededSlots, refX, refY, refZ) {
return await this.db.all(`
SELECT c.id, c.pos_x, c.pos_y, c.pos_z, c.chest_type,
(CASE WHEN c.chest_type = 'double' THEN 54 ELSE 27 END) - COUNT(s.id) as empty_slots
FROM chests c
LEFT JOIN shulkers s ON s.chest_id = c.id
GROUP BY c.id
HAVING empty_slots > 0
ORDER BY ((c.pos_x - ?) * (c.pos_x - ?) + (c.pos_y - ?) * (c.pos_y - ?) + (c.pos_z - ?) * (c.pos_z - ?)) ASC
`, [refX, refX, refY, refY, refZ, refZ]);
}
// Find a chest slot that doesn't have a shulker (for placing newly crafted ones)
async findEmptyChestSlot() {
const chests = await this.db.all(`
@@ -560,7 +574,7 @@ class Database {
// Clear and rebuild from shulker_items
await this.db.run('DELETE FROM item_index');
await this.db.run(`
INSERT INTO item_index (item_id, item_name, total_count, shulker_ids, last_updated)
INSERT OR REPLACE INTO item_index (item_id, item_name, total_count, shulker_ids, last_updated)
SELECT
si.item_id,
si.item_name,
File diff suppressed because it is too large Load Diff
+6 -2
View File
@@ -193,7 +193,7 @@ class Scanner {
}
}
async scanAllChests(bot, database) {
async scanAllChests(bot, database, interruptCheck) {
const chests = await database.getChests();
console.log(`Scanner: Scanning all ${chests.length} tracked chests`);
@@ -246,7 +246,11 @@ class Scanner {
}
// Wait for anti-ESP to reveal nearby blocks after arriving
await sleep(250);
if (interruptCheck && interruptCheck()) {
console.log(`Scanner: Interrupted after ${scannedCount} chests`);
return totalShulkers;
}
await sleep(250);
// Discover any new chests now visible from this position (every 5th stop or first)
if (scannedCount % 5 === 0) {
+107 -147
View File
@@ -2,12 +2,11 @@
const Vec3 = require('vec3');
const { sleep } = require('../../utils');
const { goals: { GoalNear } } = require('mineflayer-pathfinder');
const Database = require('./database');
class ShulkerHandler {
constructor() {
this.operationInProgress = false;
this.scanner = null; // set by Storage after init
}
@@ -89,7 +88,7 @@ class ShulkerHandler {
bot.bot.setControlState('sneak', false);
// Stop pathfinder movement too
try { bot.bot.pathfinder.stop(); } catch (e) { /* ignore */ }
bot.bot.clearControlStates();
// Retry dig up to 3 times — shulker closing animation can block the first attempt
for (let digAttempt = 0; digAttempt < 3; digAttempt++) {
@@ -395,18 +394,9 @@ class ShulkerHandler {
async takeWholeShulker(bot, chestPos, chestSlot, shulkerId) {
console.log(`ShulkerHandler: Taking whole shulker from chest at ${chestPos}, slot ${chestSlot}`);
if (this.operationInProgress) {
throw new Error('Another shulker operation is in progress');
}
this.operationInProgress = true;
try {
const invSlot = await this.takeShulkerFromChest(bot, chestPos, chestSlot, shulkerId);
console.log(`ShulkerHandler: Whole shulker taken, now in inventory slot ${invSlot}`);
return invSlot;
} finally {
this.operationInProgress = false;
}
const invSlot = await this.takeShulkerFromChest(bot, chestPos, chestSlot, shulkerId);
console.log(`ShulkerHandler: Whole shulker taken, now in inventory slot ${invSlot}`);
return invSlot;
}
/**
@@ -435,7 +425,7 @@ class ShulkerHandler {
const retreatZ = botPos.z + (dz / dist) * 1.5;
try {
await bot.bot.pathfinder.goto(new GoalNear(retreatX, botPos.y, retreatZ, 0.5));
await bot.goTo({ where: new Vec3(retreatX, botPos.y, retreatZ), range: 0 });
await sleep(300);
} catch (e) {
console.log(`ShulkerHandler: Retreat pathfind failed: ${e.message}`);
@@ -503,7 +493,7 @@ class ShulkerHandler {
const angle = (attempt * Math.PI / 2) + Math.PI / 4;
const moveX = pos.x + Math.cos(angle) * 3;
const moveZ = pos.z + Math.sin(angle) * 3;
await bot.bot.pathfinder.goto(new GoalNear(moveX, pos.y, moveZ, 1));
await bot.goTo({ where: new Vec3(moveX, pos.y, moveZ), range: 1 });
} catch (e) {
console.log(`ShulkerHandler: Move failed: ${e.message}`);
}
@@ -591,11 +581,6 @@ class ShulkerHandler {
async depositIntoShulker(bot, chestPos, chestSlot, itemName, count, shulkerId, chestId, itemFilter = null) {
console.log(`ShulkerHandler: Depositing ${count}x ${itemName} into shulker at chest ${chestPos} slot ${chestSlot}`);
if (this.operationInProgress) {
throw new Error('Another shulker operation is in progress');
}
this.operationInProgress = true;
let placedPos = null;
try {
@@ -716,8 +701,6 @@ class ShulkerHandler {
}
if (!recovered) throw error;
return { deposited: 0, updatedSlotItem: null };
} finally {
this.operationInProgress = false;
}
}
@@ -729,132 +712,123 @@ class ShulkerHandler {
async unpackShulkerFromInventory(bot, shulkerItem) {
console.log(`ShulkerHandler: Unpacking shulker ${shulkerItem.name} from inventory`);
if (this.operationInProgress) {
throw new Error('Another shulker operation is in progress');
// Step 1: Find placement spot
const spot = this.findPlacementSpot(bot);
// Ensure bot is far enough from placement spot
const botPos = bot.bot.entity.position;
const placeDist = botPos.distanceTo(spot.position.offset(0.5, 0, 0.5));
if (placeDist < 1.5) {
console.log(`ShulkerHandler: Too close to placement spot (${placeDist.toFixed(1)} blocks), stepping back`);
const dx = botPos.x - spot.position.x;
const dz = botPos.z - spot.position.z;
const dist = Math.sqrt(dx * dx + dz * dz) || 1;
const retreatX = botPos.x + (dx / dist) * 1.5;
const retreatZ = botPos.z + (dz / dist) * 1.5;
try {
await bot.goTo({ where: new Vec3(retreatX, botPos.y, retreatZ), range: 0 });
await sleep(300);
} catch (e) {
console.log(`ShulkerHandler: Retreat pathfind failed: ${e.message}`);
}
}
this.operationInProgress = true;
try {
// Step 1: Find placement spot
const spot = this.findPlacementSpot(bot);
// Step 2: Equip and place the shulker
await bot.bot.equip(shulkerItem, 'hand');
await bot.bot.waitForTicks(5);
// Ensure bot is far enough from placement spot
const botPos = bot.bot.entity.position;
const placeDist = botPos.distanceTo(spot.position.offset(0.5, 0, 0.5));
if (placeDist < 1.5) {
console.log(`ShulkerHandler: Too close to placement spot (${placeDist.toFixed(1)} blocks), stepping back`);
const dx = botPos.x - spot.position.x;
const dz = botPos.z - spot.position.z;
const dist = Math.sqrt(dx * dx + dz * dz) || 1;
const retreatX = botPos.x + (dx / dist) * 1.5;
const retreatZ = botPos.z + (dz / dist) * 1.5;
try {
// Verify we're actually holding the shulker
const heldItem = bot.bot.heldItem;
if (!heldItem || !heldItem.name.includes('shulker_box')) {
throw new Error(`Cannot equip shulker for unpack (holding: ${heldItem?.name || 'nothing'})`);
}
await bot.bot.pathfinder.goto(new GoalNear(retreatX, botPos.y, retreatZ, 0.5));
await sleep(300);
} catch (e) {
console.log(`ShulkerHandler: Retreat pathfind failed: ${e.message}`);
// Look at the top face center of placement block
await bot.bot.lookAt(spot.placeOn.position.offset(0.5, 1, 0.5), true);
await bot.bot.waitForTicks(3);
// SAFETY: verify held item is a shulker RIGHT before placing — never place anything else
const prePlaceItem = bot.bot.heldItem;
if (!prePlaceItem || !prePlaceItem.name.includes('shulker_box')) {
throw new Error(`ABORT: held item changed before place (holding: ${prePlaceItem?.name || 'nothing'})`);
}
await bot.bot.placeBlock(spot.placeOn, spot.faceVec);
await bot.bot.waitForTicks(10);
// Verify shulker was placed
const placedBlock = bot.bot.blockAt(spot.position);
if (!placedBlock || !placedBlock.name.includes('shulker_box')) {
// Something wrong was placed — break it immediately
console.error(`ShulkerHandler: WRONG BLOCK placed at ${spot.position} (${placedBlock?.name}), breaking it`);
try { await bot.bot.dig(bot.bot.blockAt(spot.position)); } catch (e) { /* best effort */ }
throw new Error(`Failed to place shulker at ${spot.position}`);
}
// Step 3: Open the shulker
const shulkerWindow = await bot.openContainer(placedBlock);
await bot.bot.waitForTicks(5);
// Step 4: Move ALL items from shulker into bot inventory
const extracted = [];
let inventoryFull = false;
const shulkerSlotCount = shulkerWindow.inventoryStart;
for (let s = 0; s < shulkerSlotCount; s++) {
const shulkerSlotItem = shulkerWindow.slots[s];
if (!shulkerSlotItem) continue;
// Find a target slot in bot inventory (stack first, then empty)
let targetSlot = null;
for (let i = shulkerWindow.inventoryStart; i < shulkerWindow.inventoryEnd; i++) {
const invItem = shulkerWindow.slots[i];
if (invItem && invItem.name === shulkerSlotItem.name && invItem.count < invItem.stackSize) {
targetSlot = i;
break;
}
}
// Step 2: Equip and place the shulker
await bot.bot.equip(shulkerItem, 'hand');
await bot.bot.waitForTicks(5);
// Verify we're actually holding the shulker
const heldItem = bot.bot.heldItem;
if (!heldItem || !heldItem.name.includes('shulker_box')) {
throw new Error(`Cannot equip shulker for unpack (holding: ${heldItem?.name || 'nothing'})`);
}
// Look at the top face center of placement block
await bot.bot.lookAt(spot.placeOn.position.offset(0.5, 1, 0.5), true);
await bot.bot.waitForTicks(3);
// SAFETY: verify held item is a shulker RIGHT before placing — never place anything else
const prePlaceItem = bot.bot.heldItem;
if (!prePlaceItem || !prePlaceItem.name.includes('shulker_box')) {
throw new Error(`ABORT: held item changed before place (holding: ${prePlaceItem?.name || 'nothing'})`);
}
await bot.bot.placeBlock(spot.placeOn, spot.faceVec);
await bot.bot.waitForTicks(10);
// Verify shulker was placed
const placedBlock = bot.bot.blockAt(spot.position);
if (!placedBlock || !placedBlock.name.includes('shulker_box')) {
// Something wrong was placed — break it immediately
console.error(`ShulkerHandler: WRONG BLOCK placed at ${spot.position} (${placedBlock?.name}), breaking it`);
try { await bot.bot.dig(bot.bot.blockAt(spot.position)); } catch (e) { /* best effort */ }
throw new Error(`Failed to place shulker at ${spot.position}`);
}
// Step 3: Open the shulker
const shulkerWindow = await bot.openContainer(placedBlock);
await bot.bot.waitForTicks(5);
// Step 4: Move ALL items from shulker into bot inventory
const extracted = [];
let inventoryFull = false;
const shulkerSlotCount = shulkerWindow.inventoryStart;
for (let s = 0; s < shulkerSlotCount; s++) {
const shulkerSlotItem = shulkerWindow.slots[s];
if (!shulkerSlotItem) continue;
// Find a target slot in bot inventory (stack first, then empty)
let targetSlot = null;
if (targetSlot === null) {
for (let i = shulkerWindow.inventoryStart; i < shulkerWindow.inventoryEnd; i++) {
const invItem = shulkerWindow.slots[i];
if (invItem && invItem.name === shulkerSlotItem.name && invItem.count < invItem.stackSize) {
if (!shulkerWindow.slots[i]) {
targetSlot = i;
break;
}
}
if (targetSlot === null) {
for (let i = shulkerWindow.inventoryStart; i < shulkerWindow.inventoryEnd; i++) {
if (!shulkerWindow.slots[i]) {
targetSlot = i;
break;
}
}
}
if (targetSlot === null) {
console.log('ShulkerHandler: Bot inventory full during unpack');
inventoryFull = true;
break;
}
try {
const itemName = shulkerSlotItem.name;
const itemCount = shulkerSlotItem.count;
await bot.bot.moveSlotItem(s, targetSlot);
await sleep(200);
extracted.push({ name: itemName, count: itemCount });
} catch (error) {
console.error(`ShulkerHandler: Error moving item from shulker slot ${s}:`, error);
}
}
// Step 5: Close the shulker window — wait for closing animation
await bot.bot.closeWindow(shulkerWindow);
await bot.bot.waitForTicks(30);
// Step 6: Break the shulker block and pick it up
const collected = await this.digAndCollectShulker(bot, spot.position);
if (!collected) {
console.error('ShulkerHandler: Shulker not found in inventory after breaking');
if (targetSlot === null) {
console.log('ShulkerHandler: Bot inventory full during unpack');
inventoryFull = true;
break;
}
console.log(`ShulkerHandler: Unpacked ${extracted.length} item stacks from shulker`);
return { extracted, inventoryFull };
} finally {
this.operationInProgress = false;
try {
const itemName = shulkerSlotItem.name;
const itemCount = shulkerSlotItem.count;
await bot.bot.moveSlotItem(s, targetSlot);
await sleep(200);
extracted.push({ name: itemName, count: itemCount });
} catch (error) {
console.error(`ShulkerHandler: Error moving item from shulker slot ${s}:`, error);
}
}
// Step 5: Close the shulker window — wait for closing animation
await bot.bot.closeWindow(shulkerWindow);
await bot.bot.waitForTicks(30);
// Step 6: Break the shulker block and pick it up
const collected = await this.digAndCollectShulker(bot, spot.position);
if (!collected) {
console.error('ShulkerHandler: Shulker not found in inventory after breaking');
}
console.log(`ShulkerHandler: Unpacked ${extracted.length} item stacks from shulker`);
return { extracted, inventoryFull };
}
/**
@@ -865,11 +839,6 @@ class ShulkerHandler {
async withdrawFromShulker(bot, chestPos, chestSlot, itemName, count, shulkerId, chestId) {
console.log(`ShulkerHandler: Withdrawing ${count}x ${itemName} from shulker at chest ${chestPos} slot ${chestSlot}`);
if (this.operationInProgress) {
throw new Error('Another shulker operation is in progress');
}
this.operationInProgress = true;
let placedPos = null;
try {
@@ -1000,8 +969,6 @@ class ShulkerHandler {
}
if (!recovered) throw error;
return { withdrawn: 0, updatedSlotItem: null };
} finally {
this.operationInProgress = false;
}
}
/**
@@ -1012,11 +979,6 @@ class ShulkerHandler {
async withdrawFromShulkerSlot(bot, chestPos, chestSlot, shulkerSlot, count, shulkerId, chestId) {
console.log(`ShulkerHandler: Withdrawing from shulker slot ${shulkerSlot} at chest ${chestPos} slot ${chestSlot}`);
if (this.operationInProgress) {
throw new Error('Another shulker operation is in progress');
}
this.operationInProgress = true;
let placedPos = null;
try {
@@ -1119,8 +1081,6 @@ class ShulkerHandler {
console.error('ShulkerHandler: Slot withdraw failed, attempting recovery:', error.message);
await this.returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId);
return { withdrawn: 0, updatedSlotItem: null };
} finally {
this.operationInProgress = false;
}
}
}