From a8c689dd3a0fd6823053f1580993008c84d29ae2 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sat, 4 May 2024 15:20:10 +0300 Subject: Add chocolate factory helper Highlights the best pick based on the price and CPS gain. --- .../skyblocker/mixins/HandledScreenMixin.java | 4 + .../chocolatefactory/ChocolateFactorySolver.java | 93 ++++++++++++++++++++++ .../utils/render/gui/ContainerSolverManager.java | 4 +- 3 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java (limited to 'src/main/java/de') diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java index f662be7c..87b1bc79 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java @@ -4,6 +4,7 @@ import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.systems.RenderSystem; import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.chocolatefactory.ChocolateFactorySolver; import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; import de.hysky.skyblocker.skyblock.experiment.ExperimentSolver; import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver; @@ -284,6 +285,9 @@ public abstract class HandledScreenMixin extends Screen default -> { /*Do Nothing*/ } } } + if (currentSolver instanceof ChocolateFactorySolver chocolateFactorySolver) { + chocolateFactorySolver.markDirty(); + } } @Inject(method = "drawSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawItem(Lnet/minecraft/item/ItemStack;III)V")) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java new file mode 100644 index 00000000..ef79cd41 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -0,0 +1,93 @@ +package de.hysky.skyblocker.skyblock.chocolatefactory; + +import de.hysky.skyblocker.skyblock.experiment.ExperimentSolver; +import de.hysky.skyblocker.utils.render.gui.ColorHighlight; +import de.hysky.skyblocker.utils.render.gui.ContainerSolver; +import it.unimi.dsi.fastutil.ints.*; +import net.fabricmc.loader.impl.lib.sat4j.minisat.core.Solver; +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.nbt.NbtElement; +import net.minecraft.nbt.NbtList; +import net.minecraft.text.Text; + +import java.awt.*; +import java.util.*; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +public class ChocolateFactorySolver extends ContainerSolver { + private static final Pattern CPS_PATTERN = Pattern.compile("\\+([\\d,]+) Chocolate per second"); + private static final Pattern COST_PATTERN = Pattern.compile("Cost ([\\d,]+) Chocolate"); + + public ChocolateFactorySolver() { + super("^Chocolate Factory$"); + } + + @Override + protected boolean isEnabled() { + return true; //Todo: add a config option and check if it's enabled from there + } + + @Override + protected List getColors(String[] groups, Map slots) { + Int2DoubleMap cpsIncreaseFactors = new Int2DoubleLinkedOpenHashMap(5); //There are only 5 rabbits on the screen. + for (Map.Entry entry : slots.entrySet()) { + ItemStack item = entry.getValue(); + if (item.getItem() != Items.PLAYER_HEAD || !item.hasNbt() || item.isEmpty()) continue; + + String lore = getLore(item); + if (lore.isBlank()) continue; + + OptionalDouble cpsIncreaseFactor = getCPSIncreaseFactor(lore); + if (cpsIncreaseFactor.isEmpty()) continue; //Something went wrong, skip this item + cpsIncreaseFactors.put(entry.getKey().intValue(), cpsIncreaseFactor.getAsDouble()); + } + Optional bestSlot = cpsIncreaseFactors.int2DoubleEntrySet().stream().max(Map.Entry.comparingByValue()); + if (bestSlot.isEmpty()) return List.of(); //No valid slots found, somehow. This means something went wrong, despite all the checks thus far. + return List.of(ColorHighlight.green(bestSlot.get().getIntKey())); + } + + private String getLore(ItemStack item) { + NbtCompound display = item.getSubNbt(ItemStack.DISPLAY_KEY); + if (display == null || display.isEmpty() || !display.contains(ItemStack.LORE_KEY, NbtElement.LIST_TYPE)) return ""; + NbtList lore = display.getList(ItemStack.LORE_KEY, NbtElement.STRING_TYPE); + return lore.stream() + .map(NbtElement::asString) + .map(Text.Serialization::fromJson) //Serialize the nbt string into a text to get the content string + .filter(Objects::nonNull) + .map(Text::getString) + .collect(Collectors.joining(" ")); //Join all lore lines into one string for ease of regexing + //The space is so that the regex pattern still matches even if the word is split into 2 lines, + //as normally the line end and line start contain no spaces and would not match the pattern when concatenated + } + + /** + * The "CPS increase factor" here is the increase in CPS per chocolate spent. + * The highest value among the choices is the best one to pick. + * + * @param lore The lore of the item + * @return The CPS increase factor of the item, or an empty optional if it couldn't be found + */ + private OptionalDouble getCPSIncreaseFactor(String lore) { + Matcher cpsMatcher = CPS_PATTERN.matcher(lore); + if (!cpsMatcher.find()) return OptionalDouble.empty(); + int currentCps = Integer.parseInt(cpsMatcher.group(1).replace(",", "")); + if (!cpsMatcher.find()) return OptionalDouble.empty(); //If there is no second match, we can't get the CPS increase + int nextCps = Integer.parseInt(cpsMatcher.group(1).replace(",", "")); + + Matcher costMatcher = COST_PATTERN.matcher(lore); + if (!costMatcher.find(cpsMatcher.end())) return OptionalDouble.empty(); //Cost is always at the end of the string, so we can start check from the end of the last match + int cost = Integer.parseInt(costMatcher.group(1).replace(",", "")); + return OptionalDouble.of((nextCps - currentCps) / (double) cost); + } + + //Publicize this method so that the HandledScreenMixin can call it, as the values change on upgrade (when clicked) and there is a need for recalculation + public void markDirty() { + super.markHighlightsDirty(); + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java index 08fb6a86..8a5d32be 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/ContainerSolverManager.java @@ -5,6 +5,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import de.hysky.skyblocker.mixins.accessors.HandledScreenAccessor; import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakeBagHelper; import de.hysky.skyblocker.skyblock.accessories.newyearcakes.NewYearCakesHelper; +import de.hysky.skyblocker.skyblock.chocolatefactory.ChocolateFactorySolver; import de.hysky.skyblocker.skyblock.dungeon.CroesusHelper; import de.hysky.skyblocker.skyblock.dungeon.CroesusProfit; import de.hysky.skyblocker.skyblock.dungeon.terminal.ColorTerminal; @@ -55,7 +56,8 @@ public class ContainerSolverManager { new SuperpairsSolver(), UltrasequencerSolver.INSTANCE, new NewYearCakeBagHelper(), - NewYearCakesHelper.INSTANCE + NewYearCakesHelper.INSTANCE, + new ChocolateFactorySolver() }; } -- cgit From 0ec2707b63961ea0f7acab1c38311ef696ba8003 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sun, 5 May 2024 16:26:29 +0300 Subject: Updated for 1.20.6 PR compatibility Add EggFinderDebugUtil for finding egg locations quickly, to be removed later. Not tested. --- .../skyblocker/mixins/HandledScreenMixin.java | 4 -- .../chocolatefactory/ChocolateFactorySolver.java | 35 ++++++++-------- .../chocolatefactory/EggFinderDebugUtil.java | 48 ++++++++++++++++++++++ 3 files changed, 67 insertions(+), 20 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinderDebugUtil.java (limited to 'src/main/java/de') diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java index 87b1bc79..f662be7c 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java @@ -4,7 +4,6 @@ import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.systems.RenderSystem; import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.skyblock.chocolatefactory.ChocolateFactorySolver; import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; import de.hysky.skyblocker.skyblock.experiment.ExperimentSolver; import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver; @@ -285,9 +284,6 @@ public abstract class HandledScreenMixin extends Screen default -> { /*Do Nothing*/ } } } - if (currentSolver instanceof ChocolateFactorySolver chocolateFactorySolver) { - chocolateFactorySolver.markDirty(); - } } @Inject(method = "drawSlot", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/DrawContext;drawItem(Lnet/minecraft/item/ItemStack;III)V")) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java index ef79cd41..cc5ae471 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -6,6 +6,9 @@ import de.hysky.skyblocker.utils.render.gui.ContainerSolver; import it.unimi.dsi.fastutil.ints.*; import net.fabricmc.loader.impl.lib.sat4j.minisat.core.Solver; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.LoreComponent; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.nbt.NbtCompound; @@ -34,18 +37,26 @@ public class ChocolateFactorySolver extends ContainerSolver { } @Override - protected List getColors(String[] groups, Map slots) { + protected void start(GenericContainerScreen screen) { + markHighlightsDirty(); //Recalculate highlights when the screen is opened, which happens when upgrading rabbits + } + + + //Todo: Handle unemployed rabbits as well. They have a different lore format. + @Override + protected List getColors(String[] groups, Int2ObjectMap slots) { Int2DoubleMap cpsIncreaseFactors = new Int2DoubleLinkedOpenHashMap(5); //There are only 5 rabbits on the screen. - for (Map.Entry entry : slots.entrySet()) { + for (Int2ObjectMap.Entry entry : slots.int2ObjectEntrySet()) { + if (entry.getIntKey() < 29 || entry.getIntKey() > 33) continue; //Only check the rabbit slots (29,30,31,32,33) ItemStack item = entry.getValue(); - if (item.getItem() != Items.PLAYER_HEAD || !item.hasNbt() || item.isEmpty()) continue; + if (item.getItem() != Items.PLAYER_HEAD || item.isEmpty()) continue; String lore = getLore(item); if (lore.isBlank()) continue; OptionalDouble cpsIncreaseFactor = getCPSIncreaseFactor(lore); if (cpsIncreaseFactor.isEmpty()) continue; //Something went wrong, skip this item - cpsIncreaseFactors.put(entry.getKey().intValue(), cpsIncreaseFactor.getAsDouble()); + cpsIncreaseFactors.put(entry.getIntKey(), cpsIncreaseFactor.getAsDouble()); } Optional bestSlot = cpsIncreaseFactors.int2DoubleEntrySet().stream().max(Map.Entry.comparingByValue()); if (bestSlot.isEmpty()) return List.of(); //No valid slots found, somehow. This means something went wrong, despite all the checks thus far. @@ -53,13 +64,10 @@ public class ChocolateFactorySolver extends ContainerSolver { } private String getLore(ItemStack item) { - NbtCompound display = item.getSubNbt(ItemStack.DISPLAY_KEY); - if (display == null || display.isEmpty() || !display.contains(ItemStack.LORE_KEY, NbtElement.LIST_TYPE)) return ""; - NbtList lore = display.getList(ItemStack.LORE_KEY, NbtElement.STRING_TYPE); - return lore.stream() - .map(NbtElement::asString) - .map(Text.Serialization::fromJson) //Serialize the nbt string into a text to get the content string - .filter(Objects::nonNull) + LoreComponent lore = item.get(DataComponentTypes.LORE); + if (lore == null || lore.lines().isEmpty()) return ""; + return lore.lines() + .stream() .map(Text::getString) .collect(Collectors.joining(" ")); //Join all lore lines into one string for ease of regexing //The space is so that the regex pattern still matches even if the word is split into 2 lines, @@ -85,9 +93,4 @@ public class ChocolateFactorySolver extends ContainerSolver { int cost = Integer.parseInt(costMatcher.group(1).replace(",", "")); return OptionalDouble.of((nextCps - currentCps) / (double) cost); } - - //Publicize this method so that the HandledScreenMixin can call it, as the values change on upgrade (when clicked) and there is a need for recalculation - public void markDirty() { - super.markHighlightsDirty(); - } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinderDebugUtil.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinderDebugUtil.java new file mode 100644 index 00000000..6dc7a9e4 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinderDebugUtil.java @@ -0,0 +1,48 @@ +package de.hysky.skyblocker.skyblock.chocolatefactory; + +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.Text; +import net.minecraft.world.World; + +public class EggFinderDebugUtil { + public static void init() { + ClientEntityEvents.ENTITY_LOAD.register(EggFinderDebugUtil::printIfEgg); + } + + public static void printIfEgg(Entity entity, World world) { + if (entity.getType() != EntityType.ARMOR_STAND) return; + ArmorStandEntity armorStand = (ArmorStandEntity) entity; + ItemStack firstItem = armorStand.getArmorItems().iterator().next(); + if (firstItem.isEmpty() || firstItem.getItem() != Items.PLAYER_HEAD) return; + String texture; + try { + texture = firstItem.get(DataComponentTypes.PROFILE) + .properties() + .get("textures") + .iterator() + .next() + .value(); + } catch (Exception ignored) { //Why bother null-checking everything when you can throw a try-catch around it? + return; + } + + if (texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0") + || texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0") + || texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9")) { + debugLog("Egg found!"); + debugLog(entity.getBlockX() + " " + entity.getBlockY() + " " + entity.getBlockZ()); + } + entity.setGlowing(true); + } + + public static void debugLog(String message) { + MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.of(message)); + } +} -- cgit From 16b449cac78f61f935d8754c32317998b1b3014c Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Mon, 6 May 2024 16:51:21 +0300 Subject: Delete EggFinderDebugUtil to prepare for PR --- .../chocolatefactory/EggFinderDebugUtil.java | 48 ---------------------- 1 file changed, 48 deletions(-) delete mode 100644 src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinderDebugUtil.java (limited to 'src/main/java/de') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinderDebugUtil.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinderDebugUtil.java deleted file mode 100644 index 6dc7a9e4..00000000 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinderDebugUtil.java +++ /dev/null @@ -1,48 +0,0 @@ -package de.hysky.skyblocker.skyblock.chocolatefactory; - -import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientEntityEvents; -import net.minecraft.client.MinecraftClient; -import net.minecraft.component.DataComponentTypes; -import net.minecraft.entity.Entity; -import net.minecraft.entity.EntityType; -import net.minecraft.entity.decoration.ArmorStandEntity; -import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; -import net.minecraft.text.Text; -import net.minecraft.world.World; - -public class EggFinderDebugUtil { - public static void init() { - ClientEntityEvents.ENTITY_LOAD.register(EggFinderDebugUtil::printIfEgg); - } - - public static void printIfEgg(Entity entity, World world) { - if (entity.getType() != EntityType.ARMOR_STAND) return; - ArmorStandEntity armorStand = (ArmorStandEntity) entity; - ItemStack firstItem = armorStand.getArmorItems().iterator().next(); - if (firstItem.isEmpty() || firstItem.getItem() != Items.PLAYER_HEAD) return; - String texture; - try { - texture = firstItem.get(DataComponentTypes.PROFILE) - .properties() - .get("textures") - .iterator() - .next() - .value(); - } catch (Exception ignored) { //Why bother null-checking everything when you can throw a try-catch around it? - return; - } - - if (texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0") - || texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0") - || texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9")) { - debugLog("Egg found!"); - debugLog(entity.getBlockX() + " " + entity.getBlockY() + " " + entity.getBlockZ()); - } - entity.setGlowing(true); - } - - public static void debugLog(String message) { - MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(Text.of(message)); - } -} -- cgit From 9fea533e30fdb5c825a72d2cf560958187764c6f Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 9 May 2024 21:36:20 +0300 Subject: Egg Finder initial commit Current issues: - Rendered waypoints do not turn off, can't figure out why - Entity equipment is sent before the chat message for new eggs, creating situations where the "found" message is repeated twice --- .../java/de/hysky/skyblocker/SkyblockerMod.java | 2 + .../mixins/ClientPlayNetworkHandlerMixin.java | 6 + .../skyblock/chocolatefactory/EggFinder.java | 132 +++++++++++++++++++++ .../java/de/hysky/skyblocker/utils/ColorUtils.java | 16 +++ 4 files changed, 156 insertions(+) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/ColorUtils.java (limited to 'src/main/java/de') diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index e6b2d25a..e0e30b3c 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -11,6 +11,7 @@ import de.hysky.skyblocker.skyblock.*; import de.hysky.skyblocker.skyblock.calculators.CalculatorCommand; import de.hysky.skyblocker.skyblock.chat.ChatRuleAnnouncementScreen; import de.hysky.skyblocker.skyblock.chat.ChatRulesHandler; +import de.hysky.skyblocker.skyblock.chocolatefactory.EggFinder; import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra; import de.hysky.skyblocker.skyblock.dungeon.*; import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen; @@ -183,6 +184,7 @@ public class SkyblockerMod implements ClientModInitializer { BeaconHighlighter.init(); WarpAutocomplete.init(); MobBoundingBoxes.init(); + EggFinder.init(); Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); Scheduler.INSTANCE.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 200); diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index 2c2c1376..34e8264d 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -6,6 +6,7 @@ import com.llamalad7.mixinextras.sugar.Local; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.CompactDamage; import de.hysky.skyblocker.skyblock.FishingHelper; +import de.hysky.skyblocker.skyblock.chocolatefactory.EggFinder; import de.hysky.skyblocker.skyblock.dungeon.DungeonScore; import de.hysky.skyblocker.skyblock.dungeon.secrets.DungeonManager; import de.hysky.skyblocker.skyblock.end.BeaconHighlighter; @@ -118,4 +119,9 @@ public abstract class ClientPlayNetworkHandlerMixin { LOGGER.error("[Skyblocker Compact Damage] Failed to compact damage number", e); } } + + @Inject(method = "onEntityEquipmentUpdate", at = @At(value = "TAIL")) + private void skyblocker$onEntityEquip(EntityEquipmentUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { + EggFinder.checkIfEgg(entity); + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java new file mode 100644 index 00000000..d5e1a540 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -0,0 +1,132 @@ +package de.hysky.skyblocker.skyblock.chocolatefactory; + +import de.hysky.skyblocker.utils.ColorUtils; +import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.waypoint.Waypoint; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.entity.Entity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.Text; +import org.apache.commons.lang3.mutable.MutableObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class EggFinder { + private static final Pattern eggFoundPattern = Pattern.compile("^(?:HOPPITY'S HUNT You found a Chocolate|You have already collected this Chocolate) (Breakfast|Lunch|Dinner)"); + private static final Pattern newEggPattern = Pattern.compile("^HOPPITY'S HUNT A Chocolate (Breakfast|Lunch|Dinner) Egg has appeared!$"); + private static final MutableObject breakfastEgg = new MutableObject<>(null); + private static final MutableObject lunchEgg = new MutableObject<>(null); + private static final MutableObject dinnerEgg = new MutableObject<>(null); + private static final Logger logger = LoggerFactory.getLogger("Skyblocker Egg Finder"); + + private EggFinder() { + } + + public static void init() { + ClientPlayConnectionEvents.JOIN.register((ignored, ignored2, ignored3) -> { + breakfastEgg.setValue(null); + lunchEgg.setValue(null); + dinnerEgg.setValue(null); + }); + ClientReceiveMessageEvents.GAME.register(EggFinder::onChatMessage); + WorldRenderEvents.AFTER_TRANSLUCENT.register(EggFinder::renderWaypoints); + } + + public static void checkIfEgg(Entity entity) { + if (breakfastEgg.getValue() != null && dinnerEgg.getValue() != null && lunchEgg.getValue() != null) return; //Don't check for eggs if we already found all of them + if (!(entity instanceof ArmorStandEntity armorStand) || armorStand.hasCustomName() || !armorStand.isInvisible() || !armorStand.shouldHideBasePlate()) return; + for (ItemStack itemStack : armorStand.getArmorItems()) { + try { + if (!itemStack.isEmpty() || itemStack.getItem() == Items.PLAYER_HEAD) { + String texture = itemStack.getComponents() + .get(DataComponentTypes.PROFILE) + .properties() + .get("textures") + .iterator() + .next() + .value(); + + //Don't turn this into a switch statement, it's unreadable + if (texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0")) { + handleFoundEgg(armorStand, EggType.BREAKFAST); + } else if (texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0")) { + handleFoundEgg(armorStand, EggType.DINNER); + } else if (texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9")) { + handleFoundEgg(armorStand, EggType.LUNCH); + } + } + } catch (Exception e) { + //Ignored. This simply exists to make the code cleaner without a bunch of if statements to check the existence of each key. + } + } + } + + private static void handleFoundEgg(ArmorStandEntity entity, EggType eggType) { + eggType.egg.setValue(new Egg(entity, new Waypoint(entity.getBlockPos().up(2), () -> Waypoint.Type.WAYPOINT, ColorUtils.getFloatComponents(eggType.color)))); + MinecraftClient.getInstance().player.sendMessage(Constants.PREFIX.get().append("Found a ").append(Text.literal("Chocolate " + eggType + " Egg").withColor(eggType.color)).append(" at " + entity.getBlockPos().up(2).toShortString() + "!")); + } + + private static void renderWaypoints(WorldRenderContext context) { + if (breakfastEgg.getValue() != null && breakfastEgg.getValue().waypoint.shouldRender()) breakfastEgg.getValue().waypoint.render(context); + if (lunchEgg.getValue() != null && lunchEgg.getValue().waypoint.shouldRender()) lunchEgg.getValue().waypoint.render(context); + if (dinnerEgg.getValue() != null && dinnerEgg.getValue().waypoint.shouldRender()) dinnerEgg.getValue().waypoint.render(context); + } + + private static void onChatMessage(Text text, boolean overlay) { + if (overlay) return; + Matcher matcher = eggFoundPattern.matcher(text.getString()); + if (matcher.matches()) { + try { + Egg egg = EggType.valueOf(matcher.group(1).toUpperCase()).egg.getValue(); + if (egg != null) egg.waypoint.setFound(); + } catch (IllegalArgumentException e) { + logger.error("Failed to find egg type for egg found message. Tried to match against: " + matcher.group(0), e); + } + } + + //There's only one egg of the same type at any given time, so we can set the changed egg to null + matcher = newEggPattern.matcher(text.getString()); + if (matcher.matches()) { + try { + EggType.valueOf(matcher.group(1).toUpperCase()).egg.setValue(null); + } catch (IllegalArgumentException e) { + logger.error("Failed to find egg type for egg spawn message. Tried to match against: " + matcher.group(0), e); + } + } + } + + private record Egg(ArmorStandEntity entity, Waypoint waypoint) { } + + private enum EggType { + LUNCH(lunchEgg, 0x5555FF), + DINNER(dinnerEgg, 0x55FF55), + BREAKFAST(breakfastEgg, 0xFFAA00); + + public final MutableObject egg; + public final int color; + + EggType(MutableObject egg, int color) { + this.egg = egg; + this.color = color; + } + + @Override + public String toString() { + return switch (this) { + case LUNCH -> "Lunch"; + case DINNER -> "Dinner"; + case BREAKFAST -> "Breakfast"; + }; + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/ColorUtils.java b/src/main/java/de/hysky/skyblocker/utils/ColorUtils.java new file mode 100644 index 00000000..0196edf2 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/ColorUtils.java @@ -0,0 +1,16 @@ +package de.hysky.skyblocker.utils; + +public class ColorUtils { + /** + * Takes an RGB color as an integer and returns an array of the color's components as floats, in RGB format. + * @param color The color to get the components of. + * @return An array of the color's components as floats. + */ + public static float[] getFloatComponents(int color) { + return new float[] { + ((color >> 16) & 0xFF) / 255f, + ((color >> 8) & 0xFF) / 255f, + (color & 0xFF) / 255f + }; + } +} -- cgit From 05a18be31a137148bcd446654c550eb2d713990c Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 10 May 2024 21:00:00 +0300 Subject: Add configs and minor code refactor --- .../config/categories/HelperCategory.java | 42 +++++++++++++ .../skyblocker/config/configs/HelperConfig.java | 18 ++++++ .../chocolatefactory/ChocolateFactorySolver.java | 8 +-- .../skyblock/chocolatefactory/EggFinder.java | 72 +++++++++++++--------- 4 files changed, 107 insertions(+), 33 deletions(-) (limited to 'src/main/java/de') diff --git a/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java index 1528f853..d9824a23 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java @@ -2,11 +2,14 @@ package de.hysky.skyblocker.config.categories; import de.hysky.skyblocker.config.ConfigUtils; import de.hysky.skyblocker.config.SkyblockerConfig; +import de.hysky.skyblocker.utils.waypoint.Waypoint; import dev.isxander.yacl3.api.*; import dev.isxander.yacl3.api.ConfigCategory; import dev.isxander.yacl3.api.Option; import dev.isxander.yacl3.api.OptionDescription; import dev.isxander.yacl3.api.controller.FloatFieldControllerBuilder; +import dev.isxander.yacl3.api.controller.TickBoxControllerBuilder; +import dev.isxander.yacl3.config.v2.api.autogen.TickBox; import net.minecraft.text.Text; public class HelperCategory { @@ -137,6 +140,45 @@ public class HelperCategory { .build()) .build()) + //Chocolate Factory + .group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.helpers.chocolateFactory")) + .collapsed(true) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper.@Tooltip"))) + .binding(defaults.helpers.chocolateFactory.enableChocolateFactoryHelper, + () -> config.helpers.chocolateFactory.enableChocolateFactoryHelper, + newValue -> config.helpers.chocolateFactory.enableChocolateFactoryHelper = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.helpers.chocolateFactory.enableEggFinder")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.helpers.chocolateFactory.enableEggFinder.@Tooltip"))) + .binding(defaults.helpers.chocolateFactory.enableEggFinder, + () -> config.helpers.chocolateFactory.enableEggFinder, + newValue -> config.helpers.chocolateFactory.enableEggFinder = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.helpers.chocolateFactory.sendEggFoundMessages")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.helpers.chocolateFactory.sendEggFoundMessages.@Tooltip"))) + .binding(defaults.helpers.chocolateFactory.sendEggFoundMessages, + () -> config.helpers.chocolateFactory.sendEggFoundMessages, + newValue -> config.helpers.chocolateFactory.sendEggFoundMessages = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.helpers.chocolateFactory.waypointType")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.helpers.chocolateFactory.waypointType.@Tooltip"))) + .binding(defaults.helpers.chocolateFactory.waypointType, + () -> config.helpers.chocolateFactory.waypointType, + newValue -> config.helpers.chocolateFactory.waypointType = newValue) + .controller(ConfigUtils::createEnumCyclingListController) + .build()) + + .build()) + .build(); } } diff --git a/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java index 2abff6ac..8721e35c 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java @@ -1,5 +1,6 @@ package de.hysky.skyblocker.config.configs; +import de.hysky.skyblocker.utils.waypoint.Waypoint; import dev.isxander.yacl3.config.v2.api.SerialEntry; public class HelperConfig { @@ -19,6 +20,9 @@ public class HelperConfig { @SerialEntry public FairySouls fairySouls = new FairySouls(); + @SerialEntry + public ChocolateFactory chocolateFactory = new ChocolateFactory(); + public static class MythologicalRitual { @SerialEntry public boolean enableMythologicalRitualHelper = true; @@ -62,4 +66,18 @@ public class HelperConfig { @SerialEntry public boolean highlightOnlyNearbySouls = false; } + + public static class ChocolateFactory { + @SerialEntry + public boolean enableChocolateFactoryHelper = true; + + @SerialEntry + public boolean enableEggFinder = true; + + @SerialEntry + public boolean sendEggFoundMessages = true; + + @SerialEntry + public Waypoint.Type waypointType = Waypoint.Type.WAYPOINT; + } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java index cc5ae471..a59a5147 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -1,5 +1,6 @@ package de.hysky.skyblocker.skyblock.chocolatefactory; +import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.experiment.ExperimentSolver; import de.hysky.skyblocker.utils.render.gui.ColorHighlight; import de.hysky.skyblocker.utils.render.gui.ContainerSolver; @@ -33,7 +34,7 @@ public class ChocolateFactorySolver extends ContainerSolver { @Override protected boolean isEnabled() { - return true; //Todo: add a config option and check if it's enabled from there + return SkyblockerConfigManager.get().helpers.chocolateFactory.enableChocolateFactoryHelper; } @Override @@ -41,7 +42,6 @@ public class ChocolateFactorySolver extends ContainerSolver { markHighlightsDirty(); //Recalculate highlights when the screen is opened, which happens when upgrading rabbits } - //Todo: Handle unemployed rabbits as well. They have a different lore format. @Override protected List getColors(String[] groups, Int2ObjectMap slots) { @@ -70,8 +70,8 @@ public class ChocolateFactorySolver extends ContainerSolver { .stream() .map(Text::getString) .collect(Collectors.joining(" ")); //Join all lore lines into one string for ease of regexing - //The space is so that the regex pattern still matches even if the word is split into 2 lines, - //as normally the line end and line start contain no spaces and would not match the pattern when concatenated +// The space is so that the regex pattern still matches even if the word is split into 2 lines, +// as normally the line end and line start contain no spaces and would not match the pattern when concatenated } /** diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java index d5e1a540..7f8e9879 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -1,8 +1,10 @@ package de.hysky.skyblocker.skyblock.chocolatefactory; +import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.utils.ColorUtils; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.waypoint.Waypoint; +import it.unimi.dsi.fastutil.objects.ObjectImmutableList; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; @@ -21,29 +23,27 @@ import org.slf4j.LoggerFactory; import java.util.regex.Matcher; import java.util.regex.Pattern; +import static de.hysky.skyblocker.skyblock.chocolatefactory.EggFinder.EggType.BREAKFAST; +import static de.hysky.skyblocker.skyblock.chocolatefactory.EggFinder.EggType.DINNER; +import static de.hysky.skyblocker.skyblock.chocolatefactory.EggFinder.EggType.LUNCH; + public class EggFinder { private static final Pattern eggFoundPattern = Pattern.compile("^(?:HOPPITY'S HUNT You found a Chocolate|You have already collected this Chocolate) (Breakfast|Lunch|Dinner)"); private static final Pattern newEggPattern = Pattern.compile("^HOPPITY'S HUNT A Chocolate (Breakfast|Lunch|Dinner) Egg has appeared!$"); - private static final MutableObject breakfastEgg = new MutableObject<>(null); - private static final MutableObject lunchEgg = new MutableObject<>(null); - private static final MutableObject dinnerEgg = new MutableObject<>(null); private static final Logger logger = LoggerFactory.getLogger("Skyblocker Egg Finder"); private EggFinder() { } public static void init() { - ClientPlayConnectionEvents.JOIN.register((ignored, ignored2, ignored3) -> { - breakfastEgg.setValue(null); - lunchEgg.setValue(null); - dinnerEgg.setValue(null); - }); + ClientPlayConnectionEvents.JOIN.register((ignored, ignored2, ignored3) -> clearEggs()); ClientReceiveMessageEvents.GAME.register(EggFinder::onChatMessage); WorldRenderEvents.AFTER_TRANSLUCENT.register(EggFinder::renderWaypoints); } public static void checkIfEgg(Entity entity) { - if (breakfastEgg.getValue() != null && dinnerEgg.getValue() != null && lunchEgg.getValue() != null) return; //Don't check for eggs if we already found all of them + if (!SkyblockerConfigManager.get().helpers.chocolateFactory.enableEggFinder) return; + if (BREAKFAST.egg.getValue() != null && DINNER.egg.getValue() != null && LUNCH.egg.getValue() != null) return; //Don't check for eggs if we already found all of them if (!(entity instanceof ArmorStandEntity armorStand) || armorStand.hasCustomName() || !armorStand.isInvisible() || !armorStand.shouldHideBasePlate()) return; for (ItemStack itemStack : armorStand.getArmorItems()) { try { @@ -56,13 +56,11 @@ public class EggFinder { .next() .value(); - //Don't turn this into a switch statement, it's unreadable - if (texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0")) { - handleFoundEgg(armorStand, EggType.BREAKFAST); - } else if (texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0")) { - handleFoundEgg(armorStand, EggType.DINNER); - } else if (texture.equals("ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9")) { - handleFoundEgg(armorStand, EggType.LUNCH); + for (EggType type : EggType.entries) { + if (texture.equals(type.texture)) { + handleFoundEgg(armorStand, type); + return; + } } } } catch (Exception e) { @@ -71,26 +69,37 @@ public class EggFinder { } } + private static void clearEggs() { + if (!SkyblockerConfigManager.get().helpers.chocolateFactory.enableEggFinder) return; + for (EggType type : EggType.entries) { + type.egg.setValue(null); + } + } + private static void handleFoundEgg(ArmorStandEntity entity, EggType eggType) { - eggType.egg.setValue(new Egg(entity, new Waypoint(entity.getBlockPos().up(2), () -> Waypoint.Type.WAYPOINT, ColorUtils.getFloatComponents(eggType.color)))); + eggType.egg.setValue(new Egg(entity, new Waypoint(entity.getBlockPos().up(2), SkyblockerConfigManager.get().helpers.chocolateFactory.waypointType, ColorUtils.getFloatComponents(eggType.color)))); + + if (!SkyblockerConfigManager.get().helpers.chocolateFactory.sendEggFoundMessages) return; MinecraftClient.getInstance().player.sendMessage(Constants.PREFIX.get().append("Found a ").append(Text.literal("Chocolate " + eggType + " Egg").withColor(eggType.color)).append(" at " + entity.getBlockPos().up(2).toShortString() + "!")); } private static void renderWaypoints(WorldRenderContext context) { - if (breakfastEgg.getValue() != null && breakfastEgg.getValue().waypoint.shouldRender()) breakfastEgg.getValue().waypoint.render(context); - if (lunchEgg.getValue() != null && lunchEgg.getValue().waypoint.shouldRender()) lunchEgg.getValue().waypoint.render(context); - if (dinnerEgg.getValue() != null && dinnerEgg.getValue().waypoint.shouldRender()) dinnerEgg.getValue().waypoint.render(context); + if (!SkyblockerConfigManager.get().helpers.chocolateFactory.enableEggFinder) return; + for (EggType type : EggType.entries) { + Egg egg = type.egg.getValue(); + if (egg != null && egg.waypoint.shouldRender()) egg.waypoint.render(context); + } } private static void onChatMessage(Text text, boolean overlay) { - if (overlay) return; + if (overlay || !SkyblockerConfigManager.get().helpers.chocolateFactory.enableEggFinder) return; Matcher matcher = eggFoundPattern.matcher(text.getString()); if (matcher.matches()) { try { Egg egg = EggType.valueOf(matcher.group(1).toUpperCase()).egg.getValue(); if (egg != null) egg.waypoint.setFound(); } catch (IllegalArgumentException e) { - logger.error("Failed to find egg type for egg found message. Tried to match against: " + matcher.group(0), e); + logger.error("[Skyblocker Egg Finder] Failed to find egg type for egg found message. Tried to match against: " + matcher.group(0), e); } } @@ -100,24 +109,29 @@ public class EggFinder { try { EggType.valueOf(matcher.group(1).toUpperCase()).egg.setValue(null); } catch (IllegalArgumentException e) { - logger.error("Failed to find egg type for egg spawn message. Tried to match against: " + matcher.group(0), e); + logger.error("[Skyblocker Egg Finder] Failed to find egg type for egg spawn message. Tried to match against: " + matcher.group(0), e); } } } - private record Egg(ArmorStandEntity entity, Waypoint waypoint) { } + record Egg(ArmorStandEntity entity, Waypoint waypoint) { } - private enum EggType { - LUNCH(lunchEgg, 0x5555FF), - DINNER(dinnerEgg, 0x55FF55), - BREAKFAST(breakfastEgg, 0xFFAA00); + enum EggType { + LUNCH(new MutableObject<>(), 0x5555FF, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0"), + DINNER(new MutableObject<>(), 0x55FF55, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0"), + BREAKFAST(new MutableObject<>(), 0xFFAA00, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9"); public final MutableObject egg; public final int color; + public final String texture; + + //This is to not create an array each time we iterate over the values + public static final ObjectImmutableList entries = ObjectImmutableList.of(BREAKFAST, DINNER); - EggType(MutableObject egg, int color) { + EggType(MutableObject egg, int color, String texture) { this.egg = egg; this.color = color; + this.texture = texture; } @Override -- cgit From e2f18c84ce2de2da228ff88b6b67fa5d46945255 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sat, 11 May 2024 14:08:59 +0300 Subject: Add support for unemployed rabbits - Refactored ItemUtils.getHeadTexture to use the property iterator instead of streams and return Optional rather than empty string to make the results clear and easier to work with. - Refactored regex matching logic in ChocolateFactorySolver.getCPSIncreaseFactor to a separate function --- src/main/java/de/hysky/skyblocker/debug/Debug.java | 6 +- .../chocolatefactory/ChocolateFactorySolver.java | 77 +++++++++++++--------- .../skyblock/chocolatefactory/EggFinder.java | 27 ++------ .../hysky/skyblocker/skyblock/entity/MobGlow.java | 2 +- .../skyblocker/skyblock/garden/VisitorHelper.java | 4 +- .../java/de/hysky/skyblocker/utils/ItemUtils.java | 17 ++--- 6 files changed, 68 insertions(+), 65 deletions(-) (limited to 'src/main/java/de') diff --git a/src/main/java/de/hysky/skyblocker/debug/Debug.java b/src/main/java/de/hysky/skyblocker/debug/Debug.java index d9ac668c..038e7a5a 100644 --- a/src/main/java/de/hysky/skyblocker/debug/Debug.java +++ b/src/main/java/de/hysky/skyblocker/debug/Debug.java @@ -83,9 +83,9 @@ public class Debug { Iterable equippedItems = armorStand.getEquippedItems(); for (ItemStack stack : equippedItems) { - String texture = ItemUtils.getHeadTexture(stack); - - if (!texture.isEmpty()) context.getSource().sendFeedback(Text.of(texture)); + ItemUtils.getHeadTexture(stack).ifPresent(texture -> { + context.getSource().sendFeedback(Text.of(texture)); + }); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java index a59a5147..94c2b209 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -1,23 +1,15 @@ package de.hysky.skyblocker.skyblock.chocolatefactory; import de.hysky.skyblocker.config.SkyblockerConfigManager; -import de.hysky.skyblocker.skyblock.experiment.ExperimentSolver; +import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.render.gui.ColorHighlight; import de.hysky.skyblocker.utils.render.gui.ContainerSolver; import it.unimi.dsi.fastutil.ints.*; -import net.fabricmc.loader.impl.lib.sat4j.minisat.core.Solver; -import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; -import net.minecraft.component.DataComponentTypes; -import net.minecraft.component.type.LoreComponent; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtElement; -import net.minecraft.nbt.NbtList; import net.minecraft.text.Text; -import java.awt.*; import java.util.*; import java.util.List; import java.util.regex.Matcher; @@ -27,6 +19,7 @@ import java.util.stream.Collectors; public class ChocolateFactorySolver extends ContainerSolver { private static final Pattern CPS_PATTERN = Pattern.compile("\\+([\\d,]+) Chocolate per second"); private static final Pattern COST_PATTERN = Pattern.compile("Cost ([\\d,]+) Chocolate"); + private static final Pattern HIRE_PATTERN = Pattern.compile("(HIRE|PROMOTE) ➜ \\[\\d+] \\S+ *"); public ChocolateFactorySolver() { super("^Chocolate Factory$"); @@ -42,7 +35,6 @@ public class ChocolateFactorySolver extends ContainerSolver { markHighlightsDirty(); //Recalculate highlights when the screen is opened, which happens when upgrading rabbits } - //Todo: Handle unemployed rabbits as well. They have a different lore format. @Override protected List getColors(String[] groups, Int2ObjectMap slots) { Int2DoubleMap cpsIncreaseFactors = new Int2DoubleLinkedOpenHashMap(5); //There are only 5 rabbits on the screen. @@ -51,7 +43,7 @@ public class ChocolateFactorySolver extends ContainerSolver { ItemStack item = entry.getValue(); if (item.getItem() != Items.PLAYER_HEAD || item.isEmpty()) continue; - String lore = getLore(item); + String lore = getConcattedLore(item); if (lore.isBlank()) continue; OptionalDouble cpsIncreaseFactor = getCPSIncreaseFactor(lore); @@ -63,15 +55,13 @@ public class ChocolateFactorySolver extends ContainerSolver { return List.of(ColorHighlight.green(bestSlot.get().getIntKey())); } - private String getLore(ItemStack item) { - LoreComponent lore = item.get(DataComponentTypes.LORE); - if (lore == null || lore.lines().isEmpty()) return ""; - return lore.lines() - .stream() - .map(Text::getString) - .collect(Collectors.joining(" ")); //Join all lore lines into one string for ease of regexing -// The space is so that the regex pattern still matches even if the word is split into 2 lines, -// as normally the line end and line start contain no spaces and would not match the pattern when concatenated + private String getConcattedLore(ItemStack item) { + return ItemUtils.getLore(item) + .stream() + .map(Text::getString) + .collect(Collectors.joining(" ")); //Join all lore lines into one string for ease of regexing +// The space is so that the regex pattern still matches even if the word is split into 2 lines, +// as normally the line end and the line start contain no spaces and would not match the pattern when concatenated } /** @@ -82,15 +72,42 @@ public class ChocolateFactorySolver extends ContainerSolver { * @return The CPS increase factor of the item, or an empty optional if it couldn't be found */ private OptionalDouble getCPSIncreaseFactor(String lore) { - Matcher cpsMatcher = CPS_PATTERN.matcher(lore); - if (!cpsMatcher.find()) return OptionalDouble.empty(); - int currentCps = Integer.parseInt(cpsMatcher.group(1).replace(",", "")); - if (!cpsMatcher.find()) return OptionalDouble.empty(); //If there is no second match, we can't get the CPS increase - int nextCps = Integer.parseInt(cpsMatcher.group(1).replace(",", "")); - - Matcher costMatcher = COST_PATTERN.matcher(lore); - if (!costMatcher.find(cpsMatcher.end())) return OptionalDouble.empty(); //Cost is always at the end of the string, so we can start check from the end of the last match - int cost = Integer.parseInt(costMatcher.group(1).replace(",", "")); - return OptionalDouble.of((nextCps - currentCps) / (double) cost); + Matcher hireMatcher = HIRE_PATTERN.matcher(lore); + if (!hireMatcher.find()) return OptionalDouble.empty(); //Not a hireable/promotable rabbit. Could be a locked or maxed rabbit. + + switch (hireMatcher.group(1)) { + case "HIRE" -> { + Matcher cpsMatcher = CPS_PATTERN.matcher(lore); + OptionalInt cps = getValueFromMatcher(cpsMatcher, hireMatcher.end()); //Cps line is right after the hire line + if (cps.isEmpty()) return OptionalDouble.empty(); + + Matcher costMatcher = COST_PATTERN.matcher(lore); + OptionalInt cost = getValueFromMatcher(costMatcher, cpsMatcher.end()); //Cost comes after the cps line + if (cost.isEmpty()) return OptionalDouble.empty(); + return OptionalDouble.of(cps.getAsInt() / (double) cost.getAsInt()); + } + case "PROMOTE" -> { + Matcher cpsMatcher = CPS_PATTERN.matcher(lore); + OptionalInt currentCps = getValueFromMatcher(cpsMatcher); //Current cps is before the hire line + if (currentCps.isEmpty()) return OptionalDouble.empty(); + OptionalInt nextCps = getValueFromMatcher(cpsMatcher, hireMatcher.end()); //Next cps is right after the hire line + if (nextCps.isEmpty()) return OptionalDouble.empty(); + + Matcher costMatcher = COST_PATTERN.matcher(lore); + OptionalInt cost = getValueFromMatcher(costMatcher, cpsMatcher.end()); //Cost comes after the cps line + if (cost.isEmpty()) return OptionalDouble.empty(); + return OptionalDouble.of((nextCps.getAsInt() - currentCps.getAsInt()) / (double) cost.getAsInt()); + } + default -> { return OptionalDouble.empty(); } + } + } + + private OptionalInt getValueFromMatcher(Matcher matcher) { + return getValueFromMatcher(matcher, matcher.hasMatch() ? matcher.end() : 0); + } + + private OptionalInt getValueFromMatcher(Matcher matcher, int startingIndex) { + if (!matcher.find(startingIndex)) return OptionalInt.empty(); + return OptionalInt.of(Integer.parseInt(matcher.group(1).replace(",", ""))); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java index 7f8e9879..ee1d4aa7 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -3,6 +3,7 @@ package de.hysky.skyblocker.skyblock.chocolatefactory; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.utils.ColorUtils; import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.waypoint.Waypoint; import it.unimi.dsi.fastutil.objects.ObjectImmutableList; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; @@ -10,11 +11,9 @@ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.minecraft.client.MinecraftClient; -import net.minecraft.component.DataComponentTypes; import net.minecraft.entity.Entity; import net.minecraft.entity.decoration.ArmorStandEntity; import net.minecraft.item.ItemStack; -import net.minecraft.item.Items; import net.minecraft.text.Text; import org.apache.commons.lang3.mutable.MutableObject; import org.slf4j.Logger; @@ -46,26 +45,14 @@ public class EggFinder { if (BREAKFAST.egg.getValue() != null && DINNER.egg.getValue() != null && LUNCH.egg.getValue() != null) return; //Don't check for eggs if we already found all of them if (!(entity instanceof ArmorStandEntity armorStand) || armorStand.hasCustomName() || !armorStand.isInvisible() || !armorStand.shouldHideBasePlate()) return; for (ItemStack itemStack : armorStand.getArmorItems()) { - try { - if (!itemStack.isEmpty() || itemStack.getItem() == Items.PLAYER_HEAD) { - String texture = itemStack.getComponents() - .get(DataComponentTypes.PROFILE) - .properties() - .get("textures") - .iterator() - .next() - .value(); - - for (EggType type : EggType.entries) { - if (texture.equals(type.texture)) { - handleFoundEgg(armorStand, type); - return; - } + ItemUtils.getHeadTexture(itemStack).ifPresent(texture -> { + for (EggType type : EggType.entries) { + if (texture.equals(type.texture)) { + handleFoundEgg(armorStand, type); + return; } } - } catch (Exception e) { - //Ignored. This simply exists to make the code cleaner without a bunch of if statements to check the existence of each key. - } + }); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java index d6f9410b..df9a32c6 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/entity/MobGlow.java @@ -104,7 +104,7 @@ public class MobGlow { // eb07594e2df273921a77c101d0bfdfa1115abed5b9b2029eb496ceba9bdbb4b3 is texture id for the nukekubi head, // compare against it to exclusively find armorstands that are nukekubi heads // get the texture of the nukekubi head item itself and compare it - String texture = ItemUtils.getHeadTexture(armorItem); + String texture = ItemUtils.getHeadTexture(armorItem).orElse(""); return texture.contains("eyJ0ZXh0dXJlcyI6eyJTS0lOIjp7InVybCI6Imh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZWIwNzU5NGUyZGYyNzM5MjFhNzdjMTAxZDBiZmRmYTExMTVhYmVkNWI5YjIwMjllYjQ5NmNlYmE5YmRiYjRiMyJ9fX0="); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java index 682933f4..0f349a4f 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java @@ -94,9 +94,7 @@ public class VisitorHelper { } private static @Nullable String getTextureOrNull(ItemStack stack) { - String texture = ItemUtils.getHeadTexture(stack); - - return texture.isEmpty() ? null : texture; + return ItemUtils.getHeadTexture(stack).orElse(null); } private static void processLore(String visi