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: [PATCH 01/52] Add chocolate factory helper Highlights the best pick based on the price and CPS gain. --- .../skyblocker/mixins/HandledScreenMixin.java | 4 + .../ChocolateFactorySolver.java | 93 +++++++++++++++++++ .../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 diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java index f662be7c78..87b1bc791d 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.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 @@ protected HandledScreenMixin(Text title) { 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 0000000000..ef79cd4165 --- /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 08fb6a8637..8a5d32bed6 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 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 ContainerSolverManager() { new SuperpairsSolver(), UltrasequencerSolver.INSTANCE, new NewYearCakeBagHelper(), - NewYearCakesHelper.INSTANCE + NewYearCakesHelper.INSTANCE, + new ChocolateFactorySolver() }; } 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: [PATCH 02/52] 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 -- .../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 diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java index 87b1bc791d..f662be7c78 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.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 @@ protected HandledScreenMixin(Text title) { 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 ef79cd4165..cc5ae4716f 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 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 @@ protected boolean isEnabled() { } @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 @@ protected List getColors(String[] groups, Map Date: Mon, 6 May 2024 16:51:21 +0300 Subject: [PATCH 03/52] 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 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 6dc7a9e4fc..0000000000 --- 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)); - } -} 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: [PATCH 04/52] 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 --- .../de/hysky/skyblocker/SkyblockerMod.java | 2 + .../mixins/ClientPlayNetworkHandlerMixin.java | 6 + .../skyblock/chocolatefactory/EggFinder.java | 132 ++++++++++++++++++ .../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 diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index e6b2d25a2d..e0e30b3c2a 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.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 void onInitializeClient() { 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 2c2c1376d4..34e8264d2a 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 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 0000000000..d5e1a54060 --- /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 0000000000..0196edf2df --- /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 + }; + } +} 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: [PATCH 05/52] Add configs and minor code refactor --- .../config/categories/HelperCategory.java | 42 +++++++++++ .../config/configs/HelperConfig.java | 18 +++++ .../ChocolateFactorySolver.java | 8 +-- .../skyblock/chocolatefactory/EggFinder.java | 72 +++++++++++-------- .../assets/skyblocker/lang/en_us.json | 10 +++ 5 files changed, 117 insertions(+), 33 deletions(-) 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 1528f853b6..d9824a2333 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 @@ 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 static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig .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 2abff6ac81..8721e35cc7 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 static class FairySouls { @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 cc5ae4716f..a59a5147d2 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 ChocolateFactorySolver() { @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 @@ 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) { @@ -70,8 +70,8 @@ private String getLore(ItemStack 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 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 d5e1a54060..7f8e9879be 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 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 static void checkIfEgg(Entity entity) { .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 static void checkIfEgg(Entity entity) { } } + 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 @@ private static void onChatMessage(Text text, boolean overlay) { 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 diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 4304ff3d6a..6cdf5fa183 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -273,6 +273,16 @@ "skyblocker.config.helpers": "Helpers", + "skyblocker.config.helpers.chocolateFactory": "Chocolate Factory", + "skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper": "Enable Chocolate Factory Helper", + "skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper.@Tooltip": "Highlights the best upgrade when enabled.", + "skyblocker.config.helpers.chocolateFactory.enableEggFinder": "Enable Egg Finder", + "skyblocker.config.helpers.chocolateFactory.enableEggFinder.@Tooltip": "Highlights eggs from Hoppity's Hunt.", + "skyblocker.config.helpers.chocolateFactory.sendEggFoundMessages": "Send Egg Found Messages", + "skyblocker.config.helpers.chocolateFactory.sendEggFoundMessages.@Tooltip": "Sends a message in chat when an egg is found in the current island.", + "skyblocker.config.helpers.chocolateFactory.waypointType": "Egg Waypoint Type", + "skyblocker.config.helpers.chocolateFactory.waypointType.@Tooltip": "Waypoint: Displays a highlight and a beacon beam.\n\nOutlined Waypoint: Displays both a waypoint and an outline.\n\nHighlight: Only displays a highlight.\n\nOutlined Highlight: Displays both a highlight and an outline.\n\nOutline: Only displays an outline.", + "skyblocker.config.helpers.enableNewYearCakesHelper": "Enable New Year Cakes Helper", "skyblocker.config.helpers.enableNewYearCakesHelper.@Tooltip": "Highlights the missing new year cakes green and the cakes you have already red.\n\nRequires you to open your cake bag at least once to work.", 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: [PATCH 06/52] 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 --- .../java/de/hysky/skyblocker/debug/Debug.java | 6 +- .../ChocolateFactorySolver.java | 77 +++++++++++-------- .../skyblock/chocolatefactory/EggFinder.java | 27 ++----- .../skyblocker/skyblock/entity/MobGlow.java | 2 +- .../skyblock/garden/VisitorHelper.java | 4 +- .../de/hysky/skyblocker/utils/ItemUtils.java | 17 ++-- 6 files changed, 68 insertions(+), 65 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/debug/Debug.java b/src/main/java/de/hysky/skyblocker/debug/Debug.java index d9ac668c31..038e7a5ae3 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 @@ private static LiteralArgumentBuilder dumpArmorStandH 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 a59a5147d2..94c2b20939 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 @@ 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 @@ 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. @@ -51,7 +43,7 @@ protected List getColors(String[] groups, Int2ObjectMap getColors(String[] groups, Int2ObjectMap { + 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 7f8e9879be..ee1d4aa7f8 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 @@ 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.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 static void checkIfEgg(Entity entity) { 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 d6f9410b90..df9a32c608 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 @@ private static boolean isNukekubiHead(ArmorStandEntity entity) { // 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 682933f4e4..0f349a4f71 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 @@ private static void processVisitorItem(String visitorName, ScreenHandler handler } 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 visitorName, @Nullable String visitorTexture, List loreList) { diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java index 1aa7708045..6b850b3ba9 100644 --- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java @@ -33,6 +33,7 @@ import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.time.temporal.TemporalAccessor; +import java.util.Iterator; import java.util.List; import java.util.Locale; import java.util.Optional; @@ -202,16 +203,16 @@ public static PropertyMap propertyMapWithTexture(String textureValue) { return Codecs.GAME_PROFILE_PROPERTY_MAP.parse(JsonOps.INSTANCE, JsonParser.parseString("[{\"name\":\"textures\",\"value\":\"" + textureValue + "\"}]")).getOrThrow(); } - public static String getHeadTexture(ItemStack stack) { - if (!stack.isOf(Items.PLAYER_HEAD) || !stack.contains(DataComponentTypes.PROFILE)) return ""; + public static Optional getHeadTexture(ItemStack stack) { + if (!stack.isOf(Items.PLAYER_HEAD) || !stack.contains(DataComponentTypes.PROFILE)) return Optional.empty(); - ProfileComponent profile = stack.get(DataComponentTypes.PROFILE); - String texture = profile.properties().get("textures").stream() - .map(Property::value) - .findFirst() - .orElse(""); + Iterator iterator = stack.get(DataComponentTypes.PROFILE) + .properties() + .get("textures") + .iterator(); - return texture; + if (!iterator.hasNext()) return Optional.empty(); + return Optional.of(iterator.next().value()); } public static ItemStack getSkyblockerStack() { From 45643ed032a239381d8b9009241a8d3002e4402c Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 00:46:33 +0300 Subject: [PATCH 07/52] Fix incorrect textures for EggFinder.EggType --- .../hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ee1d4aa7f8..e796717f1a 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -104,9 +104,9 @@ private static void onChatMessage(Text text, boolean overlay) { record Egg(ArmorStandEntity entity, Waypoint waypoint) { } enum EggType { - LUNCH(new MutableObject<>(), 0x5555FF, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0"), + LUNCH(new MutableObject<>(), 0x5555FF, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9"), DINNER(new MutableObject<>(), 0x55FF55, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0"), - BREAKFAST(new MutableObject<>(), 0xFFAA00, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9"); + BREAKFAST(new MutableObject<>(), 0xFFAA00, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0"); public final MutableObject egg; public final int color; From 07c352c2ad7be9c4160ba479016000d83f71f64c Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 00:47:12 +0300 Subject: [PATCH 08/52] Refactor null handling in EggFinder.checkIfEgg --- .../skyblocker/skyblock/chocolatefactory/EggFinder.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) 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 e796717f1a..91aa39db75 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -22,10 +22,6 @@ 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!$"); @@ -42,12 +38,11 @@ public static void init() { public static void checkIfEgg(Entity entity) { 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()) { ItemUtils.getHeadTexture(itemStack).ifPresent(texture -> { for (EggType type : EggType.entries) { - if (texture.equals(type.texture)) { + if (texture.equals(type.texture) && type.egg.getValue() == null) { handleFoundEgg(armorStand, type); return; } From e0327e3a8e15fab7afcf446f48f4b12f1a01d7f3 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 01:03:05 +0300 Subject: [PATCH 09/52] Fix waypoint rendering not turning off --- .../hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 91aa39db75..abb9af25c9 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -76,7 +76,7 @@ private static void renderWaypoints(WorldRenderContext context) { private static void onChatMessage(Text text, boolean overlay) { if (overlay || !SkyblockerConfigManager.get().helpers.chocolateFactory.enableEggFinder) return; Matcher matcher = eggFoundPattern.matcher(text.getString()); - if (matcher.matches()) { + if (matcher.find()) { try { Egg egg = EggType.valueOf(matcher.group(1).toUpperCase()).egg.getValue(); if (egg != null) egg.waypoint.setFound(); @@ -87,7 +87,7 @@ private static void onChatMessage(Text text, boolean overlay) { //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()) { + if (matcher.find()) { try { EggType.valueOf(matcher.group(1).toUpperCase()).egg.setValue(null); } catch (IllegalArgumentException e) { From 589692ac8df2173faa54f20fc97cfa7d4308b4b4 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 01:03:40 +0300 Subject: [PATCH 10/52] Code formatting --- .../skyblocker/skyblock/chocolatefactory/EggFinder.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) 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 abb9af25c9..c467dc5365 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -62,7 +62,11 @@ private static void handleFoundEgg(ArmorStandEntity entity, EggType eggType) { 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() + "!")); + 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) { From 1d6969a7c513c1830b7360f12d2492cd36a62bf0 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 01:36:40 +0300 Subject: [PATCH 11/52] Add missing LUNCH EggType to EggType.entries --- .../hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 c467dc5365..11d9339fd4 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -112,7 +112,7 @@ enum EggType { 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); + public static final ObjectImmutableList entries = ObjectImmutableList.of(BREAKFAST, LUNCH, DINNER); EggType(MutableObject egg, int color, String texture) { this.egg = egg; From 4a0284f96148f2b0d3d4d8ce07bc1aa8a92b53a9 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 02:10:27 +0300 Subject: [PATCH 12/52] Add Time Tower Reminder --- .../de/hysky/skyblocker/SkyblockerMod.java | 2 + .../config/categories/HelperCategory.java | 8 +++ .../config/configs/HelperConfig.java | 3 + .../chocolatefactory/TimeTowerReminder.java | 71 +++++++++++++++++++ .../assets/skyblocker/lang/en_us.json | 2 + 5 files changed, 86 insertions(+) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index e0e30b3c2a..37f6d2a8c5 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -12,6 +12,7 @@ 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.chocolatefactory.TimeTowerReminder; import de.hysky.skyblocker.skyblock.crimson.kuudra.Kuudra; import de.hysky.skyblocker.skyblock.dungeon.*; import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen; @@ -185,6 +186,7 @@ public void onInitializeClient() { WarpAutocomplete.init(); MobBoundingBoxes.init(); EggFinder.init(); + TimeTowerReminder.init(); Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); Scheduler.INSTANCE.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 200); 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 d9824a2333..79bd0d657d 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java @@ -176,6 +176,14 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.helpers.chocolateFactory.waypointType = newValue) .controller(ConfigUtils::createEnumCyclingListController) .build()) + .option(Option.createBuilder() + .name(Text.translatable("skyblocker.config.helpers.chocolateFactory.enableTimeTowerReminder")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.helpers.chocolateFactory.enableTimeTowerReminder.@Tooltip"))) + .binding(defaults.helpers.chocolateFactory.enableTimeTowerReminder, + () -> config.helpers.chocolateFactory.enableTimeTowerReminder, + newValue -> config.helpers.chocolateFactory.enableTimeTowerReminder = newValue) + .controller(ConfigUtils::createBooleanController) + .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 8721e35cc7..c031492422 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/HelperConfig.java @@ -79,5 +79,8 @@ public static class ChocolateFactory { @SerialEntry public Waypoint.Type waypointType = Waypoint.Type.WAYPOINT; + + @SerialEntry + public boolean enableTimeTowerReminder = true; } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java new file mode 100644 index 0000000000..3b7fa3bc2e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java @@ -0,0 +1,71 @@ +package de.hysky.skyblocker.skyblock.chocolatefactory; + +import com.mojang.brigadier.Message; +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.text.Text; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.nio.file.Files; +import java.time.Instant; +import java.util.regex.Pattern; + +public class TimeTowerReminder { + private static final String TIME_TOWER_FILE = "time_tower.txt"; + private static final Pattern TIME_TOWER_PATTERN = Pattern.compile("^TIME TOWER! Your Chocolate Factory production has increased"); + private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Time Tower Reminder"); + + private TimeTowerReminder() { + } + + public static void init() { + SkyblockEvents.JOIN.register(TimeTowerReminder::checkTempFile); + ClientReceiveMessageEvents.GAME.register(TimeTowerReminder::checkIfTimeTower); + } + + public static void checkIfTimeTower(Message message, boolean overlay) { + if (!TIME_TOWER_PATTERN.matcher(message.getString()).matches()) return; + Scheduler.INSTANCE.schedule(TimeTowerReminder::sendMessage, 60*60*20); // 1 hour + + File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); + if (!tempFile.exists() && !tempFile.mkdir()) { + LOGGER.error("[Skyblocker Time Tower Reminder] Failed to create temp file for Time Tower Reminder!"); + return; + } + + try (FileWriter writer = new FileWriter(tempFile)) { + writer.write(Instant.now().toString()); + } catch (IOException e) { + LOGGER.error("[Skyblocker Time Tower Reminder] Failed to write to temp file for Time Tower Reminder!", e); + } + } + + private static void sendMessage() { + if (MinecraftClient.getInstance().player == null) return; + MinecraftClient.getInstance().player.sendMessage(Constants.PREFIX.get().append(Text.literal("Your Chocolate Factory's Time Tower has deactivated!").withColor(0xFF5555))); + + File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); + try { + if (tempFile.exists()) Files.delete(tempFile.toPath()); + } catch (Exception e) { + LOGGER.error("[Skyblocker Time Tower Reminder] Failed to delete temp file for Time Tower Reminder!", e); + } + } + + private static void checkTempFile() { + File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); + if (!tempFile.exists()) return; + + long time = tempFile.lastModified(); + if (System.currentTimeMillis() - time >= 60*60*1000) sendMessage(); + else Scheduler.INSTANCE.schedule(TimeTowerReminder::sendMessage, 60*60*20 - (int) ((System.currentTimeMillis() - time) / 50)); // 50 milliseconds is 1 tick + } +} diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 6cdf5fa183..6b309d3953 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -282,6 +282,8 @@ "skyblocker.config.helpers.chocolateFactory.sendEggFoundMessages.@Tooltip": "Sends a message in chat when an egg is found in the current island.", "skyblocker.config.helpers.chocolateFactory.waypointType": "Egg Waypoint Type", "skyblocker.config.helpers.chocolateFactory.waypointType.@Tooltip": "Waypoint: Displays a highlight and a beacon beam.\n\nOutlined Waypoint: Displays both a waypoint and an outline.\n\nHighlight: Only displays a highlight.\n\nOutlined Highlight: Displays both a highlight and an outline.\n\nOutline: Only displays an outline.", + "skyblocker.config.helpers.chocolateFactory.enableTimeTowerReminder": "Enable Time Tower Reminder", + "skyblocker.config.helpers.chocolateFactory.enableTimeTowerReminder.@Tooltip": "Sends a message in chat when your Time Tower deactivates.", "skyblocker.config.helpers.enableNewYearCakesHelper": "Enable New Year Cakes Helper", "skyblocker.config.helpers.enableNewYearCakesHelper.@Tooltip": "Highlights the missing new year cakes green and the cakes you have already red.\n\nRequires you to open your cake bag at least once to work.", From 9e0f4272c6f7864a45d43163799075661f40b9bf Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 02:11:40 +0300 Subject: [PATCH 13/52] Add coach cps calculation to the ChocolateFactorySolver --- .../ChocolateFactorySolver.java | 100 +++++++++++++----- 1 file changed, 75 insertions(+), 25 deletions(-) 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 94c2b20939..5c809a9877 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -14,12 +14,14 @@ 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 CPS_PATTERN = Pattern.compile("([\\d,.]+) Chocolate per second"); + private static final Pattern CPS_INCREASE_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+ *"); + private static final Pattern TOTAL_MULTIPLIER_PATTERN = Pattern.compile("Total Multiplier: ([\\d.]+)x"); + private static final Pattern MULTIPLIER_INCREASE_PATTERN = Pattern.compile("\\+([\\d.]+)x Chocolate per second"); public ChocolateFactorySolver() { super("^Chocolate Factory$"); @@ -37,31 +39,70 @@ protected void start(GenericContainerScreen screen) { @Override protected List getColors(String[] groups, Int2ObjectMap slots) { - Int2DoubleMap cpsIncreaseFactors = new Int2DoubleLinkedOpenHashMap(5); //There are only 5 rabbits on the screen. - 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(); + final Int2DoubleMap cpsIncreaseFactors = new Int2DoubleLinkedOpenHashMap(6); //There are only 5 rabbits on the screen + 1 for the coach + for (int i = 29; i <= 33; i++) { // The 5 rabbits slots are in 29, 30, 31, 32 and 33. + ItemStack item = slots.get(i); if (item.getItem() != Items.PLAYER_HEAD || item.isEmpty()) continue; String lore = getConcattedLore(item); if (lore.isBlank()) continue; - OptionalDouble cpsIncreaseFactor = getCPSIncreaseFactor(lore); + OptionalDouble cpsIncreaseFactor = getRabbitCPSIncreaseFactor(lore); // The 5 usual rabbits if (cpsIncreaseFactor.isEmpty()) continue; //Something went wrong, skip this item - cpsIncreaseFactors.put(entry.getIntKey(), cpsIncreaseFactor.getAsDouble()); + cpsIncreaseFactors.put(i, cpsIncreaseFactor.getAsDouble()); } + OptionalDouble coachCpsIncreaseFactor = getCoachCPSIncreaseFactor(slots.get(45), slots.get(42)); // The coach + + if (!coachCpsIncreaseFactor.isEmpty()) cpsIncreaseFactors.put(42, coachCpsIncreaseFactor.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())); } + /** + * Utility method. + */ 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 + return concatenateLore(ItemUtils.getLore(item)); + } + + /** + * Concatenates the lore of an item into one string. + * This is useful in case some pattern we're looking for is split into multiple lines, which would make it harder to regex. + */ + private String concatenateLore(List lore) { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < lore.size(); i++) { + stringBuilder.append(lore.get(i).getString()); + if (i != lore.size() - 1) stringBuilder.append(" "); + } + return stringBuilder.toString(); + } + + private OptionalDouble getCoachCPSIncreaseFactor(ItemStack cpsItem, ItemStack coachItem) { + String cpsItemLore = getConcattedLore(cpsItem); + + Matcher cpsMatcher = CPS_PATTERN.matcher(cpsItemLore); + OptionalDouble currentCps = getDoubleFromMatcher(cpsMatcher); + if (currentCps.isEmpty()) return OptionalDouble.empty(); + + Matcher multiplierMatcher = TOTAL_MULTIPLIER_PATTERN.matcher(cpsItemLore); + OptionalDouble totalMultiplier = getDoubleFromMatcher(multiplierMatcher, cpsMatcher.end()); + if (totalMultiplier.isEmpty()) return OptionalDouble.empty(); + + String coachLore = getConcattedLore(coachItem); + + Matcher multiplierIncreaseMatcher = MULTIPLIER_INCREASE_PATTERN.matcher(coachLore); + OptionalDouble currentCpsMultiplier = getDoubleFromMatcher(multiplierIncreaseMatcher); + if (currentCpsMultiplier.isEmpty()) return OptionalDouble.empty(); + OptionalDouble nextCpsMultiplier = getDoubleFromMatcher(multiplierIncreaseMatcher); + if (nextCpsMultiplier.isEmpty()) { //This means that the coach isn't hired yet. + nextCpsMultiplier = currentCpsMultiplier; //So the first instance of the multiplier is actually the amount we'll get upon upgrading. + currentCpsMultiplier = OptionalDouble.of(0.0); //And so, we can re-assign values to the variables to make the calculation more readable. + } + + return OptionalDouble.of((currentCps.getAsDouble()/totalMultiplier.getAsDouble()) * (nextCpsMultiplier.getAsDouble() - currentCpsMultiplier.getAsDouble())); } /** @@ -71,30 +112,30 @@ private String getConcattedLore(ItemStack item) { * @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) { + private OptionalDouble getRabbitCPSIncreaseFactor(String lore) { 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 + Matcher cpsMatcher = CPS_INCREASE_PATTERN.matcher(lore); + OptionalInt cps = getIntFromMatcher(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 + OptionalInt cost = getIntFromMatcher(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 + Matcher cpsMatcher = CPS_INCREASE_PATTERN.matcher(lore); + OptionalInt currentCps = getIntFromMatcher(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 + OptionalInt nextCps = getIntFromMatcher(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 + OptionalInt cost = getIntFromMatcher(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()); } @@ -102,12 +143,21 @@ private OptionalDouble getCPSIncreaseFactor(String lore) { } } - private OptionalInt getValueFromMatcher(Matcher matcher) { - return getValueFromMatcher(matcher, matcher.hasMatch() ? matcher.end() : 0); + private OptionalInt getIntFromMatcher(Matcher matcher) { + return getIntFromMatcher(matcher, matcher.hasMatch() ? matcher.end() : 0); } - private OptionalInt getValueFromMatcher(Matcher matcher, int startingIndex) { + private OptionalInt getIntFromMatcher(Matcher matcher, int startingIndex) { if (!matcher.find(startingIndex)) return OptionalInt.empty(); return OptionalInt.of(Integer.parseInt(matcher.group(1).replace(",", ""))); } + + private OptionalDouble getDoubleFromMatcher(Matcher matcher) { + return getDoubleFromMatcher(matcher, matcher.hasMatch() ? matcher.end() : 0); + } + + private OptionalDouble getDoubleFromMatcher(Matcher matcher, int startingIndex) { + if (!matcher.find(startingIndex)) return OptionalDouble.empty(); + return OptionalDouble.of(Double.parseDouble(matcher.group(1).replace(",", ""))); + } } From bb2d694dc3ec376d790dabf4db3797ec1531a3cd Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 02:22:46 +0300 Subject: [PATCH 14/52] Improve TIME_TOWER_PATTERN regex in case of failure with the use of Matcher#matches --- .../skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java index 3b7fa3bc2e..89e1de7487 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java @@ -20,7 +20,7 @@ public class TimeTowerReminder { private static final String TIME_TOWER_FILE = "time_tower.txt"; - private static final Pattern TIME_TOWER_PATTERN = Pattern.compile("^TIME TOWER! Your Chocolate Factory production has increased"); + private static final Pattern TIME_TOWER_PATTERN = Pattern.compile("^TIME TOWER! Your Chocolate Factory production has increased by \\+[\\d.]+x for \\dh!$"); private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Time Tower Reminder"); private TimeTowerReminder() { From bf20bed1576eed02e14dede96a70bc9e6f41387e Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 02:44:51 +0300 Subject: [PATCH 15/52] Change uses of raw integers to colors from Formatting --- .../skyblocker/skyblock/chocolatefactory/EggFinder.java | 7 ++++--- .../skyblock/chocolatefactory/TimeTowerReminder.java | 3 ++- 2 files changed, 6 insertions(+), 4 deletions(-) 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 11d9339fd4..031db52e40 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -15,6 +15,7 @@ import net.minecraft.entity.decoration.ArmorStandEntity; import net.minecraft.item.ItemStack; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; import org.apache.commons.lang3.mutable.MutableObject; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -103,9 +104,9 @@ private static void onChatMessage(Text text, boolean overlay) { record Egg(ArmorStandEntity entity, Waypoint waypoint) { } enum EggType { - LUNCH(new MutableObject<>(), 0x5555FF, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9"), - DINNER(new MutableObject<>(), 0x55FF55, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0"), - BREAKFAST(new MutableObject<>(), 0xFFAA00, "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0"); + LUNCH(new MutableObject<>(), Formatting.BLUE.getColorValue(), "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjU2ODExMiwKICAicHJvZmlsZUlkIiA6ICI3NzUwYzFhNTM5M2Q0ZWQ0Yjc2NmQ4ZGUwOWY4MjU0NiIsCiAgInByb2ZpbGVOYW1lIiA6ICJSZWVkcmVsIiwKICAic2lnbmF0dXJlUmVxdWlyZWQiIDogdHJ1ZSwKICAidGV4dHVyZXMiIDogewogICAgIlNLSU4iIDogewogICAgICAidXJsIiA6ICJodHRwOi8vdGV4dHVyZXMubWluZWNyYWZ0Lm5ldC90ZXh0dXJlLzdhZTZkMmQzMWQ4MTY3YmNhZjk1MjkzYjY4YTRhY2Q4NzJkNjZlNzUxZGI1YTM0ZjJjYmM2NzY2YTAzNTZkMGEiCiAgICB9CiAgfQp9"), + DINNER(new MutableObject<>(), Formatting.GREEN.getColorValue(), "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY0OTcwMSwKICAicHJvZmlsZUlkIiA6ICI3NGEwMzQxNWY1OTI0ZTA4YjMyMGM2MmU1NGE3ZjJhYiIsCiAgInByb2ZpbGVOYW1lIiA6ICJNZXp6aXIiLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvZTVlMzYxNjU4MTlmZDI4NTBmOTg1NTJlZGNkNzYzZmY5ODYzMTMxMTkyODNjMTI2YWNlMGM0Y2M0OTVlNzZhOCIKICAgIH0KICB9Cn0"), + BREAKFAST(new MutableObject<>(), Formatting.GOLD.getColorValue(), "ewogICJ0aW1lc3RhbXAiIDogMTcxMTQ2MjY3MzE0OSwKICAicHJvZmlsZUlkIiA6ICJiN2I4ZTlhZjEwZGE0NjFmOTY2YTQxM2RmOWJiM2U4OCIsCiAgInByb2ZpbGVOYW1lIiA6ICJBbmFiYW5hbmFZZzciLAogICJzaWduYXR1cmVSZXF1aXJlZCIgOiB0cnVlLAogICJ0ZXh0dXJlcyIgOiB7CiAgICAiU0tJTiIgOiB7CiAgICAgICJ1cmwiIDogImh0dHA6Ly90ZXh0dXJlcy5taW5lY3JhZnQubmV0L3RleHR1cmUvYTQ5MzMzZDg1YjhhMzE1ZDAzMzZlYjJkZjM3ZDhhNzE0Y2EyNGM1MWI4YzYwNzRmMWI1YjkyN2RlYjUxNmMyNCIKICAgIH0KICB9Cn0"); public final MutableObject egg; public final int color; diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java index 89e1de7487..aa3a49d827 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java @@ -8,6 +8,7 @@ import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; import net.minecraft.client.MinecraftClient; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,7 +51,7 @@ public static void checkIfTimeTower(Message message, boolean overlay) { private static void sendMessage() { if (MinecraftClient.getInstance().player == null) return; - MinecraftClient.getInstance().player.sendMessage(Constants.PREFIX.get().append(Text.literal("Your Chocolate Factory's Time Tower has deactivated!").withColor(0xFF5555))); + MinecraftClient.getInstance().player.sendMessage(Constants.PREFIX.get().append(Text.literal("Your Chocolate Factory's Time Tower has deactivated!").formatted(Formatting.RED))); File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); try { From ba5047d708858ed863bbc207de20afcd42337451 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 03:50:00 +0300 Subject: [PATCH 16/52] Mixin into ClientPlayNetworkHandler.onEntityTrackerUpdate to check for egg spawns --- .../skyblocker/mixins/ClientPlayNetworkHandlerMixin.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index 34e8264d2a..9ff1fc84e4 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -124,4 +124,9 @@ public abstract class ClientPlayNetworkHandlerMixin { private void skyblocker$onEntityEquip(EntityEquipmentUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { EggFinder.checkIfEgg(entity); } + + @Inject(method = "onEntityTrackerUpdate", at = @At(value = "TAIL")) + private void skyblocker$onEntityTrackerUpdate(EntityTrackerUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { + EggFinder.checkIfEgg(entity); + } } From eee4ee696a4c4263c7e00af54c6faac40fe93ed0 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 08:04:10 +0300 Subject: [PATCH 17/52] Time tower fix attempt --- .../chocolatefactory/TimeTowerReminder.java | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java index aa3a49d827..f1458b59d9 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java @@ -16,8 +16,8 @@ import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; -import java.time.Instant; import java.util.regex.Pattern; +import java.util.stream.Stream; public class TimeTowerReminder { private static final String TIME_TOWER_FILE = "time_tower.txt"; @@ -34,16 +34,20 @@ public static void init() { public static void checkIfTimeTower(Message message, boolean overlay) { if (!TIME_TOWER_PATTERN.matcher(message.getString()).matches()) return; - Scheduler.INSTANCE.schedule(TimeTowerReminder::sendMessage, 60*60*20); // 1 hour + Scheduler.INSTANCE.schedule(TimeTowerReminder::sendMessage, 60 * 60 * 20); // 1 hour File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); - if (!tempFile.exists() && !tempFile.mkdir()) { - LOGGER.error("[Skyblocker Time Tower Reminder] Failed to create temp file for Time Tower Reminder!"); - return; + if (!tempFile.exists()) { + try { + tempFile.createNewFile(); + } catch (IOException e) { + LOGGER.error("[Skyblocker Time Tower Reminder] Failed to create temp file for Time Tower Reminder!", e); + return; + } } try (FileWriter writer = new FileWriter(tempFile)) { - writer.write(Instant.now().toString()); + writer.write(String.valueOf(System.currentTimeMillis())); } catch (IOException e) { LOGGER.error("[Skyblocker Time Tower Reminder] Failed to write to temp file for Time Tower Reminder!", e); } @@ -65,8 +69,15 @@ private static void checkTempFile() { File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); if (!tempFile.exists()) return; - long time = tempFile.lastModified(); - if (System.currentTimeMillis() - time >= 60*60*1000) sendMessage(); - else Scheduler.INSTANCE.schedule(TimeTowerReminder::sendMessage, 60*60*20 - (int) ((System.currentTimeMillis() - time) / 50)); // 50 milliseconds is 1 tick + long time; + try (Stream file = Files.lines(tempFile.toPath())) { + time = Long.parseLong(file.findFirst().orElseThrow()); + } catch (Exception e) { + LOGGER.error("[Skyblocker Time Tower Reminder] Failed to read temp file for Time Tower Reminder!", e); + return; + } + + if (System.currentTimeMillis() - time >= 60 * 60 * 1000) sendMessage(); + else Scheduler.INSTANCE.schedule(TimeTowerReminder::sendMessage, 60 * 60 * 20 - (int) ((System.currentTimeMillis() - time) / 50)); // 50 milliseconds is 1 tick } } From e8d5f6f8380ad0baca3f43b26298e852643127b1 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 09:35:43 +0300 Subject: [PATCH 18/52] Add SkyblockTime Not sure if the AtomicReferences are helping with anything, just followed copilot. --- .../de/hysky/skyblocker/SkyblockerMod.java | 6 +- .../hysky/skyblocker/utils/SkyblockTime.java | 61 +++++++++++++++++++ 2 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 37f6d2a8c5..79f241b8df 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -47,10 +47,7 @@ import de.hysky.skyblocker.skyblock.waypoint.MythologicalRitual; import de.hysky.skyblocker.skyblock.waypoint.OrderedWaypoints; import de.hysky.skyblocker.skyblock.waypoint.Relics; -import de.hysky.skyblocker.utils.ApiUtils; -import de.hysky.skyblocker.utils.NEURepoManager; -import de.hysky.skyblocker.utils.ProfileUtils; -import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.*; import de.hysky.skyblocker.utils.chat.ChatMessageListener; import de.hysky.skyblocker.utils.discord.DiscordRPCManager; import de.hysky.skyblocker.utils.render.RenderHelper; @@ -187,6 +184,7 @@ public void onInitializeClient() { MobBoundingBoxes.init(); EggFinder.init(); TimeTowerReminder.init(); + SkyblockTime.init(); Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20); Scheduler.INSTANCE.scheduleCyclic(DiscordRPCManager::updateDataAndPresence, 200); diff --git a/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java new file mode 100644 index 0000000000..36db4ef3b6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java @@ -0,0 +1,61 @@ +package de.hysky.skyblocker.utils; + +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +public class SkyblockTime { + private static final long SKYBLOCK_POCH = 1560275700000L; + public static final AtomicInteger skyblockYear = new AtomicInteger(0); + public static final AtomicReference skyblockSeason = new AtomicReference<>(Season.SPRING); + public static final AtomicReference skyblockMonth = new AtomicReference<>(Month.EARLY_SPRING); + public static final AtomicInteger skyblockDay = new AtomicInteger(0); + private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Time"); + + private SkyblockTime() { + } + + public static void init() { + updateTime(); + //ScheduleCyclic already runs the task upon scheduling, so there's no need to call updateTime() here + Scheduler.INSTANCE.schedule(() -> Scheduler.INSTANCE.scheduleCyclic(SkyblockTime::updateTime, 1200 * 24), (int) (1200000 - (getSkyblockMillis() % 1200000)) / 50); + } + + private static long getSkyblockMillis() { + return System.currentTimeMillis() - SKYBLOCK_POCH; + } + + private static int getSkyblockYear() { + return (int) (Math.floor(getSkyblockMillis() / 446400000.0) + 1); + } + + private static int getSkyblockMonth() { + return (int) (Math.floor(getSkyblockMillis() / 37200000.0) % 12); + } + + private static int getSkyblockDay() { + return (int) (Math.floor(getSkyblockMillis() / 1200000.0) % 31 + 1); + } + + private static void updateTime() { + skyblockYear.set(getSkyblockYear()); + skyblockSeason.set(Season.values()[getSkyblockMonth() / 3]); + skyblockMonth.set(Month.values()[getSkyblockMonth()]); + skyblockDay.set(getSkyblockDay()); + LOGGER.info("[Skyblocker Time] Skyblock time updated to Year {}, Season {}, Month {}, Day {}",skyblockYear.get(), skyblockSeason.get(), skyblockMonth.get(), skyblockDay.get()); + } + + public enum Season { + SPRING, SUMMER, FALL, WINTER; + } + + public enum Month { + EARLY_SPRING, SPRING, LATE_SPRING, + EARLY_SUMMER, SUMMER, LATE_SUMMER, + EARLY_FALL, FALL, LATE_FALL, + EARLY_WINTER, WINTER, LATE_WINTER; + } +} From 87c382b7ea32467d99f919ed8bdcbd2e7355e437 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 09:37:01 +0300 Subject: [PATCH 19/52] Add season check to EggFinder --- .../hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java | 2 ++ 1 file changed, 2 insertions(+) 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 031db52e40..0e887fd276 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -4,6 +4,7 @@ import de.hysky.skyblocker.utils.ColorUtils; import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.SkyblockTime; import de.hysky.skyblocker.utils.waypoint.Waypoint; import it.unimi.dsi.fastutil.objects.ObjectImmutableList; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; @@ -39,6 +40,7 @@ public static void init() { public static void checkIfEgg(Entity entity) { if (!SkyblockerConfigManager.get().helpers.chocolateFactory.enableEggFinder) return; + if (SkyblockTime.skyblockSeason.get() != SkyblockTime.Season.SPRING) return; if (!(entity instanceof ArmorStandEntity armorStand) || armorStand.hasCustomName() || !armorStand.isInvisible() || !armorStand.shouldHideBasePlate()) return; for (ItemStack itemStack : armorStand.getArmorItems()) { ItemUtils.getHeadTexture(itemStack).ifPresent(texture -> { From 5ea7ccf01559a12f79bb06f4cf8f762895620038 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 14 May 2024 10:08:16 +0300 Subject: [PATCH 20/52] Refactor code, fix coach calculation and attempt to highlight the best affordable pick I tried to make the best pick still have yellow highlight while keeping the best affordable pick green but it's not working for some reason and I have no idea what's causing it'. --- .../ChocolateFactorySolver.java | 120 +++++++++--------- 1 file changed, 61 insertions(+), 59 deletions(-) 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 5c809a9877..ed4d0f5e63 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -5,23 +5,25 @@ import de.hysky.skyblocker.utils.render.gui.ColorHighlight; import de.hysky.skyblocker.utils.render.gui.ContainerSolver; import it.unimi.dsi.fastutil.ints.*; -import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.client.MinecraftClient; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.text.Text; +import org.apache.commons.math3.analysis.function.Min; 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 CPS_INCREASE_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+ *"); private static final Pattern TOTAL_MULTIPLIER_PATTERN = Pattern.compile("Total Multiplier: ([\\d.]+)x"); private static final Pattern MULTIPLIER_INCREASE_PATTERN = Pattern.compile("\\+([\\d.]+)x Chocolate per second"); + private static final Pattern TOTAL_CHOCOLATE_PATTERN = Pattern.compile("([\\d,]+) Chocolate"); public ChocolateFactorySolver() { super("^Chocolate Factory$"); @@ -32,32 +34,37 @@ protected boolean isEnabled() { return SkyblockerConfigManager.get().helpers.chocolateFactory.enableChocolateFactoryHelper; } - @Override - protected void start(GenericContainerScreen screen) { - markHighlightsDirty(); //Recalculate highlights when the screen is opened, which happens when upgrading rabbits - } - @Override protected List getColors(String[] groups, Int2ObjectMap slots) { - final Int2DoubleMap cpsIncreaseFactors = new Int2DoubleLinkedOpenHashMap(6); //There are only 5 rabbits on the screen + 1 for the coach + final Int2ObjectMap cpsIncreaseFactors = new Int2ObjectLinkedOpenHashMap<>(6); for (int i = 29; i <= 33; i++) { // The 5 rabbits slots are in 29, 30, 31, 32 and 33. ItemStack item = slots.get(i); if (item.getItem() != Items.PLAYER_HEAD || item.isEmpty()) continue; - String lore = getConcattedLore(item); - if (lore.isBlank()) continue; - - OptionalDouble cpsIncreaseFactor = getRabbitCPSIncreaseFactor(lore); // The 5 usual rabbits - if (cpsIncreaseFactor.isEmpty()) continue; //Something went wrong, skip this item - cpsIncreaseFactors.put(i, cpsIncreaseFactor.getAsDouble()); + int finalI = i; //Java, pfft. + getRabbit(item).ifPresent(rabbit -> cpsIncreaseFactors.put(finalI, rabbit)); } - OptionalDouble coachCpsIncreaseFactor = getCoachCPSIncreaseFactor(slots.get(45), slots.get(42)); // The coach - if (!coachCpsIncreaseFactor.isEmpty()) cpsIncreaseFactors.put(42, coachCpsIncreaseFactor.getAsDouble()); + //Coach is in slot 42 while the factory info item is in slot 45. + getCoach(slots.get(45), slots.get(42)).ifPresent(coach -> cpsIncreaseFactors.put(42, coach)); + if (cpsIncreaseFactors.isEmpty()) return List.of(); //Something went wrong. + + OptionalLong totalChocolate = getTotalChocolate(slots.get(13)); - 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())); + List> sorted = cpsIncreaseFactors.int2ObjectEntrySet() + .stream() //Compare cost/cpsIncrease rather than cpsIncrease/cost to avoid getting close to 0 and losing precision. + .sorted(Comparator.comparingDouble(entry -> entry.getValue().cost() / entry.getValue().cpsIncrease())) //Ascending order, lower = better + .dropWhile(entry -> entry.getValue().cost == 0) + .collect(Collectors.toCollection(LinkedList::new)); + + Int2ObjectMap.Entry bestEntry = sorted.removeFirst(); + if (totalChocolate.isEmpty() || bestEntry.getValue().cost < totalChocolate.getAsLong()) return List.of(ColorHighlight.green(bestEntry.getIntKey())); + + for (Int2ObjectMap.Entry entry : sorted) { + if (entry.getValue().cost > totalChocolate.getAsLong()) continue; + return List.of(ColorHighlight.green(entry.getIntKey()), ColorHighlight.yellow(bestEntry.getIntKey())); + } + return List.of(ColorHighlight.green(bestEntry.getIntKey())); } /** @@ -80,67 +87,59 @@ private String concatenateLore(List lore) { return stringBuilder.toString(); } - private OptionalDouble getCoachCPSIncreaseFactor(ItemStack cpsItem, ItemStack coachItem) { + private Optional getCoach(ItemStack cpsItem, ItemStack coachItem) { + if (coachItem.isEmpty() || cpsItem.isEmpty() || coachItem.getItem() != Items.PLAYER_HEAD || cpsItem.getItem() != Items.COCOA_BEANS) return Optional.empty(); String cpsItemLore = getConcattedLore(cpsItem); Matcher cpsMatcher = CPS_PATTERN.matcher(cpsItemLore); OptionalDouble currentCps = getDoubleFromMatcher(cpsMatcher); - if (currentCps.isEmpty()) return OptionalDouble.empty(); + if (currentCps.isEmpty()) return Optional.empty(); Matcher multiplierMatcher = TOTAL_MULTIPLIER_PATTERN.matcher(cpsItemLore); OptionalDouble totalMultiplier = getDoubleFromMatcher(multiplierMatcher, cpsMatcher.end()); - if (totalMultiplier.isEmpty()) return OptionalDouble.empty(); + if (totalMultiplier.isEmpty()) return Optional.empty(); String coachLore = getConcattedLore(coachItem); Matcher multiplierIncreaseMatcher = MULTIPLIER_INCREASE_PATTERN.matcher(coachLore); OptionalDouble currentCpsMultiplier = getDoubleFromMatcher(multiplierIncreaseMatcher); - if (currentCpsMultiplier.isEmpty()) return OptionalDouble.empty(); + if (currentCpsMultiplier.isEmpty()) return Optional.empty(); + OptionalDouble nextCpsMultiplier = getDoubleFromMatcher(multiplierIncreaseMatcher); if (nextCpsMultiplier.isEmpty()) { //This means that the coach isn't hired yet. nextCpsMultiplier = currentCpsMultiplier; //So the first instance of the multiplier is actually the amount we'll get upon upgrading. currentCpsMultiplier = OptionalDouble.of(0.0); //And so, we can re-assign values to the variables to make the calculation more readable. } - return OptionalDouble.of((currentCps.getAsDouble()/totalMultiplier.getAsDouble()) * (nextCpsMultiplier.getAsDouble() - currentCpsMultiplier.getAsDouble())); + Matcher costMatcher = COST_PATTERN.matcher(coachLore); + OptionalInt cost = getIntFromMatcher(costMatcher, multiplierIncreaseMatcher.end()); //Cost comes after the multiplier line + if (cost.isEmpty()) return Optional.empty(); + + return Optional.of(new Rabbit(currentCps.getAsDouble() / totalMultiplier.getAsDouble() * (nextCpsMultiplier.getAsDouble() - currentCpsMultiplier.getAsDouble()), cost.getAsInt())); } - /** - * 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 getRabbitCPSIncreaseFactor(String lore) { - 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_INCREASE_PATTERN.matcher(lore); - OptionalInt cps = getIntFromMatcher(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 = getIntFromMatcher(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_INCREASE_PATTERN.matcher(lore); - OptionalInt currentCps = getIntFromMatcher(cpsMatcher); //Current cps is before the hire line - if (currentCps.isEmpty()) return OptionalDouble.empty(); - OptionalInt nextCps = getIntFromMatcher(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 = getIntFromMatcher(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 Optional getRabbit(ItemStack item) { + String lore = getConcattedLore(item); + Matcher cpsMatcher = CPS_INCREASE_PATTERN.matcher(lore); + OptionalInt currentCps = getIntFromMatcher(cpsMatcher); + if (currentCps.isEmpty()) return Optional.empty(); + OptionalInt nextCps = getIntFromMatcher(cpsMatcher); + if (nextCps.isEmpty()) { + nextCps = currentCps; //This means that the rabbit isn't hired yet. + currentCps = OptionalInt.of(0); //So the first instance of the cps is actually the amount we'll get upon hiring. } + + Matcher costMatcher = COST_PATTERN.matcher(lore); + OptionalInt cost = getIntFromMatcher(costMatcher, cpsMatcher.end()); //Cost comes after the cps line + if (cost.isEmpty()) return Optional.empty(); + return Optional.of(new Rabbit(nextCps.getAsInt() - currentCps.getAsInt(), cost.getAsInt())); + } + + private OptionalLong getTotalChocolate(ItemStack item) { + if (item.isEmpty() || item.getItem() != Items.PLAYER_HEAD) return OptionalLong.empty(); + Matcher matcher = TOTAL_CHOCOLATE_PATTERN.matcher(item.getName().getString()); + if (!matcher.find()) return OptionalLong.empty(); + return OptionalLong.of(Long.parseLong(matcher.group(1).replace(",", ""))); } private OptionalInt getIntFromMatcher(Matcher matcher) { @@ -160,4 +159,7 @@ private OptionalDouble getDoubleFromMatcher(Matcher matcher, int startingIndex) if (!matcher.find(startingIndex)) return OptionalDouble.empty(); return OptionalDouble.of(Double.parseDouble(matcher.group(1).replace(",", ""))); } + + private record Rabbit(double cpsIncrease, int cost) { + } } From 5b7c40ea4cf6495dad5c935eb566f851d9062e57 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 01:21:00 +0300 Subject: [PATCH 21/52] Refactor EggFinder for CompactDamage compatibility --- .../mixins/ClientPlayNetworkHandlerMixin.java | 183 +++++++++--------- .../skyblocker/skyblock/CompactDamage.java | 1 + .../skyblock/chocolatefactory/EggFinder.java | 6 +- 3 files changed, 96 insertions(+), 94 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index 9ff1fc84e4..3ca68df379 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -35,98 +35,95 @@ @Mixin(ClientPlayNetworkHandler.class) public abstract class ClientPlayNetworkHandlerMixin { - @Shadow - private ClientWorld world; - - @Shadow - @Final - private static Logger LOGGER; - - @Inject(method = "onBlockUpdate", at = @At("RETURN")) - private void skyblocker$onBlockUpdate(BlockUpdateS2CPacket packet, CallbackInfo ci) { - if (Utils.isInTheEnd() && SlayerUtils.isInSlayer()) { - BeaconHighlighter.beaconPositions.remove(packet.getPos()); - if (packet.getState().isOf(Blocks.BEACON)) { - BeaconHighlighter.beaconPositions.add(packet.getPos()); - } - } - } - - @Inject(method = "method_37472", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V")) - private void skyblocker$onItemDestroy(int entityId, CallbackInfo ci) { - if (world.getEntityById(entityId) instanceof ItemEntity itemEntity) { - DungeonManager.onItemPickup(itemEntity); - } - } - - @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V", ordinal = 0)) - private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity) { - DungeonManager.onItemPickup(itemEntity); - return itemEntity; - } - - @WrapWithCondition(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false)) - private boolean skyblocker$cancelEntityPassengersWarning(Logger instance, String msg) { - return !Utils.isOnHypixel(); - } - - @WrapWithCondition(method = "onPlayerList", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$cancelPlayerListWarning(Logger instance, String format, Object arg1, Object arg2) { - return !Utils.isOnHypixel(); - } - - @Inject(method = "onPlaySound", at = @At("RETURN")) - private void skyblocker$onPlaySound(PlaySoundS2CPacket packet, CallbackInfo ci) { - FishingHelper.onSound(packet); - } - - @WrapWithCondition(method = "warnOnUnknownPayload", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$dropBadlionPacketWarnings(Logger instance, String message, Object identifier) { - return !(Utils.isOnHypixel() && ((Identifier) identifier).getNamespace().equals("badlion")); - } - - @WrapWithCondition(method = { "onScoreboardScoreUpdate", "onScoreboardScoreReset" }, at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$cancelUnknownScoreboardObjectiveWarnings(Logger instance, String message, Object objectiveName) { - return !Utils.isOnHypixel(); - } - - @WrapWithCondition(method = "onTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$cancelTeamWarning(Logger instance, String format, Object... arg) { - return !Utils.isOnHypixel(); - } - - @Inject(method = "onParticle", at = @At("RETURN")) - private void skyblocker$onParticle(ParticleS2CPacket packet, CallbackInfo ci) { - MythologicalRitual.onParticle(packet); - EnderNodes.onParticle(packet); - } - - @ModifyExpressionValue(method = "onEntityStatus", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/EntityStatusS2CPacket;getEntity(Lnet/minecraft/world/World;)Lnet/minecraft/entity/Entity;")) - private Entity skyblocker$onEntityDeath(Entity entity, @Local(argsOnly = true) EntityStatusS2CPacket packet) { - if (packet.getStatus() == EntityStatuses.PLAY_DEATH_SOUND_OR_ADD_PROJECTILE_HIT_PARTICLES) { - DungeonScore.handleEntityDeath(entity); - TheEnd.onEntityDeath(entity); - } - return entity; - } - - @Inject(method = "onEntityTrackerUpdate", at = @At("TAIL")) - private void skyblocker$onEntityTrackerUpdate(EntityTrackerUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { - if (!SkyblockerConfigManager.get().uiAndVisuals.compactDamage.enabled || !(entity instanceof ArmorStandEntity armorStandEntity)) return; - try { //Prevent packet handling fails if something goes wrong so that entity trackers still update, just without compact damage numbers + @Shadow + private ClientWorld world; + + @Shadow + @Final + private static Logger LOGGER; + + @Inject(method = "onBlockUpdate", at = @At("RETURN")) + private void skyblocker$onBlockUpdate(BlockUpdateS2CPacket packet, CallbackInfo ci) { + if (Utils.isInTheEnd() && SlayerUtils.isInSlayer()) { + BeaconHighlighter.beaconPositions.remove(packet.getPos()); + if (packet.getState().isOf(Blocks.BEACON)) { + BeaconHighlighter.beaconPositions.add(packet.getPos()); + } + } + } + + @Inject(method = "method_37472", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V")) + private void skyblocker$onItemDestroy(int entityId, CallbackInfo ci) { + if (world.getEntityById(entityId) instanceof ItemEntity itemEntity) { + DungeonManager.onItemPickup(itemEntity); + } + } + + @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V", ordinal = 0)) + private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity) { + DungeonManager.onItemPickup(itemEntity); + return itemEntity; + } + + @WrapWithCondition(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false)) + private boolean skyblocker$cancelEntityPassengersWarning(Logger instance, String msg) { + return !Utils.isOnHypixel(); + } + + @WrapWithCondition(method = "onPlayerList", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$cancelPlayerListWarning(Logger instance, String format, Object arg1, Object arg2) { + return !Utils.isOnHypixel(); + } + + @Inject(method = "onPlaySound", at = @At("RETURN")) + private void skyblocker$onPlaySound(PlaySoundS2CPacket packet, CallbackInfo ci) { + FishingHelper.onSound(packet); + } + + @WrapWithCondition(method = "warnOnUnknownPayload", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$dropBadlionPacketWarnings(Logger instance, String message, Object identifier) { + return !(Utils.isOnHypixel() && ((Identifier) identifier).getNamespace().equals("badlion")); + } + + @WrapWithCondition(method = {"onScoreboardScoreUpdate", "onScoreboardScoreReset"}, at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$cancelUnknownScoreboardObjectiveWarnings(Logger instance, String message, Object objectiveName) { + return !Utils.isOnHypixel(); + } + + @WrapWithCondition(method = "onTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$cancelTeamWarning(Logger instance, String format, Object... arg) { + return !Utils.isOnHypixel(); + } + + @Inject(method = "onParticle", at = @At("RETURN")) + private void skyblocker$onParticle(ParticleS2CPacket packet, CallbackInfo ci) { + MythologicalRitual.onParticle(packet); + EnderNodes.onParticle(packet); + } + + @ModifyExpressionValue(method = "onEntityStatus", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/EntityStatusS2CPacket;getEntity(Lnet/minecraft/world/World;)Lnet/minecraft/entity/Entity;")) + private Entity skyblocker$onEntityDeath(Entity entity, @Local(argsOnly = true) EntityStatusS2CPacket packet) { + if (packet.getStatus() == EntityStatuses.PLAY_DEATH_SOUND_OR_ADD_PROJECTILE_HIT_PARTICLES) { + DungeonScore.handleEntityDeath(entity); + TheEnd.onEntityDeath(entity); + } + return entity; + } + + @Inject(method = "onEntityTrackerUpdate", at = @At("TAIL")) + private void skyblocker$onEntityTrackerUpdate(EntityTrackerUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { + if (!(entity instanceof ArmorStandEntity armorStandEntity)) return; + + EggFinder.checkIfEgg(armorStandEntity); + try { //Prevent packet handling fails if something goes wrong so that entity trackers still update, just without compact damage numbers CompactDamage.compactDamage(armorStandEntity); - } catch (Exception e) { - 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); - } - - @Inject(method = "onEntityTrackerUpdate", at = @At(value = "TAIL")) - private void skyblocker$onEntityTrackerUpdate(EntityTrackerUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { - EggFinder.checkIfEgg(entity); - } + } catch (Exception e) { + 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/CompactDamage.java b/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java index 2837364b30..8285a823c9 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/CompactDamage.java @@ -19,6 +19,7 @@ private CompactDamage() { } public static void compactDamage(ArmorStandEntity entity) { + if (!SkyblockerConfigManager.get().uiAndVisuals.compactDamage.enabled) return; if (!entity.isInvisible() || !entity.hasCustomName() || !entity.isCustomNameVisible()) return; Text customName = entity.getCustomName(); String customNameStringified = customName.getString(); 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 0e887fd276..ee16abd9e4 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -39,9 +39,13 @@ public static void init() { } public static void checkIfEgg(Entity entity) { + if (entity instanceof ArmorStandEntity armorStand) checkIfEgg(armorStand); + } + + public static void checkIfEgg(ArmorStandEntity armorStand) { if (!SkyblockerConfigManager.get().helpers.chocolateFactory.enableEggFinder) return; if (SkyblockTime.skyblockSeason.get() != SkyblockTime.Season.SPRING) return; - if (!(entity instanceof ArmorStandEntity armorStand) || armorStand.hasCustomName() || !armorStand.isInvisible() || !armorStand.shouldHideBasePlate()) return; + if (armorStand.hasCustomName() || !armorStand.isInvisible() || !armorStand.shouldHideBasePlate()) return; for (ItemStack itemStack : armorStand.getArmorItems()) { ItemUtils.getHeadTexture(itemStack).ifPresent(texture -> { for (EggType type : EggType.entries) { From 2a1321b8038e44770f9d0dbd6a876079a50f7159 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 01:22:00 +0300 Subject: [PATCH 22/52] Update src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java Co-authored-by: Julienraptor01 --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ed4d0f5e63..52704fe19d 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -88,7 +88,7 @@ private String concatenateLore(List lore) { } private Optional getCoach(ItemStack cpsItem, ItemStack coachItem) { - if (coachItem.isEmpty() || cpsItem.isEmpty() || coachItem.getItem() != Items.PLAYER_HEAD || cpsItem.getItem() != Items.COCOA_BEANS) return Optional.empty(); + if (!coachItem.isOf(Items.PLAYER_HEAD) || !cpsItem.isOf(Items.COCOA_BEANS)) return Optional.empty(); String cpsItemLore = getConcattedLore(cpsItem); Matcher cpsMatcher = CPS_PATTERN.matcher(cpsItemLore); From fc3c1c00f4621511be3e4ed5bc47f1a138501fe5 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 01:22:19 +0300 Subject: [PATCH 23/52] Update src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java Co-authored-by: Julienraptor01 --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 52704fe19d..2ba1a7e1fb 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -136,7 +136,7 @@ private Optional getRabbit(ItemStack item) { } private OptionalLong getTotalChocolate(ItemStack item) { - if (item.isEmpty() || item.getItem() != Items.PLAYER_HEAD) return OptionalLong.empty(); + if (!item.isOf(Items.PLAYER_HEAD)) return OptionalLong.empty(); Matcher matcher = TOTAL_CHOCOLATE_PATTERN.matcher(item.getName().getString()); if (!matcher.find()) return OptionalLong.empty(); return OptionalLong.of(Long.parseLong(matcher.group(1).replace(",", ""))); From c12dab27809e528161fe7090c4000f2b4db49942 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 01:22:57 +0300 Subject: [PATCH 24/52] Update src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java Co-authored-by: Julienraptor01 --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 1 - 1 file changed, 1 deletion(-) 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 2ba1a7e1fb..148956dbc9 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -5,7 +5,6 @@ import de.hysky.skyblocker.utils.render.gui.ColorHighlight; import de.hysky.skyblocker.utils.render.gui.ContainerSolver; import it.unimi.dsi.fastutil.ints.*; -import net.minecraft.client.MinecraftClient; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.text.Text; From cb4964c45a89983a889f0b32f72a3868d061a2d1 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 01:23:07 +0300 Subject: [PATCH 25/52] Update src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java Co-authored-by: Julienraptor01 --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 1 - 1 file changed, 1 deletion(-) 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 148956dbc9..e989ee28b4 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -8,7 +8,6 @@ import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.text.Text; -import org.apache.commons.math3.analysis.function.Min; import java.util.*; import java.util.List; From 4ba6a0f1fe89032152c8103b821c8451cfd52242 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 01:23:24 +0300 Subject: [PATCH 26/52] Update src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java Co-authored-by: Julienraptor01 --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 e989ee28b4..7c4bbbd69e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -37,10 +37,10 @@ protected List getColors(String[] groups, Int2ObjectMap cpsIncreaseFactors = new Int2ObjectLinkedOpenHashMap<>(6); for (int i = 29; i <= 33; i++) { // The 5 rabbits slots are in 29, 30, 31, 32 and 33. ItemStack item = slots.get(i); - if (item.getItem() != Items.PLAYER_HEAD || item.isEmpty()) continue; - - int finalI = i; //Java, pfft. - getRabbit(item).ifPresent(rabbit -> cpsIncreaseFactors.put(finalI, rabbit)); + if (item.isOf(Items.PLAYER_HEAD)) { + int finalI = i; //Java, pfft. + getRabbit(item).ifPresent(rabbit -> cpsIncreaseFactors.put(finalI, rabbit)); + } } //Coach is in slot 42 while the factory info item is in slot 45. From fff992ae9f09bc086fb128327790266f5f917446 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 01:23:56 +0300 Subject: [PATCH 27/52] Update src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java Co-authored-by: Julienraptor01 --- .../chocolatefactory/ChocolateFactorySolver.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 7c4bbbd69e..bceee64935 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -56,13 +56,13 @@ protected List getColors(String[] groups, Int2ObjectMap bestEntry = sorted.removeFirst(); - if (totalChocolate.isEmpty() || bestEntry.getValue().cost < totalChocolate.getAsLong()) return List.of(ColorHighlight.green(bestEntry.getIntKey())); + if (totalChocolate.isEmpty()) return List.of(ColorHighlight.yellow(bestEntry.getIntKey())); - for (Int2ObjectMap.Entry entry : sorted) { - if (entry.getValue().cost > totalChocolate.getAsLong()) continue; - return List.of(ColorHighlight.green(entry.getIntKey()), ColorHighlight.yellow(bestEntry.getIntKey())); - } - return List.of(ColorHighlight.green(bestEntry.getIntKey())); + if (bestEntry.getValue().cost <= totalChocolate.getAsLong()) return List.of(ColorHighlight.green(bestEntry.getIntKey())); + + for (Int2ObjectMap.Entry entry : sorted) if (entry.getValue().cost <= totalChocolate.getAsLong()) return List.of(ColorHighlight.green(entry.getIntKey()), ColorHighlight.yellow(bestEntry.getIntKey())); + + return List.of(ColorHighlight.yellow(bestEntry.getIntKey())); } /** From 58f2e28cd3cc8d83caf19167c14edd58ef81238d Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 03:00:49 +0300 Subject: [PATCH 28/52] Refactor ChocolateFactorySolver in preparation of tooltips --- .../ChocolateFactorySolver.java | 57 ++++++++++--------- 1 file changed, 31 insertions(+), 26 deletions(-) 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 bceee64935..2a6ad6b7e5 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -5,6 +5,7 @@ import de.hysky.skyblocker.utils.render.gui.ColorHighlight; import de.hysky.skyblocker.utils.render.gui.ContainerSolver; import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.text.Text; @@ -13,7 +14,6 @@ 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"); @@ -22,6 +22,8 @@ public class ChocolateFactorySolver extends ContainerSolver { private static final Pattern TOTAL_MULTIPLIER_PATTERN = Pattern.compile("Total Multiplier: ([\\d.]+)x"); private static final Pattern MULTIPLIER_INCREASE_PATTERN = Pattern.compile("\\+([\\d.]+)x Chocolate per second"); private static final Pattern TOTAL_CHOCOLATE_PATTERN = Pattern.compile("([\\d,]+) Chocolate"); + private static final ObjectArrayList cpsIncreaseFactors = new ObjectArrayList<>(6); + private static long totalChocolate = -1L; public ChocolateFactorySolver() { super("^Chocolate Factory$"); @@ -34,35 +36,38 @@ protected boolean isEnabled() { @Override protected List getColors(String[] groups, Int2ObjectMap slots) { - final Int2ObjectMap cpsIncreaseFactors = new Int2ObjectLinkedOpenHashMap<>(6); + updateFactoryInfo(slots); + + if (totalChocolate <= 0 || cpsIncreaseFactors.isEmpty()) return List.of(); //Something went wrong or there's nothing we can afford. + Rabbit bestRabbit = cpsIncreaseFactors.getFirst(); + + if (bestRabbit.cost <= totalChocolate) return List.of(ColorHighlight.green(bestRabbit.slot)); + + for (Rabbit rabbit : cpsIncreaseFactors.subList(1, cpsIncreaseFactors.size())) { + if (rabbit.cost <= totalChocolate) { + return List.of(ColorHighlight.green(rabbit.slot), ColorHighlight.yellow(bestRabbit.slot)); + } + } + + return List.of(ColorHighlight.yellow(bestRabbit.slot)); + } + + private void updateFactoryInfo(Int2ObjectMap slots) { + cpsIncreaseFactors.clear(); + for (int i = 29; i <= 33; i++) { // The 5 rabbits slots are in 29, 30, 31, 32 and 33. ItemStack item = slots.get(i); if (item.isOf(Items.PLAYER_HEAD)) { - int finalI = i; //Java, pfft. - getRabbit(item).ifPresent(rabbit -> cpsIncreaseFactors.put(finalI, rabbit)); + getRabbit(item, i).ifPresent(cpsIncreaseFactors::add); } } //Coach is in slot 42 while the factory info item is in slot 45. - getCoach(slots.get(45), slots.get(42)).ifPresent(coach -> cpsIncreaseFactors.put(42, coach)); - if (cpsIncreaseFactors.isEmpty()) return List.of(); //Something went wrong. - - OptionalLong totalChocolate = getTotalChocolate(slots.get(13)); - - List> sorted = cpsIncreaseFactors.int2ObjectEntrySet() - .stream() //Compare cost/cpsIncrease rather than cpsIncrease/cost to avoid getting close to 0 and losing precision. - .sorted(Comparator.comparingDouble(entry -> entry.getValue().cost() / entry.getValue().cpsIncrease())) //Ascending order, lower = better - .dropWhile(entry -> entry.getValue().cost == 0) - .collect(Collectors.toCollection(LinkedList::new)); - - Int2ObjectMap.Entry bestEntry = sorted.removeFirst(); - if (totalChocolate.isEmpty()) return List.of(ColorHighlight.yellow(bestEntry.getIntKey())); - - if (bestEntry.getValue().cost <= totalChocolate.getAsLong()) return List.of(ColorHighlight.green(bestEntry.getIntKey())); - - for (Int2ObjectMap.Entry entry : sorted) if (entry.getValue().cost <= totalChocolate.getAsLong()) return List.of(ColorHighlight.green(entry.getIntKey()), ColorHighlight.yellow(bestEntry.getIntKey())); + getCoach(slots.get(45), slots.get(42)).ifPresent(cpsIncreaseFactors::add); + getTotalChocolate(slots.get(13)).ifPresent(l -> totalChocolate = l); - return List.of(ColorHighlight.yellow(bestEntry.getIntKey())); + //Compare cost/cpsIncrease rather than cpsIncrease/cost to avoid getting close to 0 and losing precision. + cpsIncreaseFactors.sort(Comparator.comparingDouble(rabbit -> rabbit.cost() / rabbit.cpsIncrease())); //Ascending order, lower = better } /** @@ -113,10 +118,10 @@ private Optional getCoach(ItemStack cpsItem, ItemStack coachItem) { OptionalInt cost = getIntFromMatcher(costMatcher, multiplierIncreaseMatcher.end()); //Cost comes after the multiplier line if (cost.isEmpty()) return Optional.empty(); - return Optional.of(new Rabbit(currentCps.getAsDouble() / totalMultiplier.getAsDouble() * (nextCpsMultiplier.getAsDouble() - currentCpsMultiplier.getAsDouble()), cost.getAsInt())); + return Optional.of(new Rabbit(currentCps.getAsDouble() / totalMultiplier.getAsDouble() * (nextCpsMultiplier.getAsDouble() - currentCpsMultiplier.getAsDouble()), cost.getAsInt(), 42)); } - private Optional getRabbit(ItemStack item) { + private Optional getRabbit(ItemStack item, int slot) { String lore = getConcattedLore(item); Matcher cpsMatcher = CPS_INCREASE_PATTERN.matcher(lore); OptionalInt currentCps = getIntFromMatcher(cpsMatcher); @@ -130,7 +135,7 @@ private Optional getRabbit(ItemStack item) { Matcher costMatcher = COST_PATTERN.matcher(lore); OptionalInt cost = getIntFromMatcher(costMatcher, cpsMatcher.end()); //Cost comes after the cps line if (cost.isEmpty()) return Optional.empty(); - return Optional.of(new Rabbit(nextCps.getAsInt() - currentCps.getAsInt(), cost.getAsInt())); + return Optional.of(new Rabbit(nextCps.getAsInt() - currentCps.getAsInt(), cost.getAsInt(), slot)); } private OptionalLong getTotalChocolate(ItemStack item) { @@ -158,6 +163,6 @@ private OptionalDouble getDoubleFromMatcher(Matcher matcher, int startingIndex) return OptionalDouble.of(Double.parseDouble(matcher.group(1).replace(",", ""))); } - private record Rabbit(double cpsIncrease, int cost) { + private record Rabbit(double cpsIncrease, int cost, int slot) { } } From 5fd12a306d35cebbf990015b05668ddee95b08e4 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 03:45:38 +0300 Subject: [PATCH 29/52] Fix unemployed rabbits crashing the game --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 2a6ad6b7e5..6606f69c28 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -115,7 +115,7 @@ private Optional getCoach(ItemStack cpsItem, ItemStack coachItem) { } Matcher costMatcher = COST_PATTERN.matcher(coachLore); - OptionalInt cost = getIntFromMatcher(costMatcher, multiplierIncreaseMatcher.end()); //Cost comes after the multiplier line + OptionalInt cost = getIntFromMatcher(costMatcher, multiplierIncreaseMatcher.hasMatch() ? multiplierIncreaseMatcher.end() : 0); //Cost comes after the multiplier line if (cost.isEmpty()) return Optional.empty(); return Optional.of(new Rabbit(currentCps.getAsDouble() / totalMultiplier.getAsDouble() * (nextCpsMultiplier.getAsDouble() - currentCpsMultiplier.getAsDouble()), cost.getAsInt(), 42)); @@ -133,7 +133,7 @@ private Optional getRabbit(ItemStack item, int slot) { } Matcher costMatcher = COST_PATTERN.matcher(lore); - OptionalInt cost = getIntFromMatcher(costMatcher, cpsMatcher.end()); //Cost comes after the cps line + OptionalInt cost = getIntFromMatcher(costMatcher, cpsMatcher.hasMatch() ? cpsMatcher.end() : 0); //Cost comes after the cps line if (cost.isEmpty()) return Optional.empty(); return Optional.of(new Rabbit(nextCps.getAsInt() - currentCps.getAsInt(), cost.getAsInt(), slot)); } From 30ecf3e856d4d10a4dfe1b0050ec71e70e4647a9 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 05:21:13 +0300 Subject: [PATCH 30/52] Add tooltips for the chocolate factory Also fix unemployed coach crashing the game --- .../ChocolateFactorySolver.java | 165 ++++++++++++------ .../skyblock/item/tooltip/ItemTooltip.java | 6 +- .../de/hysky/skyblocker/utils/RegexUtils.java | 55 ++++++ 3 files changed, 170 insertions(+), 56 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/utils/RegexUtils.java 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 6606f69c28..b0bf3a5693 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -1,15 +1,25 @@ package de.hysky.skyblocker.skyblock.chocolatefactory; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.RegexUtils; import de.hysky.skyblocker.utils.render.gui.ColorHighlight; import de.hysky.skyblocker.utils.render.gui.ContainerSolver; import it.unimi.dsi.fastutil.ints.*; import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.GenericContainerScreen; +import net.minecraft.client.item.TooltipType; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; +import java.text.DecimalFormat; import java.util.*; import java.util.List; import java.util.regex.Matcher; @@ -21,12 +31,18 @@ public class ChocolateFactorySolver extends ContainerSolver { private static final Pattern COST_PATTERN = Pattern.compile("Cost ([\\d,]+) Chocolate"); private static final Pattern TOTAL_MULTIPLIER_PATTERN = Pattern.compile("Total Multiplier: ([\\d.]+)x"); private static final Pattern MULTIPLIER_INCREASE_PATTERN = Pattern.compile("\\+([\\d.]+)x Chocolate per second"); - private static final Pattern TOTAL_CHOCOLATE_PATTERN = Pattern.compile("([\\d,]+) Chocolate"); + private static final Pattern CHOCOLATE_PATTERN = Pattern.compile("^([\\d,]+) Chocolate$"); private static final ObjectArrayList cpsIncreaseFactors = new ObjectArrayList<>(6); private static long totalChocolate = -1L; + private static double totalCps = -1.0; + private static double totalCpsMultiplier = -1.0; + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.#"); + private static ItemStack bestUpgrade = null; + private static ItemStack bestAffordableUpgrade = null; public ChocolateFactorySolver() { super("^Chocolate Factory$"); + ItemTooltipCallback.EVENT.register(ChocolateFactorySolver::handleTooltip); } @Override @@ -40,11 +56,12 @@ protected List getColors(String[] groups, Int2ObjectMap getColors(String[] groups, Int2ObjectMap slots) { + private static void updateFactoryInfo(Int2ObjectMap slots) { cpsIncreaseFactors.clear(); for (int i = 29; i <= 33; i++) { // The 5 rabbits slots are in 29, 30, 31, 32 and 33. @@ -62,18 +79,89 @@ private void updateFactoryInfo(Int2ObjectMap slots) { } } - //Coach is in slot 42 while the factory info item is in slot 45. - getCoach(slots.get(45), slots.get(42)).ifPresent(cpsIncreaseFactors::add); - getTotalChocolate(slots.get(13)).ifPresent(l -> totalChocolate = l); + //Coach is in slot 42 + getCoach(slots.get(42)).ifPresent(cpsIncreaseFactors::add); + RegexUtils.getLongFromMatcher(CHOCOLATE_PATTERN.matcher(slots.get(13).getName().getString())).ifPresent(l -> totalChocolate = l); + + //Cps item (cocoa bean) is in slot 45 + String cpsItemLore = getConcatenatedLore(slots.get(45)); + Matcher cpsMatcher = CPS_PATTERN.matcher(cpsItemLore); + RegexUtils.getDoubleFromMatcher(cpsMatcher).ifPresent(d -> totalCps = d); + + Matcher multiplierMatcher = TOTAL_MULTIPLIER_PATTERN.matcher(cpsItemLore); + RegexUtils.getDoubleFromMatcher(multiplierMatcher, cpsMatcher.hasMatch() ? cpsMatcher.end() : 0).ifPresent(d -> totalCpsMultiplier = d); //Compare cost/cpsIncrease rather than cpsIncrease/cost to avoid getting close to 0 and losing precision. cpsIncreaseFactors.sort(Comparator.comparingDouble(rabbit -> rabbit.cost() / rabbit.cpsIncrease())); //Ascending order, lower = better } + private static void handleTooltip(ItemStack stack, Item.TooltipContext tooltipContext, TooltipType tooltipType, List lines) { + if (!SkyblockerConfigManager.get().helpers.chocolateFactory.enableChocolateFactoryHelper) return; + if (!(MinecraftClient.getInstance().currentScreen instanceof GenericContainerScreen screen) || !screen.getTitle().getString().equals("Chocolate Factory") ) return; + + String lore = concatenateLore(lines); + Matcher costMatcher = COST_PATTERN.matcher(lore); + OptionalLong cost = RegexUtils.getLongFromMatcher(costMatcher); + if (cost.isEmpty() || totalChocolate == -1L || totalCps == -1.0) return; + + lines.add(ItemTooltip.createSmoothLine()); + + lines.add(Text.literal("") + .append(Text.literal("Time until upgrade: ").formatted(Formatting.GRAY)) + .append(formatTime((cost.getAsLong() - totalChocolate) / totalCps))); + + if (cpsIncreaseFactors.isEmpty()) return; + + for (int j = 0; j < cpsIncreaseFactors.size(); j++) { + Rabbit rabbit = cpsIncreaseFactors.get(j); + if (rabbit.itemStack != stack) continue; + + lines.add(Text.literal("") + .append(Text.literal("CPS Increase: ").formatted(Formatting.GRAY)) + .append(Text.literal(DECIMAL_FORMAT.format(rabbit.cpsIncrease)).formatted(Formatting.GOLD))); + + lines.add(Text.literal("") + .append(Text.literal("Cost per CPS: ").formatted(Formatting.GRAY)) + .append(Text.literal(DECIMAL_FORMAT.format(rabbit.cost / rabbit.cpsIncrease)).formatted(Formatting.GOLD))); + + if (rabbit.itemStack == bestUpgrade) { + if (cost.getAsLong() <= totalChocolate) { + lines.add(Text.literal("Best upgrade").formatted(Formatting.GREEN)); + } else { + lines.add(Text.literal("Best upgrade, can't afford").formatted(Formatting.YELLOW)); + } + } else if (rabbit.itemStack == bestAffordableUpgrade && cost.getAsLong() <= totalChocolate) { + lines.add(Text.literal("Best upgrade you can afford").formatted(Formatting.GREEN)); + } + } + } + + private static MutableText formatTime(double seconds) { + if (seconds <= 0) return Text.literal("Now").formatted(Formatting.GREEN); + + StringBuilder builder = new StringBuilder(); + if (seconds >= 86400) { + builder.append((int) (seconds / 86400)).append("d "); + seconds %= 86400; + } + if (seconds >= 3600) { + builder.append((int) (seconds / 3600)).append("h "); + seconds %= 3600; + } + if (seconds >= 60) { + builder.append((int) (seconds / 60)).append("m "); + seconds %= 60; + } + if (seconds >= 1) { + builder.append((int) seconds).append("s"); + } + return Text.literal(builder.toString()).formatted(Formatting.GOLD); + } + /** * Utility method. */ - private String getConcattedLore(ItemStack item) { + private static String getConcatenatedLore(ItemStack item) { return concatenateLore(ItemUtils.getLore(item)); } @@ -81,7 +169,7 @@ private String getConcattedLore(ItemStack item) { * Concatenates the lore of an item into one string. * This is useful in case some pattern we're looking for is split into multiple lines, which would make it harder to regex. */ - private String concatenateLore(List lore) { + private static String concatenateLore(List lore) { StringBuilder stringBuilder = new StringBuilder(); for (int i = 0; i < lore.size(); i++) { stringBuilder.append(lore.get(i).getString()); @@ -90,25 +178,17 @@ private String concatenateLore(List lore) { return stringBuilder.toString(); } - private Optional getCoach(ItemStack cpsItem, ItemStack coachItem) { - if (!coachItem.isOf(Items.PLAYER_HEAD) || !cpsItem.isOf(Items.COCOA_BEANS)) return Optional.empty(); - String cpsItemLore = getConcattedLore(cpsItem); - - Matcher cpsMatcher = CPS_PATTERN.matcher(cpsItemLore); - OptionalDouble currentCps = getDoubleFromMatcher(cpsMatcher); - if (currentCps.isEmpty()) return Optional.empty(); - - Matcher multiplierMatcher = TOTAL_MULTIPLIER_PATTERN.matcher(cpsItemLore); - OptionalDouble totalMultiplier = getDoubleFromMatcher(multiplierMatcher, cpsMatcher.end()); - if (totalMultiplier.isEmpty()) return Optional.empty(); + private static Optional getCoach(ItemStack coachItem) { + if (!coachItem.isOf(Items.PLAYER_HEAD)) return Optional.empty(); + String coachLore = getConcatenatedLore(coachItem); - String coachLore = getConcattedLore(coachItem); + if (totalCpsMultiplier == -1.0) return Optional.empty(); //We need the total multiplier to calculate the increase in cps. Matcher multiplierIncreaseMatcher = MULTIPLIER_INCREASE_PATTERN.matcher(coachLore); - OptionalDouble currentCpsMultiplier = getDoubleFromMatcher(multiplierIncreaseMatcher); + OptionalDouble currentCpsMultiplier = RegexUtils.getDoubleFromMatcher(multiplierIncreaseMatcher); if (currentCpsMultiplier.isEmpty()) return Optional.empty(); - OptionalDouble nextCpsMultiplier = getDoubleFromMatcher(multiplierIncreaseMatcher); + OptionalDouble nextCpsMultiplier = RegexUtils.getDoubleFromMatcher(multiplierIncreaseMatcher); if (nextCpsMultiplier.isEmpty()) { //This means that the coach isn't hired yet. nextCpsMultiplier = currentCpsMultiplier; //So the first instance of the multiplier is actually the amount we'll get upon upgrading. currentCpsMultiplier = OptionalDouble.of(0.0); //And so, we can re-assign values to the variables to make the calculation more readable. @@ -118,51 +198,26 @@ private Optional getCoach(ItemStack cpsItem, ItemStack coachItem) { OptionalInt cost = getIntFromMatcher(costMatcher, multiplierIncreaseMatcher.hasMatch() ? multiplierIncreaseMatcher.end() : 0); //Cost comes after the multiplier line if (cost.isEmpty()) return Optional.empty(); - return Optional.of(new Rabbit(currentCps.getAsDouble() / totalMultiplier.getAsDouble() * (nextCpsMultiplier.getAsDouble() - currentCpsMultiplier.getAsDouble()), cost.getAsInt(), 42)); + return Optional.of(new Rabbit(totalCps / totalCpsMultiplier * (nextCpsMultiplier.getAsDouble() - currentCpsMultiplier.getAsDouble()), cost.getAsInt(), 42, coachItem)); } - private Optional getRabbit(ItemStack item, int slot) { - String lore = getConcattedLore(item); + private static Optional getRabbit(ItemStack item, int slot) { + String lore = getConcatenatedLore(item); Matcher cpsMatcher = CPS_INCREASE_PATTERN.matcher(lore); - OptionalInt currentCps = getIntFromMatcher(cpsMatcher); + OptionalInt currentCps = RegexUtils.getIntFromMatcher(cpsMatcher); if (currentCps.isEmpty()) return Optional.empty(); - OptionalInt nextCps = getIntFromMatcher(cpsMatcher); + OptionalInt nextCps = RegexUtils.getIntFromMatcher(cpsMatcher); if (nextCps.isEmpty()) { nextCps = currentCps; //This means that the rabbit isn't hired yet. currentCps = OptionalInt.of(0); //So the first instance of the cps is actually the amount we'll get upon hiring. } Matcher costMatcher = COST_PATTERN.matcher(lore); - OptionalInt cost = getIntFromMatcher(costMatcher, cpsMatcher.hasMatch() ? cpsMatcher.end() : 0); //Cost comes after the cps line + OptionalInt cost = RegexUtils.getIntFromMatcher(costMatcher, cpsMatcher.hasMatch() ? cpsMatcher.end() : 0); //Cost comes after the cps line if (cost.isEmpty()) return Optional.empty(); - return Optional.of(new Rabbit(nextCps.getAsInt() - currentCps.getAsInt(), cost.getAsInt(), slot)); - } - - private OptionalLong getTotalChocolate(ItemStack item) { - if (!item.isOf(Items.PLAYER_HEAD)) return OptionalLong.empty(); - Matcher matcher = TOTAL_CHOCOLATE_PATTERN.matcher(item.getName().getString()); - if (!matcher.find()) return OptionalLong.empty(); - return OptionalLong.of(Long.parseLong(matcher.group(1).replace(",", ""))); - } - - private OptionalInt getIntFromMatcher(Matcher matcher) { - return getIntFromMatcher(matcher, matcher.hasMatch() ? matcher.end() : 0); - } - - private OptionalInt getIntFromMatcher(Matcher matcher, int startingIndex) { - if (!matcher.find(startingIndex)) return OptionalInt.empty(); - return OptionalInt.of(Integer.parseInt(matcher.group(1).replace(",", ""))); - } - - private OptionalDouble getDoubleFromMatcher(Matcher matcher) { - return getDoubleFromMatcher(matcher, matcher.hasMatch() ? matcher.end() : 0); - } - - private OptionalDouble getDoubleFromMatcher(Matcher matcher, int startingIndex) { - if (!matcher.find(startingIndex)) return OptionalDouble.empty(); - return OptionalDouble.of(Double.parseDouble(matcher.group(1).replace(",", ""))); + return Optional.of(new Rabbit(nextCps.getAsInt() - currentCps.getAsInt(), cost.getAsInt(), slot, item)); } - private record Rabbit(double cpsIncrease, int cost, int slot) { + private record Rabbit(double cpsIncrease, int cost, int slot, ItemStack itemStack) { } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java index c6caaf41fb..6e269790d2 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java @@ -398,11 +398,15 @@ private static void smoothenLines(List lines) { for (int i = 0; i < lines.size(); i++) { Text line = lines.get(i); if (line.getString().equals("-----------------")) { - lines.set(i, Text.literal(" ").formatted(Formatting.DARK_GRAY, Formatting.STRIKETHROUGH, Formatting.BOLD)); + lines.set(i, createSmoothLine()); } } } + public static Text createSmoothLine() { + return Text.literal(" ").formatted(Formatting.DARK_GRAY, Formatting.STRIKETHROUGH, Formatting.BOLD); + } + // If these options is true beforehand, the client will get first data of these options while loading. // After then, it will only fetch the data if it is on Skyblock. public static int minute = 0; diff --git a/src/main/java/de/hysky/skyblocker/utils/RegexUtils.java b/src/main/java/de/hysky/skyblocker/utils/RegexUtils.java new file mode 100644 index 0000000000..5b91a80b3c --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/RegexUtils.java @@ -0,0 +1,55 @@ +package de.hysky.skyblocker.utils; + +import java.util.OptionalDouble; +import java.util.OptionalInt; +import java.util.OptionalLong; +import java.util.regex.Matcher; + +public class RegexUtils { + /** + * @return An OptionalLong of the first group in the matcher, or an empty OptionalLong if the matcher doesn't find anything. + */ + public static OptionalLong getLongFromMatcher(Matcher matcher) { + return getLongFromMatcher(matcher, matcher.hasMatch() ? matcher.end() : 0); + } + + /** + * @return An OptionalLong of the first group in the matcher, or an empty OptionalLong if the matcher doesn't find anything. + */ + public static OptionalLong getLongFromMatcher(Matcher matcher, int startingIndex) { + if (!matcher.find(startingIndex)) return OptionalLong.empty(); + return OptionalLong.of(Long.parseLong(matcher.group(1).replace(",", ""))); + } + + /** + * @return An OptionalInt of the first group in the matcher, or an empty OptionalInt if the matcher doesn't find anything. + */ + public static OptionalInt getIntFromMatcher(Matcher matcher) { + return getIntFromMatcher(matcher, matcher.hasMatch() ? matcher.end() : 0); + } + + /** + * @return An OptionalInt of the first group in the matcher, or an empty OptionalInt if the matcher doesn't find anything. + */ + public static OptionalInt getIntFromMatcher(Matcher matcher, int startingIndex) { + if (!matcher.find(startingIndex)) return OptionalInt.empty(); + return OptionalInt.of(Integer.parseInt(matcher.group(1).replace(",", ""))); + } + + /** + * @return An OptionalDouble of the first group in the matcher, or an empty OptionalDouble if the matcher doesn't find anything. + * @implNote Assumes the decimal separator is `.` + */ + public static OptionalDouble getDoubleFromMatcher(Matcher matcher) { + return getDoubleFromMatcher(matcher, matcher.hasMatch() ? matcher.end() : 0); + } + + /** + * @return An OptionalDouble of the first group in the matcher, or an empty OptionalDouble if the matcher doesn't find anything. + * @implNote Assumes the decimal separator is `.` + */ + public static OptionalDouble getDoubleFromMatcher(Matcher matcher, int startingIndex) { + if (!matcher.find(startingIndex)) return OptionalDouble.empty(); + return OptionalDouble.of(Double.parseDouble(matcher.group(1).replace(",", ""))); + } +} From 710680242b375b8835d4550af4ec021225dde362 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 05:24:41 +0300 Subject: [PATCH 31/52] Fix missing `RegexUtils.` --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 b0bf3a5693..fb7d4b8880 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -195,7 +195,7 @@ private static Optional getCoach(ItemStack coachItem) { } Matcher costMatcher = COST_PATTERN.matcher(coachLore); - OptionalInt cost = getIntFromMatcher(costMatcher, multiplierIncreaseMatcher.hasMatch() ? multiplierIncreaseMatcher.end() : 0); //Cost comes after the multiplier line + OptionalInt cost = RegexUtils.getIntFromMatcher(costMatcher, multiplierIncreaseMatcher.hasMatch() ? multiplierIncreaseMatcher.end() : 0); //Cost comes after the multiplier line if (cost.isEmpty()) return Optional.empty(); return Optional.of(new Rabbit(totalCps / totalCpsMultiplier * (nextCpsMultiplier.getAsDouble() - currentCpsMultiplier.getAsDouble()), cost.getAsInt(), 42, coachItem)); From 8f060cf5e46d54d25841a7b5eef09eaef5d31861 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 05:28:10 +0300 Subject: [PATCH 32/52] Add the meaning of highlight colors to the config description --- src/main/resources/assets/skyblocker/lang/en_us.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 6b309d3953..99ccdba091 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -275,7 +275,7 @@ "skyblocker.config.helpers.chocolateFactory": "Chocolate Factory", "skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper": "Enable Chocolate Factory Helper", - "skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper.@Tooltip": "Highlights the best upgrade when enabled.", + "skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper.@Tooltip": "Highlights the best upgrade when enabled. \n\nThe best upgrade is marked as green, but if you can't afford it, it's marked as yellow while marking the next best upgrade that you can afford as green.", "skyblocker.config.helpers.chocolateFactory.enableEggFinder": "Enable Egg Finder", "skyblocker.config.helpers.chocolateFactory.enableEggFinder.@Tooltip": "Highlights eggs from Hoppity's Hunt.", "skyblocker.config.helpers.chocolateFactory.sendEggFoundMessages": "Send Egg Found Messages", From 40096023d7c8cd80597d7aa27c2c601f8365c7d5 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Wed, 15 May 2024 18:42:01 +0300 Subject: [PATCH 33/52] Add thousand separators to the CPS format --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 fb7d4b8880..6defcab4e1 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -36,7 +36,7 @@ public class ChocolateFactorySolver extends ContainerSolver { private static long totalChocolate = -1L; private static double totalCps = -1.0; private static double totalCpsMultiplier = -1.0; - private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.#"); + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,###.#"); private static ItemStack bestUpgrade = null; private static ItemStack bestAffordableUpgrade = null; @@ -112,8 +112,7 @@ private static void handleTooltip(ItemStack stack, Item.TooltipContext tooltipCo if (cpsIncreaseFactors.isEmpty()) return; - for (int j = 0; j < cpsIncreaseFactors.size(); j++) { - Rabbit rabbit = cpsIncreaseFactors.get(j); + for (Rabbit rabbit : cpsIncreaseFactors) { if (rabbit.itemStack != stack) continue; lines.add(Text.literal("") From 4b477d0efdbb54460c3e58e515450565c350042a Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 16 May 2024 14:18:10 +0300 Subject: [PATCH 34/52] Revert ItemUtils.getHeadTexture change and add ItemUtils.getHeadTextureOptional instead --- src/main/java/de/hysky/skyblocker/debug/Debug.java | 4 +--- .../skyblock/chocolatefactory/EggFinder.java | 2 +- .../hysky/skyblocker/skyblock/entity/MobGlow.java | 2 +- .../skyblocker/skyblock/garden/VisitorHelper.java | 4 +++- .../java/de/hysky/skyblocker/utils/ItemUtils.java | 14 ++++++++++---- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/debug/Debug.java b/src/main/java/de/hysky/skyblocker/debug/Debug.java index 038e7a5ae3..d642ca5bc2 100644 --- a/src/main/java/de/hysky/skyblocker/debug/Debug.java +++ b/src/main/java/de/hysky/skyblocker/debug/Debug.java @@ -83,9 +83,7 @@ private static LiteralArgumentBuilder dumpArmorStandH Iterable equippedItems = armorStand.getEquippedItems(); for (ItemStack stack : equippedItems) { - ItemUtils.getHeadTexture(stack).ifPresent(texture -> { - context.getSource().sendFeedback(Text.of(texture)); - }); + ItemUtils.getHeadTextureOptional(stack).ifPresent(texture -> context.getSource().sendFeedback(Text.of(texture))); } } 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 ee16abd9e4..2f027f8117 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -47,7 +47,7 @@ public static void checkIfEgg(ArmorStandEntity armorStand) { if (SkyblockTime.skyblockSeason.get() != SkyblockTime.Season.SPRING) return; if (armorStand.hasCustomName() || !armorStand.isInvisible() || !armorStand.shouldHideBasePlate()) return; for (ItemStack itemStack : armorStand.getArmorItems()) { - ItemUtils.getHeadTexture(itemStack).ifPresent(texture -> { + ItemUtils.getHeadTextureOptional(itemStack).ifPresent(texture -> { for (EggType type : EggType.entries) { if (texture.equals(type.texture) && type.egg.getValue() == null) { handleFoundEgg(armorStand, type); 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 df9a32c608..d6f9410b90 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 @@ private static boolean isNukekubiHead(ArmorStandEntity entity) { // 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).orElse(""); + String texture = ItemUtils.getHeadTexture(armorItem); 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 0f349a4f71..682933f4e4 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/garden/VisitorHelper.java @@ -94,7 +94,9 @@ private static void processVisitorItem(String visitorName, ScreenHandler handler } private static @Nullable String getTextureOrNull(ItemStack stack) { - return ItemUtils.getHeadTexture(stack).orElse(null); + String texture = ItemUtils.getHeadTexture(stack); + + return texture.isEmpty() ? null : texture; } private static void processLore(String visitorName, @Nullable String visitorTexture, List loreList) { diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java index 6b850b3ba9..6f98efa6b7 100644 --- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java @@ -203,16 +203,22 @@ public static PropertyMap propertyMapWithTexture(String textureValue) { return Codecs.GAME_PROFILE_PROPERTY_MAP.parse(JsonOps.INSTANCE, JsonParser.parseString("[{\"name\":\"textures\",\"value\":\"" + textureValue + "\"}]")).getOrThrow(); } - public static Optional getHeadTexture(ItemStack stack) { - if (!stack.isOf(Items.PLAYER_HEAD) || !stack.contains(DataComponentTypes.PROFILE)) return Optional.empty(); + public static String getHeadTexture(ItemStack stack) { + if (!stack.isOf(Items.PLAYER_HEAD) || !stack.contains(DataComponentTypes.PROFILE)) return ""; Iterator iterator = stack.get(DataComponentTypes.PROFILE) .properties() .get("textures") .iterator(); - if (!iterator.hasNext()) return Optional.empty(); - return Optional.of(iterator.next().value()); + if (!iterator.hasNext()) return ""; + return iterator.next().value(); + } + + public static Optional getHeadTextureOptional(ItemStack stack) { + String texture = getHeadTexture(stack); + if (texture == null) return Optional.empty(); + return Optional.of(texture); } public static ItemStack getSkyblockerStack() { From fa371138fc588256bba0751d2f76e5b8b57940f2 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 16 May 2024 14:45:36 +0300 Subject: [PATCH 35/52] Attempt to fix decimal format depending on the user's locale --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 6defcab4e1..0e8918deb2 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -20,6 +20,7 @@ import net.minecraft.util.Formatting; import java.text.DecimalFormat; +import java.text.DecimalFormatSymbols; import java.util.*; import java.util.List; import java.util.regex.Matcher; @@ -36,7 +37,7 @@ public class ChocolateFactorySolver extends ContainerSolver { private static long totalChocolate = -1L; private static double totalCps = -1.0; private static double totalCpsMultiplier = -1.0; - private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,###.#"); + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,###.#", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); private static ItemStack bestUpgrade = null; private static ItemStack bestAffordableUpgrade = null; From 8fed85f6a02054eaecbdcbb8e596632a899c1cae Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 17 May 2024 06:32:54 +0300 Subject: [PATCH 36/52] Do a bunch of things that I can't split into separate commits - Added prestige highlight, time tower and prestige tooltips - Refactored handleTooltip to be more modular. --- .../ChocolateFactorySolver.java | 151 ++++++++++++++++-- 1 file changed, 135 insertions(+), 16 deletions(-) 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 0e8918deb2..a6ec144e40 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -18,6 +18,8 @@ import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; @@ -33,10 +35,14 @@ public class ChocolateFactorySolver extends ContainerSolver { private static final Pattern TOTAL_MULTIPLIER_PATTERN = Pattern.compile("Total Multiplier: ([\\d.]+)x"); private static final Pattern MULTIPLIER_INCREASE_PATTERN = Pattern.compile("\\+([\\d.]+)x Chocolate per second"); private static final Pattern CHOCOLATE_PATTERN = Pattern.compile("^([\\d,]+) Chocolate$"); + private static final Pattern PRESTIGE_REQUIREMENT_PATTERN = Pattern.compile("Chocolate this Prestige: ([\\d,]+) +Requires (\\S+) Chocolate this Prestige!"); private static final ObjectArrayList cpsIncreaseFactors = new ObjectArrayList<>(6); private static long totalChocolate = -1L; private static double totalCps = -1.0; private static double totalCpsMultiplier = -1.0; + private static long requiredUntilNextPrestige = -1L; + private static double timeTowerMultiplier = -1.0; + private static boolean isTimeTowerActive = false; private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("#,###.#", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); private static ItemStack bestUpgrade = null; private static ItemStack bestAffordableUpgrade = null; @@ -54,20 +60,28 @@ protected boolean isEnabled() { @Override protected List getColors(String[] groups, Int2ObjectMap slots) { updateFactoryInfo(slots); + List highlights = new ArrayList<>(); - if (totalChocolate <= 0 || cpsIncreaseFactors.isEmpty()) return List.of(); //Something went wrong or there's nothing we can afford. + getPrestigeHighlight(slots.get(28)).ifPresent(highlights::add); + + if (totalChocolate <= 0 || cpsIncreaseFactors.isEmpty()) return highlights; //Something went wrong or there's nothing we can afford. Rabbit bestRabbit = cpsIncreaseFactors.getFirst(); bestUpgrade = bestRabbit.itemStack; - if (bestRabbit.cost <= totalChocolate) return List.of(ColorHighlight.green(bestRabbit.slot)); + if (bestRabbit.cost <= totalChocolate) { + highlights.add(ColorHighlight.green(bestRabbit.slot)); + return highlights; + } + highlights.add(ColorHighlight.yellow(bestRabbit.slot)); for (Rabbit rabbit : cpsIncreaseFactors.subList(1, cpsIncreaseFactors.size())) { if (rabbit.cost <= totalChocolate) { bestAffordableUpgrade = rabbit.itemStack; - return List.of(ColorHighlight.green(rabbit.slot), ColorHighlight.yellow(bestRabbit.slot)); + highlights.add(ColorHighlight.green(rabbit.slot)); + break; } } - return List.of(ColorHighlight.yellow(bestRabbit.slot)); + return highlights; } private static void updateFactoryInfo(Int2ObjectMap slots) { @@ -82,58 +96,132 @@ private static void updateFactoryInfo(Int2ObjectMap slots) { //Coach is in slot 42 getCoach(slots.get(42)).ifPresent(cpsIncreaseFactors::add); + + //The clickable chocolate is in slot 13, holds the total chocolate RegexUtils.getLongFromMatcher(CHOCOLATE_PATTERN.matcher(slots.get(13).getName().getString())).ifPresent(l -> totalChocolate = l); //Cps item (cocoa bean) is in slot 45 String cpsItemLore = getConcatenatedLore(slots.get(45)); Matcher cpsMatcher = CPS_PATTERN.matcher(cpsItemLore); RegexUtils.getDoubleFromMatcher(cpsMatcher).ifPresent(d -> totalCps = d); - Matcher multiplierMatcher = TOTAL_MULTIPLIER_PATTERN.matcher(cpsItemLore); RegexUtils.getDoubleFromMatcher(multiplierMatcher, cpsMatcher.hasMatch() ? cpsMatcher.end() : 0).ifPresent(d -> totalCpsMultiplier = d); + //Prestige item is in slot 28 + Matcher matcher = PRESTIGE_REQUIREMENT_PATTERN.matcher(getConcatenatedLore(slots.get(28))); + OptionalLong currentChocolate = RegexUtils.getLongFromMatcher(matcher); + if (currentChocolate.isPresent()) { + String requirement = matcher.group(2); //If the first one matched, we can assume the 2nd one is also matched since it's one whole regex + //Since the last character is either M or B we can just try to replace both characters. Only the correct one will actually replace anything. + String amountString = requirement.replace("M", "000000").replace("B", "000000000"); + if (NumberUtils.isParsable(amountString)) { + requiredUntilNextPrestige = Long.parseLong(amountString) - currentChocolate.getAsLong(); + } + } + + //Time Tower is in slot 39 + timeTowerMultiplier = romanToDecimal(StringUtils.substringAfterLast(slots.get(39).getName().getString(), ' ')) / 10.0; //The name holds the level, which is multiplier * 10 in roman numerals + isTimeTowerActive = ItemUtils.getLore(slots.get(39)).getLast().getString().equals("The Time Tower is active!"); + //Compare cost/cpsIncrease rather than cpsIncrease/cost to avoid getting close to 0 and losing precision. cpsIncreaseFactors.sort(Comparator.comparingDouble(rabbit -> rabbit.cost() / rabbit.cpsIncrease())); //Ascending order, lower = better } private static void handleTooltip(ItemStack stack, Item.TooltipContext tooltipContext, TooltipType tooltipType, List lines) { if (!SkyblockerConfigManager.get().helpers.chocolateFactory.enableChocolateFactoryHelper) return; - if (!(MinecraftClient.getInstance().currentScreen instanceof GenericContainerScreen screen) || !screen.getTitle().getString().equals("Chocolate Factory") ) return; + if (!(MinecraftClient.getInstance().currentScreen instanceof GenericContainerScreen screen) || !screen.getTitle().getString().equals("Chocolate Factory")) return; + + int lineIndex = lines.size(); + boolean shouldAddLine = false; String lore = concatenateLore(lines); Matcher costMatcher = COST_PATTERN.matcher(lore); OptionalLong cost = RegexUtils.getLongFromMatcher(costMatcher); - if (cost.isEmpty() || totalChocolate == -1L || totalCps == -1.0) return; + //Available on all items with a chocolate cost + if (cost.isPresent()) shouldAddLine = addUpgradeTimerToLore(lines, cost.getAsLong()); + + //Prestige item + if (stack.isOf(Items.DROPPER) && requiredUntilNextPrestige != -1L) { + shouldAddLine = addPrestigeTimerToLore(lines) || shouldAddLine; + } + //Time tower + else if (stack.isOf(Items.CLOCK)) { + shouldAddLine = addTimeTowerStatsToLore(lines) || shouldAddLine; + } + //Rabbits + else if (stack.isOf(Items.PLAYER_HEAD)) { + shouldAddLine = addRabbitStatsToLore(lines, stack) || shouldAddLine ; + } - lines.add(ItemTooltip.createSmoothLine()); + //This is an ArrayList, so this operation is probably not very efficient, but logically it's pretty much the only way I can think of + if (shouldAddLine) lines.add(lineIndex, ItemTooltip.createSmoothLine()); + } - lines.add(Text.literal("") + private static boolean addUpgradeTimerToLore(List lines, long cost) { + if (totalChocolate == -1L || totalCps == -1.0) return false; + lines.add(Text.empty() .append(Text.literal("Time until upgrade: ").formatted(Formatting.GRAY)) - .append(formatTime((cost.getAsLong() - totalChocolate) / totalCps))); + .append(formatTime((cost - totalChocolate) / totalCps))); + return true; + } - if (cpsIncreaseFactors.isEmpty()) return; + private static boolean addPrestigeTimerToLore(List lines) { + if (requiredUntilNextPrestige == -1L || totalCps == -1.0) return false; + lines.add(Text.empty() + .append(Text.literal("Chocolate until next prestige: ").formatted(Formatting.GRAY)) + .append(Text.literal(DECIMAL_FORMAT.format(requiredUntilNextPrestige)).formatted(Formatting.GOLD))); + lines.add(Text.empty() + .append(Text.literal("Time until next prestige: ").formatted(Formatting.GRAY)) + .append(formatTime(requiredUntilNextPrestige / totalCps))); + return true; + } + private static boolean addTimeTowerStatsToLore(List lines) { + if (totalCps == -1.0 || totalCpsMultiplier == -1.0 || timeTowerMultiplier == -1.0) return false; + lines.add(Text.literal("Current stats:").formatted(Formatting.GRAY)); + lines.add(Text.empty() + .append(Text.literal(" CPS increase: ").formatted(Formatting.GRAY)) + .append(Text.literal(DECIMAL_FORMAT.format(totalCps / totalCpsMultiplier * timeTowerMultiplier)).formatted(Formatting.GOLD))); + lines.add(Text.empty() + .append(Text.literal(" CPS when active: ").formatted(Formatting.GRAY)) + .append(Text.literal(DECIMAL_FORMAT.format(isTimeTowerActive ? totalCps : totalCps / totalCpsMultiplier * (timeTowerMultiplier + totalCpsMultiplier))).formatted(Formatting.GOLD))); + if (timeTowerMultiplier < 1.5) { + lines.add(Text.literal("Stats after upgrade:").formatted(Formatting.GRAY)); + lines.add(Text.empty() + .append(Text.literal(" CPS increase: ").formatted(Formatting.GRAY)) + .append(Text.literal(DECIMAL_FORMAT.format(totalCps / (totalCpsMultiplier) * (timeTowerMultiplier + 0.1))).formatted(Formatting.GOLD))); + lines.add(Text.empty() + .append(Text.literal(" CPS when active: ").formatted(Formatting.GRAY)) + .append(Text.literal(DECIMAL_FORMAT.format(isTimeTowerActive ? totalCps / totalCpsMultiplier * (totalCpsMultiplier + 0.1) : totalCps / totalCpsMultiplier * (timeTowerMultiplier + 0.1 + totalCpsMultiplier))).formatted(Formatting.GOLD))); + } + return true; + } + + private static boolean addRabbitStatsToLore(List lines, ItemStack stack) { + if (cpsIncreaseFactors.isEmpty()) return false; + boolean changed = false; for (Rabbit rabbit : cpsIncreaseFactors) { if (rabbit.itemStack != stack) continue; - - lines.add(Text.literal("") + changed = true; + lines.add(Text.empty() .append(Text.literal("CPS Increase: ").formatted(Formatting.GRAY)) .append(Text.literal(DECIMAL_FORMAT.format(rabbit.cpsIncrease)).formatted(Formatting.GOLD))); - lines.add(Text.literal("") + lines.add(Text.empty() .append(Text.literal("Cost per CPS: ").formatted(Formatting.GRAY)) .append(Text.literal(DECIMAL_FORMAT.format(rabbit.cost / rabbit.cpsIncrease)).formatted(Formatting.GOLD))); if (rabbit.itemStack == bestUpgrade) { - if (cost.getAsLong() <= totalChocolate) { + if (rabbit.cost <= totalChocolate) { lines.add(Text.literal("Best upgrade").formatted(Formatting.GREEN)); } else { lines.add(Text.literal("Best upgrade, can't afford").formatted(Formatting.YELLOW)); } - } else if (rabbit.itemStack == bestAffordableUpgrade && cost.getAsLong() <= totalChocolate) { + } else if (rabbit.itemStack == bestAffordableUpgrade && rabbit.cost <= totalChocolate) { lines.add(Text.literal("Best upgrade you can afford").formatted(Formatting.GREEN)); } } + return changed; } private static MutableText formatTime(double seconds) { @@ -218,6 +306,37 @@ private static Optional getRabbit(ItemStack item, int slot) { return Optional.of(new Rabbit(nextCps.getAsInt() - currentCps.getAsInt(), cost.getAsInt(), slot, item)); } + private static Optional getPrestigeHighlight(ItemStack item) { + List loreList = ItemUtils.getLore(item); + if (loreList.isEmpty()) return Optional.empty(); + + String lore = loreList.getLast().getString(); //The last line holds the text we're looking for + if (lore.equals("Click to prestige!")) return Optional.of(ColorHighlight.green(28)); + return Optional.of(ColorHighlight.red(28)); + } + private record Rabbit(double cpsIncrease, int cost, int slot, ItemStack itemStack) { } + + //Perhaps the part below can go to a separate file later on, but I couldn't find a proper name for the class, so they're staying here. + private static final Map romanMap = Map.of( + 'I', 1, + 'V', 5, + 'X', 10, + 'L', 50, + 'C', 100, + 'D', 500, + 'M', 1000 + ); + + public static int romanToDecimal(String romanNumeral) { + int decimal = 0; + int lastNumber = 0; + for (int i = romanNumeral.length() - 1; i >= 0; i--) { + char ch = romanNumeral.charAt(i); + decimal = romanMap.get(ch) >= lastNumber ? decimal + romanMap.get(ch) : decimal - romanMap.get(ch); + lastNumber = romanMap.get(ch); + } + return decimal; + } } From 5de131909a2e8461c2c19329aa8e9c4d34672e4b Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 17 May 2024 06:33:14 +0300 Subject: [PATCH 37/52] Remove unused import --- .../hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index 3ca68df379..bc555e6b43 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -3,7 +3,6 @@ import com.llamalad7.mixinextras.injector.ModifyExpressionValue; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; 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; From 8ad266d53a4a7316eabd5150fda434dfc5bf2109 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Fri, 17 May 2024 06:33:52 +0300 Subject: [PATCH 38/52] Add location detection to EggFinder --- .../skyblock/chocolatefactory/EggFinder.java | 41 ++++++++++++++++--- 1 file changed, 35 insertions(+), 6 deletions(-) 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 2f027f8117..a38a7396bc 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -1,10 +1,8 @@ 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.SkyblockTime; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.utils.*; import de.hysky.skyblocker.utils.waypoint.Waypoint; import it.unimi.dsi.fastutil.objects.ObjectImmutableList; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; @@ -21,6 +19,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.LinkedList; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -28,16 +27,37 @@ 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 Logger logger = LoggerFactory.getLogger("Skyblocker Egg Finder"); + private static final LinkedList armorStandQueue = new LinkedList<>(); + private static final Location[] possibleLocations = {Location.CRIMSON_ISLE, Location.CRYSTAL_HOLLOWS, Location.DUNGEON_HUB, Location.DWARVEN_MINES, Location.HUB, Location.THE_END, Location.THE_PARK, Location.GOLD_MINE}; + private static boolean isLocationCorrect = false; private EggFinder() { } public static void init() { - ClientPlayConnectionEvents.JOIN.register((ignored, ignored2, ignored3) -> clearEggs()); + ClientPlayConnectionEvents.JOIN.register((ignored, ignored2, ignored3) -> invalidateState()); + SkyblockEvents.LOCATION_CHANGE.register(EggFinder::handleLocationChange); ClientReceiveMessageEvents.GAME.register(EggFinder::onChatMessage); WorldRenderEvents.AFTER_TRANSLUCENT.register(EggFinder::renderWaypoints); } + private static void handleLocationChange(Location location) { + for (Location possibleLocation : possibleLocations) { + if (location == possibleLocation) { + isLocationCorrect = true; + break; + } + } + if (!isLocationCorrect) { + armorStandQueue.clear(); + return; + } + + while (!armorStandQueue.isEmpty()) { + handleArmorStand(armorStandQueue.poll()); + } + } + public static void checkIfEgg(Entity entity) { if (entity instanceof ArmorStandEntity armorStand) checkIfEgg(armorStand); } @@ -46,6 +66,14 @@ public static void checkIfEgg(ArmorStandEntity armorStand) { if (!SkyblockerConfigManager.get().helpers.chocolateFactory.enableEggFinder) return; if (SkyblockTime.skyblockSeason.get() != SkyblockTime.Season.SPRING) return; if (armorStand.hasCustomName() || !armorStand.isInvisible() || !armorStand.shouldHideBasePlate()) return; + if (Utils.getLocation() == Location.UNKNOWN) { //The location is unknown upon world change and will be changed via /locraw soon, so we can queue it for now + armorStandQueue.add(armorStand); + return; + } + if (isLocationCorrect) handleArmorStand(armorStand); + } + + private static void handleArmorStand(ArmorStandEntity armorStand) { for (ItemStack itemStack : armorStand.getArmorItems()) { ItemUtils.getHeadTextureOptional(itemStack).ifPresent(texture -> { for (EggType type : EggType.entries) { @@ -58,8 +86,9 @@ public static void checkIfEgg(ArmorStandEntity armorStand) { } } - private static void clearEggs() { + private static void invalidateState() { if (!SkyblockerConfigManager.get().helpers.chocolateFactory.enableEggFinder) return; + isLocationCorrect = false; for (EggType type : EggType.entries) { type.egg.setValue(null); } From 8866c7af5555298a29bbc26675289529b15b7cee Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sun, 19 May 2024 09:10:03 +0300 Subject: [PATCH 39/52] Change String == null check into String#isBlank --- src/main/java/de/hysky/skyblocker/utils/ItemUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java index 6f98efa6b7..ba41a1df3b 100644 --- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java @@ -217,7 +217,7 @@ public static String getHeadTexture(ItemStack stack) { public static Optional getHeadTextureOptional(ItemStack stack) { String texture = getHeadTexture(stack); - if (texture == null) return Optional.empty(); + if (texture.isBlank()) return Optional.empty(); return Optional.of(texture); } From 475be1aa49068ce46e46c112842f72f28e5e680b Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sun, 19 May 2024 09:21:34 +0300 Subject: [PATCH 40/52] Narrow the scope of the line smoother in ItemTooltip Not necessarily related to the chocolate factory, but it was also added by me so should be fine --- .../skyblocker/skyblock/item/tooltip/ItemTooltip.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java index 6e269790d2..70586d89e8 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java @@ -394,10 +394,14 @@ private static Text getMotesMessage(int price, int count) { return message; } + //This is static to not create a new text object for each line in every item + private static final Text bumpyLine = Text.literal("-----------------").formatted(Formatting.DARK_GRAY, Formatting.STRIKETHROUGH); + private static void smoothenLines(List lines) { for (int i = 0; i < lines.size(); i++) { - Text line = lines.get(i); - if (line.getString().equals("-----------------")) { + List lineSiblings = lines.get(i).getSiblings(); + //Compare the first sibling rather than the whole object as the style of the root object can change while visually staying the same + if (lineSiblings.size() == 1 && lineSiblings.getFirst().equals(bumpyLine)) { lines.set(i, createSmoothLine()); } } From 63833288d637e67902ac07c90f43c89825d6811e Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sun, 19 May 2024 09:22:47 +0300 Subject: [PATCH 41/52] Fix crash caused by Time Tower lore being empty in rare cases --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 a6ec144e40..b3c4e85add 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -121,7 +121,10 @@ private static void updateFactoryInfo(Int2ObjectMap slots) { //Time Tower is in slot 39 timeTowerMultiplier = romanToDecimal(StringUtils.substringAfterLast(slots.get(39).getName().getString(), ' ')) / 10.0; //The name holds the level, which is multiplier * 10 in roman numerals - isTimeTowerActive = ItemUtils.getLore(slots.get(39)).getLast().getString().equals("The Time Tower is active!"); + List timeTowerLore = ItemUtils.getLore(slots.get(39)); + if (!timeTowerLore.isEmpty()) { + isTimeTowerActive = timeTowerLore.getLast().getString().equals("The Time Tower is active!"); + } //Compare cost/cpsIncrease rather than cpsIncrease/cost to avoid getting close to 0 and losing precision. cpsIncreaseFactors.sort(Comparator.comparingDouble(rabbit -> rabbit.cost() / rabbit.cpsIncrease())); //Ascending order, lower = better From 25080e6f171946e22ba1292decfb7b3823c2a6fd Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sun, 19 May 2024 09:23:29 +0300 Subject: [PATCH 42/52] Code cleanup --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 b3c4e85add..2b403330c9 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -135,6 +135,8 @@ private static void handleTooltip(ItemStack stack, Item.TooltipContext tooltipCo if (!(MinecraftClient.getInstance().currentScreen instanceof GenericContainerScreen screen) || !screen.getTitle().getString().equals("Chocolate Factory")) return; int lineIndex = lines.size(); + //This boolean is used to determine if we should add a smooth line to separate the added information from the rest of the tooltip. + //It should be set to true if there's any information added, false otherwise. boolean shouldAddLine = false; String lore = concatenateLore(lines); @@ -161,7 +163,7 @@ else if (stack.isOf(Items.PLAYER_HEAD)) { } private static boolean addUpgradeTimerToLore(List lines, long cost) { - if (totalChocolate == -1L || totalCps == -1.0) return false; + if (totalChocolate < 0L || totalCps < 0.0) return false; lines.add(Text.empty() .append(Text.literal("Time until upgrade: ").formatted(Formatting.GRAY)) .append(formatTime((cost - totalChocolate) / totalCps))); @@ -180,7 +182,7 @@ private static boolean addPrestigeTimerToLore(List lines) { } private static boolean addTimeTowerStatsToLore(List lines) { - if (totalCps == -1.0 || totalCpsMultiplier == -1.0 || timeTowerMultiplier == -1.0) return false; + if (totalCps < 0.0 || totalCpsMultiplier < 0.0 || timeTowerMultiplier < 0.0) return false; lines.add(Text.literal("Current stats:").formatted(Formatting.GRAY)); lines.add(Text.empty() .append(Text.literal(" CPS increase: ").formatted(Formatting.GRAY)) From 7a6fc4c624946719de43feed4d86b4a43a30e6cc Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sun, 19 May 2024 09:32:57 +0300 Subject: [PATCH 43/52] Fix time tower reminder being sent outside skyblock --- .../skyblock/chocolatefactory/TimeTowerReminder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java index f1458b59d9..eb5f60cf6e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java @@ -4,6 +4,7 @@ import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.events.SkyblockEvents; import de.hysky.skyblocker.utils.Constants; +import de.hysky.skyblocker.utils.Utils; import de.hysky.skyblocker.utils.scheduler.Scheduler; import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; import net.minecraft.client.MinecraftClient; @@ -54,7 +55,7 @@ public static void checkIfTimeTower(Message message, boolean overlay) { } private static void sendMessage() { - if (MinecraftClient.getInstance().player == null) return; + if (MinecraftClient.getInstance().player == null || !Utils.isOnSkyblock()) return; MinecraftClient.getInstance().player.sendMessage(Constants.PREFIX.get().append(Text.literal("Your Chocolate Factory's Time Tower has deactivated!").formatted(Formatting.RED))); File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); From eb256675c76ffd8ba6c69d9c3fed92fb9a87cd93 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sun, 19 May 2024 09:33:11 +0300 Subject: [PATCH 44/52] Fix time tower reminder being sent multiple times --- .../skyblock/chocolatefactory/TimeTowerReminder.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java index eb5f60cf6e..72cbeb2a65 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/TimeTowerReminder.java @@ -24,6 +24,7 @@ public class TimeTowerReminder { private static final String TIME_TOWER_FILE = "time_tower.txt"; private static final Pattern TIME_TOWER_PATTERN = Pattern.compile("^TIME TOWER! Your Chocolate Factory production has increased by \\+[\\d.]+x for \\dh!$"); private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Time Tower Reminder"); + private static boolean scheduled = false; private TimeTowerReminder() { } @@ -34,9 +35,9 @@ public static void init() { } public static void checkIfTimeTower(Message message, boolean overlay) { - if (!TIME_TOWER_PATTERN.matcher(message.getString()).matches()) return; + if (!TIME_TOWER_PATTERN.matcher(message.getString()).matches() || scheduled) return; Scheduler.INSTANCE.schedule(TimeTowerReminder::sendMessage, 60 * 60 * 20); // 1 hour - + scheduled = true; File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); if (!tempFile.exists()) { try { @@ -60,6 +61,7 @@ private static void sendMessage() { File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); try { + scheduled = false; if (tempFile.exists()) Files.delete(tempFile.toPath()); } catch (Exception e) { LOGGER.error("[Skyblocker Time Tower Reminder] Failed to delete temp file for Time Tower Reminder!", e); @@ -68,7 +70,7 @@ private static void sendMessage() { private static void checkTempFile() { File tempFile = SkyblockerMod.CONFIG_DIR.resolve(TIME_TOWER_FILE).toFile(); - if (!tempFile.exists()) return; + if (!tempFile.exists() || scheduled) return; long time; try (Stream file = Files.lines(tempFile.toPath())) { From 0c4284c92fa4e4b3ca637f9b22f38195d7b44500 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Sun, 19 May 2024 09:36:30 +0300 Subject: [PATCH 45/52] Sort chocolate factory translation keys --- src/main/resources/assets/skyblocker/lang/en_us.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json index 99ccdba091..c73f49e088 100644 --- a/src/main/resources/assets/skyblocker/lang/en_us.json +++ b/src/main/resources/assets/skyblocker/lang/en_us.json @@ -278,12 +278,12 @@ "skyblocker.config.helpers.chocolateFactory.enableChocolateFactoryHelper.@Tooltip": "Highlights the best upgrade when enabled. \n\nThe best upgrade is marked as green, but if you can't afford it, it's marked as yellow while marking the next best upgrade that you can afford as green.", "skyblocker.config.helpers.chocolateFactory.enableEggFinder": "Enable Egg Finder", "skyblocker.config.helpers.chocolateFactory.enableEggFinder.@Tooltip": "Highlights eggs from Hoppity's Hunt.", + "skyblocker.config.helpers.chocolateFactory.enableTimeTowerReminder": "Enable Time Tower Reminder", + "skyblocker.config.helpers.chocolateFactory.enableTimeTowerReminder.@Tooltip": "Sends a message in chat when your Time Tower deactivates.", "skyblocker.config.helpers.chocolateFactory.sendEggFoundMessages": "Send Egg Found Messages", "skyblocker.config.helpers.chocolateFactory.sendEggFoundMessages.@Tooltip": "Sends a message in chat when an egg is found in the current island.", "skyblocker.config.helpers.chocolateFactory.waypointType": "Egg Waypoint Type", "skyblocker.config.helpers.chocolateFactory.waypointType.@Tooltip": "Waypoint: Displays a highlight and a beacon beam.\n\nOutlined Waypoint: Displays both a waypoint and an outline.\n\nHighlight: Only displays a highlight.\n\nOutlined Highlight: Displays both a highlight and an outline.\n\nOutline: Only displays an outline.", - "skyblocker.config.helpers.chocolateFactory.enableTimeTowerReminder": "Enable Time Tower Reminder", - "skyblocker.config.helpers.chocolateFactory.enableTimeTowerReminder.@Tooltip": "Sends a message in chat when your Time Tower deactivates.", "skyblocker.config.helpers.enableNewYearCakesHelper": "Enable New Year Cakes Helper", "skyblocker.config.helpers.enableNewYearCakesHelper.@Tooltip": "Highlights the missing new year cakes green and the cakes you have already red.\n\nRequires you to open your cake bag at least once to work.", From b43cf96525534465dc94584f04dffd8ef38814d3 Mon Sep 17 00:00:00 2001 From: Kevin <92656833+kevinthegreat1@users.noreply.github.com> Date: Mon, 20 May 2024 15:42:43 -0400 Subject: [PATCH 46/52] Revert backwards compatible changes in ItemUtils.getHeadTexture to avoid merge conflicts --- .../java/de/hysky/skyblocker/utils/ItemUtils.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java index ba41a1df3b..13b2880864 100644 --- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java @@ -206,13 +206,13 @@ public static PropertyMap propertyMapWithTexture(String textureValue) { public static String getHeadTexture(ItemStack stack) { if (!stack.isOf(Items.PLAYER_HEAD) || !stack.contains(DataComponentTypes.PROFILE)) return ""; - Iterator iterator = stack.get(DataComponentTypes.PROFILE) - .properties() - .get("textures") - .iterator(); + ProfileComponent profile = stack.get(DataComponentTypes.PROFILE); + String texture = profile.properties().get("textures").stream() + .map(Property::value) + .findFirst() + .orElse(""); - if (!iterator.hasNext()) return ""; - return iterator.next().value(); + return texture; } public static Optional getHeadTextureOptional(ItemStack stack) { From c28c60bcc2b0aa308d8550161f2fcd27ee270201 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 21 May 2024 14:29:45 +0300 Subject: [PATCH 47/52] Fix time tower calculations being wrong due to incorrect string matching --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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 2b403330c9..b06b65f767 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -36,6 +36,7 @@ public class ChocolateFactorySolver extends ContainerSolver { private static final Pattern MULTIPLIER_INCREASE_PATTERN = Pattern.compile("\\+([\\d.]+)x Chocolate per second"); private static final Pattern CHOCOLATE_PATTERN = Pattern.compile("^([\\d,]+) Chocolate$"); private static final Pattern PRESTIGE_REQUIREMENT_PATTERN = Pattern.compile("Chocolate this Prestige: ([\\d,]+) +Requires (\\S+) Chocolate this Prestige!"); + private static final Pattern TIME_TOWER_STATUS_PATTERN = Pattern.compile("Status: (ACTIVE|INACTIVE)"); private static final ObjectArrayList cpsIncreaseFactors = new ObjectArrayList<>(6); private static long totalChocolate = -1L; private static double totalCps = -1.0; @@ -121,9 +122,9 @@ private static void updateFactoryInfo(Int2ObjectMap slots) { //Time Tower is in slot 39 timeTowerMultiplier = romanToDecimal(StringUtils.substringAfterLast(slots.get(39).getName().getString(), ' ')) / 10.0; //The name holds the level, which is multiplier * 10 in roman numerals - List timeTowerLore = ItemUtils.getLore(slots.get(39)); - if (!timeTowerLore.isEmpty()) { - isTimeTowerActive = timeTowerLore.getLast().getString().equals("The Time Tower is active!"); + Matcher timeTowerStatusMatcher = TIME_TOWER_STATUS_PATTERN.matcher(getConcatenatedLore(slots.get(39))); + if (timeTowerStatusMatcher.find()) { + isTimeTowerActive = timeTowerStatusMatcher.group(1).equals("ACTIVE"); } //Compare cost/cpsIncrease rather than cpsIncrease/cost to avoid getting close to 0 and losing precision. From 92865430f6b9c7e1646b199a5ae1d7c3482956b5 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 21 May 2024 14:30:04 +0300 Subject: [PATCH 48/52] Renamed variable --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 b06b65f767..306d09eea8 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -109,10 +109,10 @@ private static void updateFactoryInfo(Int2ObjectMap slots) { RegexUtils.getDoubleFromMatcher(multiplierMatcher, cpsMatcher.hasMatch() ? cpsMatcher.end() : 0).ifPresent(d -> totalCpsMultiplier = d); //Prestige item is in slot 28 - Matcher matcher = PRESTIGE_REQUIREMENT_PATTERN.matcher(getConcatenatedLore(slots.get(28))); - OptionalLong currentChocolate = RegexUtils.getLongFromMatcher(matcher); + Matcher prestigeMatcher = PRESTIGE_REQUIREMENT_PATTERN.matcher(getConcatenatedLore(slots.get(28))); + OptionalLong currentChocolate = RegexUtils.getLongFromMatcher(prestigeMatcher); if (currentChocolate.isPresent()) { - String requirement = matcher.group(2); //If the first one matched, we can assume the 2nd one is also matched since it's one whole regex + String requirement = prestigeMatcher.group(2); //If the first one matched, we can assume the 2nd one is also matched since it's one whole regex //Since the last character is either M or B we can just try to replace both characters. Only the correct one will actually replace anything. String amountString = requirement.replace("M", "000000").replace("B", "000000000"); if (NumberUtils.isParsable(amountString)) { From 2bbeccd147b715f1eb766d2c34f9326986160557 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Thu, 23 May 2024 02:01:32 -0400 Subject: [PATCH 49/52] Clean up small formatting --- .../de/hysky/skyblocker/SkyblockerMod.java | 3 +- .../config/categories/HelperCategory.java | 5 +- .../mixins/ClientPlayNetworkHandlerMixin.java | 180 +++++++++--------- .../ChocolateFactorySolver.java | 5 +- .../skyblock/item/tooltip/ItemTooltip.java | 5 +- .../hysky/skyblocker/utils/SkyblockTime.java | 6 +- 6 files changed, 99 insertions(+), 105 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 79f241b8df..a57b4177aa 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -2,10 +2,9 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; - -import de.hysky.skyblocker.config.datafixer.ConfigDataFixer; import de.hysky.skyblocker.config.ImageRepoLoader; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.config.datafixer.ConfigDataFixer; import de.hysky.skyblocker.debug.Debug; import de.hysky.skyblocker.skyblock.*; import de.hysky.skyblocker.skyblock.calculators.CalculatorCommand; 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 79bd0d657d..9e4935cb8d 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/HelperCategory.java @@ -3,13 +3,11 @@ 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.OptionGroup; 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 { @@ -184,7 +182,6 @@ public static ConfigCategory create(SkyblockerConfig defaults, SkyblockerConfig newValue -> config.helpers.chocolateFactory.enableTimeTowerReminder = newValue) .controller(ConfigUtils::createBooleanController) .build()) - .build()) .build(); diff --git a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java index bc555e6b43..48389d4020 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ClientPlayNetworkHandlerMixin.java @@ -34,95 +34,95 @@ @Mixin(ClientPlayNetworkHandler.class) public abstract class ClientPlayNetworkHandlerMixin { - @Shadow - private ClientWorld world; - - @Shadow - @Final - private static Logger LOGGER; - - @Inject(method = "onBlockUpdate", at = @At("RETURN")) - private void skyblocker$onBlockUpdate(BlockUpdateS2CPacket packet, CallbackInfo ci) { - if (Utils.isInTheEnd() && SlayerUtils.isInSlayer()) { - BeaconHighlighter.beaconPositions.remove(packet.getPos()); - if (packet.getState().isOf(Blocks.BEACON)) { - BeaconHighlighter.beaconPositions.add(packet.getPos()); - } - } - } - - @Inject(method = "method_37472", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V")) - private void skyblocker$onItemDestroy(int entityId, CallbackInfo ci) { - if (world.getEntityById(entityId) instanceof ItemEntity itemEntity) { - DungeonManager.onItemPickup(itemEntity); - } - } - - @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V", ordinal = 0)) - private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity) { - DungeonManager.onItemPickup(itemEntity); - return itemEntity; - } - - @WrapWithCondition(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false)) - private boolean skyblocker$cancelEntityPassengersWarning(Logger instance, String msg) { - return !Utils.isOnHypixel(); - } - - @WrapWithCondition(method = "onPlayerList", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$cancelPlayerListWarning(Logger instance, String format, Object arg1, Object arg2) { - return !Utils.isOnHypixel(); - } - - @Inject(method = "onPlaySound", at = @At("RETURN")) - private void skyblocker$onPlaySound(PlaySoundS2CPacket packet, CallbackInfo ci) { - FishingHelper.onSound(packet); - } - - @WrapWithCondition(method = "warnOnUnknownPayload", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$dropBadlionPacketWarnings(Logger instance, String message, Object identifier) { - return !(Utils.isOnHypixel() && ((Identifier) identifier).getNamespace().equals("badlion")); - } - - @WrapWithCondition(method = {"onScoreboardScoreUpdate", "onScoreboardScoreReset"}, at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$cancelUnknownScoreboardObjectiveWarnings(Logger instance, String message, Object objectiveName) { - return !Utils.isOnHypixel(); - } - - @WrapWithCondition(method = "onTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", remap = false)) - private boolean skyblocker$cancelTeamWarning(Logger instance, String format, Object... arg) { - return !Utils.isOnHypixel(); - } - - @Inject(method = "onParticle", at = @At("RETURN")) - private void skyblocker$onParticle(ParticleS2CPacket packet, CallbackInfo ci) { - MythologicalRitual.onParticle(packet); - EnderNodes.onParticle(packet); - } - - @ModifyExpressionValue(method = "onEntityStatus", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/EntityStatusS2CPacket;getEntity(Lnet/minecraft/world/World;)Lnet/minecraft/entity/Entity;")) - private Entity skyblocker$onEntityDeath(Entity entity, @Local(argsOnly = true) EntityStatusS2CPacket packet) { - if (packet.getStatus() == EntityStatuses.PLAY_DEATH_SOUND_OR_ADD_PROJECTILE_HIT_PARTICLES) { - DungeonScore.handleEntityDeath(entity); - TheEnd.onEntityDeath(entity); - } - return entity; - } - - @Inject(method = "onEntityTrackerUpdate", at = @At("TAIL")) - private void skyblocker$onEntityTrackerUpdate(EntityTrackerUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { - if (!(entity instanceof ArmorStandEntity armorStandEntity)) return; - - EggFinder.checkIfEgg(armorStandEntity); - try { //Prevent packet handling fails if something goes wrong so that entity trackers still update, just without compact damage numbers + @Shadow + private ClientWorld world; + + @Shadow + @Final + private static Logger LOGGER; + + @Inject(method = "onBlockUpdate", at = @At("RETURN")) + private void skyblocker$onBlockUpdate(BlockUpdateS2CPacket packet, CallbackInfo ci) { + if (Utils.isInTheEnd() && SlayerUtils.isInSlayer()) { + BeaconHighlighter.beaconPositions.remove(packet.getPos()); + if (packet.getState().isOf(Blocks.BEACON)) { + BeaconHighlighter.beaconPositions.add(packet.getPos()); + } + } + } + + @Inject(method = "method_37472", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V")) + private void skyblocker$onItemDestroy(int entityId, CallbackInfo ci) { + if (world.getEntityById(entityId) instanceof ItemEntity itemEntity) { + DungeonManager.onItemPickup(itemEntity); + } + } + + @ModifyVariable(method = "onItemPickupAnimation", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/world/ClientWorld;removeEntity(ILnet/minecraft/entity/Entity$RemovalReason;)V", ordinal = 0)) + private ItemEntity skyblocker$onItemPickup(ItemEntity itemEntity) { + DungeonManager.onItemPickup(itemEntity); + return itemEntity; + } + + @WrapWithCondition(method = "onEntityPassengersSet", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;)V", remap = false)) + private boolean skyblocker$cancelEntityPassengersWarning(Logger instance, String msg) { + return !Utils.isOnHypixel(); + } + + @WrapWithCondition(method = "onPlayerList", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$cancelPlayerListWarning(Logger instance, String format, Object arg1, Object arg2) { + return !Utils.isOnHypixel(); + } + + @Inject(method = "onPlaySound", at = @At("RETURN")) + private void skyblocker$onPlaySound(PlaySoundS2CPacket packet, CallbackInfo ci) { + FishingHelper.onSound(packet); + } + + @WrapWithCondition(method = "warnOnUnknownPayload", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$dropBadlionPacketWarnings(Logger instance, String message, Object identifier) { + return !(Utils.isOnHypixel() && ((Identifier) identifier).getNamespace().equals("badlion")); + } + + @WrapWithCondition(method = {"onScoreboardScoreUpdate", "onScoreboardScoreReset"}, at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$cancelUnknownScoreboardObjectiveWarnings(Logger instance, String message, Object objectiveName) { + return !Utils.isOnHypixel(); + } + + @WrapWithCondition(method = "onTeam", at = @At(value = "INVOKE", target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", remap = false)) + private boolean skyblocker$cancelTeamWarning(Logger instance, String format, Object... arg) { + return !Utils.isOnHypixel(); + } + + @Inject(method = "onParticle", at = @At("RETURN")) + private void skyblocker$onParticle(ParticleS2CPacket packet, CallbackInfo ci) { + MythologicalRitual.onParticle(packet); + EnderNodes.onParticle(packet); + } + + @ModifyExpressionValue(method = "onEntityStatus", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/s2c/play/EntityStatusS2CPacket;getEntity(Lnet/minecraft/world/World;)Lnet/minecraft/entity/Entity;")) + private Entity skyblocker$onEntityDeath(Entity entity, @Local(argsOnly = true) EntityStatusS2CPacket packet) { + if (packet.getStatus() == EntityStatuses.PLAY_DEATH_SOUND_OR_ADD_PROJECTILE_HIT_PARTICLES) { + DungeonScore.handleEntityDeath(entity); + TheEnd.onEntityDeath(entity); + } + return entity; + } + + @Inject(method = "onEntityTrackerUpdate", at = @At("TAIL")) + private void skyblocker$onEntityTrackerUpdate(EntityTrackerUpdateS2CPacket packet, CallbackInfo ci, @Local Entity entity) { + if (!(entity instanceof ArmorStandEntity armorStandEntity)) return; + + EggFinder.checkIfEgg(armorStandEntity); + try { //Prevent packet handling fails if something goes wrong so that entity trackers still update, just without compact damage numbers CompactDamage.compactDamage(armorStandEntity); - } catch (Exception e) { - 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); - } + } catch (Exception e) { + 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/ChocolateFactorySolver.java b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java index 306d09eea8..33bf7b11a4 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -6,7 +6,7 @@ import de.hysky.skyblocker.utils.RegexUtils; import de.hysky.skyblocker.utils.render.gui.ColorHighlight; import de.hysky.skyblocker.utils.render.gui.ContainerSolver; -import it.unimi.dsi.fastutil.ints.*; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.fabricmc.fabric.api.client.item.v1.ItemTooltipCallback; import net.minecraft.client.MinecraftClient; @@ -24,7 +24,6 @@ import java.text.DecimalFormat; import java.text.DecimalFormatSymbols; import java.util.*; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -156,7 +155,7 @@ else if (stack.isOf(Items.CLOCK)) { } //Rabbits else if (stack.isOf(Items.PLAYER_HEAD)) { - shouldAddLine = addRabbitStatsToLore(lines, stack) || shouldAddLine ; + shouldAddLine = addRabbitStatsToLore(lines, stack) || shouldAddLine; } //This is an ArrayList, so this operation is probably not very efficient, but logically it's pretty much the only way I can think of diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java index 70586d89e8..8c083e252a 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java @@ -2,7 +2,6 @@ import com.google.gson.JsonObject; import de.hysky.skyblocker.SkyblockerMod; -import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.config.configs.GeneralConfig; import de.hysky.skyblocker.skyblock.item.MuseumItemCache; @@ -395,13 +394,13 @@ private static Text getMotesMessage(int price, int count) { } //This is static to not create a new text object for each line in every item - private static final Text bumpyLine = Text.literal("-----------------").formatted(Formatting.DARK_GRAY, Formatting.STRIKETHROUGH); + private static final Text BUMPY_LINE = Text.literal("-----------------").formatted(Formatting.DARK_GRAY, Formatting.STRIKETHROUGH); private static void smoothenLines(List lines) { for (int i = 0; i < lines.size(); i++) { List lineSiblings = lines.get(i).getSiblings(); //Compare the first sibling rather than the whole object as the style of the root object can change while visually staying the same - if (lineSiblings.size() == 1 && lineSiblings.getFirst().equals(bumpyLine)) { + if (lineSiblings.size() == 1 && lineSiblings.getFirst().equals(BUMPY_LINE)) { lines.set(i, createSmoothLine()); } } diff --git a/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java index 36db4ef3b6..5a6074e1ca 100644 --- a/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java +++ b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java @@ -45,17 +45,17 @@ private static void updateTime() { skyblockSeason.set(Season.values()[getSkyblockMonth() / 3]); skyblockMonth.set(Month.values()[getSkyblockMonth()]); skyblockDay.set(getSkyblockDay()); - LOGGER.info("[Skyblocker Time] Skyblock time updated to Year {}, Season {}, Month {}, Day {}",skyblockYear.get(), skyblockSeason.get(), skyblockMonth.get(), skyblockDay.get()); + LOGGER.info("[Skyblocker Time] Skyblock time updated to Year {}, Season {}, Month {}, Day {}", skyblockYear.get(), skyblockSeason.get(), skyblockMonth.get(), skyblockDay.get()); } public enum Season { - SPRING, SUMMER, FALL, WINTER; + SPRING, SUMMER, FALL, WINTER } public enum Month { EARLY_SPRING, SPRING, LATE_SPRING, EARLY_SUMMER, SUMMER, LATE_SUMMER, EARLY_FALL, FALL, LATE_FALL, - EARLY_WINTER, WINTER, LATE_WINTER; + EARLY_WINTER, WINTER, LATE_WINTER } } From 76019594b31c053f03ad76465de8d371d33f0c15 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 23 May 2024 13:26:05 +0300 Subject: [PATCH 50/52] Fix egg found message being sent twice in rare cases --- .../hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 a38a7396bc..c4fd7d4d36 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/EggFinder.java @@ -76,8 +76,8 @@ public static void checkIfEgg(ArmorStandEntity armorStand) { private static void handleArmorStand(ArmorStandEntity armorStand) { for (ItemStack itemStack : armorStand.getArmorItems()) { ItemUtils.getHeadTextureOptional(itemStack).ifPresent(texture -> { - for (EggType type : EggType.entries) { - if (texture.equals(type.texture) && type.egg.getValue() == null) { + for (EggType type : EggType.entries) { //Compare blockPos rather than entity to avoid incorrect matches when the entity just moves rather than a new one being spawned elsewhere + if (texture.equals(type.texture) && (type.egg.getValue() == null || !type.egg.getValue().entity.getBlockPos().equals(armorStand.getBlockPos()))) { handleFoundEgg(armorStand, type); return; } From 3c578b31af7eecc541619473c8c8c3eacb554a23 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Thu, 23 May 2024 14:44:25 +0300 Subject: [PATCH 51/52] Fix 1 second gap in formatTime --- .../skyblock/chocolatefactory/ChocolateFactorySolver.java | 1 + 1 file changed, 1 insertion(+) 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 33bf7b11a4..2d530b6d73 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/chocolatefactory/ChocolateFactorySolver.java @@ -230,6 +230,7 @@ private static boolean addRabbitStatsToLore(List lines, ItemStack stack) { } private static MutableText formatTime(double seconds) { + seconds = Math.ceil(seconds); if (seconds <= 0) return Text.literal("Now").formatted(Formatting.GREEN); StringBuilder builder = new StringBuilder(); From 1f6798df58af5eec4dfe954ab413c5a92d0fee63 Mon Sep 17 00:00:00 2001 From: Kevin <92656833+kevinthegreat1@users.noreply.github.com> Date: Thu, 23 May 2024 22:12:26 -0400 Subject: [PATCH 52/52] Change poch to epoch --- src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java index 5a6074e1ca..045ecc4e65 100644 --- a/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java +++ b/src/main/java/de/hysky/skyblocker/utils/SkyblockTime.java @@ -8,7 +8,7 @@ import java.util.concurrent.atomic.AtomicReference; public class SkyblockTime { - private static final long SKYBLOCK_POCH = 1560275700000L; + private static final long SKYBLOCK_EPOCH = 1560275700000L; public static final AtomicInteger skyblockYear = new AtomicInteger(0); public static final AtomicReference skyblockSeason = new AtomicReference<>(Season.SPRING); public static final AtomicReference skyblockMonth = new AtomicReference<>(Month.EARLY_SPRING); @@ -25,7 +25,7 @@ public static void init() { } private static long getSkyblockMillis() { - return System.currentTimeMillis() - SKYBLOCK_POCH; + return System.currentTimeMillis() - SKYBLOCK_EPOCH; } private static int getSkyblockYear() {