Skip to content

Commit d36111d

Browse files
authored
Add maxDurability field, fix handling item sent by server with new sentByServer arg (#106)
* Add `maxDurability` field, fix handling item sent by server with new `sentByServer` arg * fix type typo * anvil: only set new item durability if it changed
1 parent 6ce147e commit d36111d

File tree

5 files changed

+48
-35
lines changed

5 files changed

+48
-35
lines changed

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,11 @@ console.log(Item.fromNotch(notchItem))
1919

2020
## API
2121

22-
### Item(type, count[, metadata, nbt, stackId])
22+
### Item(type, count[, metadata, nbt, stackId, sentByServer])
23+
24+
* sentByServer - whether this item was sent by the server to the client, so default
25+
initialization will not be done on the item. For example, tools will not have the
26+
default item NBT written to them.
2327

2428
#### Item.toNotch(item[, serverAuthoritative])
2529

@@ -116,6 +120,9 @@ See https://minecraft.gamepedia.com/Anvil_mechanics#Anvil_Uses
116120

117121
If the current item is a type of Spawn Egg, the protocol name of the entity that will be spawned. For example, a zombie spawn egg on 1.8 will return `Zombie`.
118122

123+
#### item.maxDurability
124+
125+
Max durability for the item, if it supports durability
119126

120127
## History
121128

index.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { Tags, TagType } from 'prismarine-nbt'
55
export type ItemLike = Item | null
66

77
export class Item {
8-
constructor(type: number, count: number, metadata?: number, nbt?: object, stackId?: number);
8+
constructor(type: number, count: number, metadata?: number, nbt?: object, stackId?: number, sentByServer?: boolean);
99
type: number;
1010
slot: number;
1111
count: number;
@@ -15,6 +15,7 @@ export class Item {
1515
name: string;
1616
displayName: string;
1717
stackSize: number;
18+
maxDurability: number;
1819
durabilityUsed: number;
1920
get enchants(): { name: string; lvl: number }[];
2021
set enchants(enchantments: { name: string; lvl: number }[]);

index.js

+20-16
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ const nbt = require('prismarine-nbt')
33
function loader (registryOrVersion) {
44
const registry = typeof registryOrVersion === 'string' ? require('prismarine-registry')(registryOrVersion) : registryOrVersion
55
class Item {
6-
constructor (type, count, metadata, nbt, stackId) {
6+
constructor (type, count, metadata, nbt, stackId, sentByServer) {
77
if (type == null) return
88

99
if (metadata instanceof Object) {
10+
sentByServer = stackId
1011
stackId = nbt
1112
nbt = metadata
1213
metadata = 0
@@ -19,7 +20,8 @@ function loader (registryOrVersion) {
1920

2021
// Probably add a new feature to mcdata, e.g itemsCanHaveStackId
2122
if (registry.type === 'bedrock') {
22-
this.stackId = stackId ?? Item.nextStackId()
23+
if (stackId == null && !sentByServer) stackId = Item.nextStackId()
24+
this.stackId = stackId
2325
} else {
2426
this.stackId = null
2527
}
@@ -29,14 +31,18 @@ function loader (registryOrVersion) {
2931
this.name = itemEnum.name
3032
this.displayName = itemEnum.displayName
3133
this.stackSize = itemEnum.stackSize
34+
this.maxDurability = itemEnum.maxDurability
3235

3336
if ('variations' in itemEnum) {
3437
const variation = itemEnum.variations.find((item) => item.metadata === metadata)
3538
if (variation) this.displayName = variation.displayName
3639
}
3740

38-
// The 'itemEnum.maxDurability' checks to see if this item can lose durability
39-
if (itemEnum.maxDurability && !this.durabilityUsed) this.durabilityUsed = 0
41+
// Can't initialize fields if the item was sent by the server
42+
if (!sentByServer) {
43+
// The 'itemEnum.maxDurability' checks to see if this item can lose durability
44+
if (registry.supportFeature('explicitMaxDurability') && this.maxDurability && !this.durabilityUsed) this.durabilityUsed = 0
45+
}
4046
} else {
4147
this.name = 'unknown'
4248
this.displayName = 'unknown'
@@ -126,24 +132,24 @@ function loader (registryOrVersion) {
126132
if (registry.type === 'pc') {
127133
if (registry.supportFeature('itemSerializationWillOnlyUsePresent')) {
128134
if (networkItem.present === false) return null
129-
return new Item(networkItem.itemId, networkItem.itemCount, networkItem.nbtData)
135+
return new Item(networkItem.itemId, networkItem.itemCount, networkItem.nbtData, null, true)
130136
} else if (registry.supportFeature('itemSerializationAllowsPresent')) {
131137
if (networkItem.itemId === -1 || networkItem.present === false) return null
132-
return new Item(networkItem.itemId, networkItem.itemCount, networkItem.nbtData)
138+
return new Item(networkItem.itemId, networkItem.itemCount, networkItem.nbtData, null, true)
133139
} else if (registry.supportFeature('itemSerializationUsesBlockId')) {
134140
if (networkItem.blockId === -1) return null
135-
return new Item(networkItem.blockId, networkItem.itemCount, networkItem.itemDamage, networkItem.nbtData)
141+
return new Item(networkItem.blockId, networkItem.itemCount, networkItem.itemDamage, networkItem.nbtData, null, true)
136142
}
137143
} else if (registry.type === 'bedrock') {
138144
if (networkItem.network_id === 0) return null
139145

140146
if (registry.supportFeature('itemSerializeUsesAuxValue')) {
141-
const item = new Item(networkItem.network_id, networkItem.auxiliary_value & 0xff, networkItem.auxiliary_value >> 8, networkItem.nbt?.nbt, stackId)
147+
const item = new Item(networkItem.network_id, networkItem.auxiliary_value & 0xff, networkItem.auxiliary_value >> 8, networkItem.nbt?.nbt, stackId, true)
142148
if (networkItem.can_place_on.length > 0) item.blocksCanPlaceOn = networkItem.can_place_on
143149
if (networkItem.can_destroy.length > 0) item.blocksCanDestroy = networkItem.can_destroy
144150
return item
145151
} else {
146-
const item = new Item(networkItem.network_id, networkItem.count, networkItem.metadata, networkItem.extra.nbt?.nbt, networkItem.stack_id)
152+
const item = new Item(networkItem.network_id, networkItem.count, networkItem.metadata, networkItem.extra.nbt?.nbt, networkItem.stack_id, true)
147153
if (networkItem.extra.can_place_on.length > 0) item.blocksCanPlaceOn = networkItem.extra.can_place_on
148154
if (networkItem.extra.can_destroy.length > 0) item.blocksCanDestroy = networkItem.extra.can_destroy
149155
return item
@@ -298,14 +304,12 @@ function loader (registryOrVersion) {
298304
}
299305

300306
get durabilityUsed () {
301-
if (Object.keys(this).length === 0) return null
302307
const where = registry.supportFeature('whereDurabilityIsSerialized')
303-
if (where === 'Damage') {
304-
return this?.nbt?.value?.Damage?.value ?? 0
305-
} else if (where === 'metadata') {
306-
return this.metadata ?? 0
307-
}
308-
throw new Error("Don't know how to get item durability for this mc version")
308+
let ret
309+
if (where === 'Damage') ret = this.nbt?.value?.Damage?.value
310+
else if (where === 'metadata') ret = this.metadata
311+
else throw new Error('unknown durability location')
312+
return ret ?? (this.maxDurability ? 0 : null)
309313
}
310314

311315
set durabilityUsed (value) {

lib/anvil.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ function loader (registry, Item) {
3939

4040
let finalItem = null
4141
if (itemOne) {
42-
finalItem = new Item(itemOne.type, itemOne.count, 0, JSON.parse(JSON.stringify(itemOne.nbt)))
42+
finalItem = new Item(itemOne.type, itemOne.count, 0, JSON.parse(JSON.stringify(itemOne.nbt)), null, true)
43+
const resultDurability = itemOne.durabilityUsed - data.fixedDurability
4344
const repairCost = Math.max(itemOne.repairCost, (itemTwo?.repairCost ?? 0)) * 2 + 1
4445
if (data?.finalEnchs.length > 0) finalItem.enchants = data.finalEnchs
4546
if (rename) finalItem.customName = renamedName
4647
finalItem.repairCost = repairCost
47-
if (itemOne.name !== 'enchanted_book') finalItem.durabilityUsed = itemOne.durabilityUsed - data.fixedDurability
48+
if (resultDurability && itemOne.name !== 'enchanted_book') finalItem.durabilityUsed = resultDurability
4849
}
4950
return { xpCost: cost, item: finalItem, usedMats: data.usedMats }
5051
}

test/basic.test.js

+15-15
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ describe('test based on examples', () => {
88
const ironShovelItem = new Item(256, 1)
99

1010
it('constructor makes item correctly', () => {
11-
const val = { type: 256, count: 1, metadata: 0, nbt: null, name: 'iron_shovel', displayName: 'Iron Shovel', stackSize: 1, stackId: null }
11+
const val = { type: 256, count: 1, metadata: 0, nbt: null, name: 'iron_shovel', displayName: 'Iron Shovel', stackSize: 1, stackId: null, maxDurability: 250 }
1212
expect(JSON.parse(JSON.stringify(ironShovelItem))).toStrictEqual(val)
1313
})
1414

@@ -19,7 +19,7 @@ describe('test based on examples', () => {
1919
it('use .fromNotch', () => {
2020
const toNotch = Item.toNotch(ironShovelItem)
2121
const fromNotch = Item.fromNotch(toNotch)
22-
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: null, stackSize: 1, type: 256, stackId: null }
22+
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: null, stackSize: 1, type: 256, stackId: null, maxDurability: 250 }
2323
expect(JSON.parse(JSON.stringify(fromNotch))).toStrictEqual(expectedObj)
2424
})
2525
})
@@ -28,19 +28,19 @@ describe('test based on examples', () => {
2828
const ironShovelItem = new Item(472, 1)
2929

3030
it('constructor makes item correctly', () => {
31-
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: { name: '', type: 'compound', value: { Damage: { type: 'int', value: 0 } } }, stackSize: 1, type: 472, stackId: null }
31+
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: null, stackSize: 1, type: 472, stackId: null, maxDurability: 250 }
3232
expect(JSON.parse(JSON.stringify(ironShovelItem))).toStrictEqual(expectedObj)
3333
})
3434

3535
it('use .toNotch', () => {
36-
const expectedObj = { itemCount: 1, itemId: 472, present: true, nbtData: { name: '', type: 'compound', value: { Damage: { type: 'int', value: 0 } } } }
36+
const expectedObj = { itemCount: 1, itemId: 472, present: true, nbtData: undefined }
3737
expect(Item.toNotch(ironShovelItem)).toStrictEqual(expectedObj)
3838
})
3939

4040
it('use .fromNotch', () => {
4141
const toNotch = Item.toNotch(ironShovelItem)
4242
const fromNotch = Item.fromNotch(toNotch)
43-
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: { name: '', type: 'compound', value: { Damage: { type: 'int', value: 0 } } }, stackSize: 1, type: 472, stackId: null }
43+
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: null, stackSize: 1, type: 472, stackId: null, maxDurability: 250 }
4444
expect(JSON.parse(JSON.stringify(fromNotch))).toStrictEqual(expectedObj)
4545
})
4646
})
@@ -50,7 +50,7 @@ describe('test based on examples', () => {
5050
const ironShovelItem = new Item(registry.itemsByName.iron_shovel.id, 1)
5151

5252
it('constructor makes item correctly', () => {
53-
const val = { type: registry.itemsByName.iron_shovel.id, count: 1, metadata: 0, nbt: { name: '', type: 'compound', value: { Damage: { type: 'int', value: 0 } } }, name: 'iron_shovel', displayName: 'Iron Shovel', stackSize: 1, stackId: 0 }
53+
const val = { type: registry.itemsByName.iron_shovel.id, count: 1, metadata: 0, nbt: { name: '', type: 'compound', value: { Damage: { type: 'int', value: 0 } } }, name: 'iron_shovel', displayName: 'Iron Shovel', stackSize: 1, stackId: 0, maxDurability: 250 }
5454
expect(JSON.parse(JSON.stringify(ironShovelItem))).toStrictEqual(val)
5555
})
5656

@@ -61,7 +61,7 @@ describe('test based on examples', () => {
6161
it('use .fromNotch', () => {
6262
const toNotch = Item.toNotch(ironShovelItem)
6363
const fromNotch = Item.fromNotch(toNotch)
64-
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: { name: '', type: 'compound', value: { Damage: { type: 'int', value: 0 } } }, stackSize: 1, type: registry.itemsByName.iron_shovel.id, stackId: 0 }
64+
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: { name: '', type: 'compound', value: { Damage: { type: 'int', value: 0 } } }, stackSize: 1, type: registry.itemsByName.iron_shovel.id, stackId: 0, maxDurability: 250 }
6565
expect(JSON.parse(JSON.stringify(fromNotch))).toStrictEqual(expectedObj)
6666
})
6767
})
@@ -350,30 +350,30 @@ describe('set item.enchants', () => {
350350
const Item = require('prismarine-item')(registry)
351351

352352
it('unenchanted stone sword', () => {
353-
const newItem = new Item(704, 1)
353+
const newItem = new Item(704, 1, undefined, undefined, 0, true)
354354
const item = Item.fromNotch({ network_id: 704, count: 1, metadata: 0, stack_id: 0, has_stack_id: true, extra: { has_nbt: false, can_place_on: [], can_destroy: [] } })
355355
const enchs = item.enchants
356356
newItem.enchants = enchs
357357
expect(newItem).toStrictEqual(item)
358358
})
359359
it('unbreaking 1 iron pickaxe', () => {
360-
const newItem = new Item(716, 1)
360+
const newItem = new Item(716, 1, undefined, undefined, 1, true)
361361
const item = Item.fromNotch({ network_id: 716, count: 1, metadata: 0, stack_id: 1, has_stack_id: true, extra: { has_nbt: true, nbt: { version: 1, nbt: { name: '', type: 'compound', value: { ench: { type: 'list', value: { type: 'compound', value: [{ id: { type: 'short', value: 17 }, lvl: { type: 'short', value: 1 } }] } }, RepairCost: { type: 'int', value: 3 } } } }, can_place_on: [], can_destroy: [] } })
362362
const enchs = item.enchants
363363
newItem.enchants = enchs
364364
newItem.repairCost = 3
365365
expect(newItem).toStrictEqual(item)
366366
})
367367
it('efficiency 5 diamond shovel', () => {
368-
const newItem = new Item(720, 1)
368+
const newItem = new Item(720, 1, undefined, undefined, 2, true)
369369
const item = Item.fromNotch({ network_id: 720, count: 1, metadata: 0, stack_id: 2, has_stack_id: true, extra: { has_nbt: true, nbt: { version: 1, nbt: { name: '', type: 'compound', value: { ench: { type: 'list', value: { type: 'compound', value: [{ id: { type: 'short', value: 15 }, lvl: { type: 'short', value: 5 } }] } }, RepairCost: { type: 'int', value: 2 } } } }, can_place_on: [], can_destroy: [] } })
370370
const enchs = item.enchants
371371
newItem.enchants = enchs
372372
newItem.repairCost = 2
373373
expect(newItem).toStrictEqual(item)
374374
})
375375
it('protection 4, mending diamond leggings', () => {
376-
const newItem = new Item(752, 1)
376+
const newItem = new Item(752, 1, undefined, undefined, 3, true)
377377
const item = Item.fromNotch({ network_id: 752, count: 1, metadata: 0, stack_id: 3, has_stack_id: true, extra: { has_nbt: true, nbt: { version: 1, nbt: { name: '', type: 'compound', value: { ench: { type: 'list', value: { type: 'compound', value: [{ id: { type: 'short', value: 0 }, lvl: { type: 'short', value: 4 } }, { id: { type: 'short', value: 26 }, lvl: { type: 'short', value: 1 } }] } }, RepairCost: { type: 'int', value: 3 } } } }, can_place_on: [], can_destroy: [] } })
378378
const enchs = item.enchants
379379
newItem.enchants = enchs
@@ -385,30 +385,30 @@ describe('set item.enchants', () => {
385385
const Item = require('prismarine-item')('bedrock_1.19.1')
386386

387387
it('unenchanted iron hoe', () => {
388-
const newItem = new Item(754, 1)
388+
const newItem = new Item(754, 1, undefined, undefined, 0, true)
389389
const item = Item.fromNotch({ network_id: 754, count: 1, metadata: 0, stack_id: 0, has_stack_id: true, extra: { has_nbt: false, can_place_on: [], can_destroy: [] } })
390390
const enchs = item.enchants
391391
newItem.enchants = enchs
392392
expect(newItem).toStrictEqual(item)
393393
})
394394
it('silk touch stone axe', () => {
395-
const newItem = new Item(743, 1)
395+
const newItem = new Item(743, 1, undefined, undefined, 1, true)
396396
const item = Item.fromNotch({ network_id: 743, count: 1, metadata: 0, stack_id: 1, has_stack_id: true, extra: { has_nbt: true, nbt: { version: 1, nbt: { name: '', type: 'compound', value: { ench: { type: 'list', value: { type: 'compound', value: [{ id: { type: 'short', value: 16 }, lvl: { type: 'short', value: 1 } }] } }, RepairCost: { type: 'int', value: 1 } } } }, can_place_on: [], can_destroy: [] } })
397397
const enchs = item.enchants
398398
newItem.enchants = enchs
399399
newItem.repairCost = 1
400400
expect(newItem).toStrictEqual(item)
401401
})
402402
it('lure 3 fishing rod', () => {
403-
const newItem = new Item(836, 1)
403+
const newItem = new Item(836, 1, undefined, undefined, 2, true)
404404
const item = Item.fromNotch({ network_id: 836, count: 1, metadata: 0, stack_id: 2, has_stack_id: true, extra: { has_nbt: true, nbt: { version: 1, nbt: { name: '', type: 'compound', value: { ench: { type: 'list', value: { type: 'compound', value: [{ id: { type: 'short', value: 24 }, lvl: { type: 'short', value: 3 } }] } }, RepairCost: { type: 'int', value: 2 } } } }, can_place_on: [], can_destroy: [] } })
405405
const enchs = item.enchants
406406
newItem.enchants = enchs
407407
newItem.repairCost = 2
408408
expect(newItem).toStrictEqual(item)
409409
})
410410
it('fire prot 3, unbreaking 2, respiration 3 diamond helmet', () => {
411-
const newItem = new Item(786, 1)
411+
const newItem = new Item(786, 1, undefined, undefined, 3, true)
412412
const item = Item.fromNotch({ network_id: 786, count: 1, metadata: 0, stack_id: 3, has_stack_id: true, extra: { has_nbt: true, nbt: { version: 1, nbt: { name: '', type: 'compound', value: { ench: { type: 'list', value: { type: 'compound', value: [{ id: { type: 'short', value: 1 }, lvl: { type: 'short', value: 3 } }, { id: { type: 'short', value: 17 }, lvl: { type: 'short', value: 2 } }, { id: { type: 'short', value: 6 }, lvl: { type: 'short', value: 3 } }] } }, RepairCost: { type: 'int', value: 3 } } } }, can_place_on: [], can_destroy: [] } })
413413
const enchs = item.enchants
414414
newItem.enchants = enchs

0 commit comments

Comments
 (0)