From c671d5c03fb5d0410645d8b6edc56adeda3d94a7 Mon Sep 17 00:00:00 2001 From: Roman / Linnea Gräf Date: Thu, 8 Sep 2022 14:12:03 +0200 Subject: Add item ids to Books in more places (#256) * Add item ids to Books in more places Add item ids to Enchanted Books in the Bazaar and in the Experimentation Table. Add ItemResolutionQuery as a new way to query items and item ids. Co-authored-by: romangraef * Update 2.1.md * Infer found a bug that already existed before. * add support for animated crab hats Co-authored-by: hannibal00212 <24389977+hannibal00212@users.noreply.github.com> Co-authored-by: nopo --- Update Notes/2.1.md | 1 + .../moulberry/notenoughupdates/NEUManager.java | 108 +++------- .../notenoughupdates/auction/APIManager.java | 1 + .../notenoughupdates/core/util/StringUtils.java | 8 + .../listener/ItemTooltipListener.java | 31 +-- .../miscfeatures/EnchantingSolvers.java | 4 +- .../notenoughupdates/util/ItemResolutionQuery.java | 239 +++++++++++++++++++++ .../moulberry/notenoughupdates/util/ItemUtils.java | 14 +- 8 files changed, 312 insertions(+), 94 deletions(-) create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/util/ItemResolutionQuery.java diff --git a/Update Notes/2.1.md b/Update Notes/2.1.md index 90ebbf87..b5853248 100644 --- a/Update Notes/2.1.md +++ b/Update Notes/2.1.md @@ -109,6 +109,7 @@ - Added blur limit to map at 100 - nopo - Added bazaar sacks profit feature - hannibal2 - Added an option to disable etherwarp overlay when TP is denied - hannibal2 +- Added bazaar prices to enchants in the enchantment table - hannibal2 / nea89 ### **Bug Fixes:** diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java index 4e5c5b7d..35385f38 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NEUManager.java @@ -36,6 +36,8 @@ import io.github.moulberry.notenoughupdates.recipes.NeuRecipe; import io.github.moulberry.notenoughupdates.util.Constants; import io.github.moulberry.notenoughupdates.util.HotmInformation; import io.github.moulberry.notenoughupdates.util.HypixelApi; +import io.github.moulberry.notenoughupdates.util.ItemResolutionQuery; +import io.github.moulberry.notenoughupdates.util.ItemUtils; import io.github.moulberry.notenoughupdates.util.SBInfo; import io.github.moulberry.notenoughupdates.util.Utils; import net.minecraft.client.Minecraft; @@ -679,82 +681,18 @@ public class NEUManager { return null; } + /** + * Replaced with {@link #createItemResolutionQuery()} + */ + @Deprecated public String getInternalnameFromNBT(NBTTagCompound tag) { - String internalname = null; - if (tag != null && tag.hasKey("ExtraAttributes", 10)) { - NBTTagCompound ea = tag.getCompoundTag("ExtraAttributes"); - - if (ea.hasKey("id", 8)) { - internalname = ea.getString("id").replaceAll(":", "-"); - } else { - return null; - } - - if ("PET".equals(internalname)) { - String petInfo = ea.getString("petInfo"); - if (petInfo.length() > 0) { - JsonObject petInfoObject = gson.fromJson(petInfo, JsonObject.class); - internalname = petInfoObject.get("type").getAsString(); - String tier = petInfoObject.get("tier").getAsString(); - switch (tier) { - case "COMMON": - internalname += ";0"; - break; - case "UNCOMMON": - internalname += ";1"; - break; - case "RARE": - internalname += ";2"; - break; - case "EPIC": - internalname += ";3"; - break; - case "LEGENDARY": - internalname += ";4"; - break; - case "MYTHIC": - internalname += ";5"; - break; - } - } - } - if ("ENCHANTED_BOOK".equals(internalname) && ea.hasKey("enchantments", 10)) { - NBTTagCompound enchants = ea.getCompoundTag("enchantments"); - - for (String enchname : enchants.getKeySet()) { - internalname = enchname.toUpperCase() + ";" + enchants.getInteger(enchname); - break; - } - } - if ("RUNE".equals(internalname) && ea.hasKey("runes", 10)) { - NBTTagCompound rune = ea.getCompoundTag("runes"); - - for (String runename : rune.getKeySet()) { - internalname = runename.toUpperCase() + "_RUNE" + ";" + rune.getInteger(runename); - break; - } - } - if ("PARTY_HAT_CRAB".equals(internalname) && (ea.getString("party_hat_color") != null)) { - String crabhat = ea.getString("party_hat_color"); - internalname = "PARTY_HAT_CRAB" + "_" + crabhat.toUpperCase(); - } - } - - return internalname; + return createItemResolutionQuery() + .withItemNBT(tag) + .resolveInternalName(); } public String[] getLoreFromNBT(NBTTagCompound tag) { - String[] lore = new String[0]; - NBTTagCompound display = tag.getCompoundTag("display"); - - if (display.hasKey("Lore", 9)) { - NBTTagList list = display.getTagList("Lore", 8); - lore = new String[list.tagCount()]; - for (int k = 0; k < list.tagCount(); k++) { - lore[k] = list.getStringTagAt(k); - } - } - return lore; + return ItemUtils.getLore(tag).toArray(new String[0]); } public JsonObject getJsonFromNBT(NBTTagCompound tag) { @@ -914,10 +852,18 @@ public class NEUManager { return getSkullValueFromNBT(tag); } + public ItemResolutionQuery createItemResolutionQuery() { + return new ItemResolutionQuery(this); + } + + /** + * Replaced with {@link #createItemResolutionQuery()} + */ + @Deprecated public String getInternalNameForItem(ItemStack stack) { - if (stack == null) return null; - NBTTagCompound tag = stack.getTagCompound(); - return getInternalnameFromNBT(tag); + return createItemResolutionQuery() + .withItemStack(stack) + .resolveInternalName(); } public String getUUIDForItem(ItemStack stack) { @@ -1573,9 +1519,13 @@ public class NEUManager { return comp; } - public ItemStack createItem(String internalname) { - JsonObject jsonObject = itemMap.get(internalname); - if (jsonObject == null) return null; - return jsonToStack(jsonObject); + public ItemStack createItem(String internalName) { + return createItemResolutionQuery() + .withKnownInternalName(internalName) + .resolveToItemStack(); + } + + public boolean isValidInternalName(String internalName) { + return itemMap.containsKey(internalName); } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java index c3999fb6..74ef2483 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/auction/APIManager.java @@ -168,6 +168,7 @@ public class APIManager { return stack; } else { JsonObject item = manager.getJsonFromNBT(item_tag); + if (item == null) return null; ItemStack stack = manager.jsonToStack(item, false); JsonObject itemDefault = manager.getItemInformation().get(item.get("internalname").getAsString()); diff --git a/src/main/java/io/github/moulberry/notenoughupdates/core/util/StringUtils.java b/src/main/java/io/github/moulberry/notenoughupdates/core/util/StringUtils.java index ecb4cd8b..6143085c 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/core/util/StringUtils.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/core/util/StringUtils.java @@ -74,4 +74,12 @@ public class StringUtils { throw new RuntimeException(e); // UTF 8 should always be present } } + + /** + * taken and modified from https://stackoverflow.com/a/23326014/5507634 + */ + public static String replaceLast(String string, String toReplace, String replacement) { + int start = string.lastIndexOf(toReplace); + return string.substring(0, start) + replacement + string.substring(start + toReplace.length()); + } } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java b/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java index f3ed8e7c..e5c3324f 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java @@ -28,6 +28,7 @@ import com.mojang.authlib.minecraft.MinecraftProfileTexture; import io.github.moulberry.notenoughupdates.ItemPriceInformation; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.core.util.MiscUtils; +import io.github.moulberry.notenoughupdates.core.util.StringUtils; import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay; import io.github.moulberry.notenoughupdates.miscgui.GuiEnchantColour; import io.github.moulberry.notenoughupdates.profileviewer.GuiProfileViewer; @@ -42,7 +43,6 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTUtil; import net.minecraft.util.EnumChatFormatting; -import net.minecraft.util.StringUtils; import net.minecraftforge.event.entity.player.ItemTooltipEvent; import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.eventhandler.EventPriority; @@ -104,8 +104,13 @@ public class ItemTooltipListener { public void onItemTooltipLow(ItemTooltipEvent event) { if (!NotEnoughUpdates.INSTANCE.isOnSkyblock()) return; - String internalname = NotEnoughUpdates.INSTANCE.manager.getInternalNameForItem(event.itemStack); - if (internalname == null) { + String internalName = NotEnoughUpdates.INSTANCE.manager + .createItemResolutionQuery() + .withCurrentGuiContext() + .withItemStack(event.itemStack) + .resolveInternalName(); + + if (internalName == null) { return; } petToolTipXPExtendPetMenu(event); @@ -155,7 +160,8 @@ public class ItemTooltipListener { for (int j = 0; j < Utils.rarityArrC.length; j++) { for (Map.Entry entry : enchantsObj.entrySet()) { if (line.contains(Utils.rarityArrC[j] + " " + entry.getKey()) || line.contains( - Utils.rarityArrC[j] + " DUNGEON " + entry.getKey()) || line.contains("SHINY " + Utils.rarityArrC[j].replaceAll("§.§.","") + " DUNGEON " + entry.getKey())) { + Utils.rarityArrC[j] + " DUNGEON " + entry.getKey()) || line.contains( + "SHINY " + Utils.rarityArrC[j].replaceAll("§.§.", "") + " DUNGEON " + entry.getKey())) { allItemEnchs = entry.getValue().getAsJsonArray(); break out; } @@ -180,7 +186,7 @@ public class ItemTooltipListener { NotEnoughUpdates.INSTANCE.config.tooltipTweaks.showReforgeStats) { JsonObject reforgeStones = Constants.REFORGESTONES; - if (reforgeStones != null && reforgeStones.has(internalname)) { + if (reforgeStones != null && reforgeStones.has(internalName)) { boolean shift = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT); if (!pressedShiftLast && shift) { showReforgeStoneStats = !showReforgeStoneStats; @@ -195,7 +201,7 @@ public class ItemTooltipListener { newTooltip.add(EnumChatFormatting.DARK_GRAY + "[Press SHIFT to hide extra info]"); } - JsonObject reforgeInfo = reforgeStones.get(internalname).getAsJsonObject(); + JsonObject reforgeInfo = reforgeStones.get(internalName).getAsJsonObject(); JsonArray requiredRaritiesArray = reforgeInfo.get("requiredRarities").getAsJsonArray(); if (showReforgeStoneStats && requiredRaritiesArray.size() > 0) { @@ -526,7 +532,7 @@ public class ItemTooltipListener { newTooltip.add(""); newTooltip.add(EnumChatFormatting.GRAY + "[SHIFT for Price Info]"); } else { - ItemPriceInformation.addToTooltip(newTooltip, internalname, event.itemStack); + ItemPriceInformation.addToTooltip(newTooltip, internalName, event.itemStack); } } } @@ -583,9 +589,10 @@ public class ItemTooltipListener { JsonObject auctionInfo = neu.manager.auctionManager.getItemAuctionInfo(internal); if (auctionInfo != null) { if (auctionInfo.has("clean_price")) { - worth = (long)auctionInfo.get("clean_price").getAsDouble(); + worth = (long) auctionInfo.get("clean_price").getAsDouble(); } else { - worth = (long) (auctionInfo.get("price").getAsDouble() / auctionInfo.get("count").getAsDouble()); + worth = + (long) (auctionInfo.get("price").getAsDouble() / auctionInfo.get("count").getAsDouble()); } } break; @@ -703,7 +710,7 @@ public class ItemTooltipListener { event.toolTip.addAll(newTooltip); if (NotEnoughUpdates.INSTANCE.config.tooltipTweaks.showPriceInfoInvItem) { - ItemPriceInformation.addToTooltip(event.toolTip, internalname, event.itemStack); + ItemPriceInformation.addToTooltip(event.toolTip, internalName, event.itemStack); } if (event.itemStack.getTagCompound() != null && event.itemStack.getTagCompound().getBoolean("NEUHIDEPETTOOLTIP") && @@ -824,14 +831,14 @@ public class ItemTooltipListener { if (event.toolTip == null) return; if (event.toolTip.size() > 2 && NotEnoughUpdates.INSTANCE.config.tooltipTweaks.hideDefaultReforgeStats) { - String secondLine = StringUtils.stripControlCodes(event.toolTip.get(1)); + String secondLine = StringUtils.cleanColour(event.toolTip.get(1)); if (secondLine.equals("Reforge Stone")) { Integer startIndex = null; Integer cutoffIndex = null; //loop from the back of the List to find the wanted index sooner for (int i = event.toolTip.size() - 1; i >= 0; i--) { //rarity or mining level requirement - String line = StringUtils.stripControlCodes(event.toolTip.get(i)); + String line = StringUtils.cleanColour(event.toolTip.get(i)); if (line.contains("REFORGE STONE") || line.contains("Requires Mining Skill Level")) { cutoffIndex = i; } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/EnchantingSolvers.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/EnchantingSolvers.java index 6b541256..558917fe 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/EnchantingSolvers.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/EnchantingSolvers.java @@ -45,9 +45,9 @@ import java.util.List; import java.util.Map; public class EnchantingSolvers { - private static SolverType currentSolver = SolverType.NONE; + public static SolverType currentSolver = SolverType.NONE; - private enum SolverType { + public enum SolverType { NONE, CHRONOMATRON, ULTRASEQUENCER, diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/ItemResolutionQuery.java b/src/main/java/io/github/moulberry/notenoughupdates/util/ItemResolutionQuery.java new file mode 100644 index 00000000..1ff659ac --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/ItemResolutionQuery.java @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2022 NotEnoughUpdates contributors + * + * This file is part of NotEnoughUpdates. + * + * NotEnoughUpdates is free software: you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, either + * version 3 of the License, or (at your option) any later version. + * + * NotEnoughUpdates is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with NotEnoughUpdates. If not, see . + */ + +package io.github.moulberry.notenoughupdates.util; + +import com.google.common.collect.Iterables; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import io.github.moulberry.notenoughupdates.NEUManager; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Gui; +import net.minecraft.client.gui.inventory.GuiChest; +import net.minecraft.init.Items; +import net.minecraft.inventory.ContainerChest; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; + +import javax.annotation.Nullable; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class ItemResolutionQuery { + + private static final Pattern ENCHANTED_BOOK_NAME_PATTERN = Pattern.compile("^((?:§.)+)([^§]+) ([IVXL]+)$"); + private static final String EXTRA_ATTRIBUTES = "ExtraAttributes"; + private static final List PET_RARITIES = Arrays.asList( + "COMMON", + "UNCOMMON", + "RARE", + "EPIC", + "LEGENDARY", + "MYTHIC" + ); + private final NEUManager manager; + private NBTTagCompound compound; + private Item itemType; + private int stackSize = -1; + private Gui guiContext; + private String knownInternalName; + + public ItemResolutionQuery(NEUManager manager) { + this.manager = manager; + } + + public ItemResolutionQuery withItemNBT(NBTTagCompound compound) { + this.compound = compound; + return this; + } + + public ItemResolutionQuery withItemStack(ItemStack stack) { + if (stack == null) return this; + this.itemType = stack.getItem(); + this.compound = stack.getTagCompound(); + this.stackSize = stack.stackSize; + return this; + } + + public ItemResolutionQuery withGuiContext(Gui gui) { + this.guiContext = gui; + return this; + } + + public ItemResolutionQuery withCurrentGuiContext() { + this.guiContext = Minecraft.getMinecraft().currentScreen; + return this; + } + + public ItemResolutionQuery withKnownInternalName(String knownInternalName) { + this.knownInternalName = knownInternalName; + return this; + } + + @Nullable + public String resolveInternalName() { + if (knownInternalName != null) { + return knownInternalName; + } + String resolvedName = resolveFromSkyblock(); + if (resolvedName == null) { + resolvedName = resolveContextualName(); + } else { + switch (resolvedName.intern()) { + case "PET": + resolvedName = resolvePetName(); + break; + case "RUNE": + resolvedName = resolveRuneName(); + break; + case "ENCHANTED_BOOK": + resolvedName = resolveEnchantedBookNameFromNBT(); + break; + case "PARTY_HAT_CRAB": + case "PARTY_HAT_CRAB_ANIMATED": + resolvedName = resolveCrabHatName(); + break; + } + } + + return resolvedName; + } + + @Nullable + public JsonObject resolveToItemListJson() { + String internalName = resolveInternalName(); + if (internalName == null) { + return null; + } + return manager.getItemInformation().get(internalName); + } + + @Nullable + public ItemStack resolveToItemStack() { + JsonObject jsonObject = resolveToItemListJson(); + if (jsonObject == null) return null; + return manager.jsonToStack(jsonObject); + } + + // + private boolean isBazaar(IInventory chest) { + if (chest.getDisplayName().getFormattedText().startsWith("Bazaar ➜ ")) { + return true; + } + int bazaarSlot = chest.getSizeInventory() - 5; + if (bazaarSlot < 0) return false; + ItemStack stackInSlot = chest.getStackInSlot(bazaarSlot); + if (stackInSlot == null || stackInSlot.stackSize == 0) return false; + // NBT lore, we do not care about rendered lore + List lore = ItemUtils.getLore(stackInSlot); + return lore.contains("§7To Bazaar"); + } + + private String resolveContextualName() { + if (!(guiContext instanceof GuiChest)) { + return null; + } + GuiChest chest = (GuiChest) guiContext; + ContainerChest inventorySlots = (ContainerChest) chest.inventorySlots; + String guiName = inventorySlots.getLowerChestInventory().getDisplayName().getUnformattedText(); + boolean isOnBazaar = isBazaar(inventorySlots.getLowerChestInventory()); + String displayName = ItemUtils.getDisplayName(compound); + if (displayName == null) return null; + if (itemType == Items.enchanted_book && isOnBazaar && compound != null) { + return resolveEnchantmentByName(displayName); + } + if (displayName.endsWith("Enchanted Book") && guiName.startsWith("Superpairs")) { + for (String loreLine : ItemUtils.getLore(compound)) { + String enchantmentIdCandidate = resolveEnchantmentByName(loreLine); + if (enchantmentIdCandidate != null) return enchantmentIdCandidate; + } + return null; + } + return null; + } + + private String resolveEnchantmentByName(String name) { + Matcher matcher = ENCHANTED_BOOK_NAME_PATTERN.matcher(name); + if (!matcher.matches()) return null; + String format = matcher.group(1).toLowerCase(Locale.ROOT); + String enchantmentName = matcher.group(2).trim(); + String romanLevel = matcher.group(3); + boolean ultimate = (format.contains("§l")); + + return (ultimate ? "ULTIMATE_" : "") + + enchantmentName.replace(" ", "_").toUpperCase(Locale.ROOT) + + ";" + Utils.parseRomanNumeral(romanLevel); + } + + private String resolveCrabHatName() { + int crabHatYear = getExtraAttributes().getInteger("party_hat_year"); + String color = getExtraAttributes().getString("party_hat_color"); + return "PARTY_HAT_CRAB_" + color.toUpperCase(Locale.ROOT) + (crabHatYear == 2022 ? "_ANIMATED" : ""); + } + + private String resolveEnchantedBookNameFromNBT() { + NBTTagCompound enchantments = getExtraAttributes().getCompoundTag("enchantments"); + String enchantName = Iterables.getOnlyElement(enchantments.getKeySet(), null); + if (enchantName == null || enchantName.isEmpty()) return null; + return enchantName.toUpperCase(Locale.ROOT) + ";" + enchantments.getInteger(enchantName); + } + + private String resolveRuneName() { + NBTTagCompound runes = getExtraAttributes().getCompoundTag("runes"); + String runeName = Iterables.getOnlyElement(runes.getKeySet(), null); + if (runeName == null || runeName.isEmpty()) return null; + return runeName.toUpperCase(Locale.ROOT) + "_RUNE;" + runes.getInteger(runeName); + } + + private String resolvePetName() { + String petInfo = getExtraAttributes().getString("petInfo"); + if (petInfo == null || petInfo.isEmpty()) return null; + try { + JsonObject petInfoObject = manager.gson.fromJson(petInfo, JsonObject.class); + String petId = petInfoObject.get("type").getAsString(); + String petTier = petInfoObject.get("tier").getAsString(); + int rarityIndex = PET_RARITIES.indexOf(petTier); + return petId.toUpperCase(Locale.ROOT) + ";" + rarityIndex; + } catch (JsonParseException | ClassCastException ex) { + /* This happens if Hypixel changed the pet json format; + I still log this exception, since this case *is* exceptional and cannot easily be recovered from */ + ex.printStackTrace(); + return null; + } + } + + private NBTTagCompound getExtraAttributes() { + if (compound == null) return new NBTTagCompound(); + return compound.getCompoundTag(EXTRA_ATTRIBUTES); + } + + private String resolveFromSkyblock() { + String internalName = getExtraAttributes().getString("id"); + if (internalName == null || internalName.isEmpty()) return null; + return internalName.toUpperCase(Locale.ROOT); + } + + // + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java index 3a2db0fb..cffdd164 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/ItemUtils.java @@ -73,7 +73,10 @@ public class ItemUtils { } public static List getLore(ItemStack is) { - NBTTagCompound tagCompound = is.getTagCompound(); + return getLore(is.getTagCompound()); + } + + public static List getLore(NBTTagCompound tagCompound) { if (tagCompound == null) { return Collections.emptyList(); } @@ -84,4 +87,13 @@ public class ItemUtils { } return list; } + + public static String getDisplayName(NBTTagCompound compound) { + if (compound == null) return null; + String string = compound.getCompoundTag("display").getString("Name"); + if (string == null || string.isEmpty()) + return null; + return string; + } + } -- cgit