Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix several bugs in villager trading #3230

Merged
merged 3 commits into from
Dec 30, 2023

Conversation

evan-goode
Copy link
Contributor

These changes should fix issues with trades that have more than one item in the output stack, trades that cost more than one emerald, and trades with two input items, such as trades for enchanted books and tipped arrows.

Resolves #3209 and probably #2681.

I've been developing a bot to automate void trading, and while the current villager plugin works fine for trades of 1 emerald to 1 item, I encountered many issues with trades of 1 emerald to multiple items, multiple emeralds to one item, and trades with a second input item. After these changes, all trades I've tested work correctly.

I play on a vanilla 1.20.1 server.

I'll add comments on this PR detailing the reason for each change.

These changes should fix issues with trades that have more than one item
in the output stack, trades that cost more than one emerald, and trades
with two input items, such as trades for enchanted books and tipped
arrows.
limitItem !== null &&
targetItem.type === limitItem.type &&
targetItem.count >= limitItem.count
)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sometimes, tradeMatch is called with a null targetItem. If there is nothing in the targetItem slot, then the trade requirements cannot be satisfied, so return false in that case.

}

function expectTradeUpdate (window) {
const trade = window.selectedTrade
const hasItem = !!window.slots[2]

if (hasItem !== tradeMatch(trade.inputItem1, window.slots[0])) {
if (trade.hasItems2) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was a typo, the field is called hasItem2.

@@ -418,7 +423,7 @@ function inject (bot, { hideErrors }) {
}
} else if (window.type === 'minecraft:merchant') {
const toUpdate = []
if (slot <= 2 && !window.selectedTrade.tradeDisabled && expectTradeUpdate(window)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The updateSlot:2 event is already awaited by promisePutAway in putAway(), so if we await it here too, we'll never get it. I think waitForWindowUpdate should only wait for updates on slots other than the slot it was called on.

// After the trade goes through, if the inputs are still satisfied,
// expect another update in slot 2
await once(bot.currentWindow, 'updateSlot:2')
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the trade loop, if the requirements of a trade are still satisfied after putAway but the window doesn't have an item in slot 2, then putRequirements will not deposit anything, an update in slot 2 will not be awaited, and putAway will be immediately called again without anything in slot 2. So here, we should only consider the trading window "fully updated" if the state of slot 2 correctly reflects whether the trade inputs are satisfied.

throw new Error('Not enough item 1 to trade')
}
if (!hasEnoughItem2) {
throw new Error('Not enough item 2 to trade')
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Log a bit more information here. I'm happy to leave this out if it amounts to an unnecessary breaking change.

}
}
villager.on('updateSlot', listener)
})
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bot consistently gets hung up here, and I honestly can't figure out what the purpose of this is. It's possible that it was necessary before some of the other changes I made. @nickelpro do you remember by chance?


const input1 = slot1
? Math.max(0, Trade.realPrice - slot1.count)
: Trade.realPrice
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only put the requirements for a single trade at a time. This seems to greatly increase reliability, especially for trades with multiple inputs.

? Math.max(0, Trade.realPrice - slot1.count)
: Trade.realPrice
if (input1) {
await deposit(window, type1, metadata1, input1, 0)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only call deposit if we actually need to move items

@evan-goode
Copy link
Contributor Author

Could someone with permissions perhaps re-run https://github.com/PrismarineJS/mineflayer/actions/runs/6843109062/job/18605343146?pr=3230? The tests pass locally for me, this seems like a transient error unrelated to the changes.

@rom1504
Copy link
Member

rom1504 commented Dec 17, 2023

can you add a test to make sure what you fixed will keep working in the future ?

- Add a test trade taking 1 emerald and returning 4 glass
- Add a test trade taking [36 emeralds, 1 book] and returning 1 wooden sword
  (any unstackable should work to accurately simulate an enchanted_book
  trade)
- Increase maximumNbTradeUses to 12 for all test trades
@evan-goode evan-goode force-pushed the evan-goode/fix-trading branch from ce8702d to b1abd24 Compare December 21, 2023 22:40
@evan-goode
Copy link
Contributor Author

can you add a test to make sure what you fixed will keep working in the future ?

Sure, I added a couple tests in test/externalTests/trade.js for the types of trade that weren't working.

@rom1504 rom1504 merged commit 1caa2c2 into PrismarineJS:master Dec 30, 2023
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Closed
Development

Successfully merging this pull request may close these issues.

villager.trade(index, count) fails for enchanted books
2 participants