LookAtMySuitBot/js/node_modules/mineflayer/lib/plugins/digging.js

234 lines
7.8 KiB
JavaScript
Raw Normal View History

2023-12-24 20:08:39 -05:00
const { performance } = require('perf_hooks')
const { createDoneTask, createTask } = require('../promise_utils')
const BlockFaces = require('prismarine-world').iterators.BlockFace
const { Vec3 } = require('vec3')
module.exports = inject
function inject (bot) {
let swingInterval = null
let waitTimeout = null
let diggingTask = createDoneTask()
bot.targetDigBlock = null
bot.lastDigTime = null
async function dig (block, forceLook, digFace) {
if (block === null || block === undefined) {
throw new Error('dig was called with an undefined or null block')
}
if (!digFace || typeof digFace === 'function') {
digFace = 'auto'
}
if (bot.targetDigBlock) bot.stopDigging()
let diggingFace = 1 // Default (top)
if (forceLook !== 'ignore') {
if (digFace?.x || digFace?.y || digFace?.z) {
// Determine the block face the bot should mine
if (digFace.x) {
diggingFace = digFace.x > 0 ? BlockFaces.EAST : BlockFaces.WEST
} else if (digFace.y) {
diggingFace = digFace.y > 0 ? BlockFaces.TOP : BlockFaces.BOTTOM
} else if (digFace.z) {
diggingFace = digFace.z > 0 ? BlockFaces.SOUTH : BlockFaces.NORTH
}
await bot.lookAt(
block.position.offset(0.5, 0.5, 0.5).offset(digFace.x * 0.5, digFace.y * 0.5, digFace.z * 0.5),
forceLook
)
} else if (digFace === 'raycast') {
// Check faces that could be seen from the current position. If the delta is smaller then 0.5 that means the
// bot cam most likely not see the face as the block is 1 block thick
// this could be false for blocks that have a smaller bounding box then 1x1x1
const dx = bot.entity.position.x - (block.position.x + 0.5)
const dy = bot.entity.position.y + bot.entity.height - (block.position.y + 0.5)
const dz = bot.entity.position.z - (block.position.z + 0.5)
// Check y first then x and z
const visibleFaces = {
y: Math.sign(Math.abs(dy) > 0.5 ? dy : 0),
x: Math.sign(Math.abs(dx) > 0.5 ? dx : 0),
z: Math.sign(Math.abs(dz) > 0.5 ? dz : 0)
}
const validFaces = []
for (const i in visibleFaces) {
if (!visibleFaces[i]) continue // skip as this face is not visible
// target position on the target block face. -> 0.5 + (current face) * 0.5
const targetPos = block.position.offset(
0.5 + (i === 'x' ? visibleFaces[i] * 0.5 : 0),
0.5 + (i === 'y' ? visibleFaces[i] * 0.5 : 0),
0.5 + (i === 'z' ? visibleFaces[i] * 0.5 : 0)
)
const startPos = bot.entity.position.offset(0, bot.entity.height, 0)
const rayBlock = bot.world.raycast(startPos, targetPos.clone().subtract(startPos).normalize(), 5)
if (rayBlock) {
const rayPos = rayBlock.position
if (
rayPos.x === block.position.x &&
rayPos.y === block.position.y &&
rayPos.z === block.position.z
) {
// console.info(rayBlock)
validFaces.push({
face: rayBlock.face,
targetPos: rayBlock.intersect
})
}
}
}
if (validFaces.length > 0) {
// Chose closest valid face
let closest
let distSqrt = 999
for (const i in validFaces) {
const tPos = validFaces[i].targetPos
const cDist = new Vec3(tPos.x, tPos.y, tPos.z).distanceSquared(
bot.entity.position.offset(0, bot.entity.height, 0)
)
if (distSqrt > cDist) {
closest = validFaces[i]
distSqrt = cDist
}
}
await bot.lookAt(closest.targetPos, forceLook)
diggingFace = closest.face
} else {
// Block is obstructed return error?
throw new Error('Block not in view')
}
} else {
await bot.lookAt(block.position.offset(0.5, 0.5, 0.5), forceLook)
}
}
diggingTask = createTask()
bot._client.write('block_dig', {
status: 0, // start digging
location: block.position,
face: diggingFace // default face is 1 (top)
})
const waitTime = bot.digTime(block)
waitTimeout = setTimeout(finishDigging, waitTime)
bot.targetDigBlock = block
bot.swingArm()
swingInterval = setInterval(() => {
bot.swingArm()
}, 350)
function finishDigging () {
clearInterval(swingInterval)
clearTimeout(waitTimeout)
swingInterval = null
waitTimeout = null
if (bot.targetDigBlock) {
bot._client.write('block_dig', {
status: 2, // finish digging
location: bot.targetDigBlock.position,
face: diggingFace // hard coded to always dig from the top
})
}
bot.targetDigBlock = null
bot.lastDigTime = performance.now()
bot._updateBlockState(block.position, 0)
}
const eventName = `blockUpdate:${block.position}`
bot.on(eventName, onBlockUpdate)
bot.stopDigging = () => {
if (!bot.targetDigBlock) return
bot.removeListener(eventName, onBlockUpdate)
clearInterval(swingInterval)
clearTimeout(waitTimeout)
swingInterval = null
waitTimeout = null
bot._client.write('block_dig', {
status: 1, // cancel digging
location: bot.targetDigBlock.position,
face: 1 // hard coded to always dig from the top
})
const block = bot.targetDigBlock
bot.targetDigBlock = null
bot.lastDigTime = performance.now()
bot.emit('diggingAborted', block)
bot.stopDigging = noop
diggingTask.cancel(new Error('Digging aborted'))
}
function onBlockUpdate (oldBlock, newBlock) {
// vanilla server never actually interrupt digging, but some server send block update when you start digging
// so ignore block update if not air
// All block update listeners receive (null, null) when the world is unloaded. So newBlock can be null.
if (newBlock?.type !== 0) return
bot.removeListener(eventName, onBlockUpdate)
clearInterval(swingInterval)
clearTimeout(waitTimeout)
swingInterval = null
waitTimeout = null
bot.targetDigBlock = null
bot.lastDigTime = performance.now()
bot.emit('diggingCompleted', newBlock)
diggingTask.finish()
}
await diggingTask.promise
}
bot.on('death', () => {
bot.removeAllListeners('diggingAborted')
bot.removeAllListeners('diggingCompleted')
bot.stopDigging()
})
function canDigBlock (block) {
return (
block &&
block.diggable &&
block.position.offset(0.5, 0.5, 0.5).distanceTo(bot.entity.position.offset(0, 1.65, 0)) <= 5.1
)
}
function digTime (block) {
let type = null
let enchantments = []
// Retrieve currently held item ID and active enchantments from heldItem
const currentlyHeldItem = bot.heldItem
if (currentlyHeldItem) {
type = currentlyHeldItem.type
enchantments = currentlyHeldItem.enchants
}
// Append helmet enchantments (because Aqua Affinity actually affects dig speed)
const headEquipmentSlot = bot.getEquipmentDestSlot('head')
const headEquippedItem = bot.inventory.slots[headEquipmentSlot]
if (headEquippedItem) {
const helmetEnchantments = headEquippedItem.enchants
enchantments = enchantments.concat(helmetEnchantments)
}
const creative = bot.game.gameMode === 'creative'
return block.digTime(
type,
creative,
bot.entity.isInWater,
!bot.entity.onGround,
enchantments,
bot.entity.effects
)
}
bot.dig = dig
bot.stopDigging = noop
bot.canDigBlock = canDigBlock
bot.digTime = digTime
}
function noop (err) {
if (err) throw err
}