PrismarineJS / mineflayer

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

Block Iterator function, similar to find block. [Quality of Life] #1081

Closed TheDudeFromCI closed 4 years ago

TheDudeFromCI commented 4 years ago

Is your feature request related to a problem? Please describe.

I have an instance where I would like to iterate over a region of blocks, looking for a specific set of conditions. (Basically a block-scanning operation) I'm not looking for a block and I don't need to sort by distance or anything like that.

Describe the solution you'd like

I find the best solution would be to extract that block iterator out of the findBlock function into its own function. This would allow the bot to use a general-purpose block iterator with a callback for an action to be performed on that block.

The findBlock function could then call on that function, making it more recyclable, too.

Describe alternatives you've considered

My current method abuses the findBlock function. It works well but feels messy and hack-ish.

function myAction(block)
{
    // ...
}

bot.findBlock({
    maxDistance = 16,
    matching: block => {
        myAction(block);
        return false;
    },
})

Additional context

This is a very simple quality of life feature request. It's easily doable without any changes to the code base but the API feels a bit messy. It also doesn't allow the bounds of a specific region to be specified, which would be super nice.

minPos = {x: 10, y: 20: z: 30};
maxPos = {x: 15, y: 50, z: 35};
// Iterate over the blocks within the given region between minPos and maxPos
TheDudeFromCI commented 4 years ago

Actually also having a region indicator on the standard findBlock operation would also be super useful as well.

Karang commented 4 years ago

Block iterators are defined here https://github.com/PrismarineJS/mineflayer/blob/master/lib/iterators.js If you want to iterate over a simple cuboid region, it is as simple as:

const cursor = new Vec3(0, 0, 0)
for (cursor.x = min.x; cursor.x < max.x; cursor.x++) {
  for (cursor.y = min.y; cursor.y < max.y; cursor.y++) {
    for (cursor.z = min.z; cursor.z < max.z; cursor.z++) {
      const block = bot.blockAt(cursor)
      // do your stuff
    }
  }
}

I don't think you need an iterator for that, but feel free to add it to iterator.js If you prefer a search order similar to findBlocks (with maxDistance defined as a 3D manhattan distance, thus forming an octahedron arround the player), you may use the octahedron iterator directly:

const it = new OctahedronIterator(start, maxDistance))
let next = start
while (next) {
  const block = bot.blockAt(next)
  // do your stuff
  next = it.next()
}

Abusing the findBlock for that is definitly wrong as it will do some unnecessary stuff.

What do you mean by "a region indicator" ?

TheDudeFromCI commented 4 years ago

While I could use those iterators, even roll my own like your top example, I was more specifically referring to the implementation used by findBlock to iterate over blocks in a more performant way. I can iterate over blocks already, I just want to do it very fast. My request wasn't referring to the order in which blocks were iterated, but especially to do it quickly as findBlock does.

By region indicator, I was just talking about a rectangular bounding region to select blocks in. The min and max positions in your example.

Karang commented 4 years ago

FindBlock is efficient because it skips some blocks, if you want to iterate over all blocks to do some action, you can't skip blocks. If you want to iterate over blocks of a specific type in an area, you should use findBlocks and then iterate on the returned list of positions.

Btw, if your matching always return false, it will never test any block because it will skip the entire sections.

TheDudeFromCI commented 4 years ago

Ah, so there's no speeding this up then, I see. Alright, thanks.

rom1504 commented 4 years ago

Could you describe what you actually want to do ? What's your condition ? I bet there is something that would work well in your case.

TheDudeFromCI commented 4 years ago

Just general purpose. Nothing I need at this exact time.

In my current instance, I only need to search for a small 5x5x5 area to decide the optimal position to stand when interacting with a block. So performance is not a big deal here.

However, I imagine there are many instances where I'll need to scan much bigger areas, say 16x16x16 or 32x32x32, running lots of calculations per block to decide more complex functions.

My long term goal is to make my bots behave as human-like as possible, so I want to eventually analyze everything in the way a human would. I.e, don't stand on gravel near a ravine, don't mine down, don't mine blocks you can't see (xray), plan-out building structures to adapt to the landscape and block placement order, etc.

JavaScript is a much, much, much slower language than I'm used to (I'm used to very low-level programming), and it's only pseudo-multi-threaded, so I'm scrapping for every clock cycle of performance I can find. :sweat_smile:

It's not something that needs fixing. I'm just still learning what tools are available.

Karang commented 4 years ago

My long term goal is to make my bots behave as human-like as possible, so I want to eventually analyze everything in the way a human would. I.e, don't stand on gravel near a ravine, don't mine down, don't mine blocks you can't see (xray), plan-out building structures to adapt to the landscape and block placement order, etc.

For that, consider contributing to pathfinder, the goals you listed here are (or should be) on the TODO list. The analysis you describe can be done as part of the path search (in the movement generator), it's relatively easy to integrate.

JavaScript is a much, much, much slower language than I'm used to (I'm used to very low-level programming), and it's only pseudo-multi-threaded, so I'm scrapping for every clock cycle of performance I can find. sweat_smile

JS is pretty fast nowadays. Well written, it can compete with lower-levels languages quite easely, and is far easier to maintain. Out of curiosity, what do you consider low-level programming ? As for the multi-threading, the worker threads of node js are working quite well, so it's just a matter of defining what you want to use the threads for.

TheDudeFromCI commented 4 years ago

Don't worry, I have full plans of looking into contributing to pathfinder. It's a well-written plugin with lots of potentials. It'll definitely help quite a bit with a ton of stuff.

As for what I consider low-level, I normally work with Java and C#, and have messed a bit with C++. I primarily target game engine development in all 3 languages, where I need to actively hit that 90+ fps mark no matter what the player tries to throw at it. Languages like Python and JS are nice to play with but feel sluggish compared to the manual memory management that I'm used to. (Yes I know Java and C# are GC based, but creating garbage in a game puts more work on the GC and makes the game jitter and leaves unstable framerates. Very messy and disorienting.)

I kind of lived by a rule to pool every object, reuse anything and every variable possible, minimize garbage, keep everything cached where practical, etc. I have rewritten entire frameworks just to save a few ms of processing time per frame. I can't tell you how many thousands of hours of my life I poured into various profilers, shaving off a few clock cycles here, few clock cycles there.