Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add maxDurability field, fix handling item sent by server with new sentByServer arg #106

Merged
merged 3 commits into from
Jul 30, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Add maxDurability field, fix handling item sent by server with new …
…`sentByServer` arg
  • Loading branch information
extremeheat committed Jul 30, 2023
commit 865fd2498a94eda3898dbb6f63e57456c38f3dff
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,11 @@ console.log(Item.fromNotch(notchItem))

## API

### Item(type, count[, metadata, nbt, stackId])
### Item(type, count[, metadata, nbt, stackId, sentByServer])

* sentByServer - whether this item was sent by the server to the client, so default
initialization will not be done on the item. For example, tools will not have the
default item NBT written to them.

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

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

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`.

#### item.maxDurability

Max durability for the item, if it supports durability

## History

Expand Down
3 changes: 2 additions & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Tags, TagType } from 'prismarine-nbt'
export type ItemLike = Item | null

export class Item {
constructor(type: number, count: number, metadata?: number, nbt?: object, stackId?: number);
constructor(type: number, count: number, metadata?: number, nbt?: object, stackId?: number, sentByServer?: boolean);
type: number;
slot: number;
count: number;
Expand All @@ -15,6 +15,7 @@ export class Item {
name: string;
displayName: string;
stackSize: number;
maxDurability: boolean;
durabilityUsed: number;
get enchants(): { name: string; lvl: number }[];
set enchants(enchantments: { name: string; lvl: number }[]);
Expand Down
36 changes: 20 additions & 16 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ const nbt = require('prismarine-nbt')
function loader (registryOrVersion) {
const registry = typeof registryOrVersion === 'string' ? require('prismarine-registry')(registryOrVersion) : registryOrVersion
class Item {
constructor (type, count, metadata, nbt, stackId) {
constructor (type, count, metadata, nbt, stackId, sentByServer) {
if (type == null) return

if (metadata instanceof Object) {
sentByServer = stackId
stackId = nbt
nbt = metadata
metadata = 0
Expand All @@ -19,7 +20,8 @@ function loader (registryOrVersion) {

// Probably add a new feature to mcdata, e.g itemsCanHaveStackId
if (registry.type === 'bedrock') {
this.stackId = stackId ?? Item.nextStackId()
if (stackId == null && !sentByServer) stackId = Item.nextStackId()
this.stackId = stackId
} else {
this.stackId = null
}
Expand All @@ -29,14 +31,18 @@ function loader (registryOrVersion) {
this.name = itemEnum.name
this.displayName = itemEnum.displayName
this.stackSize = itemEnum.stackSize
this.maxDurability = itemEnum.maxDurability

if ('variations' in itemEnum) {
const variation = itemEnum.variations.find((item) => item.metadata === metadata)
if (variation) this.displayName = variation.displayName
}

// The 'itemEnum.maxDurability' checks to see if this item can lose durability
if (itemEnum.maxDurability && !this.durabilityUsed) this.durabilityUsed = 0
// Can't initialize fields if the item was sent by the server
if (!sentByServer) {
// The 'itemEnum.maxDurability' checks to see if this item can lose durability
if (registry.supportFeature('explicitMaxDurability') && this.maxDurability && !this.durabilityUsed) this.durabilityUsed = 0
}
} else {
this.name = 'unknown'
this.displayName = 'unknown'
Expand Down Expand Up @@ -126,24 +132,24 @@ function loader (registryOrVersion) {
if (registry.type === 'pc') {
if (registry.supportFeature('itemSerializationWillOnlyUsePresent')) {
if (networkItem.present === false) return null
return new Item(networkItem.itemId, networkItem.itemCount, networkItem.nbtData)
return new Item(networkItem.itemId, networkItem.itemCount, networkItem.nbtData, null, true)
} else if (registry.supportFeature('itemSerializationAllowsPresent')) {
if (networkItem.itemId === -1 || networkItem.present === false) return null
return new Item(networkItem.itemId, networkItem.itemCount, networkItem.nbtData)
return new Item(networkItem.itemId, networkItem.itemCount, networkItem.nbtData, null, true)
} else if (registry.supportFeature('itemSerializationUsesBlockId')) {
if (networkItem.blockId === -1) return null
return new Item(networkItem.blockId, networkItem.itemCount, networkItem.itemDamage, networkItem.nbtData)
return new Item(networkItem.blockId, networkItem.itemCount, networkItem.itemDamage, networkItem.nbtData, null, true)
}
} else if (registry.type === 'bedrock') {
if (networkItem.network_id === 0) return null

if (registry.supportFeature('itemSerializeUsesAuxValue')) {
const item = new Item(networkItem.network_id, networkItem.auxiliary_value & 0xff, networkItem.auxiliary_value >> 8, networkItem.nbt?.nbt, stackId)
const item = new Item(networkItem.network_id, networkItem.auxiliary_value & 0xff, networkItem.auxiliary_value >> 8, networkItem.nbt?.nbt, stackId, true)
if (networkItem.can_place_on.length > 0) item.blocksCanPlaceOn = networkItem.can_place_on
if (networkItem.can_destroy.length > 0) item.blocksCanDestroy = networkItem.can_destroy
return item
} else {
const item = new Item(networkItem.network_id, networkItem.count, networkItem.metadata, networkItem.extra.nbt?.nbt, networkItem.stack_id)
const item = new Item(networkItem.network_id, networkItem.count, networkItem.metadata, networkItem.extra.nbt?.nbt, networkItem.stack_id, true)
if (networkItem.extra.can_place_on.length > 0) item.blocksCanPlaceOn = networkItem.extra.can_place_on
if (networkItem.extra.can_destroy.length > 0) item.blocksCanDestroy = networkItem.extra.can_destroy
return item
Expand Down Expand Up @@ -298,14 +304,12 @@ function loader (registryOrVersion) {
}

get durabilityUsed () {
if (Object.keys(this).length === 0) return null
const where = registry.supportFeature('whereDurabilityIsSerialized')
if (where === 'Damage') {
return this?.nbt?.value?.Damage?.value ?? 0
} else if (where === 'metadata') {
return this.metadata ?? 0
}
throw new Error("Don't know how to get item durability for this mc version")
let ret
if (where === 'Damage') ret = this.nbt?.value?.Damage?.value
else if (where === 'metadata') ret = this.metadata
else throw new Error('unknown durability location')
return ret ?? (this.maxDurability ? 0 : null)
}

set durabilityUsed (value) {
Expand Down
30 changes: 15 additions & 15 deletions test/basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ describe('test based on examples', () => {
const ironShovelItem = new Item(256, 1)

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

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

it('constructor makes item correctly', () => {
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 }
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: null, stackSize: 1, type: 472, stackId: null, maxDurability: 250 }
expect(JSON.parse(JSON.stringify(ironShovelItem))).toStrictEqual(expectedObj)
})

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

it('use .fromNotch', () => {
const toNotch = Item.toNotch(ironShovelItem)
const fromNotch = Item.fromNotch(toNotch)
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 }
const expectedObj = { count: 1, displayName: 'Iron Shovel', metadata: 0, name: 'iron_shovel', nbt: null, stackSize: 1, type: 472, stackId: null, maxDurability: 250 }
expect(JSON.parse(JSON.stringify(fromNotch))).toStrictEqual(expectedObj)
})
})
Expand All @@ -50,7 +50,7 @@ describe('test based on examples', () => {
const ironShovelItem = new Item(registry.itemsByName.iron_shovel.id, 1)

it('constructor makes item correctly', () => {
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 }
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 }
expect(JSON.parse(JSON.stringify(ironShovelItem))).toStrictEqual(val)
})

Expand All @@ -61,7 +61,7 @@ describe('test based on examples', () => {
it('use .fromNotch', () => {
const toNotch = Item.toNotch(ironShovelItem)
const fromNotch = Item.fromNotch(toNotch)
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 }
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 }
expect(JSON.parse(JSON.stringify(fromNotch))).toStrictEqual(expectedObj)
})
})
Expand Down Expand Up @@ -350,30 +350,30 @@ describe('set item.enchants', () => {
const Item = require('prismarine-item')(registry)

it('unenchanted stone sword', () => {
const newItem = new Item(704, 1)
const newItem = new Item(704, 1, undefined, undefined, 0, true)
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: [] } })
const enchs = item.enchants
newItem.enchants = enchs
expect(newItem).toStrictEqual(item)
})
it('unbreaking 1 iron pickaxe', () => {
const newItem = new Item(716, 1)
const newItem = new Item(716, 1, undefined, undefined, 1, true)
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: [] } })
const enchs = item.enchants
newItem.enchants = enchs
newItem.repairCost = 3
expect(newItem).toStrictEqual(item)
})
it('efficiency 5 diamond shovel', () => {
const newItem = new Item(720, 1)
const newItem = new Item(720, 1, undefined, undefined, 2, true)
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: [] } })
const enchs = item.enchants
newItem.enchants = enchs
newItem.repairCost = 2
expect(newItem).toStrictEqual(item)
})
it('protection 4, mending diamond leggings', () => {
const newItem = new Item(752, 1)
const newItem = new Item(752, 1, undefined, undefined, 3, true)
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: [] } })
const enchs = item.enchants
newItem.enchants = enchs
Expand All @@ -385,30 +385,30 @@ describe('set item.enchants', () => {
const Item = require('prismarine-item')('bedrock_1.19.1')

it('unenchanted iron hoe', () => {
const newItem = new Item(754, 1)
const newItem = new Item(754, 1, undefined, undefined, 0, true)
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: [] } })
const enchs = item.enchants
newItem.enchants = enchs
expect(newItem).toStrictEqual(item)
})
it('silk touch stone axe', () => {
const newItem = new Item(743, 1)
const newItem = new Item(743, 1, undefined, undefined, 1, true)
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: [] } })
const enchs = item.enchants
newItem.enchants = enchs
newItem.repairCost = 1
expect(newItem).toStrictEqual(item)
})
it('lure 3 fishing rod', () => {
const newItem = new Item(836, 1)
const newItem = new Item(836, 1, undefined, undefined, 2, true)
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: [] } })
const enchs = item.enchants
newItem.enchants = enchs
newItem.repairCost = 2
expect(newItem).toStrictEqual(item)
})
it('fire prot 3, unbreaking 2, respiration 3 diamond helmet', () => {
const newItem = new Item(786, 1)
const newItem = new Item(786, 1, undefined, undefined, 3, true)
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: [] } })
const enchs = item.enchants
newItem.enchants = enchs
Expand Down