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

Crafting Problem (Item tossed when crafted) #905

Closed Kshitij-Jande closed 4 years ago

Kshitij-Jande commented 4 years ago

Versions

Ok, so the question I have it that, I want a bot to craft 36x Stacks of Iron Ingots from 4 Stacks of Iron Blocks. I want to achieve this only using the 2x2 grid in the inventory. Also, I want to make it Fast, and Efficient (I mean, that since some servers lag, the crafting becomes a bit slow). I've tried this:

function craftItem (name, amount) {
  amount = parseInt(amount, 10)
  const item = require('minecraft-data')(bot.version).findItemOrBlockByName(name)
  const craftingTable = bot.findBlock({
    matching: 58
  })
  blockClick(2832, 95, 2101)
  if (item) {
    const recipe = bot.recipesFor(item.id, null, 1, null)[0]
    if (recipe) {
      console.log(`Crafting Iron Ingots...`)
      bot.craft(recipe, amount, null, (err) => {
        if (err) {
          console.log(`An Error Occured!`)
        } else {
          console.log(`Crafted all Iron Ingots successfully.`)
        }
      })
    } else {
      console.log(`I cannot make ${name}`)
    }
  } else {
    console.log(`unknown item: ${name}`)
  }
}

This is from the Examples, but as soon as the bot crafts it, it drops the Iron Ingots. Also, how do I use the bot.moveSlotItem() to maybe craft the items in the inventory. I've tried this:

bot.on('chat', (username, message) => {
    if (message == 'craft') {
        bot.clickWindow(36)
        setTimeout(() => {
            bot.putAway(1)
            setTimeout(() => {
                bot.clickWindow(0)
                setTimeout(() => {
                    bot.putAway(9)
                }, 1000);
            }, 1000);
        }, 1000);
    }
})

It'd be great if you can help me! Thanks in advance! :)

wvffle commented 4 years ago

Crafting speed is discussed in #257 Tossing after crafting is discussed in #661

So the issue that remains is crafting in inventory slots.

wvffle commented 4 years ago

https://github.com/PrismarineJS/mineflayer/blob/caccdb22442b18390de6371ffa177f5ce8160447/lib/plugins/craft.js#L42

bot.craft() has crafting in inventory slots already implemented. @Kshitij-Jande Check if it works for you.

rom1504 commented 4 years ago

item tossed when crafted should not happen. it was a bug in the past, and I'm pretty sure it doesn't happen in vanilla. If someone can reproduce it, it should definitely be fixed (and automatically tested)

Kshitij-Jande commented 4 years ago

Hey @rom1504 and @wvffle again! So I tried this bot.clickWindow(slot, mouseButton, mode, cb) However, the MODE 1 and BUTTON 0 should do a SHIFT+CLICK. But instead, it shows that the more is strictly 0.

Karang commented 4 years ago

Hi, I've encountered the same bug as well, did a bit of debugging and I think I have a solution. I tested on a local vanilla server in 1.12.2.

When picking the crafting result, the server update the slot before acknowledging the click:

client->server: play window_click : {"windowId":0,"slot":0,"mouseButton":0,"action":5,"mode":0,"item":{"blockId":58,"itemCount":1,"itemDamage":0}}
client<-server: play.unlock_recipes :{"action":1,"craftingBookOpen":false,"filteringCraftable":false,"recipes1":[]}
client<-server: play.set_slot :{"windowId":0,"slot":0,"item":{"blockId":-1}}
client<-server: play.set_slot :{"windowId":0,"slot":0,"item":{"blockId":72,"itemCount":1,"itemDamage":0}}
client<-server: play.set_slot :{"windowId":0,"slot":0,"item":{"blockId":143,"itemCount":1,"itemDamage":0}}
client<-server: play.set_slot :{"windowId":0,"slot":0,"item":{"blockId":-1}}
client<-server: play.transaction :{"windowId":0,"action":5,"accepted":true}

This result in slots[0] being set to null before window.acceptSwapAreaLeftClick(click) is called, so it cannot set window.selectedItem to the picked item (it is set to null). Because of that, putSelectedItemRange does nothing and the item is toss later because the item was never stored in an inventory slot.

The solution I have is to save the item that was in the slot at the moment of the click (in the click object itself) so we can access it when the click is accepted, even if the slot has been changed (possibly multiple time) by the server.

This requires changes in both mineflayer and prismarine-windows:

https://github.com/PrismarineJS/mineflayer/blob/master/lib/plugins/inventory.js#L512

Add:

item: slot === -999 ? null: window.slots[slot],
}

https://github.com/PrismarineJS/prismarine-windows/blob/master/lib/windows.js#L141 https://github.com/PrismarineJS/prismarine-windows/blob/master/lib/windows.js#L176

Change to:

const item = click.item;

I also noticed another bug in https://github.com/PrismarineJS/mineflayer/blob/master/lib/plugins/craft.js#L154

When forcing the crafting result to be the result of the recipe, instead of what the server sent, it can be that the metadata of the object is changed (for instance, when crafting planks from different kind of logs), so I just removed the lines 153-154.

Ps: with both fixes I can craft without tossing or errors in player inventory

Cheers

rom1504 commented 4 years ago

Why not open a pull request with these fixes ?

On Thu, Apr 9, 2020, 20:43 Arthur Hennequin notifications@github.com wrote:

Hi, I've encountered the same bug as well, did a bit of debugging and I think I have a solution. I tested on a local vanilla server in 1.12.2.

When picking the crafting result, the server update the slot before acknowledging the click:

client->server: play window_click : {"windowId":0,"slot":0,"mouseButton":0,"action":5,"mode":0,"item":{"blockId":58,"itemCount":1,"itemDamage":0}} client<-server: play.unlock_recipes :{"action":1,"craftingBookOpen":false,"filteringCraftable":false,"recipes1":[]} client<-server: play.set_slot :{"windowId":0,"slot":0,"item":{"blockId":-1}} client<-server: play.set_slot :{"windowId":0,"slot":0,"item":{"blockId":72,"itemCount":1,"itemDamage":0}} client<-server: play.set_slot :{"windowId":0,"slot":0,"item":{"blockId":143,"itemCount":1,"itemDamage":0}} client<-server: play.set_slot :{"windowId":0,"slot":0,"item":{"blockId":-1}} client<-server: play.transaction :{"windowId":0,"action":5,"accepted":true}

This result in slots[0] being set to null before window.acceptSwapAreaLeftClick(click) is called, so it cannot set window.selectedItem to the picked item (it is set to null). Because of that, putSelectedItemRange does nothing and the item is toss later because the item was never stored in an inventory slot.

The solution I have is to save the item that was in the slot at the moment of the click (in the click object itself) so we can access it when the click is accepted, even if the slot has been changed (possibly multiple time) by the server.

This requires changes in both mineflayer and prismarine-windows:

https://github.com/PrismarineJS/mineflayer/blob/master/lib/plugins/inventory.js#L512

Add:

item: slot === -999 ? null: window.slots[slot], }

https://github.com/PrismarineJS/prismarine-windows/blob/master/lib/windows.js#L141

https://github.com/PrismarineJS/prismarine-windows/blob/master/lib/windows.js#L176

Change to:

const item = click.item;

I also noticed another bug in https://github.com/PrismarineJS/mineflayer/blob/master/lib/plugins/craft.js#L154

When forcing the crafting result to be the result of the recipe, instead of what the server sent, it can be that the metadata of the object is changed (for instance, when crafting planks from different kind of logs), so I just removed the lines 153-154.

Ps: with both fixes I can craft without tossing or errors in player inventory

Cheers

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/PrismarineJS/mineflayer/issues/905#issuecomment-611690937, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAR437X7T4CDTIVNSCDI573RLYJM5ANCNFSM4KNCNJKA .

Karang commented 4 years ago

Done https://github.com/PrismarineJS/mineflayer/pull/940 https://github.com/PrismarineJS/prismarine-windows/pull/6