Skip to content

Commit 47a41c6

Browse files
Fix activateItem always having the same rotation (#3840)
* Use bot rotation for use_item packet * Add test for activateItem rotation in use_item packet Verifies that bot.activateItem() sends the bot's current yaw and pitch (converted via toNotchianYaw/toNotchianPitch) in the use_item packet's rotation field. Skips on protocol versions before 1.21.1 where the rotation field does not exist. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Add external test for activateItem rotation Verifies that activateItem sends the bot's actual yaw/pitch in the use_item packet by intercepting the outgoing packet and checking the rotation values match expected Notchian angles for two different look directions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8af96fc commit 47a41c6

3 files changed

Lines changed: 129 additions & 1 deletion

File tree

lib/plugins/inventory.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
const assert = require('assert')
22
const { Vec3 } = require('vec3')
33
const { once, sleep, createDoneTask, createTask, withTimeout } = require('../promise_utils')
4+
const { toNotchianYaw, toNotchianPitch } = require('../conversions')
45

56
module.exports = inject
67

@@ -128,7 +129,10 @@ function inject (bot, { hideErrors }) {
128129
bot._client.write('use_item', {
129130
hand: offHand ? 1 : 0,
130131
sequence,
131-
rotation: { x: 0, y: 0 }
132+
rotation: {
133+
x: toNotchianYaw(bot.entity.yaw),
134+
y: toNotchianPitch(bot.entity.pitch)
135+
}
132136
})
133137
}
134138
}

test/externalTests/activateItem.js

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
const assert = require('assert')
2+
3+
module.exports = () => async (bot) => {
4+
const Item = require('prismarine-item')(bot.registry)
5+
6+
// Test that activateItem sends the bot's actual rotation (PR #3840).
7+
// Only applies to versions that use the 'use_item' packet with rotation.
8+
if (!bot.supportFeature('useItemWithOwnPacket')) return
9+
10+
await bot.test.becomeCreative()
11+
await bot.test.clearInventory()
12+
13+
const snowballItem = bot.registry.itemsByName.snowball
14+
if (!snowballItem) return
15+
16+
// Give the bot a snowball and switch to survival to throw it
17+
await bot.test.setInventorySlot(36, new Item(snowballItem.id, 16, 0))
18+
await bot.test.becomeSurvival()
19+
await bot.test.wait(250)
20+
21+
// Look south (+Z direction): yaw = PI, pitch = 0
22+
await bot.look(Math.PI, 0, true)
23+
await bot.test.wait(250)
24+
25+
// Intercept the outgoing use_item packet to verify rotation
26+
const sentPacket = await new Promise((resolve) => {
27+
const origWrite = bot._client.write
28+
bot._client.write = function (name, data) {
29+
origWrite.apply(bot._client, arguments)
30+
if (name === 'use_item') {
31+
bot._client.write = origWrite
32+
resolve(data)
33+
}
34+
}
35+
bot.activateItem()
36+
})
37+
38+
// Verify the rotation was sent (not zeros)
39+
assert(sentPacket.rotation, 'use_item packet should have rotation field')
40+
const { x: sentYaw, y: sentPitch } = sentPacket.rotation
41+
42+
// With pitch = 0, the notchian pitch should be ~0
43+
assert(Math.abs(sentPitch) < 1, `Pitch should be near 0 for level look, got ${sentPitch}`)
44+
45+
// With yaw = PI (south), the notchian yaw should be ~0 (due to toNotchianYaw = degrees(PI - yaw))
46+
// toNotchianYaw(PI) = degrees(PI - PI) = 0
47+
assert(Math.abs(sentYaw) < 1, `Yaw should be near 0 for south-facing look, got ${sentYaw}`)
48+
49+
// Now test a different direction: look east (yaw = PI/2 in mineflayer)
50+
await bot.look(Math.PI * 3 / 2, -0.5, true)
51+
await bot.test.wait(250)
52+
53+
const sentPacket2 = await new Promise((resolve) => {
54+
const origWrite = bot._client.write
55+
bot._client.write = function (name, data) {
56+
origWrite.apply(bot._client, arguments)
57+
if (name === 'use_item') {
58+
bot._client.write = origWrite
59+
resolve(data)
60+
}
61+
}
62+
bot.activateItem()
63+
})
64+
65+
const { x: sentYaw2, y: sentPitch2 } = sentPacket2.rotation
66+
67+
// toNotchianYaw(3*PI/2) = degrees(PI - 3*PI/2) = degrees(-PI/2) = -90
68+
assert(Math.abs(sentYaw2 - (-90)) < 1, `Yaw should be near -90 for east-facing look, got ${sentYaw2}`)
69+
70+
// toNotchianPitch(-0.5) = degrees(0.5) ≈ 28.65
71+
const expectedPitch = (0.5 * 180) / Math.PI
72+
assert(Math.abs(sentPitch2 - expectedPitch) < 1, `Pitch should be near ${expectedPitch}, got ${sentPitch2}`)
73+
74+
await bot.test.becomeCreative()
75+
await bot.test.clearInventory()
76+
}

test/internalTest.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,5 +1282,53 @@ for (const supportedVersion of mineflayer.testedVersions) {
12821282
}
12831283
})
12841284
})
1285+
1286+
describe('activateItem rotation', () => {
1287+
it('should send the bot rotation in the use_item packet', function (done) {
1288+
// The rotation field in use_item was added in 1.21.1
1289+
const useItemFields = registry.protocol?.play?.toServer?.types?.packet_use_item?.[1]
1290+
const hasRotation = useItemFields && useItemFields.some(f => f.name === 'rotation')
1291+
if (!hasRotation) {
1292+
this.skip()
1293+
return
1294+
}
1295+
const { toNotchianYaw, toNotchianPitch } = require('../lib/conversions')
1296+
const testYaw = 1.5
1297+
const testPitch = -0.3
1298+
server.on('playerJoin', async (client) => {
1299+
await client.write('login', bot.test.generateLoginPacket())
1300+
await client.write('position', {
1301+
x: 0,
1302+
y: 66,
1303+
z: 0,
1304+
dx: 0,
1305+
dy: 0,
1306+
dz: 0,
1307+
yaw: 0,
1308+
pitch: 0,
1309+
flags: bot.registry.version['>=']('1.21.3') ? {} : 0,
1310+
teleportId: 0
1311+
})
1312+
1313+
client.on('packet', (data, meta) => {
1314+
if (meta.name === 'use_item') {
1315+
const expectedYaw = toNotchianYaw(testYaw)
1316+
const expectedPitch = toNotchianPitch(testPitch)
1317+
assert.ok(data.rotation, 'use_item packet should have rotation field')
1318+
assert.ok(Math.abs(data.rotation.x - expectedYaw) < 0.001,
1319+
`Expected yaw ${expectedYaw}, got ${data.rotation.x}`)
1320+
assert.ok(Math.abs(data.rotation.y - expectedPitch) < 0.001,
1321+
`Expected pitch ${expectedPitch}, got ${data.rotation.y}`)
1322+
done()
1323+
}
1324+
})
1325+
1326+
await sleep(100)
1327+
bot.entity.yaw = testYaw
1328+
bot.entity.pitch = testPitch
1329+
bot.activateItem()
1330+
})
1331+
})
1332+
})
12851333
})
12861334
}

0 commit comments

Comments
 (0)