Skip to content
19 changes: 15 additions & 4 deletions lib/plugins/inventory.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,22 @@ function inject (bot, { hideErrors }) {
cursorZ: -1
})
} else if (bot.supportFeature('useItemWithOwnPacket')) {
bot._client.write('use_item', {
const packet = {
hand: offHand ? 1 : 0,
sequence,
rotation: { x: 0, y: 0 }
})
sequence
}

// In 1.21+, use_item packet format changed from rotation vec2f to separate yaw/pitch floats
// TODO: Replace with proper feature flag once added to minecraft-data
if (bot.supportFeature('useItemHasSeparateYawPitch') ||
(bot.version && bot.version.split('.').map(Number)[1] >= 21)) {
packet.yaw = 0
packet.pitch = 0
} else {
packet.rotation = { x: 0, y: 0 }
}

bot._client.write('use_item', packet)
}
}

Expand Down
19 changes: 16 additions & 3 deletions lib/plugins/place_entity.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,22 @@ function inject (bot) {

if (type === 'boat') {
if (bot.supportFeature('useItemWithOwnPacket')) {
bot._client.write('use_item', {
hand: options.offhand ? 1 : 0
})
const packet = {
hand: options.offhand ? 1 : 0,
sequence: 0
}

// In 1.21+, use_item packet format changed from rotation vec2f to separate yaw/pitch floats
// TODO: Replace with proper feature flag once added to minecraft-data
if (bot.supportFeature('useItemHasSeparateYawPitch') ||
(bot.version && bot.version.split('.').map(Number)[1] >= 21)) {
packet.yaw = 0
packet.pitch = 0
} else {
packet.rotation = { x: 0, y: 0 }
}

bot._client.write('use_item', packet)
} else {
bot._client.write('block_place', {
location: { x: -1, y: -1, z: -1 },
Expand Down
42 changes: 42 additions & 0 deletions test/externalTests/activateItem.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
const assert = require('assert')

module.exports = () => async (bot) => {
// Test that activateItem works and has observable effects on the world
// This reproduces the issue where activateItem fails on 1.21+ due to packet format changes

const Item = require('prismarine-item')(bot.registry)

// Set up conditions for testing item activation
await bot.test.setInventorySlot(36, new Item(bot.registry.itemsByName.bread.id, 5, 0))
await bot.test.becomeSurvival()

// Reduce food level so bread can be consumed
while (bot.food > 10) {
if (bot.supportFeature('effectAreNotPrefixed')) bot.test.sayEverywhere('/effect give @s hunger 1 255')
else if (bot.supportFeature('effectAreMinecraftPrefixed')) bot.test.sayEverywhere(`/effect ${bot.username} minecraft:hunger 1 255`)
await bot.test.wait(500)
}

const initialFood = bot.food
const initialItemCount = bot.heldItem.count

// Test that activateItem works and has observable server-side effects
assert.ok(!bot.usingHeldItem, 'Should not be using item initially')

// Use activateItem instead of consume to test the packet format
bot.activateItem()
assert.ok(bot.usingHeldItem, 'Should be using item after activateItem')

// Wait for the server to process the action and observe effects
await bot.test.wait(2000)

// Verify the server processed the item use by checking observable changes
const finalFood = bot.food
const finalItemCount = bot.heldItem ? bot.heldItem.count : 0

// The server should have processed the bread consumption
assert.ok(finalFood > initialFood || finalItemCount < initialItemCount,
'Server should have processed item activation (food increased or item consumed)')

assert.ok(!bot.usingHeldItem, 'Should not be using item after server processes it')
}