PrismarineJS / mineflayer

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

Improve chest drop speed #1085

Open TheDeaX-Lab opened 4 years ago

TheDeaX-Lab commented 4 years ago

[ ] The FAQ doesn't contain a resolution to my issue

Versions

Detailed description of a problem

Very slowly cleans the chest

Current code

bot.closeWindowPromise = (window) => {
            return new Promise(resolve => {
                let tmp_func = (window1) => {
                    if (window.id === window1.id) {
                        bot.removeListener("windowClose", tmp_func)
                        resolve()
                    }
                }
                bot.on("windowClose", tmp_func)
                bot.closeWindow(window)
            })
        }

bot.openChestPromise = (chestBlock) => {
            return new Promise((resolve) => {
                let chest
                let tmp_func = (window) => {
                    if (window.type === "minecraft:chest") {
                        bot.removeListener("windowOpen", tmp_func)
                        chest.window = window
                        chest.closePromise = () => bot.closeWindowPromise(window)

                        chest.depositPromise = (itemType, metadata, count) => {
                            return new Promise(resolve1 => {
                                chest.deposit(itemType, metadata, count, () => {
                                    resolve1()
                                })
                            })
                        }

                        chest.withdrawPromise = (itemType, metadata, count) => {
                            return new Promise(resolve1 => {
                                chest.withdraw(itemType, metadata, count, () => {
                                    resolve1()
                                })
                            })
                        }

                        resolve(chest)
                    }
                }
                bot.on("windowOpen", tmp_func)
                chest = bot.openChest(chestBlock)
            })
        }

chestClear = await bot.openChestPromise(chestBlockClear);
                try {
                    await chestClear.withdrawPromise(103, null, 36 * 64)
                } catch (e) {}
                await chestClear.closePromise()

Expected behavior

he must quickly drag 36 stacks of any items into his inventory in 1 second

Additional context

We have already tried to watch the LowLevel API in https://wiki.vg/Protocol#Click_Window many of these buttons do not work at all

Karang commented 4 years ago

You should have defined "Very slow" :)

Here are some numbers:

Found chest at (36, 72, 227)
There are 3456 dirt in chest
Withdraw 2304 dirt took 3598.56 ms

Found chest at (36, 72, 227)
There are 1152 dirt in chest
Deposit 2304 dirt took 3599.53 ms

So it transfers 36 stacks in 3.6 s, that's 1 stack every 2 ticks. I wasn't able to do faster by hand (shift+click) or using impact's steal button. Do you have evidences this is even possible ? It might be possible to do 1 stack per tick, but that would still take more than 1 second. I don't think less than 1 tick is possible as it would be limited by the server.

Minimal example to reproduce the benchmark:

bot.once('spawn', () => {
  const mcData = require('minecraft-data')(bot.version)
  bot.on('chat', (username, message) => {
      const ids = [mcData.blocksByName['chest'].id]
      const dirt = mcData.blocksByName['dirt'].id
      const block = bot.findBlock({ matching: ids, maxDistance: 128 })
      if (block) {
        console.log('Found chest at ' + block.position)
        const chest = bot.openChest(block)
        chest.on('open', () => {
          let count = chest.count(dirt, 0)
          let invcount = bot.inventory.count(dirt, 0)
          console.log(`There are ${count} dirt in chest`)
          if (invcount > 0) {
            const startTime = performance.now()
            chest.deposit(dirt, 0, invcount, () => {
              console.log(`Deposit ${invcount} dirt took ${(performance.now() - startTime).toFixed(2)} ms`)
              chest.close()
            })
          } else {
            const startTime = performance.now()
            count = Math.min(36*64, count)
            chest.withdraw(dirt, 0, count, () => {
              console.log(`Withdraw ${count} dirt took ${(performance.now() - startTime).toFixed(2)} ms`)
              chest.close()
            })
          }
        })
      }
  })
})
TheDeaX-Lab commented 4 years ago

@Karang can this be a problem that there are 8 tps on the server? 1 minute for clean chest

Karang commented 4 years ago

@Karang can this be a problem that there are 8 tps on the server? 1 minute for clean chest

Yeah definitely, in the bot implementation the server needs to confirm the clicks, so if it's running slow, the transfer will also be slow. Are you able to transfer faster than that manually on low tps ?

Karang commented 4 years ago

You can make it empty the block instantly if you confirm the transactions yourself:

Always exectute the if at https://github.com/PrismarineJS/mineflayer/blob/master/lib/plugins/inventory.js#L554

This works well on vanilla servers that don't have anti-cheat, but it might be caught by anti-cheat plugins. Maybe the solution would be to add a timeout and confirm the transaction if it wasn't confirmed in less than 1 tick.

@rom1504 what do you think ?

rom1504 commented 4 years ago

Aren't there cases if you continue sending things before the server confirmed the transaction that the server will reject some future transactions ?

Karang commented 4 years ago

In vanilla, the server doesn't complain, it sends the confirmation a bit later and we can ignore it in mineflayer. I'm more worried about spigot/paper and anti-cheat / anti-spam plugins.

Also that would mean that if the server reject the transaction, we should go back and redo the action, so the auto confirmation works only if everything goes well. Maybe we could add an option to the inventory plugin to enable auto confirmation for servers that lag a lot or unprotected servers, but disabled by default ?

rom1504 commented 4 years ago

yeah I think adding an option is pretty safe. If in the future we used it a lot and see that it works pretty well in almost all cases we could change the default to true

TheDeaX-Lab commented 4 years ago

You can make it empty the block instantly if you confirm the transactions yourself:

Always exectute the if at https://github.com/PrismarineJS/mineflayer/blob/master/lib/plugins/inventory.js#L554

This works well on vanilla servers that don't have anti-cheat, but it might be caught by anti-cheat plugins. Maybe the solution would be to add a timeout and confirm the transaction if it wasn't confirmed in less than 1 tick.

@rom1504 what do you think ?

@Karang I fixed the problems, there was a bad connection with the server, even at 8 tps the speed is decent. But I would like to learn more about the option if the server does not have anti-cheats and you can activate later confirmation of clicks.

do you know MouseTweaks? There you can move everything from the chest to inventory for about 1 tick and vice versa

TheDeaX-Lab commented 4 years ago

and why the ability to click on the middle mouse button does not work? In some servers, in the homemade ChestShop plugin, they read the middle button how to buy everything or sell everything in survival. Had to comment lines with assert

Karang commented 4 years ago

You can make it empty the block instantly if you confirm the transactions yourself: Always exectute the if at https://github.com/PrismarineJS/mineflayer/blob/master/lib/plugins/inventory.js#L554 This works well on vanilla servers that don't have anti-cheat, but it might be caught by anti-cheat plugins. Maybe the solution would be to add a timeout and confirm the transaction if it wasn't confirmed in less than 1 tick. @rom1504 what do you think ?

@Karang I fixed the problems, there was a bad connection with the server, even at 8 tps the speed is decent. But I would like to learn more about the option if the server does not have anti-cheats and you can activate later confirmation of clicks.

do you know MouseTweaks? There you can move everything from the chest to inventory for about 1 tick and vice versa

With the modification I gave you, it can move everything instantly too.

and why the ability to click on the middle mouse button does not work?

Someone needs to implement it, it's just not required for basic bot operations on a vanilla server, so it is low priority. Feel free to submit a PR.

Hycord commented 4 years ago

Is this still an active issue that needs to be fixed?

imharvol commented 4 years ago

Is this still an active issue that needs to be fixed?

If you mean the middle click, it's not implemented yet, so you are free to work on it :smile: https://github.com/PrismarineJS/mineflayer/blob/5795f99ca576a73b3607acf6d3c2fb9f91cc86a2/lib/plugins/inventory.js#L527-L528

mafio900 commented 3 years ago

Is this still an active issue that needs to be fixed?

If you mean the middle click, it's not implemented yet, so you are free to work on it 😄 https://github.com/PrismarineJS/mineflayer/blob/5795f99ca576a73b3607acf6d3c2fb9f91cc86a2/lib/plugins/inventory.js#L527-L528

Will it ever be added?

u9g commented 3 years ago

Is this still an active issue that needs to be fixed?

If you mean the middle click, it's not implemented yet, so you are free to work on it 😄 https://github.com/PrismarineJS/mineflayer/blob/5795f99ca576a73b3607acf6d3c2fb9f91cc86a2/lib/plugins/inventory.js#L527-L528

Will it ever be added?

If you do it sure