From d0617ef3799fe7c91756c1e01a2710572ddb84ec Mon Sep 17 00:00:00 2001 From: Roman / Linnea Gräf Date: Sat, 26 Nov 2022 09:48:51 +0100 Subject: EnchantStyleCustomizer rewrite (#406) Co-authored-by: nea Co-authored-by: nea Co-authored-by: nea Co-authored-by: nea Co-authored-by: nea Co-authored-by: nea Co-authored-by: nea --- build.gradle.kts | 6 + .../notenoughupdates/NotEnoughUpdates.java | 2 + .../listener/ItemTooltipListener.java | 300 --------------------- .../miscfeatures/item/enchants/EnchantMatcher.java | 128 +++++++++ .../item/enchants/EnchantStyleCustomizer.java | 113 ++++++++ .../options/seperateSections/Misc.java | 2 +- .../moulberry/notenoughupdates/util/LRUCache.java | 63 +++++ .../notenoughupdates/util/LateBindingChroma.java | 103 +++++++ .../moulberry/notenoughupdates/util/Utils.java | 7 +- 9 files changed, 422 insertions(+), 302 deletions(-) create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/item/enchants/EnchantMatcher.java create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/item/enchants/EnchantStyleCustomizer.java create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/util/LRUCache.java create mode 100644 src/main/java/io/github/moulberry/notenoughupdates/util/LateBindingChroma.java diff --git a/build.gradle.kts b/build.gradle.kts index a661d6be..a93fccfc 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -30,6 +30,7 @@ plugins { id("com.github.johnrengelman.shadow") version "7.1.2" id("io.github.juuxel.loom-quiltflower") version "1.7.3" `maven-publish` + id("io.freefair.lombok") version "6.5.1" kotlin("jvm") version "1.7.20" } @@ -76,6 +77,11 @@ repositories { maven("https://jitpack.io") } +lombok { + version.set("1.18.24") +} + + val shadowImplementation by configurations.creating { configurations.implementation.get().extendsFrom(this) } diff --git a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java index 37b493e2..32c8aa31 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/NotEnoughUpdates.java @@ -67,6 +67,7 @@ import io.github.moulberry.notenoughupdates.miscfeatures.WitherCloakChanger; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBiomes; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.CustomBlockSounds; import io.github.moulberry.notenoughupdates.miscfeatures.customblockzones.DwarvenMinesTextures; +import io.github.moulberry.notenoughupdates.miscfeatures.item.enchants.EnchantStyleCustomizer; import io.github.moulberry.notenoughupdates.miscfeatures.updater.AutoUpdater; import io.github.moulberry.notenoughupdates.miscfeatures.world.EnderNodeHighlighter; import io.github.moulberry.notenoughupdates.miscfeatures.world.GlowingMushroomHighlighter; @@ -340,6 +341,7 @@ public class NotEnoughUpdates { MinecraftForge.EVENT_BUS.register(navigation); MinecraftForge.EVENT_BUS.register(new GlowingMushroomHighlighter()); MinecraftForge.EVENT_BUS.register(new WorldListener(this)); + MinecraftForge.EVENT_BUS.register(EnchantStyleCustomizer.INSTANCE); MinecraftForge.EVENT_BUS.register(TitleUtil.getInstance()); MinecraftForge.EVENT_BUS.register(EnderNodeHighlighter.getInstance()); MinecraftForge.EVENT_BUS.register(AbiphoneFavourites.getInstance()); 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 dd3f5efd..d9a00370 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/listener/ItemTooltipListener.java @@ -29,9 +29,7 @@ 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.ItemCustomizeManager; import io.github.moulberry.notenoughupdates.miscfeatures.PetInfoOverlay; -import io.github.moulberry.notenoughupdates.miscgui.GuiEnchantColour; import io.github.moulberry.notenoughupdates.profileviewer.GuiProfileViewer; import io.github.moulberry.notenoughupdates.util.Constants; import io.github.moulberry.notenoughupdates.util.Utils; @@ -45,7 +43,6 @@ import net.minecraft.nbt.NBTTagList; import net.minecraft.nbt.NBTUtil; import net.minecraft.util.EnumChatFormatting; import net.minecraftforge.event.entity.player.ItemTooltipEvent; -import net.minecraftforge.fml.common.Loader; import net.minecraftforge.fml.common.eventhandler.EventPriority; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import org.apache.commons.lang3.text.WordUtils; @@ -61,7 +58,6 @@ import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.regex.Matcher; @@ -79,81 +75,6 @@ public class ItemTooltipListener { private boolean showReforgeStoneStats = true; private boolean pressedArrowLast = false; private boolean pressedShiftLast = false; - private int sbaloaded = -1; - - //region Enchant optimisation - private String lastItemUuid; - - public static class EnchantString { - final String preEnchantText; - final String enchantName; - - final String mod; - final String colourCode; - final boolean isChroma; - - public EnchantString(String enchant, String preEnchantText, String modifier, String colourCode) { - this.preEnchantText = preEnchantText; - this.enchantName = enchant; - this.mod = modifier; - this.colourCode = colourCode; - this.isChroma = colourCode.equals("z"); - } - - private boolean matches(String line) { - return line.matches(".*" + preEnchantText + enchantName + ".*"); - } - - public String applyMods(String line, int lineIndex) { - if (!matches(line)) return null; - - if (!isChroma) { - return line.replace(preEnchantText + enchantName, - "\u00A7" + colourCode + mod + enchantName - ); - } else { - //if you couldn't tell, this is for chroma. - int newOffset = Minecraft.getMinecraft().fontRendererObj.getStringWidth(preEnchantText + enchantName); - return line.replace( - preEnchantText + enchantName, - Utils.chromaString(enchantName, newOffset / 12f + lineIndex, preEnchantText.matches(".*\\u00A7d.*")) - ); - } - } -} - -public static class EnchantLine { - final String originalLine; - final ArrayList enchants; - - public EnchantLine(String line, ArrayList enchants) { - this.enchants = enchants; - this.originalLine = line; - } - - public boolean isSameAsBefore(String line) { - return line.equals(originalLine); - } - - public String replaceLine(String line, int index) { - if (line.equals(originalLine)) { - for (EnchantString enchant: enchants) { - String modifiedLine = enchant.applyMods(line, index); - if (modifiedLine != null) { - line = modifiedLine; - } - } - } - - return line; - } -} - - private final ArrayList enchantList = new ArrayList<>(); - private int firstEnchantIndex = -1; - private int lastEnchantIndex = -1; - - //endregion public ItemTooltipListener(NotEnoughUpdates neu) { this.neu = neu; @@ -164,17 +85,6 @@ public static class EnchantLine { percentStats.add("ability_damage"); } - private boolean isSkyblockAddonsLoaded() { - if (sbaloaded == -1) { - if (Loader.isModLoaded("skyblockaddons")) { - sbaloaded = 1; - } else { - sbaloaded = 0; - } - } - return sbaloaded == 1; - } - @SubscribeEvent(priority = EventPriority.LOW) public void onItemTooltipLow(ItemTooltipEvent event) { if (!NotEnoughUpdates.INSTANCE.isOnSkyblock()) return; @@ -194,10 +104,6 @@ public static class EnchantLine { "enchantments", 10 ); - boolean hasAttributes = event.itemStack.getTagCompound().getCompoundTag("ExtraAttributes").hasKey( - "attributes", - 10 - ); Set enchantIds = new HashSet<>(); if (hasEnchantments) enchantIds = event.itemStack.getTagCompound().getCompoundTag("ExtraAttributes").getCompoundTag( "enchantments").getKeySet(); @@ -251,22 +157,10 @@ public static class EnchantLine { boolean gotToEnchants = false; boolean passedEnchants = false; - boolean foundEnchants = false; boolean dungeonProfit = false; List newTooltip = new ArrayList<>(); - String currentUuid = ItemCustomizeManager.getUuidForItem(event.itemStack); - - boolean resetEnchantCache = false; - //reset data if cache is off - if (!NotEnoughUpdates.INSTANCE.config.misc.cacheItemEnchant) { - lastItemUuid = null; - firstEnchantIndex = -1; - lastEnchantIndex = -1; - enchantList.clear(); - } - for (int k = 0; k < event.toolTip.size(); k++) { String line = event.toolTip.get(k); boolean thisLineHasEnchants = false; @@ -470,187 +364,6 @@ public static class EnchantLine { } } } - if (hasEnchantments || hasAttributes) { - Pattern findEnchantPattern = Pattern.compile(".*(?[\\w ]+) (?[0-9]+|[IVXLCDM]+)$"); - Matcher findEnchantMatcher = findEnchantPattern.matcher(line); - if (findEnchantMatcher.matches()) { - if (NotEnoughUpdates.INSTANCE.config.misc.cacheItemEnchant && !foundEnchants) { - foundEnchants = true; - if (( - lastItemUuid == null || currentUuid == null || - (currentUuid != null && - !Objects.equals(lastItemUuid, currentUuid)))) { - firstEnchantIndex = k;//k being the line index - lastEnchantIndex = k; - enchantList.clear(); - } - } - thisLineHasEnchants = true; - } - - ArrayList addedEnchants = new ArrayList<>(); - //if cacheItemEnchant option is disabled it will always be length of 0 - if (enchantList.size() == 0 || enchantList.size() <= k - firstEnchantIndex) { - ArrayList enchants = new ArrayList<>(); - final String oLine = line; - - for (String op : NotEnoughUpdates.INSTANCE.config.hidden.enchantColours) { - List colourOps = GuiEnchantColour.splitter.splitToList(op); - String enchantName = GuiEnchantColour.getColourOpIndex(colourOps, 0); - String comparator = GuiEnchantColour.getColourOpIndex(colourOps, 1); - String comparison = GuiEnchantColour.getColourOpIndex(colourOps, 2); - String colourCode = GuiEnchantColour.getColourOpIndex(colourOps, 3); - String modifier = GuiEnchantColour.getColourOpIndex(colourOps, 4); - - int modifierI = GuiEnchantColour.getIntModifier(modifier); - - assert enchantName != null; - if (enchantName.length() == 0) continue; - assert comparator != null; - if (comparator.length() == 0) continue; - assert comparison != null; - if (comparison.length() == 0) continue; - assert colourCode != null; - if (colourCode.length() == 0) continue; - - int comparatorI = ">=<".indexOf(comparator.charAt(0)); - - int levelToFind; - try { - levelToFind = Integer.parseInt(comparison); - } catch (Exception e) { - continue; - } - - if (comparatorI < 0) continue; - String regexText = "0123456789abcdefz"; - if (isSkyblockAddonsLoaded()) { - regexText = regexText + "Z"; - } - - if (regexText.indexOf(colourCode.charAt(0)) < 0) continue; - - //item_lore = item_lore.replaceAll("\\u00A79("+lvl4Max+" IV)", EnumChatFormatting.DARK_PURPLE+"$1"); - //9([a-zA-Z ]+?) ([0-9]+|(I|II|III|IV|V|VI|VII|VIII|IX|X))(,|$) - Pattern pattern; - try { - pattern = Pattern.compile( - "(\\u00A7b|\\u00A79|\\u00A7(b|9|l)\\u00A7d\\u00A7l)(?" + enchantName + ") " + - "(?[0-9]+|(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII|XIII|XIV|XV|XVI|XVII|XVIII|XIX|XX))((\\u00A79)?,|( \\u00A78(?:,?[0-9]+)*)?$)"); - } catch (Exception e) { - continue; - } - Matcher matcher = pattern.matcher(oLine); - int matchCount = 0; - while (matcher.find() && matchCount < 5) { - if (Utils.cleanColour(matcher.group("enchantName")).startsWith(" ")) continue; - - matchCount++; - int level = -1; - String levelStr = matcher.group("level"); - if (levelStr == null || levelStr.isEmpty()) continue; - level = Utils.parseIntOrRomanNumeral(levelStr); - boolean matches = false; - if (level > 0) { - switch (comparator) { - case ">": - matches = level > levelToFind; - break; - case "=": - matches = level == levelToFind; - break; - case "<": - matches = level < levelToFind; - break; - } - } - if (matches) { - String enchantText = matcher.group("enchantName"); - StringBuilder extraModifiersBuilder = new StringBuilder(); - - if ((modifierI & GuiEnchantColour.BOLD_MODIFIER) != 0) { - extraModifiersBuilder.append(EnumChatFormatting.BOLD); - } - if ((modifierI & GuiEnchantColour.ITALIC_MODIFIER) != 0) { - extraModifiersBuilder.append(EnumChatFormatting.ITALIC); - } - if ((modifierI & GuiEnchantColour.UNDERLINE_MODIFIER) != 0) { - extraModifiersBuilder.append(EnumChatFormatting.UNDERLINE); - } - if ((modifierI & GuiEnchantColour.OBFUSCATED_MODIFIER) != 0) { - extraModifiersBuilder.append(EnumChatFormatting.OBFUSCATED); - } - if ((modifierI & GuiEnchantColour.STRIKETHROUGH_MODIFIER) != 0) { - extraModifiersBuilder.append(EnumChatFormatting.STRIKETHROUGH); - } - - String extraMods = extraModifiersBuilder.toString(); - if (!colourCode.equals("z")) { - if (!addedEnchants.contains(enchantText)) { - int startMatch = matcher.start(); - int endMatch = matcher.end(); - String subString = oLine.substring(startMatch, endMatch); - String preEnchantText = subString.split(String.valueOf(enchantText.charAt(0)))[0]; - - line = line.replace(preEnchantText + enchantText, - "\u00A7" + colourCode + extraMods + enchantText - ); - - enchants.add(new EnchantString( - enchantText, - preEnchantText, - extraMods, - colourCode - )); - } - } else { - //Chroma - int startMatch = matcher.start(); - int endMatch = matcher.end(); - String subString = line.substring(startMatch, endMatch); - int newOffset = Minecraft.getMinecraft().fontRendererObj.getStringWidth(subString); - - //split the substring with the first letter found in enchant text allowing to only get the color codes. - String preEnchantText = subString.split(String.valueOf(enchantText.charAt(0)))[0]; - - line = line.replace( - preEnchantText + enchantText, - Utils.chromaString(enchantText, newOffset / 12f + k, preEnchantText.matches(".*\\u00A7d.*")) - ); - enchants.add(new EnchantString( - enchantText, - preEnchantText, - extraMods, - colourCode - )); - } - addedEnchants.add(enchantText); - } - } - } - if (NotEnoughUpdates.INSTANCE.config.misc.cacheItemEnchant) { - if (lastItemUuid == null || !Objects.equals(lastItemUuid, currentUuid)) { - EnchantLine enchantLine = new EnchantLine(oLine, enchants); - enchantList.add(enchantLine); - } - } - } - - if (NotEnoughUpdates.INSTANCE.config.misc.cacheItemEnchant && foundEnchants) { - //found enchants in the past - if (k <= lastEnchantIndex) { - if (Objects.equals(lastItemUuid, currentUuid) && (firstEnchantIndex != -1 && enchantList.size() > k - firstEnchantIndex) && (k - firstEnchantIndex) > 0) { - //if it has the line, replaces it with the cached line - EnchantLine enchantLine = enchantList.get(k - firstEnchantIndex); - if (!enchantLine.isSameAsBefore(line)) { - resetEnchantCache = true; - } - - line = enchantLine.replaceLine(line, k); - } - } - } - } newTooltip.add(line); @@ -816,7 +529,6 @@ public static class EnchantLine { } } } - if (thisLineHasEnchants) lastEnchantIndex+=1; } pressedShiftLast = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT); @@ -833,18 +545,6 @@ public static class EnchantLine { NotEnoughUpdates.INSTANCE.config.petOverlay.hidePetTooltip) { event.toolTip.clear(); } - - - if (foundEnchants && currentUuid != null && lastItemUuid != currentUuid) { - lastItemUuid = currentUuid;//cache is set; - } - - if (resetEnchantCache) { - lastItemUuid = null; - enchantList.clear(); - firstEnchantIndex = -1; - lastEnchantIndex = -1; - } } private void petToolTipXPExtendPetMenu(ItemTooltipEvent event) { diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/item/enchants/EnchantMatcher.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/item/enchants/EnchantMatcher.java new file mode 100644 index 00000000..e8ded18c --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/item/enchants/EnchantMatcher.java @@ -0,0 +1,128 @@ +/* + * 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.miscfeatures.item.enchants; + +import com.google.common.base.Supplier; +import com.google.common.base.Suppliers; +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.miscgui.GuiEnchantColour; +import io.github.moulberry.notenoughupdates.util.LRUCache; +import lombok.Value; +import net.minecraft.util.EnumChatFormatting; +import net.minecraftforge.fml.common.Loader; + +import java.util.List; +import java.util.Optional; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +@Value +public class EnchantMatcher { + public static final String GROUP_ENCHANT_NAME = "enchantName"; + public static final String GROUP_LEVEL = "level"; + + private final static String romanNumerals = + "(I|II|III|IV|V|VI|VII|VIII|IX|X|XI|XII|XIII|XIV|XV|XVI|XVII|XVIII|XIX|XX)"; + private final static Supplier validColors = Suppliers.memoize(() -> + "0123456789abcdefz" + (Loader.isModLoaded("skyblockaddons") ? "Z" : "")); + + public static LRUCache> fromSaveFormatMemoized = + LRUCache.memoize( + EnchantMatcher::fromSaveFormat, + () -> NotEnoughUpdates.INSTANCE.config.hidden.enchantColours.size() + 1 + ); + + Pattern patternWithLevels; + int compareWith; + String formatting; + char compareUsing; + + /** + * Use {@link #fromSaveFormatMemoized} instead. + */ + @Deprecated + public static Optional fromSaveFormat(String saveFormat) { + List colourOps = GuiEnchantColour.splitter.splitToList(saveFormat); + String enchantName = GuiEnchantColour.getColourOpIndex(colourOps, 0); + String comparator = GuiEnchantColour.getColourOpIndex(colourOps, 1); + String comparison = GuiEnchantColour.getColourOpIndex(colourOps, 2); + String colourCode = GuiEnchantColour.getColourOpIndex(colourOps, 3); + String modifier = GuiEnchantColour.getColourOpIndex(colourOps, 4); + + int intModifier = GuiEnchantColour.getIntModifier(modifier); + + if (comparator.length() != 1 + || colourCode.length() != 1 + || comparison.isEmpty() + || enchantName.isEmpty()) return Optional.empty(); + + if (!">=<".contains(comparator)) return Optional.empty(); + int compareWith; + try { + compareWith = Integer.parseInt(comparison); + } catch (NumberFormatException e) { + return Optional.empty(); + } + + if (!validColors.get().contains(colourCode)) return Optional.empty(); + Pattern patternWithLevels; + try { + patternWithLevels = Pattern.compile( + "(§b|§9|§([b9l])§d§l)(?<" + GROUP_ENCHANT_NAME + ">" + enchantName + ") " + + "(?<" + GROUP_LEVEL + ">\\d+|" + romanNumerals + ")(?:(?:§9)?,|(?: §8(?:,?[0-9]+)*)?$)"); + } catch (PatternSyntaxException e) { + NotEnoughUpdates.LOGGER.warn("Invalid pattern constructed for enchant matching", e); + return Optional.empty(); + } + + String formatting = "§" + colourCode; + + if ((intModifier & GuiEnchantColour.BOLD_MODIFIER) != 0) { + formatting += EnumChatFormatting.BOLD; + } + if ((intModifier & GuiEnchantColour.ITALIC_MODIFIER) != 0) { + formatting += EnumChatFormatting.ITALIC; + } + if ((intModifier & GuiEnchantColour.UNDERLINE_MODIFIER) != 0) { + formatting += EnumChatFormatting.UNDERLINE; + } + if ((intModifier & GuiEnchantColour.OBFUSCATED_MODIFIER) != 0) { + formatting += EnumChatFormatting.OBFUSCATED; + } + if ((intModifier & GuiEnchantColour.STRIKETHROUGH_MODIFIER) != 0) { + formatting += EnumChatFormatting.STRIKETHROUGH; + } + + return Optional.of(new EnchantMatcher(patternWithLevels, compareWith, formatting, comparator.charAt(0))); + } + + public boolean doesLevelMatch(int level) { + switch (compareUsing) { + case '>': + return level > compareWith; + case '=': + return level == compareWith; + case '<': + return level < compareWith; + } + return false; + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/item/enchants/EnchantStyleCustomizer.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/item/enchants/EnchantStyleCustomizer.java new file mode 100644 index 00000000..47acfa60 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/item/enchants/EnchantStyleCustomizer.java @@ -0,0 +1,113 @@ +/* + * 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.miscfeatures.item.enchants; + +import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.util.LRUCache; +import io.github.moulberry.notenoughupdates.util.LateBindingChroma; +import io.github.moulberry.notenoughupdates.util.Utils; +import lombok.var; +import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.regex.Matcher; + +public class EnchantStyleCustomizer { + + public static EnchantStyleCustomizer INSTANCE = new EnchantStyleCustomizer(); + + LRUCache enchantLineCache = LRUCache.memoize(this::replaceEnchantLine, 1000); + List lastEnchant = new ArrayList<>(); + + public LateBindingChroma replaceEnchantLine(String originalLine) { + var line = originalLine; + Set alreadyReplacedEnchants = new HashSet<>(); + for (String enchantMatcherStr : NotEnoughUpdates.INSTANCE.config.hidden.enchantColours) { + var enchantMatcherP = EnchantMatcher.fromSaveFormatMemoized.apply(enchantMatcherStr); + if (!enchantMatcherP.isPresent()) continue; + var enchantMatcher = enchantMatcherP.get(); + Matcher matcher; + var matchIterations = 0; + var last = 0; + while ((matcher = enchantMatcher.getPatternWithLevels().matcher(line)).find(last) && matchIterations++ < 5) { + var enchantName = matcher.group(EnchantMatcher.GROUP_ENCHANT_NAME); + var levelText = matcher.group(EnchantMatcher.GROUP_LEVEL); + if (enchantName == null || levelText == null + || levelText.isEmpty() || enchantName.isEmpty()) continue; + String cleanEnchantName = Utils.cleanColour(enchantName); + if (cleanEnchantName.startsWith(" ")) { + last = matcher.end(); + continue; + } + + var level = Utils.parseIntOrRomanNumeral(levelText); + if (!enchantMatcher.doesLevelMatch(level)) { + last = matcher.end(); + continue; + } + + if (alreadyReplacedEnchants.contains(cleanEnchantName)) continue; + alreadyReplacedEnchants.add(cleanEnchantName); + + var startMatch = matcher.start(); + var endLevel = matcher.end(EnchantMatcher.GROUP_LEVEL); + + var parsed = line.substring(0, startMatch) + + enchantMatcher.getFormatting() + enchantName + " " + levelText; + line = parsed + (endLevel >= line.length() ? "" : line.substring(endLevel)); + last = parsed.length(); + } + } + return LateBindingChroma.of(line); + } + + public void cacheInvalidate() { + enchantLineCache.clearCache(); + } + + @SubscribeEvent + public void onItemTooltip(ItemTooltipEvent event) { + var nbt = event.itemStack.getTagCompound(); + if (nbt == null) return; + var extraAttributes = nbt.getCompoundTag("ExtraAttributes"); + var enchantments = extraAttributes.getCompoundTag("enchantments"); + var attributes = extraAttributes.getCompoundTag("attributes"); + enchantments.merge(attributes); + if (enchantments.getKeySet().isEmpty()) return; + if (!lastEnchant.equals(NotEnoughUpdates.INSTANCE.config.hidden.enchantColours) + || !NotEnoughUpdates.INSTANCE.config.misc.cacheItemEnchant) { + cacheInvalidate(); + lastEnchant = new ArrayList<>(NotEnoughUpdates.INSTANCE.config.hidden.enchantColours); + } + var lineIndex = 0; + for (var iterator = event.toolTip.listIterator(); iterator.hasNext(); ) { + var nextLine = iterator.next(); + var replacedLine = enchantLineCache.apply(nextLine).render(lineIndex++); + if (!nextLine.equals(replacedLine)) { + iterator.set(replacedLine); + } + } + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java index ce8325bb..11ace314 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/options/seperateSections/Misc.java @@ -186,7 +186,7 @@ public class Misc { @Expose @ConfigOption( name = "Cache Tooltip Enchants", - desc = "Caches item enchants in tooltip to only use the neuec config once per item lookup.\nNOTE: It doesn't work on items without a uuid" + desc = "Caches item enchants in tooltip to only use the neuec config once per item lookup." ) @ConfigEditorBoolean @ConfigAccordionId(id = 1) diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/LRUCache.java b/src/main/java/io/github/moulberry/notenoughupdates/util/LRUCache.java new file mode 100644 index 00000000..f107d522 --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/LRUCache.java @@ -0,0 +1,63 @@ +/* + * 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 java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.function.Function; +import java.util.function.IntSupplier; + +public interface LRUCache extends Function { + + static LRUCache memoize(Function mapper, int maxCacheSize) { + return memoize(mapper, () -> maxCacheSize); + } + + static LRUCache memoize(Function mapper, IntSupplier maxCacheSize) { + Map cache = new LinkedHashMap(10, 0.75F, true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return this.size() > maxCacheSize.getAsInt(); + } + }; + Map synchronizedCache = Collections.synchronizedMap(cache); + return new LRUCache() { + @Override + public void clearCache() { + synchronizedCache.clear(); + } + + @Override + public int size() { + return synchronizedCache.size(); + } + + @Override + public V apply(K k) { + return synchronizedCache.computeIfAbsent(k, mapper); + } + }; + } + + int size(); + + void clearCache(); +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/LateBindingChroma.java b/src/main/java/io/github/moulberry/notenoughupdates/util/LateBindingChroma.java new file mode 100644 index 00000000..ace3afff --- /dev/null +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/LateBindingChroma.java @@ -0,0 +1,103 @@ +/* + * 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 lombok.EqualsAndHashCode; +import lombok.Value; +import lombok.var; +import net.minecraft.client.Minecraft; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Pattern; + +public abstract class LateBindingChroma { + private static final Pattern FORMATTING_CODE = Pattern.compile("§(.)"); + + public static LateBindingChroma of(String raw) { + if (!raw.contains("§z")) + return new LateBindingChroma.WithoutChroma(raw); + var matcher = FORMATTING_CODE.matcher(raw); + var chunks = new ArrayList(); + var sb = new StringBuffer(); + var color = ""; + var extraFormatting = ""; + while (matcher.find()) { + sb.setLength(0); + matcher.appendReplacement(sb, ""); + chunks.add(new Chunk(color, extraFormatting, sb.toString())); + var c = matcher.group(1); + if ("lnomk".contains(c)) { + extraFormatting += "§" + c; + } else { + color = c; + extraFormatting = ""; + } + } + sb.setLength(0); + matcher.appendTail(sb); + chunks.add(new Chunk(color, extraFormatting, sb.toString())); + chunks.removeIf(it -> it.text.isEmpty()); + return new LateBindingChroma.WithChroma(chunks); + } + + public abstract String render(float chromaOffset); + + @Value + @EqualsAndHashCode(callSuper = false) + static class WithoutChroma extends LateBindingChroma { + String content; + + @Override + public String render(float chromaOffset) { + return content; + } + } + + @Value + @EqualsAndHashCode(callSuper = false) + static class WithChroma extends LateBindingChroma { + List chunks; + + @Override + public String render(float offset) { + var sb = new StringBuilder(); + for (Chunk chunk : chunks) { + String text; + if (chunk.color.equals("z")) { + text = Utils.chromaString(chunk.text, offset, chunk.extraFormatting); + } else { + text = (chunk.color.isEmpty() ? "" : ("§" + chunk.color)) + chunk.extraFormatting + chunk.text; + } + sb.append(text); + offset += Minecraft.getMinecraft().fontRendererObj.getStringWidth(text); + } + return sb.toString(); + } + } + + @Value + public static class Chunk { + String color; + String extraFormatting; + String text; + } + +} diff --git a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java index cac6990f..ecc03caf 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/util/Utils.java @@ -301,7 +301,12 @@ public class Utils { } public static String chromaString(String str, float offset, boolean bold) { + return chromaString(str, offset, bold ? "§l" : ""); + } + + public static String chromaString(String str, float offset, String extraFormatting) { str = cleanColour(str); + boolean bold = extraFormatting.contains("§l"); long currentTimeMillis = System.currentTimeMillis(); if (startTime == 0) startTime = currentTimeMillis; @@ -320,7 +325,7 @@ public class Utils { if (index < 0) index += rainbow.length; rainbowText.append(rainbow[index]); - if (bold) rainbowText.append(EnumChatFormatting.BOLD); + rainbowText.append(extraFormatting); rainbowText.append(c); } return rainbowText.toString(); -- cgit