AI works here

This commit is contained in:
2026-04-30 12:48:29 -04:00
parent 92024c8a64
commit 6f4519894b
16 changed files with 1793 additions and 303 deletions
+186 -93
View File
@@ -147,7 +147,7 @@ class ShulkerHandler {
// Wait a few ticks for the item entity to spawn
await bot.bot.waitForTicks(3);
// Jump onto the drop position to collect it (like a human player)
// Attempt 1: Jump onto the drop position to collect it (like a human player)
console.log(`ShulkerHandler: Jumping onto ${placedPos} to collect drop`);
try {
await bot.bot.lookAt(placedPos.offset(0.5, 0, 0.5), true);
@@ -167,14 +167,43 @@ class ShulkerHandler {
return true;
}
// If not collected yet, pathfind directly to the drop
// Attempt 2: Pathfind directly to the drop position
try {
await bot.goTo({ where: placedPos, range: 0 });
} catch (e) {
try { await bot.goTo({ where: placedPos, range: 1 }); } catch (e2) { /* ignore */ }
}
// Poll for pickup (up to 5 seconds)
await bot.bot.waitForTicks(5);
if (bot.bot.inventory.items().find(item => item.name.includes('shulker_box'))) {
return true;
}
// Attempt 3: Search for the dropped item entity nearby and walk to it
// Item may have bounced away from the block position
let droppedEntity = null;
for (const entity of Object.values(bot.bot.entities)) {
if (!entity.name || entity.name !== 'item') continue;
const dist = entity.position.distanceTo(placedPos);
if (dist < 5 && (!droppedEntity || dist < droppedEntity.position.distanceTo(placedPos))) {
droppedEntity = entity;
}
}
if (droppedEntity) {
console.log(`ShulkerHandler: Found item entity at ${droppedEntity.position}, walking to it`);
try {
await bot.goTo({ where: droppedEntity.position, range: 0 });
} catch (e) {
try { await bot.goTo({ where: droppedEntity.position, range: 1 }); } catch (e2) { /* ignore */ }
}
await bot.bot.waitForTicks(5);
if (bot.bot.inventory.items().find(item => item.name.includes('shulker_box'))) {
return true;
}
}
// Final poll — wait up to 5 seconds in case of lag
for (let i = 0; i < 10; i++) {
await sleep(500);
if (bot.bot.inventory.items().find(item => item.name.includes('shulker_box'))) {
@@ -182,101 +211,109 @@ class ShulkerHandler {
}
}
console.error('ShulkerHandler: Failed to collect shulker after all attempts');
return !!bot.bot.inventory.items().find(item => item.name.includes('shulker_box'));
}
/**
* Best-effort recovery: return a shulker from bot inventory back to its chest slot.
* Return a shulker from bot inventory back to its chest slot.
* If placedPos is given, dig the placed shulker first.
* Never throws — returns true on success, false on failure.
* @throws Error if the shulker cannot be returned (caller should handle the failure).
*/
async returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId) {
try {
// If shulker is placed on the ground, break it first
if (placedPos) {
try {
await bot.bot.closeWindow(bot.bot.currentWindow);
} catch (e) { /* ignore */ }
await sleep(300);
await this.digAndCollectShulker(bot, placedPos);
}
// Find shulker in bot inventory
const shulkerInInv = bot.bot.inventory.items().find(item => item.name.includes('shulker_box'));
if (!shulkerInInv) {
console.error('ShulkerHandler: Recovery failed — no shulker in inventory');
return false;
}
// Go to chest and open it
await bot.goTo({ where: chestPos, range: 3 });
const chestBlock = bot.bot.blockAt(chestPos);
if (!chestBlock || !chestBlock.name.includes('chest')) {
console.error(`ShulkerHandler: Recovery failed — no chest at ${chestPos}`);
return false;
}
const window = await bot.openContainer(chestBlock);
// If shulker is placed on the ground, break it first
if (placedPos) {
try {
await bot.bot.closeWindow(bot.bot.currentWindow);
} catch (e) { /* ignore */ }
await sleep(300);
// Find shulker in inventory portion of the window
let shulkerWindowSlot = null;
for (let i = window.inventoryStart; i < window.inventoryEnd; i++) {
const item = window.slots[i];
if (item && item.name.includes('shulker_box')) {
shulkerWindowSlot = i;
await this.digAndCollectShulker(bot, placedPos);
}
// Find shulker in bot inventory — throw if not found so caller knows recovery failed
const shulkerInInv = bot.bot.inventory.items().find(item => item.name.includes('shulker_box'));
if (!shulkerInInv) {
throw new Error('Recovery failed — no shulker in inventory');
}
// Go to chest and open it
await bot.goTo({ where: chestPos, range: 3 });
const chestBlock = bot.bot.blockAt(chestPos);
if (!chestBlock || !chestBlock.name.includes('chest')) {
throw new Error(`Recovery failed — no chest at ${chestPos}`);
}
const window = await bot.openContainer(chestBlock);
await sleep(300);
// Find shulker in inventory portion of the window
let shulkerWindowSlot = null;
for (let i = window.inventoryStart; i < window.inventoryEnd; i++) {
const item = window.slots[i];
if (item && item.name.includes('shulker_box')) {
shulkerWindowSlot = i;
break;
}
}
if (shulkerWindowSlot === null) {
await bot.bot.closeWindow(window);
throw new Error('Recovery failed — shulker not found in window inventory');
}
// Try original slot first; if occupied, find any empty chest slot
let targetSlot = chestSlot;
if (window.slots[chestSlot]) {
targetSlot = null;
for (let i = 0; i < window.inventoryStart; i++) {
if (!window.slots[i]) {
targetSlot = i;
break;
}
}
if (shulkerWindowSlot === null) {
await bot.bot.closeWindow(window);
console.error('ShulkerHandler: Recovery failed — shulker not found in window inventory');
return false;
}
// Try original slot first; if occupied, find any empty chest slot
let targetSlot = chestSlot;
if (window.slots[chestSlot]) {
targetSlot = null;
for (let i = 0; i < window.inventoryStart; i++) {
if (!window.slots[i]) {
targetSlot = i;
break;
}
}
}
if (targetSlot === null) {
await bot.bot.closeWindow(window);
console.error('ShulkerHandler: Recovery failed — no empty chest slot');
return false;
}
await bot.bot.moveSlotItem(shulkerWindowSlot, targetSlot);
await sleep(300);
// Read the NBT and sync DB immediately
const updatedSlotItem = window.slots[targetSlot];
await bot.bot.closeWindow(window);
await sleep(200);
if (updatedSlotItem && chestId && this.scanner) {
try {
await this.scanner.scanShulkerFromNBT(bot, Database, chestId, targetSlot, updatedSlotItem);
await Database.rebuildItemIndex();
console.log(`ShulkerHandler: Recovery DB synced — shulker at chest slot ${targetSlot}`);
} catch (e) {
console.error(`ShulkerHandler: Recovery DB sync failed: ${e.message}`);
}
}
console.log(`ShulkerHandler: Recovery succeeded — shulker returned to chest slot ${targetSlot}`);
return true;
} catch (error) {
console.error('ShulkerHandler: Recovery error:', error.message);
return false;
}
if (targetSlot === null) {
await bot.bot.closeWindow(window);
throw new Error('Recovery failed — no empty chest slot');
}
await bot.bot.moveSlotItem(shulkerWindowSlot, targetSlot);
await sleep(300);
// Read the NBT and sync DB immediately
const updatedSlotItem = window.slots[targetSlot];
await bot.bot.closeWindow(window);
await sleep(200);
if (updatedSlotItem && chestId && this.scanner) {
try {
await this.scanner.scanShulkerFromNBT(bot, Database, chestId, targetSlot, updatedSlotItem);
await Database.rebuildItemIndex();
console.log(`ShulkerHandler: Recovery DB synced — shulker at chest slot ${targetSlot}`);
} catch (e) {
console.error(`ShulkerHandler: Recovery DB sync failed: ${e.message}`);
}
}
// Verify the shulker is actually back in the chest
await bot.goTo({ where: chestPos, range: 3 });
const verifyBlock = bot.bot.blockAt(chestPos);
if (!verifyBlock || !verifyBlock.name.includes('chest')) {
throw new Error('Recovery verification failed — chest not found');
}
const verifyWindow = await bot.openContainer(verifyBlock);
await sleep(200);
const verifyItem = verifyWindow.slots[targetSlot];
await bot.bot.closeWindow(verifyWindow);
await sleep(200);
if (!verifyItem || !verifyItem.name.includes('shulker_box')) {
throw new Error(`Recovery verification failed — shulker not found at chest slot ${targetSlot} after return`);
}
console.log(`ShulkerHandler: Recovery succeeded — shulker returned to chest slot ${targetSlot}`);
return true;
}
/**
@@ -573,7 +610,18 @@ class ShulkerHandler {
placedPos = result.placedPos;
} catch (placeError) {
console.error('ShulkerHandler: Failed to place shulker, recovering:', placeError.message);
await this.returnShulkerToChest(bot, chestPos, chestSlot, null, chestId);
let recovered = false;
try {
await this.returnShulkerToChest(bot, chestPos, chestSlot, null, chestId);
recovered = true;
} catch (recoverError) {
console.error('ShulkerHandler: Recovery failed:', recoverError.message);
}
if (!recovered) {
// Recovery failed — shulker state is uncertain, propagate error upward
// so the caller knows not to retry this same shulker
throw recoverError;
}
return { deposited: 0, updatedSlotItem: null };
}
@@ -646,13 +694,27 @@ class ShulkerHandler {
return { deposited, updatedSlotItem };
} catch (returnError) {
console.error('ShulkerHandler: Failed to close/break/return, recovering:', returnError.message);
await this.returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId);
return { deposited: 0, updatedSlotItem: null };
let recovered = false;
try {
await this.returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId);
recovered = true;
} catch (recoverError) {
console.error('ShulkerHandler: Recovery failed:', recoverError.message);
}
if (!recovered) throw returnError;
return { deposited, updatedSlotItem: null };
}
} catch (error) {
console.error('ShulkerHandler: Deposit failed, attempting recovery:', error.message);
await this.returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId);
let recovered = false;
try {
await this.returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId);
recovered = true;
} catch (recoverError) {
console.error('ShulkerHandler: Recovery failed:', recoverError.message);
}
if (!recovered) throw error;
return { deposited: 0, updatedSlotItem: null };
} finally {
this.operationInProgress = false;
@@ -822,7 +884,14 @@ class ShulkerHandler {
placedPos = result.placedPos;
} catch (placeError) {
console.error('ShulkerHandler: Failed to place shulker, recovering:', placeError.message);
await this.returnShulkerToChest(bot, chestPos, chestSlot, null, chestId);
let recovered = false;
try {
await this.returnShulkerToChest(bot, chestPos, chestSlot, null, chestId);
recovered = true;
} catch (recoverError) {
console.error('ShulkerHandler: Recovery failed:', recoverError.message);
}
if (!recovered) throw placeError;
return { withdrawn: 0, updatedSlotItem: null };
}
@@ -841,6 +910,9 @@ class ShulkerHandler {
const shulkerItem = shulkerWindow.slots[s];
if (!shulkerItem || shulkerItem.name !== itemName) continue;
// Skip special/named items — only withdraw via special item handler
if (shulkerItem.nbt?.value?.display) continue;
// Check if inventory has space
const hasInvSpace = (() => {
for (let i = shulkerWindow.inventoryStart; i < shulkerWindow.inventoryEnd; i++) {
@@ -906,13 +978,27 @@ class ShulkerHandler {
return { withdrawn, updatedSlotItem };
} catch (returnError) {
console.error('ShulkerHandler: Failed to close/break/return, recovering:', returnError.message);
await this.returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId);
let recovered = false;
try {
await this.returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId);
recovered = true;
} catch (recoverError) {
console.error('ShulkerHandler: Recovery failed:', recoverError.message);
}
if (!recovered) throw returnError;
return { withdrawn: 0, updatedSlotItem: null };
}
} catch (error) {
console.error('ShulkerHandler: Withdraw failed, attempting recovery:', error.message);
await this.returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId);
let recovered = false;
try {
await this.returnShulkerToChest(bot, chestPos, chestSlot, placedPos, chestId);
recovered = true;
} catch (recoverError) {
console.error('ShulkerHandler: Recovery failed:', recoverError.message);
}
if (!recovered) throw error;
return { withdrawn: 0, updatedSlotItem: null };
} finally {
this.operationInProgress = false;
@@ -945,7 +1031,14 @@ class ShulkerHandler {
placedPos = result.placedPos;
} catch (placeError) {
console.error('ShulkerHandler: Failed to place shulker, recovering:', placeError.message);
await this.returnShulkerToChest(bot, chestPos, chestSlot, null, chestId);
let recovered = false;
try {
await this.returnShulkerToChest(bot, chestPos, chestSlot, null, chestId);
recovered = true;
} catch (recoverError) {
console.error('ShulkerHandler: Recovery failed:', recoverError.message);
}
if (!recovered) throw placeError;
return { withdrawn: 0, updatedSlotItem: null };
}