From fa726da23c40146c78fbf5e3824616e91284f4b1 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Fri, 6 Dec 2024 01:39:50 -0500 Subject: Item DFU Unification (#1054) --- .../skyblock/itemlist/ItemFixerUpper.java | 347 --------------------- .../skyblock/itemlist/ItemStackBuilder.java | 246 +++++++-------- .../skyblock/profileviewer/inventory/Pet.java | 62 ++-- .../utils/datafixer/LegacyItemStackFixer.java | 7 +- .../utils/datafixer/LegacyStringNbtReader.java | 81 +++++ 5 files changed, 225 insertions(+), 518 deletions(-) delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemFixerUpper.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/datafixer/LegacyStringNbtReader.java (limited to 'src/main/java') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemFixerUpper.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemFixerUpper.java deleted file mode 100644 index e931c7e2..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemFixerUpper.java +++ /dev/null @@ -1,347 +0,0 @@ -package de.hysky.skyblocker.skyblock.itemlist; - -import it.unimi.dsi.fastutil.ints.Int2ObjectMap; -import it.unimi.dsi.fastutil.ints.Int2ObjectMaps; -import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; - -import java.util.Map; - -public class ItemFixerUpper { - private final static String[] ANVIL_VARIANTS = { - "minecraft:anvil", - "minecraft:chipped_anvil", - "minecraft:damaged_anvil" - }; - - private final static String[] COAL_VARIANTS = { - "minecraft:coal", - "minecraft:charcoal" - }; - - private final static String[] COBBLESTONE_WALL_VARIANTS = { - "minecraft:cobblestone_wall", - "minecraft:mossy_cobblestone_wall" - }; - - private final static String[] COOKED_FISH_VARIANTS = { - "minecraft:cooked_cod", - "minecraft:cooked_salmon" - }; - - private final static String[] DIRT_VARIANTS = { - "minecraft:dirt", - "minecraft:coarse_dirt", - "minecraft:podzol" - }; - - private final static String[] DOUBLE_PLANT_VARIANTS = { - "minecraft:sunflower", - "minecraft:lilac", - "minecraft:tall_grass", - "minecraft:large_fern", - "minecraft:rose_bush", - "minecraft:peony" - }; - - private final static String[] DYE_VARIANTS = { - "minecraft:ink_sac", - "minecraft:red_dye", - "minecraft:green_dye", - "minecraft:cocoa_beans", - "minecraft:lapis_lazuli", - "minecraft:purple_dye", - "minecraft:cyan_dye", - "minecraft:light_gray_dye", - "minecraft:gray_dye", - "minecraft:pink_dye", - "minecraft:lime_dye", - "minecraft:yellow_dye", - "minecraft:light_blue_dye", - "minecraft:magenta_dye", - "minecraft:orange_dye", - "minecraft:bone_meal" - }; - - private final static String[] FISH_VARIANTS = { - "minecraft:cod", - "minecraft:salmon", - "minecraft:tropical_fish", - "minecraft:pufferfish" - }; - - private final static String[] GOLDEN_APPLE_VARIANTS = { - "minecraft:golden_apple", - "minecraft:enchanted_golden_apple" - }; - - private final static String[] LOG_VARIANTS = { - "minecraft:oak_log", - "minecraft:spruce_log", - "minecraft:birch_log", - "minecraft:jungle_log", - "minecraft:oak_wood", - "minecraft:spruce_wood", - "minecraft:birch_wood", - "minecraft:jungle_wood", - }; - - private final static String[] LOG2_VARIANTS = { - "minecraft:acacia_log", - "minecraft:dark_oak_log", - "minecraft:acacia_wood", - "minecraft:dark_oak_wood" - }; - - private final static String[] MONSTER_EGG_VARIANTS = { - "minecraft:infested_stone", - "minecraft:infested_cobblestone", - "minecraft:infested_stone_bricks", - "minecraft:infested_mossy_stone_bricks", - "minecraft:infested_cracked_stone_bricks", - "minecraft:infested_chiseled_stone_bricks" - }; - - private final static String[] PRISMARINE_VARIANTS = { - "minecraft:prismarine", - "minecraft:prismarine_bricks", - "minecraft:dark_prismarine" - }; - - private final static String[] QUARTZ_BLOCK_VARIANTS = { - "minecraft:quartz_block", - "minecraft:chiseled_quartz_block", - "minecraft:quartz_pillar" - }; - - private final static String[] RED_FLOWER_VARIANTS = { - "minecraft:poppy", - "minecraft:blue_orchid", - "minecraft:allium", - "minecraft:azure_bluet", - "minecraft:red_tulip", - "minecraft:orange_tulip", - "minecraft:white_tulip", - "minecraft:pink_tulip", - "minecraft:oxeye_daisy" - }; - - private final static String[] SAND_VARIANTS = { - "minecraft:sand", - "minecraft:red_sand" - }; - - private final static String[] SKULL_VARIANTS = { - "minecraft:skeleton_skull", - "minecraft:wither_skeleton_skull", - "minecraft:zombie_head", - "minecraft:player_head", - "minecraft:creeper_head" - }; - - private final static String[] SPONGE_VARIANTS = { - "minecraft:sponge", - "minecraft:wet_sponge" - }; - - private final static String[] STONE_VARIANTS = { - "minecraft:stone", - "minecraft:granite", - "minecraft:polished_granite", - "minecraft:diorite", - "minecraft:polished_diorite", - "minecraft:andesite", - "minecraft:polished_andesite" - }; - - private final static String[] STONE_SLAB_VARIANTS = { - "minecraft:smooth_stone_slab", - "minecraft:sandstone_slab", - "minecraft:petrified_oak_slab", - "minecraft:cobblestone_slab", - "minecraft:brick_slab", - "minecraft:stone_brick_slab", - "minecraft:nether_brick_slab", - "minecraft:quartz_slab" - }; - - private final static String[] STONEBRICK_VARIANTS = { - "minecraft:stone_bricks", - "minecraft:mossy_stone_bricks", - "minecraft:cracked_stone_bricks", - "minecraft:chiseled_stone_bricks" - }; - - private final static String[] TALLGRASS_VARIANTS = { - "minecraft:dead_bush", - "minecraft:short_grass", - "minecraft:fern" - }; - - private final static Int2ObjectMap SPAWN_EGG_VARIANTS = Int2ObjectMaps.unmodifiable(new Int2ObjectOpenHashMap<>(Map.ofEntries( - //This entry 0 is technically not right but Hypixel decided to make it polar bear so well we use that - Map.entry(0, "minecraft:polar_bear_spawn_egg"), - //This entry 4 does not actually exist, Hypixel uses it as a placeholder for elder guardians - Map.entry(4, "minecraft:elder_guardian_spawn_egg"), - Map.entry(50, "minecraft:creeper_spawn_egg"), - Map.entry(51, "minecraft:skeleton_spawn_egg"), - Map.entry(52, "minecraft:spider_spawn_egg"), - Map.entry(54, "minecraft:zombie_spawn_egg"), - Map.entry(55, "minecraft:slime_spawn_egg"), - Map.entry(56, "minecraft:ghast_spawn_egg"), - Map.entry(57, "minecraft:zombified_piglin_spawn_egg"), - Map.entry(58, "minecraft:enderman_spawn_egg"), - Map.entry(59, "minecraft:cave_spider_spawn_egg"), - Map.entry(60, "minecraft:silverfish_spawn_egg"), - Map.entry(61, "minecraft:blaze_spawn_egg"), - Map.entry(62, "minecraft:magma_cube_spawn_egg"), - Map.entry(65, "minecraft:bat_spawn_egg"), - Map.entry(66, "minecraft:witch_spawn_egg"), - Map.entry(67, "minecraft:endermite_spawn_egg"), - Map.entry(68, "minecraft:guardian_spawn_egg"), - Map.entry(90, "minecraft:pig_spawn_egg"), - Map.entry(91, "minecraft:sheep_spawn_egg"), - Map.entry(92, "minecraft:cow_spawn_egg"), - Map.entry(93, "minecraft:chicken_spawn_egg"), - Map.entry(94, "minecraft:squid_spawn_egg"), - Map.entry(95, "minecraft:wolf_spawn_egg"), - Map.entry(96, "minecraft:mooshroom_spawn_egg"), - Map.entry(98, "minecraft:ocelot_spawn_egg"), - Map.entry(100, "minecraft:horse_spawn_egg"), - Map.entry(101, "minecraft:rabbit_spawn_egg"), - Map.entry(120, "minecraft:villager_spawn_egg") - ))); - - private final static String[] SANDSTONE_VARIANTS = { - ":", - ":chiseled_", - ":cut_" - }; - - private final static String[] COLOR_VARIANTS = { - ":white_", - ":orange_", - ":magenta_", - ":light_blue_", - ":yellow_", - ":lime_", - ":pink_", - ":gray_", - ":light_gray_", - ":cyan_", - ":purple_", - ":blue_", - ":brown_", - ":green_", - ":red_", - ":black_" - }; - - private final static String[] WOOD_VARIANTS = { - ":oak_", - ":spruce_", - ":birch_", - ":jungle_", - ":acacia_", - ":dark_oak_" - }; - - //this is the map of all renames - private final static Map RENAMED = Map.ofEntries( - Map.entry("minecraft:bed", "minecraft:red_bed"), - Map.entry("minecraft:boat", "minecraft:oak_boat"), - Map.entry("minecraft:brick_block", "minecraft:bricks"), - Map.entry("minecraft:deadbush", "minecraft:dead_bush"), - Map.entry("minecraft:fence_gate", "minecraft:oak_fence_gate"), - Map.entry("minecraft:fence", "minecraft:oak_fence"), - Map.entry("minecraft:firework_charge", "minecraft:firework_star"), - Map.entry("minecraft:fireworks", "minecraft:firework_rocket"), - Map.entry("minecraft:golden_rail", "minecraft:powered_rail"), - Map.entry("minecraft:grass", "minecraft:grass_block"), - Map.entry("minecraft:hardened_clay", "minecraft:terracotta"), - Map.entry("minecraft:lit_pumpkin", "minecraft:jack_o_lantern"), - Map.entry("minecraft:melon_block", "minecraft:melon"), - Map.entry("minecraft:melon", "minecraft:melon_slice"), - Map.entry("minecraft:mob_spawner", "minecraft:spawner"), - Map.entry("minecraft:nether_brick", "minecraft:nether_bricks"), - Map.entry("minecraft:netherbrick", "minecraft:nether_brick"), - Map.entry("minecraft:noteblock", "minecraft:note_block"), - Map.entry("minecraft:piston_extension", "minecraft:moving_piston"), - Map.entry("minecraft:portal", "minecraft:nether_portal"), - Map.entry("minecraft:pumpkin", "minecraft:carved_pumpkin"), - Map.entry("minecraft:quartz_ore", "minecraft:nether_quartz_ore"), - Map.entry("minecraft:record_11", "minecraft:music_disc_11"), - Map.entry("minecraft:record_13", "minecraft:music_disc_13"), - Map.entry("minecraft:record_blocks", "minecraft:music_disc_blocks"), - Map.entry("minecraft:record_cat", "minecraft:music_disc_cat"), - Map.entry("minecraft:record_chirp", "minecraft:music_disc_chirp"), - Map.entry("minecraft:record_far", "minecraft:music_disc_far"), - Map.entry("minecraft:record_mall", "minecraft:music_disc_mall"), - Map.entry("minecraft:record_mellohi", "minecraft:music_disc_mellohi"), - Map.entry("minecraft:record_stal", "minecraft:music_disc_stal"), - Map.entry("minecraft:record_strad", "minecraft:music_disc_strad"), - Map.entry("minecraft:record_wait", "minecraft:music_disc_wait"), - Map.entry("minecraft:record_ward", "minecraft:music_disc_ward"), - Map.entry("minecraft:red_nether_brick", "minecraft:red_nether_bricks"), - Map.entry("minecraft:reeds", "minecraft:sugar_cane"), - Map.entry("minecraft:sign", "minecraft:oak_sign"), - Map.entry("minecraft:slime", "minecraft:slime_block"), - Map.entry("minecraft:snow_layer", "minecraft:snow"), - Map.entry("minecraft:snow", "minecraft:snow_block"), - Map.entry("minecraft:speckled_melon", "minecraft:glistering_melon_slice"), - Map.entry("minecraft:stone_slab2", "minecraft:red_sandstone_slab"), - Map.entry("minecraft:stone_stairs", "minecraft:cobblestone_stairs"), - Map.entry("minecraft:trapdoor", "minecraft:oak_trapdoor"), - Map.entry("minecraft:waterlily", "minecraft:lily_pad"), - Map.entry("minecraft:web", "minecraft:cobweb"), - Map.entry("minecraft:wooden_button", "minecraft:oak_button"), - Map.entry("minecraft:wooden_door", "minecraft:oak_door"), - Map.entry("minecraft:wooden_pressure_plate", "minecraft:oak_pressure_plate"), - Map.entry("minecraft:yellow_flower", "minecraft:dandelion") - ); - - //TODO : Add mushroom block variants - //i'll do it later because it isn't used and unlike the other, it's not just a rename or a separate, it's a separate and a merge - - public static String convertItemId(String id, int damage) { - return switch (id) { - //all the case are simple separate - case "minecraft:anvil" -> ANVIL_VARIANTS[damage]; - case "minecraft:coal" -> COAL_VARIANTS[damage]; - case "minecraft:cobblestone_wall" -> COBBLESTONE_WALL_VARIANTS[damage]; - case "minecraft:cooked_fish" -> COOKED_FISH_VARIANTS[damage]; - case "minecraft:dirt" -> DIRT_VARIANTS[damage]; - case "minecraft:double_plant" -> DOUBLE_PLANT_VARIANTS[damage]; - case "minecraft:dye" -> DYE_VARIANTS[damage]; - case "minecraft:fish" -> FISH_VARIANTS[damage]; - case "minecraft:golden_apple" -> GOLDEN_APPLE_VARIANTS[damage]; - case "minecraft:log" -> LOG_VARIANTS[damage]; - case "minecraft:log2" -> LOG2_VARIANTS[damage]; - case "minecraft:monster_egg" -> MONSTER_EGG_VARIANTS[damage]; - case "minecraft:prismarine" -> PRISMARINE_VARIANTS[damage]; - case "minecraft:quartz_block" -> QUARTZ_BLOCK_VARIANTS[damage]; - case "minecraft:red_flower" -> RED_FLOWER_VARIANTS[damage]; - case "minecraft:sand" -> SAND_VARIANTS[damage]; - case "minecraft:skull" -> SKULL_VARIANTS[damage]; - case "minecraft:sponge" -> SPONGE_VARIANTS[damage]; - case "minecraft:stone" -> STONE_VARIANTS[damage]; - case "minecraft:stone_slab" -> STONE_SLAB_VARIANTS[damage]; - case "minecraft:stonebrick" -> STONEBRICK_VARIANTS[damage]; - case "minecraft:tallgrass" -> TALLGRASS_VARIANTS[damage]; - //we use a Map from int to str instead of an array because numbers are not consecutive - case "minecraft:spawn_egg" -> SPAWN_EGG_VARIANTS.get(damage); - //when we use the generalized variant we need to replaceFirst - case "minecraft:sandstone", "minecraft:red_sandstone" -> id.replaceFirst(":", SANDSTONE_VARIANTS[damage]); - //to use the general color variants we need to reverse the order because Minecraft decided so for some reason - case "minecraft:banner" -> id.replaceFirst(":", COLOR_VARIANTS[15 - damage]); - case "minecraft:carpet", "minecraft:stained_glass", "minecraft:stained_glass_pane", "minecraft:wool" -> id.replaceFirst(":", COLOR_VARIANTS[damage]); - //for the terracotta we replace the whole name by the color and append "terracotta" at the end - case "minecraft:stained_hardened_clay" -> id.replaceFirst(":stained_hardened_clay", COLOR_VARIANTS[damage]) + "terracotta"; - //for the wooden slab we need to remove the "wooden_" prefix, but otherwise it's the same, so I just combined them anyway - case "minecraft:leaves", "minecraft:planks", "minecraft:sapling", "minecraft:wooden_slab" -> id.replaceFirst(":(?:wooden_)?", WOOD_VARIANTS[damage]); - //here we replace the 2 by nothing to remove it as it's not needed anymore - case "minecraft:leaves2" -> id.replaceFirst(":", WOOD_VARIANTS[damage + 4]).replaceFirst("2", ""); - //the default case is just a rename or no change - default -> RENAMED.getOrDefault(id, id); - }; - } -} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java index ab1af83d..eccfe7a2 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemStackBuilder.java @@ -1,151 +1,131 @@ package de.hysky.skyblocker.skyblock.itemlist; -import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.NEURepoManager; import de.hysky.skyblocker.utils.TextTransformer; +import de.hysky.skyblocker.utils.datafixer.LegacyItemStackFixer; +import de.hysky.skyblocker.utils.datafixer.LegacyStringNbtReader; import io.github.moulberry.repo.constants.PetNumbers; import io.github.moulberry.repo.data.NEUItem; import io.github.moulberry.repo.data.Rarity; -import it.unimi.dsi.fastutil.ints.IntList; import net.minecraft.component.DataComponentTypes; import net.minecraft.component.type.*; import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtString; -import net.minecraft.registry.Registries; import net.minecraft.text.Text; -import net.minecraft.util.Identifier; import net.minecraft.util.Pair; import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; + +import org.slf4j.Logger; + +import com.mojang.logging.LogUtils; public class ItemStackBuilder { - public static final Pattern SKULL_UUID_PATTERN = Pattern.compile("(?<=SkullOwner:\\{)Id:\"(.{36})\""); - public static final Pattern SKULL_TEXTURE_PATTERN = Pattern.compile("(?<=Properties:\\{textures:\\[0:\\{Value:)\"(.+?)\""); - private static final Pattern COLOR_PATTERN = Pattern.compile("color:(\\d+)"); - private static final Pattern EXPLOSION_COLOR_PATTERN = Pattern.compile("\\{Explosion:\\{(?:Type:[0-9a-z]+,)?Colors:\\[(?[0-9]+)]\\}"); - private static Map> petNums; - - public static void loadPetNums() { - try { - petNums = NEURepoManager.NEU_REPO.getConstants().getPetNumbers(); - } catch (Exception e) { - ItemRepository.LOGGER.error("Failed to load petnums.json"); - } - } - - public static ItemStack fromNEUItem(NEUItem item) { - String internalName = item.getSkyblockItemId(); - - List> injectors = new ArrayList<>(petData(internalName)); - - String legacyId = item.getMinecraftItemId(); - Identifier itemId = Identifier.of(ItemFixerUpper.convertItemId(legacyId, item.getDamage())); - - ItemStack stack = new ItemStack(Registries.ITEM.get(itemId)); - - // Custom Data - NbtCompound customData = new NbtCompound(); - - // Add Skyblock Item Id - customData.put(ItemUtils.ID, NbtString.of(internalName)); - - // Item Name - String name = injectData(item.getDisplayName(), injectors); - stack.set(DataComponentTypes.CUSTOM_NAME, TextTransformer.fromLegacy(name)); - - // Lore - stack.set(DataComponentTypes.LORE, new LoreComponent(item.getLore().stream().map(line -> TextTransformer.fromLegacy(injectData(line, injectors))).map(Text.class::cast).toList())); - - String nbttag = item.getNbttag(); - // add skull texture - Matcher skullUuid = SKULL_UUID_PATTERN.matcher(nbttag); - Matcher skullTexture = SKULL_TEXTURE_PATTERN.matcher(nbttag); - if (skullUuid.find() && skullTexture.find()) { - UUID uuid = UUID.fromString(skullUuid.group(1)); - String textureValue = skullTexture.group(1); - - stack.set(DataComponentTypes.PROFILE, new ProfileComponent(Optional.of(internalName.substring(0, Math.min(internalName.length(), 15))), Optional.of(uuid), ItemUtils.propertyMapWithTexture(textureValue))); - } - - // add leather armor dye color - Matcher colorMatcher = COLOR_PATTERN.matcher(nbttag); - if (colorMatcher.find()) { - int color = Integer.parseInt(colorMatcher.group(1)); - stack.set(DataComponentTypes.DYED_COLOR, new DyedColorComponent(color, false)); - } - // add enchantment glint - if (nbttag.contains("ench:")) { - stack.set(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true); - } - - //Hide weapon damage and other useless info - stack.set(DataComponentTypes.ATTRIBUTE_MODIFIERS, new AttributeModifiersComponent(List.of(), false)); - - // Add firework star color - Matcher explosionColorMatcher = EXPLOSION_COLOR_PATTERN.matcher(nbttag); - if (explosionColorMatcher.find()) { - //We used create an IntArrayList and took the color as the list's capacity and not add anything to the list which y'know casually leaked a lot of memory... - IntList color = IntList.of(Integer.parseInt(explosionColorMatcher.group("color"))); - - //Forget about the actual ball type because it probably doesn't matter - stack.set(DataComponentTypes.FIREWORK_EXPLOSION, new FireworkExplosionComponent(FireworkExplosionComponent.Type.SMALL_BALL, color, IntList.of(), false, false)); - } - - // Attach custom nbt data - stack.set(DataComponentTypes.CUSTOM_DATA, NbtComponent.of(customData)); - - return stack; - } - - private static List> petData(String internalName) { - List> list = new ArrayList<>(); - - String petName = internalName.split(";")[0]; - if (!internalName.contains(";") || !petNums.containsKey(petName)) return list; - - final Rarity[] rarities = { - Rarity.COMMON, - Rarity.UNCOMMON, - Rarity.RARE, - Rarity.EPIC, - Rarity.LEGENDARY, - Rarity.MYTHIC, - }; - Rarity rarity = rarities[Integer.parseInt(internalName.split(";")[1])]; - PetNumbers data = petNums.get(petName).get(rarity); - - int minLevel = data.getLowLevel(); - int maxLevel = data.getHighLevel(); - list.add(new Pair<>("\\{LVL\\}", minLevel + " ➡ " + maxLevel)); - - Map statNumsMin = data.getStatsAtLowLevel().getStatNumbers(); - Map statNumsMax = data.getStatsAtHighLevel().getStatNumbers(); - Set> entrySet = statNumsMin.entrySet(); - for (Map.Entry entry : entrySet) { - String key = entry.getKey(); - String left = "\\{" + key + "\\}"; - String right = statNumsMin.get(key) + " ➡ " + statNumsMax.get(key); - list.add(new Pair<>(left, right)); - } - - List otherNumsMin = data.getStatsAtLowLevel().getOtherNumbers(); - List otherNumsMax = data.getStatsAtHighLevel().getOtherNumbers(); - for (int i = 0; i < otherNumsMin.size(); ++i) { - String left = "\\{" + i + "\\}"; - String right = otherNumsMin.get(i) + " ➡ " + otherNumsMax.get(i); - list.add(new Pair<>(left, right)); - } - - return list; - } - - public static String injectData(String string, List> injectors) { - for (Pair injector : injectors) { - string = string.replaceAll(injector.getLeft(), injector.getRight()); - } - return string; - } + private static final Logger LOGGER = LogUtils.getLogger(); + private static Map> petNums; + + protected static void loadPetNums() { + try { + petNums = NEURepoManager.NEU_REPO.getConstants().getPetNumbers(); + } catch (Exception e) { + ItemRepository.LOGGER.error("Failed to load petnums.json"); + } + } + + protected static ItemStack fromNEUItem(NEUItem item) { + try { + NbtCompound nbt = new NbtCompound(); + NbtCompound tag = LegacyStringNbtReader.parse(item.getNbttag()); + + //Construct the nbt + nbt.put("tag", tag); + nbt.putString("id", item.getMinecraftItemId()); + nbt.putShort("Damage", (short) item.getDamage()); + nbt.putInt("Count", 1); + + ItemStack stack = LegacyItemStackFixer.fixLegacyStack(nbt); + + //The item couldn't be fixed up + if (stack.isEmpty()) { + LOGGER.error("[Skyblocker ItemStackBuilder] Failed to build item with skyblock id: {}!", item.getSkyblockItemId()); + + return createErrorStack(item.getSkyblockItemId()); + } + + List> injectors = new ArrayList<>(petData(item.getSkyblockItemId())); + + //Inject data into the item name + String name = injectData(item.getDisplayName(), injectors); + stack.set(DataComponentTypes.CUSTOM_NAME, TextTransformer.fromLegacy(name)); + + //Inject Data into the lore + stack.set(DataComponentTypes.LORE, new LoreComponent(item.getLore().stream() + .map(line -> TextTransformer.fromLegacy(injectData(line, injectors))) + .map(Text.class::cast) + .toList())); + + return stack; + } catch (Exception e) { + LOGGER.error("[Skyblocker ItemStackBuilder] Failed to build item with skyblock id: {}!", item.getSkyblockItemId(), e); + } + + return createErrorStack(item.getSkyblockItemId()); + } + + private static ItemStack createErrorStack(String skyblockItemId) { + ItemStack errorStack = new ItemStack(Items.BARRIER); + errorStack.set(DataComponentTypes.CUSTOM_NAME, Text.of(skyblockItemId)); + + return errorStack; + } + + private static List> petData(String internalName) { + List> list = new ArrayList<>(); + + String petName = internalName.split(";")[0]; + if (!internalName.contains(";") || !petNums.containsKey(petName)) return list; + + final Rarity[] rarities = { + Rarity.COMMON, + Rarity.UNCOMMON, + Rarity.RARE, + Rarity.EPIC, + Rarity.LEGENDARY, + Rarity.MYTHIC, + }; + Rarity rarity = rarities[Integer.parseInt(internalName.split(";")[1])]; + PetNumbers data = petNums.get(petName).get(rarity); + + int minLevel = data.getLowLevel(); + int maxLevel = data.getHighLevel(); + list.add(new Pair<>("\\{LVL\\}", minLevel + " ➡ " + maxLevel)); + + Map statNumsMin = data.getStatsAtLowLevel().getStatNumbers(); + Map statNumsMax = data.getStatsAtHighLevel().getStatNumbers(); + Set> entrySet = statNumsMin.entrySet(); + for (Map.Entry entry : entrySet) { + String key = entry.getKey(); + String left = "\\{" + key + "\\}"; + String right = statNumsMin.get(key) + " ➡ " + statNumsMax.get(key); + list.add(new Pair<>(left, right)); + } + + List otherNumsMin = data.getStatsAtLowLevel().getOtherNumbers(); + List otherNumsMax = data.getStatsAtHighLevel().getOtherNumbers(); + for (int i = 0; i < otherNumsMin.size(); ++i) { + String left = "\\{" + i + "\\}"; + String right = otherNumsMin.get(i) + " ➡ " + otherNumsMax.get(i); + list.add(new Pair<>(left, right)); + } + + return list; + } + + private static String injectData(String string, List> injectors) { + for (Pair injector : injectors) { + string = string.replaceAll(injector.getLeft(), injector.getRight()); + } + return string; + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/inventory/Pet.java b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/inventory/Pet.java index db9b8240..9f9dbca0 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/inventory/Pet.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/profileviewer/inventory/Pet.java @@ -1,12 +1,10 @@ package de.hysky.skyblocker.skyblock.profileviewer.inventory; import de.hysky.skyblocker.skyblock.item.PetInfo; -import de.hysky.skyblocker.skyblock.itemlist.ItemFixerUpper; import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; import de.hysky.skyblocker.skyblock.profileviewer.utils.LevelFinder; import de.hysky.skyblocker.skyblock.profileviewer.utils.ProfileViewerUtils; import de.hysky.skyblocker.skyblock.tabhud.util.Ico; -import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.NEURepoManager; import io.github.moulberry.repo.constants.PetNumbers; import io.github.moulberry.repo.data.NEUItem; @@ -21,39 +19,34 @@ import net.minecraft.component.DataComponentTypes; import net.minecraft.component.type.LoreComponent; import net.minecraft.component.type.ProfileComponent; import net.minecraft.item.ItemStack; -import net.minecraft.registry.Registries; +import net.minecraft.item.Items; import net.minecraft.text.Style; import net.minecraft.text.Text; import net.minecraft.util.Formatting; -import net.minecraft.util.Identifier; - import java.math.BigDecimal; import java.math.RoundingMode; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static de.hysky.skyblocker.skyblock.itemlist.ItemStackBuilder.SKULL_TEXTURE_PATTERN; import static de.hysky.skyblocker.skyblock.profileviewer.utils.ProfileViewerUtils.numLetterFormat; public class Pet { + private static final Pattern statsMatcher = Pattern.compile("\\{[A-Za-z_]+}"); + private static final Pattern numberMatcher = Pattern.compile("\\{\\d+}"); + private final String name; private final double xp; private final String tier; private final Optional heldItem; private final Optional skin; - private final Optional skinTexture; + private final Optional skinTexture; private final int level; private final double perecentageToLevel; private final long levelXP; private final long nextLevelXP; private final ItemStack icon; - private final Pattern statsMatcher = Pattern.compile("\\{[A-Za-z_]+}"); - private final Pattern numberMatcher = Pattern.compile("\\{\\d+}"); - - - private static final Object2IntMap TIER_MAP = Object2IntMaps.unmodifiable(new Object2IntOpenHashMap<>(Map.of( "COMMON", 0, "UNCOMMON", 1, "RARE", 2, "EPIC", 3, "LEGENDARY", 4, "MYTHIC", 5 ))); @@ -99,15 +92,19 @@ public class Pet { return tier; } - private Optional calculateSkinTexture() { + private Optional calculateSkinTexture() { if (this.skin.isPresent()) { - NEUItem item = NEURepoManager.NEU_REPO.getItems().getItemBySkyblockId("PET_SKIN_" + this.skin.get()); - if (item == null) return Optional.empty(); - Matcher skullTexture = SKULL_TEXTURE_PATTERN.matcher(item.getNbttag()); - if (skullTexture.find()) return Optional.of(skullTexture.group(1)); + ItemStack item = ItemRepository.getItemStack("PET_SKIN_" + this.skin.get()); + + if (item == null || item.isEmpty()) return Optional.empty(); + + ProfileComponent profile = item.get(DataComponentTypes.PROFILE); + + return profile != null ? Optional.of(profile) : Optional.empty(); } return Optional.empty(); } + public int getLevel() { return level; } public ItemStack getIcon() { return icon; } @@ -139,14 +136,14 @@ public class Pet { * @return The ItemStack representing the pet with all its properties set. */ private ItemStack fromNEUItem(NEUItem item, ItemStack heldItem) { - if (item == null) { - ItemStack errIcon = Ico.BARRIER.copy(); - errIcon.set(DataComponentTypes.CUSTOM_NAME, Text.of(this.getName())); - return errIcon; - } + if (item == null) return getErrorStack(); + + ItemStack petStack = ItemRepository.getItemStack(item.getSkyblockItemId()); + + if (petStack == null || petStack.isEmpty()) return getErrorStack(); - Identifier itemId = Identifier.of(ItemFixerUpper.convertItemId(item.getMinecraftItemId(), item.getDamage())); - ItemStack petStack = new ItemStack(Registries.ITEM.get(itemId)).copy(); + // Copy to avoid mutating the original stack + petStack = petStack.copy(); List formattedLore = !(name.equals("GOLDEN_DRAGON") && level < 101) ? processLore(item.getLore(), heldItem) : buildGoldenDragonEggLore(item.getLore()); @@ -167,16 +164,7 @@ public class Pet { // Skin Head Texture if (skinTexture.isPresent()) { formattedLore.set(0, Text.of(formattedLore.getFirst().getString() + ", " + Formatting.strip(NEURepoManager.NEU_REPO.getItems().getItems().get("PET_SKIN_" + skin.get()).getDisplayName()))); - petStack.set(DataComponentTypes.PROFILE, new ProfileComponent( - Optional.of(item.getSkyblockItemId()), Optional.of(UUID.randomUUID()), - ItemUtils.propertyMapWithTexture(this.skinTexture.get()))); - } else { - Matcher skullTexture = SKULL_TEXTURE_PATTERN.matcher(item.getNbttag()); - if (skullTexture.find()) { - petStack.set(DataComponentTypes.PROFILE, new ProfileComponent( - Optional.of(item.getSkyblockItemId()), Optional.of(UUID.randomUUID()), - ItemUtils.propertyMapWithTexture(skullTexture.group(1)))); - } + petStack.set(DataComponentTypes.PROFILE, skinTexture.get()); } if ((boosted())) formattedLore.set(formattedLore.size() - 1, Text.literal(Rarity.values()[getTier() + 1].toString()).setStyle(style).formatted(Formatting.BOLD, RARITY_COLOR_MAP.get(getTier() + 1))); @@ -265,4 +253,10 @@ public class Pet { private boolean boosted() { return this.heldItem.isPresent() && this.heldItem.get().equals("PET_ITEM_TIER_BOOST"); } + + private ItemStack getErrorStack() { + ItemStack errIcon = new ItemStack(Items.BARRIER); + errIcon.set(DataComponentTypes.CUSTOM_NAME, Text.of(this.getName())); + return errIcon; + } } diff --git a/src/main/java/de/hysky/skyblocker/utils/datafixer/LegacyItemStackFixer.java b/src/main/java/de/hysky/skyblocker/utils/datafixer/LegacyItemStackFixer.java index 8a5c0430..c27e05dc 100644 --- a/src/main/java/de/hysky/skyblocker/utils/datafixer/LegacyItemStackFixer.java +++ b/src/main/java/de/hysky/skyblocker/utils/datafixer/LegacyItemStackFixer.java @@ -1,14 +1,13 @@ package de.hysky.skyblocker.utils.datafixer; import static net.azureaaron.legacyitemdfu.LegacyItemStackFixer.getFixer; +import static net.azureaaron.legacyitemdfu.LegacyItemStackFixer.getFirstVersion; +import static net.azureaaron.legacyitemdfu.LegacyItemStackFixer.getLatestVersion; import java.util.List; import org.slf4j.Logger; -import static net.azureaaron.legacyitemdfu.LegacyItemStackFixer.FIRST_VERSION; -import static net.azureaaron.legacyitemdfu.LegacyItemStackFixer.LATEST_VERSION; - import com.mojang.logging.LogUtils; import com.mojang.serialization.Dynamic; @@ -33,7 +32,7 @@ public class LegacyItemStackFixer { @SuppressWarnings("deprecation") public static ItemStack fixLegacyStack(NbtCompound nbt) { RegistryOps ops = ItemStackComponentizationFixer.getRegistryLookup().getOps(NbtOps.INSTANCE); - Dynamic fixed = getFixer().update(TypeReferences.LEGACY_ITEM_STACK, new Dynamic<>(ops, nbt), FIRST_VERSION, LATEST_VERSION); + Dynamic fixed = getFixer().update(TypeReferences.LEGACY_ITEM_STACK, new Dynamic<>(ops, nbt), getFirstVersion(), getLatestVersion()); ItemStack stack = ItemStack.CODEC.parse(fixed) .setPartial(ItemStack.EMPTY) .resultOrPartial(LegacyItemStackFixer::log) diff --git a/src/main/java/de/hysky/skyblocker/utils/datafixer/LegacyStringNbtReader.java b/src/main/java/de/hysky/skyblocker/utils/datafixer/LegacyStringNbtReader.java new file mode 100644 index 00000000..eb7fb4e8 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/datafixer/LegacyStringNbtReader.java @@ -0,0 +1,81 @@ +package de.hysky.skyblocker.utils.datafixer; + +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; + +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtType; +import net.minecraft.nbt.StringNbtReader; + +/** + * Implementation of {@link StringNbtReader} that is capable of reading the legacy SNBT format of 1.8. + */ +public class LegacyStringNbtReader extends StringNbtReader { + + public static NbtCompound parse(String string) throws CommandSyntaxException { + return new LegacyStringNbtReader(new StringReader(string)).readCompound(); + } + + private LegacyStringNbtReader(StringReader reader) { + super(reader); + } + + /** + * @implNote The implementation is the exact same as the vanilla StringNbtReader except where noted. + */ + @Override + protected NbtElement parseList() throws CommandSyntaxException { + this.expect('['); + this.reader.skipWhitespace(); + if (!this.reader.canRead()) { + throw EXPECTED_VALUE.createWithContext(this.reader); + } else { + NbtList nbtList = new NbtList(); + NbtType listType = null; + + while (this.reader.peek() != ']') { + //Legacy lists might have the element indices before the actual element so we need to skip them (why Mojang? :() + //Ex: [0:"Hello",1:"World!"] + int originalPos = this.reader.getCursor(); + + //Skip the index numbers + while (Character.isDigit(this.reader.peek())) { + this.reader.skip(); + } + + //Skip the : or restore the cursor position if its not present and this is an int list + if (this.reader.peek() == ':') { + this.reader.skip(); + this.reader.skipWhitespace(); + } else { + this.reader.setCursor(originalPos); + } + + int cursorPosition = this.reader.getCursor(); + NbtElement element = this.parseElement(); + NbtType elementType = element.getNbtType(); + + if (listType == null) { + listType = elementType; + } else if (elementType != listType) { + this.reader.setCursor(cursorPosition); + throw LIST_MIXED.createWithContext(this.reader, elementType.getCommandFeedbackName(), listType.getCommandFeedbackName()); + } + + nbtList.add(element); + if (!this.readComma()) { + break; + } + + if (!this.reader.canRead()) { + throw EXPECTED_VALUE.createWithContext(this.reader); + } + } + + this.expect(']'); + return nbtList; + } + } +} -- cgit