Skip to content

Commit c5e0200

Browse files
zardoyclaude
andauthored
fix firing heldItemChanged, add type (#3625)
* fix firing heldItemChanged, add type * test: add tests for heldItemChanged event, fix missing argument Add internal tests verifying heldItemChanged fires with the correct item when the held slot is updated via set_slot and via direct updateSlot. Also fix the new updateSlot listener to pass bot.heldItem as the event argument, matching the existing updateHeldItem behavior and the type definition. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> * Retrigger CI * test: add external test for heldItemChanged event Verifies that the heldItemChanged event fires with the correct item when the held slot contents are changed via server-side inventory update. 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 6d5d3ae commit c5e0200

4 files changed

Lines changed: 94 additions & 3 deletions

File tree

index.d.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ export interface BotEvents {
162162
bossBarDeleted: (bossBar: BossBar) => Promise<void> | void
163163
bossBarUpdated: (bossBar: BossBar) => Promise<void> | void
164164
resourcePack: (url: string, hash?: string, uuid?: string) => Promise<void> | void
165+
heldItemChanged: (newItem: Item | null) => Promise<void> | void
165166
particle: (particle: Particle) => Promise<void> | void
166167
}
167168

@@ -386,8 +387,8 @@ export interface Bot extends TypedEmitter<BotEvents> {
386387
times?: number
387388
) => Promise<void>
388389

389-
390-
390+
391+
391392
setCommandBlock: (pos: Vec3, command: string, options: CommandBlockOptions) => void
392393

393394
clickWindow: (

lib/plugins/inventory.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,7 @@ function inject (bot, { hideErrors }) {
547547
}
548548
const window = bot.currentWindow || bot.inventory
549549

550-
assert.ok(mode >= 0 && mode <= 4)
550+
assert.ok(mode >= 0 && mode <= 6)
551551
const actionId = createActionNumber()
552552

553553
const click = {
@@ -734,6 +734,11 @@ function inject (bot, { hideErrors }) {
734734
const newItem = Item.fromNotch(packet.contents)
735735
bot._setSlot(packet.slotId, newItem)
736736
})
737+
bot.inventory.on('updateSlot', (index) => {
738+
if (index === bot.quickBarSlot + bot.inventory.hotbarStart) {
739+
bot.emit('heldItemChanged', bot.heldItem)
740+
}
741+
})
737742
bot._client.on('window_items', (packet) => {
738743
const window = packet.windowId === 0 ? bot.inventory : bot.currentWindow
739744
if (!window || window.id !== packet.windowId) {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
const assert = require('assert')
2+
const { onceWithCleanup } = require('../../lib/promise_utils')
3+
4+
module.exports = () => async (bot) => {
5+
await bot.test.becomeCreative()
6+
await bot.test.clearInventory()
7+
await bot.test.wait(100)
8+
9+
// Give the bot a stone in the held slot (slot 36 = first hotbar slot)
10+
const stoneId = bot.registry.itemsByName.stone.id
11+
const diamondId = bot.registry.itemsByName.diamond.id
12+
13+
// Put stone in the current held slot
14+
await bot.test.setInventorySlot(bot.quickBarSlot + bot.inventory.hotbarStart, new (require('prismarine-item')(bot.registry))(stoneId, 1))
15+
await bot.test.wait(100)
16+
assert.strictEqual(bot.heldItem.type, stoneId, 'should be holding stone')
17+
18+
// Now change the held slot contents to diamond and verify heldItemChanged fires
19+
const heldItemPromise = onceWithCleanup(bot, 'heldItemChanged', {
20+
timeout: 5000
21+
})
22+
await bot.test.setInventorySlot(bot.quickBarSlot + bot.inventory.hotbarStart, new (require('prismarine-item')(bot.registry))(diamondId, 1))
23+
24+
const [newItem] = await heldItemPromise
25+
assert(newItem, 'heldItemChanged should fire with the new item')
26+
assert.strictEqual(newItem.type, diamondId, 'new item should be diamond')
27+
assert.strictEqual(bot.heldItem.type, diamondId, 'bot.heldItem should reflect diamond')
28+
}

test/internalTest.js

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -968,6 +968,63 @@ for (const supportedVersion of mineflayer.testedVersions) {
968968
})
969969
})
970970

971+
describe('heldItemChanged', () => {
972+
it('emits heldItemChanged when the held slot is updated via set_slot', (done) => {
973+
const Item = require('prismarine-item')(supportedVersion)
974+
const QUICK_BAR_SLOT = 0
975+
const HOTBAR_START = 36
976+
const stoneId = registry.itemsByName.stone.id
977+
const stoneItem = new Item(stoneId, 1)
978+
const notchItem = Item.toNotch(stoneItem)
979+
980+
server.on('playerJoin', (client) => {
981+
client.write('login', bot.test.generateLoginPacket())
982+
client.write('held_item_slot', { slot: QUICK_BAR_SLOT })
983+
984+
// Wait for the held_item_slot to be processed, then listen for the
985+
// heldItemChanged triggered by the set_slot update to the held slot
986+
setTimeout(() => {
987+
bot.once('heldItemChanged', (newItem) => {
988+
assert.ok(newItem, 'heldItemChanged should provide the new item')
989+
assert.strictEqual(newItem.type, stoneId)
990+
done()
991+
})
992+
client.write('set_slot', {
993+
windowId: 0,
994+
slot: HOTBAR_START + QUICK_BAR_SLOT,
995+
item: notchItem
996+
})
997+
}, 100)
998+
})
999+
})
1000+
1001+
it('emits heldItemChanged via updateSlot on the inventory', (done) => {
1002+
const Item = require('prismarine-item')(supportedVersion)
1003+
const QUICK_BAR_SLOT = 0
1004+
const stoneId = registry.itemsByName.stone.id
1005+
const stoneItem = new Item(stoneId, 1)
1006+
1007+
server.on('playerJoin', (client) => {
1008+
client.write('login', bot.test.generateLoginPacket())
1009+
client.write('held_item_slot', { slot: QUICK_BAR_SLOT })
1010+
1011+
setTimeout(() => {
1012+
bot.once('heldItemChanged', (newItem) => {
1013+
assert.ok(newItem, 'heldItemChanged should provide the new item')
1014+
assert.strictEqual(newItem.type, stoneId)
1015+
done()
1016+
})
1017+
// Directly call updateSlot on the inventory to simulate
1018+
// the set_player_inventory code path
1019+
bot.inventory.updateSlot(
1020+
QUICK_BAR_SLOT + bot.inventory.hotbarStart,
1021+
stoneItem
1022+
)
1023+
}, 100)
1024+
})
1025+
})
1026+
})
1027+
9711028
describe('tablist', () => {
9721029
it('handles newlines in header and footer', (done) => {
9731030
const HEADER = 'asd\ndsa'

0 commit comments

Comments
 (0)