aboutsummaryrefslogtreecommitdiff
path: root/src/cleaners/skyblock/inventory.ts
blob: 5df3eb159275c250e5010e134d7f7505dbbff7f6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import typedHypixelApi from 'typed-hypixel-api'
import * as nbt from 'prismarine-nbt'


function base64decode(base64: string): Buffer {
	return Buffer.from(base64, 'base64')
}

export interface Item {
	id: string
	count: number
	vanillaId: string

	display: {
		name: string
		lore: string[]
		glint: boolean
	}

	reforge?: string
	anvilUses?: number
	timestamp?: string
	enchantments?: { [name: string]: number }
	petInfo?: {
		id: string
	}

	headTexture?: string
}

export type Inventory = Item[]

export function headIdFromBase64(headDataBase64: string): string | undefined {
	const headData = JSON.parse(base64decode(headDataBase64).toString())
	const headDataUrl = headData?.textures?.SKIN?.url
	if (headDataUrl) {
		const splitUrl = headDataUrl.split('/')
		return splitUrl[splitUrl.length - 1]
	}
	return undefined
}

function cleanItem(rawItem): Item | null {
	// if the item doesn't have an id, it isn't an item
	if (rawItem.id === undefined) return null

	const vanillaId: number = rawItem.id
	const itemCount = rawItem.Count
	const damageValue = rawItem.Damage
	const itemTag = rawItem.tag
	const extraAttributes = itemTag?.ExtraAttributes ?? {}
	let headId: string | undefined

	if (vanillaId === 397) {
		const headDataBase64 = itemTag?.SkullOwner?.Properties?.textures?.[0]?.Value
		if (headDataBase64)
			headId = headIdFromBase64(headDataBase64)
	}


	return {
		id: extraAttributes?.id ?? null,
		count: itemCount ?? 1,
		vanillaId: damageValue ? `${vanillaId}:${damageValue}` : vanillaId.toString(),

		display: {
			name: itemTag?.display?.Name ?? 'null',
			lore: itemTag?.display?.Lore ?? [],
			// if it has an ench value in the tag, then it should have an enchant glint effect
			glint: (itemTag?.ench ?? []).length > 0
		},

		reforge: extraAttributes?.modifier,
		enchantments: extraAttributes?.enchantments,
		anvilUses: extraAttributes?.anvil_uses,
		timestamp: extraAttributes?.timestamp,
		petInfo: extraAttributes?.petInfo ? {
			id: JSON.parse(extraAttributes.petInfo).type
		} : undefined,

		headTexture: headId,
	}
}

function cleanItems(rawItems): Inventory {
	return rawItems.map(cleanItem)
}

export async function cleanInventory(encodedNbt: string): Promise<Inventory> {
	const base64Data = base64decode(encodedNbt)
	const value: any = await new Promise((resolve, reject) => nbt.parse(base64Data, false, (err, value) => { if (err) reject(err); else resolve(value) }))
	const simplifiedNbt = nbt.simplify(value)
	// do some basic cleaning on the items and return
	return cleanItems(simplifiedNbt.i)
}

export const INVENTORIES = {
	armor: 'inv_armor',
	inventory: 'inv_contents',
	ender_chest: 'ender_chest_contents',
	accessory_bag: 'talisman_bag',
	potion_bag: 'potion_bag',
	fishing_bag: 'fishing_bag',
	quiver: 'quiver',
	trick_or_treat_bag: 'candy_inventory_contents',
	wardrobe: 'wardrobe_contents',
	personal_vault: 'personal_vault_contents'
}

export type Backpack = {
	icon: Item | null,
	items: Item[],
	slot: string,
};

export type Inventories = { backpacks: Backpack[] } & { [name in keyof typeof INVENTORIES]: Item[] }

export async function cleanInventories(data: typedHypixelApi.SkyBlockProfileMember): Promise<Inventories> {
	const cleanInventories: any = {}
	for (const cleanInventoryName in INVENTORIES) {
		const hypixelInventoryName = INVENTORIES[cleanInventoryName]
		const encodedInventoryContents = data[hypixelInventoryName]?.data
		let inventoryContents: Inventory
		if (encodedInventoryContents) {
			inventoryContents = await cleanInventory(encodedInventoryContents)

			if (cleanInventoryName === 'armor')
				// the armor is sent from boots to head, the opposite makes more sense
				inventoryContents.reverse()

			cleanInventories[cleanInventoryName] = inventoryContents
		}
	}
	const backpacks = data.backpack_contents
	const backpackIcons = data.backpack_icons ?? {};
	const cleanBackpacks: Backpack[] = [];
	if (backpacks) {
		for (const backpackId in backpacks) {
			const backpack = backpacks[backpackId]
			const contents = await cleanInventory(backpack.data)
			const icon = backpackIcons[backpackId]

			cleanBackpacks.push({
				items: contents,
				slot: backpackId,
				icon: (icon && (await cleanInventory(icon.data))[0]) ?? null,
			})
		}
	}
	cleanInventories['backpacks'] = cleanBackpacks;
	return cleanInventories
}