PrismarineJS / mineflayer

Create Minecraft bots with a powerful, stable, and high level JavaScript API.
https://prismarinejs.github.io/mineflayer/
MIT License
5.1k stars 921 forks source link

bot.blockAtCursor returns null when looking at sugarcane #2181

Closed LQR471814 closed 3 years ago

LQR471814 commented 3 years ago

Versions

Detailed description of a problem

I am building a bot that farms sugarcane in rows 2 blocks wide.

What did you try yet?

The way you would usually operate this kind of farm is by holding down "a" and "forward" and hold left click while looking diagonally to get all the sugarcane.

While it is possible to poll the bot many times per second checking for a sugarcane block in front of the block and having the bot dig the block and the block adjacent to it, this is highly inefficient since the dig function has to move the camera very frequently screwing with momentum and all those things.

To try an replicate the behavior of regular farming, I wanted to use the blockAtCursor method however it returned null when I aimed at the sugarcane. When I aiming at a different angle towards a wall beyond the sugarcane it returned the block of the wall rather than the sugarcane right in front of it. This occurs no matter where the bot is in relation to the sugarcane, inside, outside, in front of it etc...

Your current code

> bot.look(<towards the sugarcane>)
Promise
> bot.blockAtCursor()
null

Expected behavior

blockAtCursor should return the sugarcane block in which I can call dig on.

Additional context

I can vouch that the sugarcane does exist, I can find it with findBlock and I can also mine it.

u9g commented 3 years ago

Expected behavior, not ideal but blockAtCursor only returns solid blocks

LQR471814 commented 3 years ago

alright, rip

LQR471814 commented 3 years ago

If anyone is looking for a workaround to this, here's my re-implementation of the logic of blockAtCursor with a bit of math and the bot.blockAt method.

//? typescript by the way, you can just remove the special syntax for vanilla JS though
const raytrace = (bot: Bot, filter: string, radius: number, steps: number) => {
    const positionInDirection = (position: Vec3, distance: number, yaw: number) => { //? Position should be in format (x, z)
        return new Vec3(
            position.x - Math.sin(yaw) * distance,
            position.y,
            position.z - Math.cos(yaw) * distance,
        )
    }

    const position = bot.entity.position.offset(0, bot.entity.height, 0)
    const blocks = []

    let lastBlock = {
        position: new Vec3(NaN, NaN, NaN)
    }

    for (let r = 0; r < radius; r += radius / steps) {
        const block = bot.blockAt(positionInDirection(
            position, r,
            bot.entity.yaw
        ))

        if (
            block &&
            block.name === filter &&
            !block.position.equals(lastBlock.position)
        ) {
            blocks.push(block)
            lastBlock = block
        }
    }

    return blocks
}

One thing to keep in mind is that the function returns an array of blocks, it doesn't stop at the first one found. The array goes from closest first, furthest last.

The parameter filter is used to filter for blocks of a specific name (like oak_wood or anything on the Block.name property) Parameter radius is the distance that you want the bot to raytrace to. Parameter steps is the amount of times you want the bot to poll through the line. You can imagine a series of dots in a line coming out of the bot's head towards the direction it's looking at. If a block is intersecting one of these dots, it will be picked up. Increasing steps will consume more compute power but will pick up blocks that aren't "solid" (they don't occupy the full space of the block, like sugarcane or a fence) more easily.

eeoms commented 2 years ago

If anyone is looking for a workaround to this, here's my re-implementation of the logic of blockAtCursor with a bit of math and the bot.blockAt method.

//? typescript by the way, you can just remove the special syntax for vanilla JS though
const raytrace = (bot: Bot, filter: string, radius: number, steps: number) => {
    const positionInDirection = (position: Vec3, distance: number, yaw: number) => { //? Position should be in format (x, z)
        return new Vec3(
            position.x - Math.sin(yaw) * distance,
            position.y,
            position.z - Math.cos(yaw) * distance,
        )
    }

    const position = bot.entity.position.offset(0, bot.entity.height, 0)
    const blocks = []

    let lastBlock = {
        position: new Vec3(NaN, NaN, NaN)
    }

    for (let r = 0; r < radius; r += radius / steps) {
        const block = bot.blockAt(positionInDirection(
            position, r,
            bot.entity.yaw
        ))

        if (
            block &&
            block.name === filter &&
            !block.position.equals(lastBlock.position)
        ) {
            blocks.push(block)
            lastBlock = block
        }
    }

    return blocks
}

One thing to keep in mind is that the function returns an array of blocks, it doesn't stop at the first one found. The array goes from closest first, furthest last.

The parameter filter is used to filter for blocks of a specific name (like oak_wood or anything on the Block.name property) Parameter radius is the distance that you want the bot to raytrace to. Parameter steps is the amount of times you want the bot to poll through the line. You can imagine a series of dots in a line coming out of the bot's head towards the direction it's looking at. If a block is intersecting one of these dots, it will be picked up. Increasing steps will consume more compute power but will pick up blocks that aren't "solid" (they don't occupy the full space of the block, like sugarcane or a fence) more easily.

How would I implement this? I'm trying to make my bot basically hold left click while moving right, but blockAtCursor doesn't work with nether wart.

My code

async function dig() {
  if (bot.blockAtCursor().name !== 'air' && bot.blockAtCursor().name !== null) await bot.dig(bot.blockAtCursor(), false);
  dig()
}

setTimeout(async () => {
  bot.chat('/play sb')
}, 3000);

setTimeout(async () => {
  bot.chat('/is')
}, 4000);

setTimeout(async () => {
  dig();
}, 5000);