diff --git a/package-lock.json b/package-lock.json index 14b65ff2..38489487 100644 --- a/package-lock.json +++ b/package-lock.json @@ -23,7 +23,7 @@ "brace": "^0.11.1", "buffer": "^6.0.3", "comment-json": "^4.1.1", - "deepslate": "^0.13.2", + "deepslate": "^0.15.5", "deepslate-1.18": "npm:deepslate@^0.9.0-beta.9", "deepslate-1.18.2": "npm:deepslate@^0.9.0-beta.13", "highlight.js": "^11.5.1", @@ -1955,9 +1955,9 @@ "dev": true }, "node_modules/deepslate": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.13.2.tgz", - "integrity": "sha512-6pa9mgPu4A+RqYoN7AH79oKzzSNfvCJsrBKHE+AQjt20Uo33qJIRNG+2+sFHx84PAPJ3Z1CCnWWV+kBniD8E2g==", + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.15.5.tgz", + "integrity": "sha512-t+ucG50ldN0HMiDfXk8Tpzpw2ld6BA6NxJClQV+sB429vn1YmTk6WZx8GXd1exgUf/XYNR+ax+cxv/9Zr6Qy3g==", "dependencies": { "gl-matrix": "^3.3.0", "md5": "^2.3.0", @@ -6698,9 +6698,9 @@ "dev": true }, "deepslate": { - "version": "0.13.2", - "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.13.2.tgz", - "integrity": "sha512-6pa9mgPu4A+RqYoN7AH79oKzzSNfvCJsrBKHE+AQjt20Uo33qJIRNG+2+sFHx84PAPJ3Z1CCnWWV+kBniD8E2g==", + "version": "0.15.5", + "resolved": "https://registry.npmjs.org/deepslate/-/deepslate-0.15.5.tgz", + "integrity": "sha512-t+ucG50ldN0HMiDfXk8Tpzpw2ld6BA6NxJClQV+sB429vn1YmTk6WZx8GXd1exgUf/XYNR+ax+cxv/9Zr6Qy3g==", "requires": { "gl-matrix": "^3.3.0", "md5": "^2.3.0", diff --git a/package.json b/package.json index 9c13d28b..46ec824a 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,7 @@ "brace": "^0.11.1", "buffer": "^6.0.3", "comment-json": "^4.1.1", - "deepslate": "^0.13.2", + "deepslate": "^0.15.5", "deepslate-1.18": "npm:deepslate@^0.9.0-beta.9", "deepslate-1.18.2": "npm:deepslate@^0.9.0-beta.13", "highlight.js": "^11.5.1", diff --git a/src/app/components/ItemDisplay.tsx b/src/app/components/ItemDisplay.tsx index 6cb89f2e..b3262089 100644 --- a/src/app/components/ItemDisplay.tsx +++ b/src/app/components/ItemDisplay.tsx @@ -1,16 +1,16 @@ +import type { ItemStack } from 'deepslate' +import { Identifier } from 'deepslate-1.18.2' import { useEffect, useRef, useState } from 'preact/hooks' import { useVersion } from '../contexts/Version.jsx' import { useAsync } from '../hooks/useAsync.js' -import type { Item } from '../previews/LootTable.js' -import { MaxDamageItems } from '../previews/LootTable.js' -import { getAssetUrl } from '../services/DataFetcher.js' +import { itemHasGlint, MaxDamageItems } from '../previews/LootTable.js' import { renderItem } from '../services/Resources.js' import { getCollections } from '../services/Schemas.js' import { ItemTooltip } from './ItemTooltip.jsx' import { Octicon } from './Octicon.jsx' interface Props { - item: Item, + item: ItemStack, slotDecoration?: boolean, advancedTooltip?: boolean, } @@ -32,7 +32,7 @@ export function ItemDisplay({ item, slotDecoration, advancedTooltip }: Props) { return () => el.current?.removeEventListener('mousemove', onMove) }, []) - const maxDamage = MaxDamageItems.get(item.id) + const maxDamage = MaxDamageItems.get(item.id.toString()) return
@@ -43,23 +43,22 @@ export function ItemDisplay({ item, slotDecoration, advancedTooltip }: Props) { } {slotDecoration && <> - {(maxDamage && (item.tag?.Damage ?? 0) > 0) && + {(maxDamage && item.tag.getNumber('Damage') > 0) && - + }
} - +
} function ItemItself({ item }: Props) { const { version } = useVersion() - const [errored, setErrored] = useState(false) - const isEnchanted = (item.tag?.Enchantments?.length ?? 0) > 0 || (item.tag?.StoredEnchantments?.length ?? 0) > 0 + const hasGlint = itemHasGlint(item) - if (errored || (item.id.includes(':') && !item.id.startsWith('minecraft:'))) { + if (item.id.namespace !== Identifier.DEFAULT_NAMESPACE) { return Octicon.package } @@ -69,31 +68,22 @@ function ItemItself({ item }: Props) { return null } - const texturePath = `item/${item.id.replace(/^minecraft:/, '')}` - if (collections.get('texture').includes('minecraft:' + texturePath)) { - const src = getAssetUrl(version, 'textures', texturePath) - return <> - setErrored(true)} draggable={false} /> - {isEnchanted &&
} - - } - - const modelPath = `item/${item.id.replace(/^minecraft:/, '')}` + const modelPath = `item/${item.id.path}` if (collections.get('model').includes('minecraft:' + modelPath)) { - return + return } return Octicon.package } -function RenderedItem({ item, isEnchanted }: Props & { isEnchanted: boolean }) { +function RenderedItem({ item, hasGlint }: Props & { hasGlint: boolean }) { const { version } = useVersion() - const { value: src } = useAsync(() => renderItem(version, item.id), [version, item]) + const { value: src } = useAsync(() => renderItem(version, item), [version, item]) if (src) { return <> - {item.id} - {isEnchanted &&
} + {item.id.toString()} + {hasGlint &&
} } diff --git a/src/app/components/ItemTooltip.tsx b/src/app/components/ItemTooltip.tsx index a1350be0..6391757e 100644 --- a/src/app/components/ItemTooltip.tsx +++ b/src/app/components/ItemTooltip.tsx @@ -1,3 +1,5 @@ +import type { ItemStack } from 'deepslate' +import { Identifier, NbtList, NbtType } from 'deepslate' import { useVersion } from '../contexts/Version.jsx' import { useAsync } from '../hooks/useAsync.js' import { getEnchantmentData, MaxDamageItems } from '../previews/LootTable.js' @@ -5,31 +7,32 @@ import { getTranslation } from '../services/Resources.js' import { TextComponent } from './TextComponent.jsx' interface Props { - id: string, - tag?: any, + item: ItemStack, advanced?: boolean, offset?: [number, number], swap?: boolean, } -export function ItemTooltip({ id, tag, advanced, offset = [0, 0], swap }: Props) { +export function ItemTooltip({ item, advanced, offset = [0, 0], swap }: Props) { const { version } = useVersion() const { value: translatedName } = useAsync(() => { - const key = id.split(':').join('.') + const key = `${item.id.namespace}.${item.id.path}` return getTranslation(version, `item.${key}`) ?? getTranslation(version, `block.${key}`) - }, [version, id]) - const displayName = tag?.display?.Name - const name = displayName ? JSON.parse(displayName) : (translatedName ?? fakeTranslation(id)) + }, [version, item.id]) + const displayName = item.tag.getCompound('display').getString('Name') + const name = displayName ? JSON.parse(displayName) : (translatedName ?? fakeTranslation(item.id.path)) - const maxDamage = MaxDamageItems.get(id) - const enchantments = (id === 'minecraft:enchanted_book' ? tag?.StoredEnchantments : tag?.Enchantments) ?? [] + const maxDamage = MaxDamageItems.get(item.id.toString()) + const enchantments = (item.id.equals(Identifier.create('enchanted_book')) ? item.tag.getList('StoredEnchantments', NbtType.Compound) : item.tag.getList('Enchantments', NbtType.Compound)) ?? NbtList.create() return
- - {enchantments.map(({ id, lvl }: { id: string, lvl: number }) => { + 0 }} /> + {enchantments.map(enchantment => { + const id = enchantment.getString('id') + const lvl = enchantment.getNumber('lvl') const ench = getEnchantmentData(id) const component: any[] = [{ translate: `enchantment.${id.replace(':', '.')}`, color: ench?.curse ? 'red' : 'gray' }] if (lvl !== 1 || ench?.maxLevel !== 1) { @@ -37,24 +40,23 @@ export function ItemTooltip({ id, tag, advanced, offset = [0, 0], swap }: Props) } return })} - {tag?.display && <> - {tag?.display?.color && (advanced - ? + {item.tag.hasCompound('display') && <> + {item.tag.getCompound('display').hasNumber('color') && (advanced + ? : )} - {(tag?.display?.Lore ?? []).map((line: any) => )} + {(item.tag.getCompound('display').getList('Lore', NbtType.String)).map((line) => )} } - {tag?.Unbreakable === true && } - {(advanced && (tag?.Damage ?? 0) > 0 && maxDamage) && } + {item.tag.getBoolean('Unbreakable') && } + {(advanced && item.tag.getNumber('Damage') > 0 && maxDamage) && } {advanced && <> - - {tag && } + + {item.tag.size > 0 && } }
} function fakeTranslation(str: string) { - const colon = str.indexOf(':') - return str.slice(colon + 1) + return str .replace(/[_\/]/g, ' ') .split(' ') .map(word => word.charAt(0).toUpperCase() + word.slice(1)) diff --git a/src/app/previews/LootTable.ts b/src/app/previews/LootTable.ts index a89ef949..b3c9c42d 100644 --- a/src/app/previews/LootTable.ts +++ b/src/app/previews/LootTable.ts @@ -1,20 +1,14 @@ import type { Random } from 'deepslate' -import { LegacyRandom } from 'deepslate' +import { Identifier, ItemStack, LegacyRandom, NbtCompound, NbtInt, NbtList, NbtShort, NbtString, NbtTag, NbtType } from 'deepslate' import type { VersionId } from '../services/Schemas.js' import { clamp, deepClone, getWeightedRandom, isObject } from '../Utils.js' -export interface Item { - id: string, - count: number, - tag?: any, -} - export interface SlottedItem { slot: number, - item: Item, + item: ItemStack, } -type ItemConsumer = (item: Item) => void +type ItemConsumer = (item: ItemStack) => void const StackMixers = { container: fillContainer, @@ -44,7 +38,7 @@ interface LootContext extends LootOptions { export function generateLootTable(lootTable: any, options: LootOptions) { const ctx = createLootContext(options) - const result: Item[] = [] + const result: ItemStack[] = [] generateTable(lootTable, item => result.push(item), ctx) const mixer = StackMixers[options.stackMixer] return mixer(result, ctx) @@ -52,11 +46,11 @@ export function generateLootTable(lootTable: any, options: LootOptions) { const SLOT_COUNT = 27 -function fillContainer(items: Item[], ctx: LootContext): SlottedItem[] { +function fillContainer(items: ItemStack[], ctx: LootContext): SlottedItem[] { const slots = shuffle([...Array(SLOT_COUNT)].map((_, i) => i), ctx) - const queue = items.filter(i => i.id !== 'minecraft:air' && i.count > 1) - items = items.filter(i => i.id !== 'minecraft:air' && i.count === 1) + const queue = items.filter(i => !i.id.equals(Identifier.create('air')) && i.count > 1) + items = items.filter(i => !i.id.equals(Identifier.create('air')) && i.count === 1) while (SLOT_COUNT - items.length - queue.length > 0 && queue.length > 0) { const [itemA] = queue.splice(ctx.random.nextInt(queue.length), 1) @@ -81,21 +75,21 @@ function fillContainer(items: Item[], ctx: LootContext): SlottedItem[] { if (slot === undefined) { break } - if (item.id !== 'minecraft:air' && item.count > 0) { + if (!item.id.equals(Identifier.create('air')) && item.count > 0) { results.push({ slot, item }) } } return results } -function assignSlots(items: Item[]): SlottedItem[] { +function assignSlots(items: ItemStack[]): SlottedItem[] { const results: SlottedItem[] = [] let slot = 0 for (const item of items) { if (slot >= 27) { break } - if (item.id !== 'minecraft:air' && item.count > 0) { + if (!item.id.equals(Identifier.create('air')) && item.count > 0) { results.push({ slot, item }) slot += 1 } @@ -103,7 +97,7 @@ function assignSlots(items: Item[]): SlottedItem[] { return results } -function splitItem(item: Item, count: number): Item { +function splitItem(item: ItemStack, count: number): ItemStack { const splitCount = Math.min(count, item.count) const other = deepClone(item) other.count = splitCount @@ -234,11 +228,11 @@ function createItem(entry: any, consumer: ItemConsumer, ctx: LootContext) { } switch (type) { case 'item': - entryConsumer({ id: entry.name, count: 1 }) + entryConsumer(new ItemStack(Identifier.parse(entry.name), 1)) break case 'tag': ctx.getItemTag(entry.name).forEach(tagEntry => { - entryConsumer({ id: tagEntry, count: 1 }) + entryConsumer(new ItemStack(Identifier.parse(tagEntry), 1)) }) break case 'loot_table': @@ -254,7 +248,7 @@ function computeWeight(entry: any, luck: number) { return Math.max(Math.floor((entry.weight ?? 1) + (entry.quality ?? 0) * luck), 0) } -type LootFunction = (item: Item, ctx: LootContext) => void +type LootFunction = (item: ItemStack, ctx: LootContext) => void function decorateFunctions(functions: any[], consumer: ItemConsumer, ctx: LootContext): ItemConsumer { const compositeFunction = composeFunctions(functions) @@ -277,12 +271,12 @@ function composeFunctions(functions: any[]): LootFunction { const LootFunctions: Record LootFunction> = { enchant_randomly: ({ enchantments }) => (item, ctx) => { - const isBook = item.id === 'minecraft:book' + const isBook = item.id.equals(Identifier.create('book')) if (enchantments === undefined || enchantments.length === 0) { enchantments = [...Enchantments.keys()] .filter(e => { const data = getEnchantmentData(e) - return data.discoverable && (isBook || data.canEnchant(item.id)) + return data.discoverable && (isBook || data.canEnchant(item.id.toString())) }) } if (enchantments.length > 0) { @@ -290,27 +284,37 @@ const LootFunctions: Record LootFunction> = { const data = getEnchantmentData(id) const lvl = ctx.random.nextInt(data.maxLevel - data.minLevel + 1) + data.minLevel if (isBook) { - item.tag = {} + item.tag = new NbtCompound() item.count = 1 } enchantItem(item, { id, lvl }) if (isBook) { - item.id = 'minecraft:enchanted_book' + item.id = Identifier.create('enchanted_book') } } }, enchant_with_levels: ({ levels, treasure }) => (item, ctx) => { const enchants = selectEnchantments(ctx.random, item, computeInt(levels, ctx), treasure) - const isBook = item.id === 'minecraft:book' + const isBook = item.id.equals(Identifier.create('book')) if (isBook) { item.count = 1 - item.tag = {} + item.tag = new NbtCompound() } for (const enchant of enchants) { enchantItem(item, enchant) } if (isBook) { - item.id = 'minecraft:enchanted_book' + item.id = Identifier.create('enchanted_book') + } + }, + exploration_map: ({ decoration }) => (item) => { + if (!item.id.equals(Identifier.create('map'))) { + return + } + item.id = Identifier.create('filled_map') + const color = decoration === 'mansion' ? 5393476 : decoration === 'monument' ? 3830373 : -1 + if (color >= 0) { + getOrCreateTag(item, 'display').set('MapColor', new NbtInt(color)) } }, limit_count: ({ limit }) => (item, ctx) => { @@ -322,12 +326,12 @@ const LootFunctions: Record LootFunction> = { item.count = clamp(oldCount + computeInt(count, ctx), 0, 64) }, set_damage: ({ damage, add }) => (item, ctx) => { - const maxDamage = MaxDamageItems.get(item.id) + const maxDamage = MaxDamageItems.get(item.id.toString()) if (maxDamage) { - const oldDamage = add ? 1 - (item.tag?.Damage ?? 0) / maxDamage : 0 + const oldDamage = add ? 1 - item.tag.getNumber('Damage') / maxDamage : 0 const newDamage = 1 - clamp(computeFloat(damage, ctx) + oldDamage, 0, 1) const finalDamage = Math.floor(newDamage * maxDamage) - item.tag = { ...item.tag, Damage: finalDamage } + item.tag.set('Damage', new NbtInt(finalDamage)) } }, set_enchantments: ({ enchantments, add }) => (item, ctx) => { @@ -337,13 +341,23 @@ const LootFunctions: Record LootFunction> = { }) }, set_lore: ({ lore, replace }) => (item) => { - const lines = lore.map((line: any) => JSON.stringify(line)) - const newLore = replace ? lines : [...(item.tag?.display?.Lore ?? []), ...lines] - item.tag = { ...item.tag, display: { ...item.tag?.display, Lore: newLore } } + const lines: string[] = lore.flatMap((line: any) => line !== undefined ? [JSON.stringify(line)] : []) + const newLore = replace ? lines : [...item.tag.getCompound('display').getList('Lore', NbtType.String).map(s => s.getAsString()), ...lines] + getOrCreateTag(item, 'display').set('Lore', new NbtList(newLore.map(l => new NbtString(l)))) }, set_name: ({ name }) => (item) => { - const newName = JSON.stringify(name) - item.tag = { ...item.tag, display: { ...item.tag?.display, Name: newName } } + if (name !== undefined) { + const newName = JSON.stringify(name) + getOrCreateTag(item, 'display').set('Name', new NbtString(newName)) + } + }, + set_nbt: ({ tag }) => (item) => { + try { + const newTag = NbtTag.fromString(tag) + if (newTag.isCompound()) { + item.tag = newTag + } + } catch (e) {} }, } @@ -517,30 +531,28 @@ function testDamageSourcePredicate(_predicate: any, _ctx: LootContext) { return false // TODO } -function enchantItem(item: Item, enchant: Enchant, additive?: boolean) { - if (!item.tag) { - item.tag = {} +function enchantItem(item: ItemStack, enchant: Enchant, additive?: boolean) { + const listKey = item.id.equals(Identifier.create('book')) ? 'StoredEnchantments' : 'Enchantments' + if (!item.tag.hasList(listKey, NbtType.Compound)) { + item.tag.set(listKey, new NbtList()) } - const listKey = (item.id === 'minecraft:book') ? 'StoredEnchantments' : 'Enchantments' - if (!item.tag[listKey] || !Array.isArray(item.tag[listKey])) { - item.tag[listKey] = [] - } - const enchantments = item.tag[listKey] as any[] + const enchantments = item.tag.getList(listKey, NbtType.Compound).getItems() let index = enchantments.findIndex((e: any) => e.id === enchant.id) if (index !== -1) { const oldEnch = enchantments[index] - oldEnch.lvl = Math.max(additive ? oldEnch.lvl + enchant.lvl : enchant.lvl, 0) + oldEnch.set('lvl', new NbtShort(Math.max(additive ? oldEnch.getNumber('lvl') + enchant.lvl : enchant.lvl, 0))) } else { - enchantments.push(enchant) + enchantments.push(new NbtCompound().set('id', new NbtString(enchant.id)).set('lvl', new NbtShort(enchant.lvl))) index = enchantments.length - 1 } - if (enchantments[index].lvl === 0) { + if (enchantments[index].getNumber('lvl') === 0) { enchantments.splice(index, 1) } + item.tag.set(listKey, new NbtList(enchantments)) } -function selectEnchantments(random: Random, item: Item, levels: number, treasure: boolean): Enchant[] { - const enchantmentValue = EnchantmentItems.get(item.id) ?? 0 +function selectEnchantments(random: Random, item: ItemStack, levels: number, treasure: boolean): Enchant[] { + const enchantmentValue = EnchantmentItems.get(item.id.toString()) ?? 0 if (enchantmentValue <= 0) { return [] } @@ -573,13 +585,13 @@ function getEnchantWeight(ench: Enchant) { return EnchantmentsRarityWeights.get(getEnchantmentData(ench.id)?.rarity ?? 'common') ?? 0 } -function getAvailableEnchantments(item: Item, levels: number, treasure: boolean): Enchant[] { +function getAvailableEnchantments(item: ItemStack, levels: number, treasure: boolean): Enchant[] { const result = [] - const isBook = item.id === 'minecraft:book' + const isBook = item.id.equals(Identifier.create('book')) for (const id of Enchantments.keys()) { const ench = getEnchantmentData(id)! - if ((!ench.treasure || treasure) && ench.discoverable && (ench.canEnchant(item.id) || isBook)) { + if ((!ench.treasure || treasure) && ench.discoverable && (ench.canEnchant(item.id.toString()) || isBook)) { for (let lvl = ench.maxLevel; lvl > ench.minLevel - 1; lvl -= 1) { if (levels >= ench.minCost(lvl) && levels <= ench.maxCost(lvl)) { result.push({ id, lvl }) @@ -1018,3 +1030,38 @@ const EnchantmentsCategories = new Map(Object.entries({ crossbow: ['minecraft:crossbow'], vanishable: [...BREAKABLE, 'minecraft:compass'], })) + +const AlwaysHasGlint = new Set([ + 'minecraft:debug_stick', + 'minecraft:enchanted_golden_apple', + 'minecraft:enchanted_book', + 'minecraft:end_crystal', + 'minecraft:experience_bottle', + 'minecraft:written_book', +]) + +export function itemHasGlint(item: ItemStack) { + if (AlwaysHasGlint.has(item.id.toString())) { + return true + } + if (item.id.equals(Identifier.create('compass')) && (item.tag.has('LodestoneDimension') || item.tag.has('LodestonePos'))) { + return true + } + if ((item.id.equals(Identifier.create('potion')) || item.id.equals(Identifier.create('splash_potion')) || item.id.equals(Identifier.create('lingering_potion'))) && (item.tag.has('Potion') || item.tag.has('CustomPotionEffects'))) { + return true + } + if (item.tag.getList('Enchantments').length > 0 || item.tag.getList('StoredEnchantments').length > 0) { + return true + } + return false +} + +function getOrCreateTag(item: ItemStack, key: string) { + if (item.tag.hasCompound(key)) { + return item.tag.getCompound(key) + } else { + const tag = new NbtCompound() + item.tag.set(key, tag) + return tag + } +} diff --git a/src/app/schema/renderHtml.tsx b/src/app/schema/renderHtml.tsx index b35cb201..5a60cfd2 100644 --- a/src/app/schema/renderHtml.tsx +++ b/src/app/schema/renderHtml.tsx @@ -1,5 +1,6 @@ import type { BooleanHookParams, EnumOption, Hook, INode, NodeChildren, NumberHookParams, StringHookParams, ValidationOption } from '@mcschema/core' import { DataModel, ListNode, MapNode, ModelPath, ObjectNode, Path, relativePath, StringNode } from '@mcschema/core' +import { Identifier, ItemStack } from 'deepslate' import type { ComponentChildren, JSX } from 'preact' import { memo } from 'preact/compat' import { useState } from 'preact/hooks' @@ -139,7 +140,7 @@ const renderHtml: RenderHook = { let label: undefined | string | JSX.Element if (['loot_pool.entries.entry', 'loot_entry.alternatives.children.entry', 'loot_entry.group.children.entry', 'loot_entry.sequence.children.entry', 'function.set_contents.entries.entry'].includes(cPath.getContext().join('.'))) { if (isObject(cValue) && typeof cValue.type === 'string' && cValue.type.replace(/^minecraft:/, '') === 'item' && typeof cValue.name === 'string') { - label = + label = } } diff --git a/src/app/services/Resources.ts b/src/app/services/Resources.ts index a8b82ad6..5618effe 100644 --- a/src/app/services/Resources.ts +++ b/src/app/services/Resources.ts @@ -1,4 +1,4 @@ -import type { BlockModelProvider, TextureAtlasProvider, UV } from 'deepslate/render' +import type { BlockModelProvider, ItemStack, TextureAtlasProvider, UV } from 'deepslate/render' import { BlockModel, Identifier, ItemRenderer, TextureAtlas, upperPowerOfTwo } from 'deepslate/render' import { message } from '../Utils.js' import { fetchLanguage, fetchResources } from './DataFetcher.js' @@ -26,8 +26,8 @@ export async function getResources(version: VersionId) { const RENDER_SIZE = 128 const ItemRenderCache = new Map>() -export async function renderItem(version: VersionId, item: string) { - const cache_key = `${version} ${item}` +export async function renderItem(version: VersionId, item: ItemStack) { + const cache_key = `${version} ${item.toString()}` const cached = ItemRenderCache.get(cache_key) if (cached !== undefined) { return cached @@ -42,7 +42,8 @@ export async function renderItem(version: VersionId, item: string) { if (!gl) { throw new Error('Cannot get WebGL2 context') } - const renderer = new ItemRenderer(gl, Identifier.parse(item), resources) + const renderer = new ItemRenderer(gl, item, resources) + console.log('Rendering', item.toString()) renderer.drawItem() return canvas.toDataURL() })()