-
-
Notifications
You must be signed in to change notification settings - Fork 926
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ability to use an anvil fully (#1666)
* Add ability to use an anvil fully * create example code * fix pr * Update api.md * Update blocks.js * shorten matchWindowType * rename example * pass an item not a slot to anvil funcs * make working test * simplify anvil.js * vastly improve test * make transfer work with nbt * cleanup rename * enable second test * add many rename tests * add to docs * fix tests * actually fix tests * fix the customname * use nbt in putsomething * greatly improve tests * make putaway match nbt so it doesnt stack 2 swords and break stuff * fix anvil tests * temp fix by await in test instead of implem * set not add xp * fix xp level for real * stop closing anvil in combine and rename and improve resolve of putSelectedItemRange * don't fail fast * wait for experience in anvil methods * add noWaiting in putAway for villager either this is correct and client needs to simulate on itself either this is just hiding a bug in villager implem removed callbackify for this as it's not part of API and more convenient for opt arg * add close to anvil example, delete example.js * improve anvil example * improve anvil example more and fix creative resolve Co-authored-by: U9G <[email protected]> Co-authored-by: Romain Beaumont <[email protected]>
- Loading branch information
1 parent
a665909
commit 02769f3
Showing
12 changed files
with
479 additions
and
46 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
/** | ||
* This example demonstrates how to use anvils w/ mineflayer | ||
* the options are: (<Option> are required, [<Option>] are optional) | ||
* 1. "anvil combine <itemName1> <itemName2> [<name>]" | ||
* 2. "anvil rename <itemName> <name>" | ||
* | ||
* to use this: | ||
* /op anvilman | ||
* /gamemode anvilman creative | ||
* /xp set anvilman 999 levels | ||
* | ||
* Put an anvil near the bot | ||
* Give him a sword and an enchanted book | ||
* say list | ||
* say xp | ||
* say anvil combine diamond_sword enchanted_book | ||
*/ | ||
const mineflayer = require('mineflayer') | ||
|
||
if (process.argv.length < 4 || process.argv.length > 6) { | ||
console.log('Usage : node use_anvil.js <host> <port> [<name>] [<password>]') | ||
process.exit(1) | ||
} | ||
|
||
const bot = mineflayer.createBot({ | ||
host: process.argv[2], | ||
port: parseInt(process.argv[3]), | ||
username: process.argv[4] ? process.argv[4] : 'anvilman', | ||
password: process.argv[5] | ||
}) | ||
|
||
let mcData | ||
|
||
bot.on('spawn', () => { mcData = require('minecraft-data')(bot.version) }) | ||
|
||
bot.on('chat', async (username, message) => { | ||
const command = message.split(' ') | ||
|
||
switch (true) { | ||
case /^list$/.test(message): | ||
sayItems() | ||
break | ||
case /^toss \w+$/.test(message): | ||
// toss name | ||
// ex: toss diamond | ||
tossItem(command[1]) | ||
break | ||
case /^xp$/.test(message): | ||
bot.chat(bot.experience.level) | ||
break | ||
case /^gamemode$/.test(message): | ||
bot.chat(bot.game.gameMode) | ||
break | ||
case /^anvil combine \w+ \w+$/.test(message): // anvil firstSlot secondSlot | ||
combine(bot, command[2], command[3]) | ||
break | ||
case /^anvil combine \w+ \w+ (.+)$/.test(message): // anvil firstSlot secondSlot name | ||
combine(bot, command[2], command[3], command.slice(4).join(' ')) | ||
break | ||
case /^anvil rename \w+ (.+)/.test((message)): | ||
rename(bot, command[2], command.slice(3).join(' ')) | ||
break | ||
} | ||
}) | ||
|
||
function tossItem (name, amount) { | ||
amount = parseInt(amount, 10) | ||
const item = itemByName(name) | ||
if (!item) { | ||
bot.chat(`I have no ${name}`) | ||
} else if (amount) { | ||
bot.toss(item.type, null, amount, checkIfTossed) | ||
} else { | ||
bot.tossStack(item, checkIfTossed) | ||
} | ||
|
||
function checkIfTossed (err) { | ||
if (err) { | ||
bot.chat(`unable to toss: ${err.message}`) | ||
} else if (amount) { | ||
bot.chat(`tossed ${amount} x ${name}`) | ||
} else { | ||
bot.chat(`tossed ${name}`) | ||
} | ||
} | ||
} | ||
|
||
function itemByName (name) { | ||
return bot.inventory.items().filter(item => item.name === name)[0] | ||
} | ||
|
||
function itemToString (item) { | ||
if (item) { | ||
return `${item.name} x ${item.count}` | ||
} else { | ||
return '(nothing)' | ||
} | ||
} | ||
|
||
function sayItems (items = bot.inventory.items()) { | ||
const output = items.map(itemToString).join(', ') | ||
if (output) { | ||
bot.chat(output) | ||
} else { | ||
bot.chat('empty') | ||
} | ||
} | ||
|
||
function getAnvilIds () { | ||
const matchingBlocks = [mcData.blocksByName.anvil.id] | ||
if (mcData.blocksByName?.chipped_anvil) { | ||
matchingBlocks.push(mcData.blocksByName.chipped_anvil.id) | ||
matchingBlocks.push(mcData.blocksByName.damaged_anvil.id) | ||
} | ||
return matchingBlocks | ||
} | ||
|
||
async function rename (bot, itemName, name) { | ||
const anvilBlock = bot.findBlock({ | ||
matching: getAnvilIds() | ||
}) | ||
const anvil = await bot.openAnvil(anvilBlock) | ||
try { | ||
await anvil.rename(itemByName(itemName), name) | ||
bot.chat('Anvil used successfully.') | ||
} catch (err) { | ||
bot.chat(err.message) | ||
} | ||
anvil.close() | ||
} | ||
|
||
async function combine (bot, itemName1, itemName2, name) { | ||
const anvilBlock = bot.findBlock({ | ||
matching: getAnvilIds() | ||
}) | ||
const anvil = await bot.openAnvil(anvilBlock) | ||
try { | ||
bot.chat('Using the anvil...') | ||
await anvil.combine(itemByName(itemName1), itemByName(itemName2), name) | ||
bot.chat('Anvil used successfully.') | ||
} catch (err) { | ||
bot.chat(err.message) | ||
} | ||
anvil.close() | ||
} | ||
|
||
bot.on('error', console.log) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
const assert = require('assert') | ||
const { callbackify, sleep } = require('../promise_utils') | ||
const { once } = require('events') | ||
|
||
module.exports = inject | ||
|
||
function inject (bot) { | ||
const Item = require('prismarine-item')(bot.version) | ||
|
||
const matchWindowType = window => /minecraft:(?:chipped_|damaged_)?anvil/.test(window.type) | ||
|
||
async function openAnvil (anvilBlock) { | ||
const anvil = await bot.openBlock(anvilBlock) | ||
if (!matchWindowType(anvil)) { | ||
throw new Error('This is not a anvil-like window') | ||
} | ||
|
||
function err (name) { | ||
anvil.close() | ||
throw new Error(name) | ||
} | ||
|
||
function sendItemName (name) { | ||
if (bot.supportFeature('useMCItemName')) { | ||
bot._client.writeChannel('MC|ItemName', name) | ||
} else { | ||
bot._client.write('name_item', { name }) | ||
} | ||
} | ||
|
||
async function addCustomName (name) { | ||
if (!name) return | ||
for (let i = 1; i < name.length + 1; i++) { | ||
sendItemName(name.substring(0, i)) | ||
await sleep(50) | ||
} | ||
} | ||
async function putInAnvil (itemOne, itemTwo) { | ||
await putSomething(0, itemOne.type, itemOne.metadata, itemOne.count, itemOne.nbt) | ||
sendItemName('') // sent like this by vnailla | ||
if (!bot.supportFeature('useMCItemName')) sendItemName('') | ||
await putSomething(1, itemTwo.type, itemTwo.metadata, itemTwo.count, itemTwo.nbt) | ||
} | ||
|
||
async function combine (itemOne, itemTwo, name) { | ||
if (name?.length > 35) err('Name is too long.') | ||
if (bot.supportFeature('useMCItemName')) { | ||
bot._client.registerChannel('MC|ItemName', 'string') | ||
} | ||
|
||
assert.ok(itemOne && itemTwo) | ||
const { xpCost: normalCost } = Item.anvil(itemOne, itemTwo, bot.game.gameMode === 'creative', name) | ||
const { xpCost: inverseCost } = Item.anvil(itemTwo, itemOne, bot.game.gameMode === 'creative', name) | ||
if (normalCost === 0 && inverseCost === 0) err('Not anvil-able (in either direction), cancelling.') | ||
|
||
const smallest = (normalCost < inverseCost ? normalCost : inverseCost) === 0 ? inverseCost : 0 | ||
if (bot.game.gameMode !== 'creative' && bot.experience.level < smallest) { | ||
err('Player does not have enough xp to do action, cancelling.') | ||
} | ||
|
||
const xpPromise = bot.game.gameMode === 'creative' ? Promise.resolve() : once(bot, 'experience') | ||
if (normalCost === 0) await putInAnvil(itemTwo, itemOne) | ||
else if (inverseCost === 0) await putInAnvil(itemOne, itemTwo) | ||
else if (normalCost < inverseCost) await putInAnvil(itemOne, itemTwo) | ||
else await putInAnvil(itemTwo, itemOne) | ||
|
||
await addCustomName(name) | ||
await bot.putAway(2) | ||
await xpPromise | ||
} | ||
|
||
async function rename (item, name) { | ||
if (name?.length > 35) err('Name is too long.') | ||
if (bot.supportFeature('useMCItemName')) { | ||
bot._client.registerChannel('MC|ItemName', 'string') | ||
} | ||
assert.ok(item) | ||
const { xpCost: normalCost } = Item.anvil(item, null, bot.game.gameMode === 'creative', name) | ||
if (normalCost === 0) err('Not valid rename, cancelling.') | ||
|
||
if (bot.game.gameMode !== 'creative' && bot.experience.level < normalCost) { | ||
err('Player does not have enough xp to do action, cancelling.') | ||
} | ||
const xpPromise = once(bot, 'experience') | ||
await putSomething(0, item.type, item.metadata, item.count, item.nbt) | ||
sendItemName('') // sent like this by vnailla | ||
if (!bot.supportFeature('useMCItemName')) sendItemName('') | ||
await addCustomName(name) | ||
await bot.putAway(2) | ||
await xpPromise | ||
} | ||
|
||
async function putSomething (destSlot, itemId, metadata, count, nbt) { | ||
const options = { | ||
window: anvil, | ||
itemType: itemId, | ||
metadata, | ||
count, | ||
nbt, | ||
sourceStart: anvil.inventoryStart, | ||
sourceEnd: anvil.inventoryEnd, | ||
destStart: destSlot, | ||
destEnd: destSlot + 1 | ||
} | ||
await bot.transfer(options) | ||
} | ||
|
||
anvil.combine = callbackify(combine) | ||
anvil.rename = callbackify(rename) | ||
|
||
return anvil | ||
} | ||
|
||
bot.openAnvil = callbackify(openAnvil) | ||
} |
Oops, something went wrong.