From d90ca18d51ba6adc3ee13e02f7a13231a6da3724 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 22 Oct 2024 07:04:02 +0300 Subject: [PATCH 1/9] Add aligned text for tooltips to align multiple lines --- .../skyblocker/injected/AlignedText.java | 40 ++++++++++++++ .../skyblocker/mixins/DrawContextMixin.java | 28 ++++++++++ .../skyblocker/mixins/MutableTextMixin.java | 52 ++++++++++++++++++ .../render/gui/AlignedTooltipComponent.java | 55 +++++++++++++++++++ src/main/resources/fabric.mod.json | 3 + src/main/resources/skyblocker.mixins.json | 1 + 6 files changed, 179 insertions(+) create mode 100644 src/main/java/de/hysky/skyblocker/injected/AlignedText.java create mode 100644 src/main/java/de/hysky/skyblocker/mixins/MutableTextMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java diff --git a/src/main/java/de/hysky/skyblocker/injected/AlignedText.java b/src/main/java/de/hysky/skyblocker/injected/AlignedText.java new file mode 100644 index 0000000000..aab8d63b31 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/injected/AlignedText.java @@ -0,0 +1,40 @@ +package de.hysky.skyblocker.injected; + +import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; +import net.minecraft.text.MutableText; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public interface AlignedText { + /** + * This method is used to display text at a certain x offset after the current text in tooltips. + * This allows for aligned text when used on multiple rows. + *

+ * This method can be chained to achieve a grid-like layout in tooltips. + * @param text The text to render after this text + * @param xOffset The x offset to apply to the given {@code text}, + * relative to the start of the text object this method is called upon. + * @return The {@code text} object passed in, for chaining purposes + * @see AlignedTooltipComponent + */ + default @NotNull MutableText align(@NotNull MutableText text, int xOffset) { + return text; + } + + default @Nullable MutableText getAlignedText() { + return null; + } + + /** + * @return The x offset to apply to the text, or {@link Integer#MIN_VALUE } if there's no aligned text + */ + default int getXOffset() { + return Integer.MIN_VALUE; + } + + default MutableText getFirstOfChain() { + return null; + } + + default void setFirstOfChain(MutableText text) {} +} diff --git a/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java b/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java index 56a5183a91..70aa112f9f 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java @@ -4,13 +4,41 @@ import com.llamalad7.mixinextras.sugar.Local; import de.hysky.skyblocker.skyblock.item.ItemCooldowns; import de.hysky.skyblocker.utils.Utils; +import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.tooltip.OrderedTextTooltipComponent; +import net.minecraft.client.gui.tooltip.TooltipComponent; import net.minecraft.item.ItemStack; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; +import java.util.ArrayList; +import java.util.List; + @Mixin(DrawContext.class) public abstract class DrawContextMixin { + @ModifyExpressionValue(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", + at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/Item;F)F")) + private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local(argsOnly = true) ItemStack stack) { + return Utils.isOnSkyblock() && ItemCooldowns.isOnCooldown(stack) ? ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent() : cooldownProgress; + } + + @SuppressWarnings("unchecked") //R is an ArrayList. + @ModifyExpressionValue(method = "drawTooltip(Lnet/minecraft/client/font/TextRenderer;Ljava/util/List;Ljava/util/Optional;II)V", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;collect(Ljava/util/stream/Collector;)Ljava/lang/Object;")) + private R skyblocker$alignedTooltip(R original, @Local(argsOnly = true) List list) { + if (!Utils.isOnSkyblock()) return original; + List result = new ArrayList<>(); + + for (Text text : list) { + if (text instanceof MutableText mutableText && mutableText.getAlignedText() != null) result.add(new AlignedTooltipComponent(mutableText)); + else result.add(new OrderedTextTooltipComponent(text.asOrderedText())); + } + + return (R) result; + } + @ModifyExpressionValue(method = "drawCooldownProgress", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/ItemStack;F)F")) private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local(argsOnly = true) ItemStack stack) { return Utils.isOnSkyblock() && ItemCooldowns.isOnCooldown(stack) ? ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent() : cooldownProgress; diff --git a/src/main/java/de/hysky/skyblocker/mixins/MutableTextMixin.java b/src/main/java/de/hysky/skyblocker/mixins/MutableTextMixin.java new file mode 100644 index 0000000000..7c531ba649 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixins/MutableTextMixin.java @@ -0,0 +1,52 @@ +package de.hysky.skyblocker.mixins; + +import de.hysky.skyblocker.injected.AlignedText; +import net.minecraft.text.MutableText; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(MutableText.class) +public abstract class MutableTextMixin implements AlignedText { + //There's an implicit linked list here, where each text has a reference to the next one. + //With the addition of firstOfChain it becomes a doubly linked list with the caveat of the reference of each text being the first of the chain rather than the previous element. + //There's no need for a real doubly linked list with references to the previous elements, as the only operation that needs to be done is to get the first element of the chain to render the whole chain properly. + @Unique + @Nullable + private MutableText alignedWith = null; + @Unique + private int xOffset = Integer.MIN_VALUE; + @Unique + // Null if this is the first of the chain, not null otherwise & always points to the first of the aligned text chain + private MutableText firstOfChain = null; + + @Override + public @NotNull MutableText align(@NotNull MutableText text, int xOffset) { + this.alignedWith = text; + this.xOffset = xOffset; + if (firstOfChain == null) text.setFirstOfChain((MutableText) (Object) this); + else text.setFirstOfChain(firstOfChain); + return text; + } + + @Override + public @Nullable MutableText getAlignedText() { + return alignedWith; + } + + @Override + public int getXOffset() { + return xOffset; + } + + @Override + public MutableText getFirstOfChain() { + return firstOfChain; + } + + @Override + public void setFirstOfChain(MutableText text) { + firstOfChain = text; + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java new file mode 100644 index 0000000000..a874c20d98 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java @@ -0,0 +1,55 @@ +package de.hysky.skyblocker.utils.render.gui; + +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.tooltip.TooltipComponent; +import net.minecraft.client.render.VertexConsumerProvider; +import net.minecraft.text.MutableText; +import net.minecraft.text.Text; +import org.joml.Matrix4f; + +/** + * Start a text with @align(x) as the first sibling to render the next line in the current line, off-set by x pixels. + * Example: + *


+ * List lines; //Assuming you have a list of lines for the tooltip
+ * lines.add(Text.literal("Left")
+ *               .align(Text.literal("Right"), 50);
+ * This will result in {@code Left<50px space>Right} (in the same line) being rendered. + */ +public class AlignedTooltipComponent implements TooltipComponent { + private final MutableText text; + + public AlignedTooltipComponent(MutableText text) { + MutableText firstOfChain = text.getFirstOfChain(); + if (firstOfChain != null) this.text = firstOfChain; + else this.text = text; + } + + @Override + public int getHeight() { + return 10; + } + + @Override + public int getWidth(TextRenderer textRenderer) { + Text tmpText = this.text; + int width = 0; + while (tmpText != null) { + int offset = tmpText.getXOffset(); + width += offset != Integer.MIN_VALUE ? Math.max(textRenderer.getWidth(tmpText), tmpText.getXOffset()) : textRenderer.getWidth(tmpText); + tmpText = tmpText.getAlignedText(); + } + return width; + } + + @Override + public void drawText(TextRenderer textRenderer, int x, int y, Matrix4f matrix, VertexConsumerProvider.Immediate vertexConsumers) { + MutableText tmpText = this.text; + int tmpX = x; + while (tmpText != null) { + textRenderer.draw(tmpText, tmpX, y, -1, true, matrix, vertexConsumers, TextRenderer.TextLayerType.NORMAL, 0, 15728880); + tmpX += Math.max(textRenderer.getWidth(tmpText), tmpText.getXOffset()); + tmpText = tmpText.getAlignedText(); + } + } +} diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index f9311b13d5..ba8aa1536c 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -64,6 +64,9 @@ "loom:injected_interfaces": { "net/minecraft/class_1799": [ "de/hysky/skyblocker/injected/SkyblockerStack" + ], + "net/minecraft/class_2561": [ + "de/hysky/skyblocker/injected/AlignedText" ] } } diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index b011be6d59..5eb6e55737 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -28,6 +28,7 @@ "LeverBlockMixin", "MinecraftClientMixin", "MouseMixin", + "MutableTextMixin", "PingMeasurerMixin", "PlayerInventoryMixin", "PlayerListHudMixin", From fac22e445e1392c7eddc884c15522a37c78fd7ad Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 22 Oct 2024 08:15:45 +0300 Subject: [PATCH 2/9] Remove chainAlign and adjust all possible usages to use .align() --- .../skyblocker/injected/AlignedText.java | 25 +++++++++- .../skyblocker/mixins/DrawContextMixin.java | 8 ++- .../skyblock/item/tooltip/ItemTooltip.java | 5 +- .../item/tooltip/adders/AccessoryTooltip.java | 25 +++++----- .../item/tooltip/adders/AvgBinTooltip.java | 19 ++++--- .../tooltip/adders/BazaarPriceTooltip.java | 49 ++++++++++--------- .../tooltip/adders/CraftPriceTooltip.java | 5 +- .../tooltip/adders/DungeonQualityTooltip.java | 26 +++++----- .../item/tooltip/adders/EssenceShopPrice.java | 4 +- .../adders/EstimatedItemValueTooltip.java | 9 ++-- .../item/tooltip/adders/LBinTooltip.java | 5 +- .../item/tooltip/adders/MotesTooltip.java | 7 +-- .../item/tooltip/adders/MuseumTooltip.java | 12 ++--- .../item/tooltip/adders/NpcPriceTooltip.java | 5 +- .../tooltip/adders/ObtainedDateTooltip.java | 5 +- .../render/gui/AlignedTooltipComponent.java | 13 +++-- src/main/resources/fabric.mod.json | 2 +- 17 files changed, 127 insertions(+), 97 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/injected/AlignedText.java b/src/main/java/de/hysky/skyblocker/injected/AlignedText.java index aab8d63b31..0c40565a85 100644 --- a/src/main/java/de/hysky/skyblocker/injected/AlignedText.java +++ b/src/main/java/de/hysky/skyblocker/injected/AlignedText.java @@ -7,13 +7,34 @@ public interface AlignedText { /** + *

+ * Aligned Text + *

+ *

* This method is used to display text at a certain x offset after the current text in tooltips. - * This allows for aligned text when used on multiple rows. + * This allows for aligned text when used on multiple rows with the same offset. + *

*

* This method can be chained to achieve a grid-like layout in tooltips. + *

+ *

+ * Styling + *

+ *

+ * The way styling applies to aligned text is slightly different from normal text, where the styling of the parent text is applied to children as well + * (which causes almost all uses of text with formatting to be appended on an empty parent text when there is more than 1 style in the same line). + *

+ *

+ * For aligned text, each node has their own formatting and there is no style inheritance between them. + *

+ *

+ * However, each aligned text node can still have their own children elements like normal text, + * where the children will inherit the style of the parent and their text content will be appended to the parent. + *

+ * * @param text The text to render after this text * @param xOffset The x offset to apply to the given {@code text}, - * relative to the start of the text object this method is called upon. + * relative to the start of the text object this method is called upon. * @return The {@code text} object passed in, for chaining purposes * @see AlignedTooltipComponent */ diff --git a/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java b/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java index 70aa112f9f..7326ffdd9a 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java @@ -32,8 +32,12 @@ public abstract class DrawContextMixin { List result = new ArrayList<>(); for (Text text : list) { - if (text instanceof MutableText mutableText && mutableText.getAlignedText() != null) result.add(new AlignedTooltipComponent(mutableText)); - else result.add(new OrderedTextTooltipComponent(text.asOrderedText())); + if (text instanceof MutableText mutableText) { + MutableText firstOfChain = mutableText.getFirstOfChain(); + if (firstOfChain != null) result.add(new AlignedTooltipComponent(firstOfChain)); + else if (mutableText.getAlignedText() != null) result.add(new AlignedTooltipComponent(mutableText)); + else result.add(new OrderedTextTooltipComponent(mutableText.asOrderedText())); + } else result.add(new OrderedTextTooltipComponent(text.asOrderedText())); } return (R) result; 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 c21fd0a14c..a424010255 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 @@ -12,6 +12,7 @@ import de.hysky.skyblocker.utils.scheduler.Scheduler; import net.minecraft.client.MinecraftClient; import net.minecraft.item.ItemStack; +import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import org.slf4j.Logger; @@ -43,11 +44,11 @@ public static void nullWarning() { } } - public static Text getCoinsMessage(double price, int count) { + public static MutableText getCoinsMessage(double price, int count) { return getCoinsMessage(price, count, false); } - public static Text getCoinsMessage(double price, int count, boolean preCounted) { + public static MutableText getCoinsMessage(double price, int count, boolean preCounted) { // Format the price string once String priceString = String.format(Locale.ENGLISH, "%1$,.1f", preCounted ? price / count : price); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AccessoryTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AccessoryTooltip.java index 7c540a68c0..4df0634af6 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AccessoryTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AccessoryTooltip.java @@ -6,7 +6,6 @@ import it.unimi.dsi.fastutil.Pair; import net.minecraft.item.ItemStack; import net.minecraft.screen.slot.Slot; -import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import org.jetbrains.annotations.Nullable; @@ -25,20 +24,18 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List Pair report = AccessoriesHelper.calculateReport4Accessory(internalID); if (report.left() != AccessoriesHelper.AccessoryReport.INELIGIBLE) { - MutableText title = Text.literal(String.format("%-19s", "Accessory: ")).withColor(0xf57542); - Text stateText = switch (report.left()) { - case HAS_HIGHEST_TIER -> Text.literal("✔ Collected").formatted(Formatting.GREEN); - case IS_GREATER_TIER -> Text.literal("✦ Upgrade ").withColor(0x218bff).append(Text.literal(report.right()).withColor(0xf8f8ff)); - case HAS_GREATER_TIER -> Text.literal("↑ Upgradable ").withColor(0xf8d048).append(Text.literal(report.right()).withColor(0xf8f8ff)); - case OWNS_BETTER_TIER -> Text.literal("↓ Downgrade ").formatted(Formatting.GRAY).append(Text.literal(report.right()).withColor(0xf8f8ff)); - case MISSING -> Text.literal("✖ Missing ").formatted(Formatting.RED).append(Text.literal(report.right()).withColor(0xf8f8ff)); - - //Should never be the case - default -> Text.literal("? Unknown").formatted(Formatting.GRAY); - }; - - lines.add(title.append(stateText)); + lines.add(Text.literal("Accessory:").withColor(0xf57542).align( + switch (report.left()) { + case HAS_HIGHEST_TIER -> Text.literal("✔ Collected").formatted(Formatting.GREEN); + case IS_GREATER_TIER -> Text.literal("✦ Upgrade ").withColor(0x218bff).append(Text.literal(report.right()).withColor(0xf8f8ff)); + case HAS_GREATER_TIER -> Text.literal("↑ Upgradable ").withColor(0xf8d048).append(Text.literal(report.right()).withColor(0xf8f8ff)); + case OWNS_BETTER_TIER -> Text.literal("↓ Downgrade ").formatted(Formatting.GRAY).append(Text.literal(report.right()).withColor(0xf8f8ff)); + case MISSING -> Text.literal("✖ Missing ").formatted(Formatting.RED).append(Text.literal(report.right()).withColor(0xf8f8ff)); + + //Should never be the case + default -> Text.literal("? Unknown").formatted(Formatting.GRAY); + }, 100)); } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java index 827fee74bf..b794ba5474 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java @@ -23,7 +23,7 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List String neuName = stack.getNeuName(); Average type = ItemTooltip.config.avg; - if ((TooltipInfoType.ONE_DAY_AVERAGE.getData() == null && type != Average.THREE_DAY) || (TooltipInfoType.THREE_DAY_AVERAGE.getData() == null && type != Average.ONE_DAY)) { + if ((TooltipInfoType.ONE_DAY_AVERAGE.getData() == null && type != Average.THREE_DAY) || (TooltipInfoType.THREE_DAY_AVERAGE.getData() == null && type != Average.ONE_DAY)) { ItemTooltip.nullWarning(); } else { /* @@ -35,11 +35,13 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List // "No data" line because of API not keeping old data, it causes NullPointerException if (type == Average.ONE_DAY || type == Average.BOTH) { lines.add( - Text.literal(String.format("%-19s", "1 Day Avg. Price:")) + Text.literal("1 Day Avg. Price:") .formatted(Formatting.GOLD) - .append(!TooltipInfoType.ONE_DAY_AVERAGE.getData().containsKey(neuName) - ? Text.literal("No data").formatted(Formatting.RED) - : ItemTooltip.getCoinsMessage(TooltipInfoType.ONE_DAY_AVERAGE.getData().getDouble(neuName), stack.getCount()) + + .align(TooltipInfoType.ONE_DAY_AVERAGE.getData().containsKey(neuName) + ? ItemTooltip.getCoinsMessage(TooltipInfoType.ONE_DAY_AVERAGE.getData().getDouble(neuName), stack.getCount()) + : Text.literal("No data").formatted(Formatting.RED), + 100 ) ); } @@ -47,9 +49,10 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List lines.add( Text.literal(String.format("%-19s", "3 Day Avg. Price:")) .formatted(Formatting.GOLD) - .append(!TooltipInfoType.THREE_DAY_AVERAGE.getData().containsKey(neuName) - ? Text.literal("No data").formatted(Formatting.RED) - : ItemTooltip.getCoinsMessage(TooltipInfoType.THREE_DAY_AVERAGE.getData().getDouble(neuName), stack.getCount()) + .align(TooltipInfoType.THREE_DAY_AVERAGE.getData().containsKey(neuName) + ? ItemTooltip.getCoinsMessage(TooltipInfoType.THREE_DAY_AVERAGE.getData().getDouble(neuName), stack.getCount()) + : Text.literal("No data").formatted(Formatting.RED), + 100 ) ); } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BazaarPriceTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BazaarPriceTooltip.java index 4e1c0dc303..bb519d5b91 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BazaarPriceTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/BazaarPriceTooltip.java @@ -14,36 +14,39 @@ import java.util.List; public class BazaarPriceTooltip extends SimpleTooltipAdder { - public BazaarPriceTooltip(int priority) { + public BazaarPriceTooltip(int priority) { super(priority); } @Override public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List lines) { - String skyblockApiId = stack.getSkyblockApiId(); + String skyblockApiId = stack.getSkyblockApiId(); + if (!TooltipInfoType.BAZAAR.hasOrNullWarning(skyblockApiId)) return; - if (TooltipInfoType.BAZAAR.hasOrNullWarning(skyblockApiId)) { - int count; - if (lines.size() >= 4 && lines.get(3).getSiblings().size() >= 2 && lines.get(1).getString().endsWith("Sack")) { - //The count is in the 2nd sibling of the 3rd line of the lore. here V - //Example line: empty[style={color=dark_purple,!italic}, siblings=[literal{Stored: }[style={color=gray}], literal{0}[style={color=dark_gray}], literal{/20k}[style={color=gray}]] - String line = lines.get(3).getSiblings().get(1).getString().replace(",", ""); - count = NumberUtils.isParsable(line) && !line.equals("0") ? Integer.parseInt(line) : stack.getCount(); - } else { - count = stack.getCount(); - } - BazaarProduct product = TooltipInfoType.BAZAAR.getData().get(skyblockApiId); - lines.add(Text.literal(String.format("%-18s", "Bazaar buy Price:")) - .formatted(Formatting.GOLD) - .append(product.buyPrice().isEmpty() - ? Text.literal("No data").formatted(Formatting.RED) - : ItemTooltip.getCoinsMessage(product.buyPrice().getAsDouble(), count))); - lines.add(Text.literal(String.format("%-19s", "Bazaar sell Price:")) - .formatted(Formatting.GOLD) - .append(product.sellPrice().isEmpty() - ? Text.literal("No data").formatted(Formatting.RED) - : ItemTooltip.getCoinsMessage(product.sellPrice().getAsDouble(), count))); + int count; + if (lines.size() >= 4 && lines.get(3).getSiblings().size() >= 2 && lines.get(1).getString().endsWith("Sack")) { + //The count is in the 2nd sibling of the 3rd line of the lore. here V + //Example line: empty[style={color=dark_purple,!italic}, siblings=[literal{Stored: }[style={color=gray}], literal{0}[style={color=dark_gray}], literal{/20k}[style={color=gray}]] + String line = lines.get(3).getSiblings().get(1).getString().replace(",", ""); + count = NumberUtils.isParsable(line) && !line.equals("0") ? Integer.parseInt(line) : stack.getCount(); + } else { + count = stack.getCount(); } + + @SuppressWarnings("DataFlowIssue") //The existence of the data is already checked via hasOrNullWarning, so the data is guaranteed to be present + BazaarProduct product = TooltipInfoType.BAZAAR.getData().get(skyblockApiId); + lines.add(Text.literal("Bazaar buy Price:") + .formatted(Formatting.GOLD) + .align(product.buyPrice().isEmpty() + ? Text.literal("No data").formatted(Formatting.RED) + : ItemTooltip.getCoinsMessage(product.buyPrice().getAsDouble(), count), + 100)); + lines.add(Text.literal("Bazaar sell Price:") + .formatted(Formatting.GOLD) + .align(product.sellPrice().isEmpty() + ? Text.literal("No data").formatted(Formatting.RED) + : ItemTooltip.getCoinsMessage(product.sellPrice().getAsDouble(), count), + 100)); } @Override diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/CraftPriceTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/CraftPriceTooltip.java index 943e855dca..d1d8da8ce1 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/CraftPriceTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/CraftPriceTooltip.java @@ -59,8 +59,9 @@ public void addToTooltip(@Nullable Slot focusedSloFt, ItemStack stack, List - lines.add(Text.literal(String.format("%-20s", "Crafting Price:")).formatted(Formatting.GOLD) - .append(ItemTooltip.getCoinsMessage(totalCraftCost / outputIngredient.getAmount(), amountInStack)))); + lines.add(Text.literal("Crafting Price:") + .formatted(Formatting.GOLD) + .align(ItemTooltip.getCoinsMessage(totalCraftCost / outputIngredient.getAmount(), amountInStack), 100))); } catch (Exception e) { LOGGER.error("[Skyblocker Craft Price] Error calculating craftprice tooltip for: " + stack.getNeuName(), e); diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/DungeonQualityTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/DungeonQualityTooltip.java index f6efc2f2af..605f05edf3 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/DungeonQualityTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/DungeonQualityTooltip.java @@ -20,22 +20,26 @@ public DungeonQualityTooltip(int priority) { @Override public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List lines) { NbtCompound customData = ItemUtils.getCustomData(stack); - if (customData == null || !customData.contains("baseStatBoostPercentage")) return; + if (customData.isEmpty() || !customData.contains("baseStatBoostPercentage")) return; int baseStatBoostPercentage = customData.getInt("baseStatBoostPercentage"); boolean maxQuality = baseStatBoostPercentage == 50; - if (maxQuality) { - lines.add(Text.literal(String.format("%-17s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.RED).formatted(Formatting.BOLD)); - } else { - lines.add(Text.literal(String.format("%-21s", "Item Quality:") + baseStatBoostPercentage + "/50").formatted(Formatting.BLUE)); - } + lines.add(Text.literal("Item Quality:").formatted(Formatting.BLUE) + .align(maxQuality + ? Text.literal(baseStatBoostPercentage + "/50") + .formatted(Formatting.RED, Formatting.BOLD) + : Text.literal(baseStatBoostPercentage + "/50") + .formatted(Formatting.BLUE), + 100)); if (customData.contains("item_tier")) { // sometimes it just isn't here? int itemTier = customData.getInt("item_tier"); - if (maxQuality) { - lines.add(Text.literal(String.format("%-17s", "Floor Tier:") + itemTier + " (" + getItemTierFloor(itemTier) + ")").formatted(Formatting.RED).formatted(Formatting.BOLD)); - } else { - lines.add(Text.literal(String.format("%-21s", "Floor Tier:") + itemTier + " (" + getItemTierFloor(itemTier) + ")").formatted(Formatting.BLUE)); - } + lines.add(Text.literal("Floor Tier:").formatted(Formatting.BLUE) + .align(maxQuality + ? Text.literal(itemTier + " (" + getItemTierFloor(itemTier) + ")") + .formatted(Formatting.RED, Formatting.BOLD) + : Text.literal(itemTier + " (" + getItemTierFloor(itemTier) + ")") + .formatted(Formatting.BLUE), + 100)); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EssenceShopPrice.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EssenceShopPrice.java index e4a85ec1dd..1c7b3e11df 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EssenceShopPrice.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EssenceShopPrice.java @@ -58,8 +58,8 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List if (priceData == 0) return; //Default value for getLong is 0 if no value exists for that key lines.add(Text.empty() - .append(Text.literal("Essence Cost: ").formatted(Formatting.AQUA)) - .append(Text.literal(DECIMAL_FORMAT.format(priceData * cost.getAsLong()) + " coins").formatted(Formatting.DARK_AQUA)) + .append(Text.literal("Essence Cost:").formatted(Formatting.AQUA)) + .align(Text.literal(DECIMAL_FORMAT.format(priceData * cost.getAsLong()) + " coins").formatted(Formatting.DARK_AQUA), 100) .append(Text.literal(" (").formatted(Formatting.GRAY)) .append(Text.literal(DECIMAL_FORMAT.format(priceData) + " each").formatted(Formatting.GRAY)) .append(Text.literal(")").formatted(Formatting.GRAY)) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EstimatedItemValueTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EstimatedItemValueTooltip.java index 3009fedb81..0e14266b28 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EstimatedItemValueTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EstimatedItemValueTooltip.java @@ -1,9 +1,5 @@ package de.hysky.skyblocker.skyblock.item.tooltip.adders; -import java.util.List; - -import org.jetbrains.annotations.Nullable; - import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; import de.hysky.skyblocker.skyblock.item.tooltip.SimpleTooltipAdder; import de.hysky.skyblocker.skyblock.item.tooltip.info.TooltipInfoType; @@ -13,6 +9,9 @@ import net.minecraft.screen.slot.Slot; import net.minecraft.text.Text; import net.minecraft.util.Formatting; +import org.jetbrains.annotations.Nullable; + +import java.util.List; public class EstimatedItemValueTooltip extends SimpleTooltipAdder { @@ -27,7 +26,7 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List if (result.price() > 0) { lines.add(Text.literal(String.format("%-20s", "Est. Item Value:")) .formatted(Formatting.GOLD) - .append(ItemTooltip.getCoinsMessage(result.price(), stack.getCount(), true))); + .align(ItemTooltip.getCoinsMessage(result.price(), stack.getCount(), true), 100)); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LBinTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LBinTooltip.java index 8aabc13ff8..cb86671ad3 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LBinTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/LBinTooltip.java @@ -27,9 +27,10 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List // Check for whether the item exist in bazaar price data, because Skytils keeps some bazaar item data in lbin api if (TooltipInfoType.LOWEST_BINS.hasOrNullWarning(skyblockApiId) && !TooltipInfoType.BAZAAR.hasOrNullWarning(skyblockApiId)) { - lines.add(Text.literal(String.format("%-19s", "Lowest BIN Price:")) + lines.add(Text.literal("Lowest BIN Price:") .formatted(Formatting.GOLD) - .append(ItemTooltip.getCoinsMessage(TooltipInfoType.LOWEST_BINS.getData().getDouble(skyblockApiId), stack.getCount()))); + .align(ItemTooltip.getCoinsMessage(TooltipInfoType.LOWEST_BINS.getData().getDouble(skyblockApiId), stack.getCount()), + 100)); } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MotesTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MotesTooltip.java index 18d50fe825..7e90300d05 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MotesTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MotesTooltip.java @@ -22,9 +22,10 @@ public MotesTooltip(int priority) { public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List lines) { final String internalID = stack.getSkyblockId(); if (TooltipInfoType.MOTES.hasOrNullWarning(internalID)) { - lines.add(Text.literal(String.format("%-20s", "Motes Price:")) + //noinspection DataFlowIssue --- The existence of the data is already checked via hasOrNullWarning, so the data is guaranteed to be present + lines.add(Text.literal("Motes Price:") .formatted(Formatting.LIGHT_PURPLE) - .append(getMotesMessage(TooltipInfoType.MOTES.getData().getInt(internalID), stack.getCount()))); + .align(getMotesMessage(TooltipInfoType.MOTES.getData().getInt(internalID), stack.getCount()), 100)); } } @@ -33,7 +34,7 @@ public boolean isEnabled() { return TooltipInfoType.MOTES.isTooltipEnabled(); } - private static Text getMotesMessage(int price, int count) { + private static MutableText getMotesMessage(int price, int count) { float motesMultiplier = SkyblockerConfigManager.get().otherLocations.rift.mcGrubberStacks * 0.05f + 1; // Calculate the total price diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java index c4bf96c7d2..2db4e15830 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java @@ -27,16 +27,12 @@ public boolean isEnabled() { public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List lines) { final String internalID = stack.getSkyblockId(); if (TooltipInfoType.MUSEUM.hasOrNullWarning(internalID)) { + //noinspection DataFlowIssue --- The existence of the data is already checked via hasOrNullWarning, so the data is guaranteed to be present String itemCategory = TooltipInfoType.MUSEUM.getData().get(internalID); - String format = switch (itemCategory) { - case "Weapons" -> "%-18s"; - case "Armor" -> "%-19s"; - default -> "%-20s"; - }; //Special case the special category so that it doesn't always display not donated if (itemCategory.equals("Special")) { - lines.add(Text.literal(String.format(format, "Museum: (" + itemCategory + ")")) + lines.add(Text.literal("Museum: (" + itemCategory + ")") .formatted(Formatting.LIGHT_PURPLE)); } else { NbtCompound customData = ItemUtils.getCustomData(stack); @@ -44,9 +40,9 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List Formatting donatedIndicatorFormatting = isInMuseum ? Formatting.GREEN : Formatting.RED; - lines.add(Text.literal(String.format(format, "Museum (" + itemCategory + "):")) + lines.add(Text.literal("Museum (" + itemCategory + "):") .formatted(Formatting.LIGHT_PURPLE) - .append(Text.literal(isInMuseum ? "✔" : "✖").formatted(donatedIndicatorFormatting, Formatting.BOLD)) + .align(Text.literal(isInMuseum ? "✔" : "✖").formatted(donatedIndicatorFormatting, Formatting.BOLD), 100) .append(Text.literal(isInMuseum ? " Donated" : " Not Donated").formatted(donatedIndicatorFormatting))); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/NpcPriceTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/NpcPriceTooltip.java index 6bb1bcdb8d..f44b28ebf4 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/NpcPriceTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/NpcPriceTooltip.java @@ -36,9 +36,10 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List } else { amount = stack.getCount(); } - lines.add(Text.literal(String.format("%-21s", "NPC Sell Price:")) + //noinspection DataFlowIssue --- The data's presence is checked above, so it's safe to assume it's present here + lines.add(Text.literal("NPC Sell Price:") .formatted(Formatting.YELLOW) - .append(ItemTooltip.getCoinsMessage(TooltipInfoType.NPC.getData().getDouble(internalID), amount))); + .align(ItemTooltip.getCoinsMessage(TooltipInfoType.NPC.getData().getDouble(internalID), amount), 100)); } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ObtainedDateTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ObtainedDateTooltip.java index 2071547cb9..6e015393fc 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ObtainedDateTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/ObtainedDateTooltip.java @@ -35,9 +35,8 @@ public boolean isEnabled() { public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List lines) { String timestamp = getTimestamp(stack); if (!timestamp.isEmpty()) { - lines.add(Text.empty() - .append(Text.literal(String.format("%-21s", "Obtained: ")).formatted(Formatting.LIGHT_PURPLE)) - .append(Text.literal(timestamp).formatted(Formatting.RED))); + lines.add(Text.literal("Obtained:").formatted(Formatting.LIGHT_PURPLE) + .align(Text.literal(timestamp).formatted(Formatting.RED), 100)); } } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java index a874c20d98..acfe96978c 100644 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java @@ -4,27 +4,25 @@ import net.minecraft.client.gui.tooltip.TooltipComponent; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.text.MutableText; -import net.minecraft.text.Text; import org.joml.Matrix4f; /** - * Start a text with @align(x) as the first sibling to render the next line in the current line, off-set by x pixels. + * This class is used to display text at a certain x offset after the current text in tooltips. * Example: *

  * List lines; //Assuming you have a list of lines for the tooltip
  * lines.add(Text.literal("Left")
  *               .align(Text.literal("Right"), 50);
- * This will result in {@code Left<50px space>Right} (in the same line) being rendered. + * This will result in {@code Left<50px - the width of the "Left" text>Right} (in the same line) being rendered. */ public class AlignedTooltipComponent implements TooltipComponent { private final MutableText text; public AlignedTooltipComponent(MutableText text) { - MutableText firstOfChain = text.getFirstOfChain(); - if (firstOfChain != null) this.text = firstOfChain; - else this.text = text; + this.text = text; } + //Same as the original OrderedTextTooltipComponent @Override public int getHeight() { return 10; @@ -32,10 +30,11 @@ public int getHeight() { @Override public int getWidth(TextRenderer textRenderer) { - Text tmpText = this.text; + MutableText tmpText = this.text; int width = 0; while (tmpText != null) { int offset = tmpText.getXOffset(); + //If the offset would cause the following text to overlap with the previous text, the width of the previous text is used instead to append the following text to the previous text width += offset != Integer.MIN_VALUE ? Math.max(textRenderer.getWidth(tmpText), tmpText.getXOffset()) : textRenderer.getWidth(tmpText); tmpText = tmpText.getAlignedText(); } diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index ba8aa1536c..5f940facac 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -65,7 +65,7 @@ "net/minecraft/class_1799": [ "de/hysky/skyblocker/injected/SkyblockerStack" ], - "net/minecraft/class_2561": [ + "net/minecraft/class_5250": [ "de/hysky/skyblocker/injected/AlignedText" ] } From 98de0b2154a836c39b46c52ab59eb13b684394d0 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 22 Oct 2024 12:22:21 +0300 Subject: [PATCH 3/9] Fix unintentional formatting change in MuseumTooltip --- .../skyblock/item/tooltip/adders/MuseumTooltip.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java index 2db4e15830..c8fa7bf555 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/MuseumTooltip.java @@ -42,8 +42,10 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List lines.add(Text.literal("Museum (" + itemCategory + "):") .formatted(Formatting.LIGHT_PURPLE) - .align(Text.literal(isInMuseum ? "✔" : "✖").formatted(donatedIndicatorFormatting, Formatting.BOLD), 100) - .append(Text.literal(isInMuseum ? " Donated" : " Not Donated").formatted(donatedIndicatorFormatting))); + .align(Text.empty() + .append(Text.literal(isInMuseum ? "✔" : "✖").formatted(donatedIndicatorFormatting, Formatting.BOLD)) + .append(Text.literal(isInMuseum ? " Donated" : " Not Donated").formatted(donatedIndicatorFormatting)), + 100)); } } } From 772e6cf19d9b351f55656a51209ad178a66fb8b0 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 22 Oct 2024 16:18:35 +0300 Subject: [PATCH 4/9] Fix broken tooltips with REI --- build.gradle | 6 ++--- .../rei/ScreenOverlayImplFabricMixin.java | 25 +++++++++++++++++++ src/main/resources/skyblocker.mixins.json | 3 ++- 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java diff --git a/build.gradle b/build.gradle index 375f80e116..6c39168cbf 100644 --- a/build.gradle +++ b/build.gradle @@ -159,8 +159,8 @@ dependencies { modImplementation "com.terraformersmc:modmenu:${project.mod_menu_version}" // REI - modCompileOnly "me.shedaniel:RoughlyEnoughItems-api-fabric:${project.rei_version}" - //modRuntimeOnly "me.shedaniel:RoughlyEnoughItems-fabric:${project.rei_version}" +// modCompileOnly "me.shedaniel:RoughlyEnoughItems-api-fabric:${project.rei_version}" + modImplementation "me.shedaniel:RoughlyEnoughItems-fabric:${project.rei_version}" // EMI modCompileOnly "dev.emi:emi-fabric:${project.emi_version}:api" @@ -286,4 +286,4 @@ publishing { // The repositories here will be used for publishing your artifact, not for // retrieving dependencies. } -} \ No newline at end of file +} diff --git a/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java b/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java new file mode 100644 index 0000000000..f9d0bf8f5e --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java @@ -0,0 +1,25 @@ +package de.hysky.skyblocker.mixins.rei; + +import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; +import me.shedaniel.rei.api.client.gui.widgets.Tooltip; +import me.shedaniel.rei.impl.client.gui.fabric.ScreenOverlayImplFabric; +import net.minecraft.client.gui.tooltip.TooltipComponent; +import net.minecraft.text.MutableText; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.stream.Stream; + +@Mixin(value = ScreenOverlayImplFabric.class, remap = false) +public class ScreenOverlayImplFabricMixin { + @Inject(method = "lambda$renderTooltipInner$0", at = @At(target = "Lme/shedaniel/rei/api/client/gui/widgets/Tooltip$Entry;isText()Z", value = "INVOKE", shift = At.Shift.AFTER), cancellable = true, require = 0) + private static void renderTooltipInner(Tooltip.Entry component, CallbackInfoReturnable> cir) { + if (!(component.getAsText() instanceof MutableText mutableText)) return; + MutableText firstOfChain = mutableText.getFirstOfChain(); + if (firstOfChain != null) cir.setReturnValue(Stream.of(new AlignedTooltipComponent(firstOfChain))); + else if (mutableText.getAlignedText() != null) cir.setReturnValue(Stream.of(new AlignedTooltipComponent(mutableText))); + // The last branch of defaulting back to OrderedTextTooltipComponent is already handled by REI, so we can just omit it here + } +} diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index 5eb6e55737..0d347901e7 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -56,7 +56,8 @@ "accessors.SlotAccessor", "accessors.WorldRendererAccessor", "discordipc.ConnectionMixin", - "jgit.SystemReaderMixin" + "jgit.SystemReaderMixin", + "rei.ScreenOverlayImplFabricMixin" ], "injectors": { "defaultRequire": 1 From 099e3901f9109e98e61d02118deacbcb9093adc4 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 22 Oct 2024 17:15:56 +0300 Subject: [PATCH 5/9] Fix broken tooltips with EMI --- build.gradle | 4 +-- .../mixins/emi/FakeScreenMixin.java | 31 +++++++++++++++++++ src/main/resources/skyblocker.mixins.json | 3 +- 3 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java diff --git a/build.gradle b/build.gradle index 6c39168cbf..43d554afaf 100644 --- a/build.gradle +++ b/build.gradle @@ -163,8 +163,8 @@ dependencies { modImplementation "me.shedaniel:RoughlyEnoughItems-fabric:${project.rei_version}" // EMI - modCompileOnly "dev.emi:emi-fabric:${project.emi_version}:api" - //modLocalRuntime "dev.emi:emi-fabric:${project.emi_version}" +// modCompileOnly "dev.emi:emi-fabric:${project.emi_version}:api" + modImplementation "dev.emi:emi-fabric:${project.emi_version}" // JEI (Using modrinth repo since official release is in mojmap and doesn't work) modCompileOnly "maven.modrinth:jei:${project.jei_version}-fabric" diff --git a/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java new file mode 100644 index 0000000000..f7ec8c7c46 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java @@ -0,0 +1,31 @@ +package de.hysky.skyblocker.mixins.emi; + +import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; +import dev.emi.emi.screen.FakeScreen; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.tooltip.OrderedTextTooltipComponent; +import net.minecraft.client.gui.tooltip.TooltipComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.text.MutableText; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +import java.util.List; + +@Mixin(value = FakeScreen.class, remap = false) +public class FakeScreenMixin { + @Inject(method = "getTooltipComponentListFromItem", at = @At(target = "Ljava/util/stream/Stream;collect(Ljava/util/stream/Collector;)Ljava/lang/Object;", value = "INVOKE", shift = At.Shift.AFTER), require = 0, cancellable = true) + private void skyblocker$alignedTooltip(ItemStack stack, CallbackInfoReturnable> cir) { + cir.setReturnValue(Screen.getTooltipFromItem(MinecraftClient.getInstance(), stack).stream().map(text -> { + if (text instanceof MutableText mutableText) { + MutableText firstOfChain = mutableText.getFirstOfChain(); + if (firstOfChain != null) return new AlignedTooltipComponent(firstOfChain); + else if (mutableText.getAlignedText() != null) return new AlignedTooltipComponent(mutableText); + else return new OrderedTextTooltipComponent(mutableText.asOrderedText()); + } else return new OrderedTextTooltipComponent(text.asOrderedText()); + }).toList()); + } +} diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index 0d347901e7..a5fdea6530 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -57,7 +57,8 @@ "accessors.WorldRendererAccessor", "discordipc.ConnectionMixin", "jgit.SystemReaderMixin", - "rei.ScreenOverlayImplFabricMixin" + "rei.ScreenOverlayImplFabricMixin", + "emi.FakeScreenMixin" ], "injectors": { "defaultRequire": 1 From 24679a8c8325ec2f13d9a89adc99eb7de532ce16 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:37:38 +0300 Subject: [PATCH 6/9] Update EMI and change Inject to ModifyReceiver in FakeScreenMixin --- gradle.properties | 2 +- .../mixins/emi/FakeScreenMixin.java | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/gradle.properties b/gradle.properties index e3dd7ed85a..cbb8b8dec1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,7 +21,7 @@ mod_menu_version = 12.0.0-beta.1 ## REI (https://modrinth.com/mod/rei/versions?l=fabric) rei_version = 17.0.789 ## EMI (https://modrinth.com/mod/emi/versions) -emi_version = 1.1.10+1.21 +emi_version = 1.1.16+1.21.1 ## JEI (https://modrinth.com/mod/jei/versions) jei_version = 19.8.4.113 diff --git a/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java index f7ec8c7c46..70976f7a2d 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java @@ -1,5 +1,7 @@ package de.hysky.skyblocker.mixins.emi; +import com.llamalad7.mixinextras.injector.ModifyReceiver; +import com.llamalad7.mixinextras.sugar.Local; import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; import dev.emi.emi.screen.FakeScreen; import net.minecraft.client.MinecraftClient; @@ -10,22 +12,21 @@ import net.minecraft.text.MutableText; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import java.util.List; +import java.util.stream.Collector; +import java.util.stream.Stream; -@Mixin(value = FakeScreen.class, remap = false) +@Mixin(value = FakeScreen.class) public class FakeScreenMixin { - @Inject(method = "getTooltipComponentListFromItem", at = @At(target = "Ljava/util/stream/Stream;collect(Ljava/util/stream/Collector;)Ljava/lang/Object;", value = "INVOKE", shift = At.Shift.AFTER), require = 0, cancellable = true) - private void skyblocker$alignedTooltip(ItemStack stack, CallbackInfoReturnable> cir) { - cir.setReturnValue(Screen.getTooltipFromItem(MinecraftClient.getInstance(), stack).stream().map(text -> { + @ModifyReceiver(method = "getTooltipComponentListFromItem", at = @At(target = "Ljava/util/stream/Stream;collect(Ljava/util/stream/Collector;)Ljava/lang/Object;", value = "INVOKE"), require = 0) + private Stream skyblocker$alignedTooltip(Stream instance, Collector> arCollector, @Local(argsOnly = true) ItemStack stack) { + return Screen.getTooltipFromItem(MinecraftClient.getInstance(), stack).stream().map(text -> { if (text instanceof MutableText mutableText) { MutableText firstOfChain = mutableText.getFirstOfChain(); if (firstOfChain != null) return new AlignedTooltipComponent(firstOfChain); else if (mutableText.getAlignedText() != null) return new AlignedTooltipComponent(mutableText); - else return new OrderedTextTooltipComponent(mutableText.asOrderedText()); - } else return new OrderedTextTooltipComponent(text.asOrderedText()); - }).toList()); + } return new OrderedTextTooltipComponent(text.asOrderedText()); + }); } } From 645f0103fcd29af3e09494a4bda6439610f93097 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 22 Oct 2024 18:38:35 +0300 Subject: [PATCH 7/9] Fix broken tooltips with JEI --- .../mixins/jei/RenderHelperMixin.java | 43 +++++++++++++++++++ src/main/resources/skyblocker.mixins.json | 3 +- 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/hysky/skyblocker/mixins/jei/RenderHelperMixin.java diff --git a/src/main/java/de/hysky/skyblocker/mixins/jei/RenderHelperMixin.java b/src/main/java/de/hysky/skyblocker/mixins/jei/RenderHelperMixin.java new file mode 100644 index 0000000000..2e2abc695a --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixins/jei/RenderHelperMixin.java @@ -0,0 +1,43 @@ +package de.hysky.skyblocker.mixins.jei; + +import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.mojang.datafixers.util.Either; +import de.hysky.skyblocker.mixins.accessors.DrawContextInvoker; +import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; +import mezz.jei.fabric.platform.RenderHelper; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.DrawContext; +import net.minecraft.client.gui.tooltip.HoveredTooltipPositioner; +import net.minecraft.client.gui.tooltip.TooltipComponent; +import net.minecraft.item.ItemStack; +import net.minecraft.item.tooltip.TooltipData; +import net.minecraft.text.MutableText; +import net.minecraft.text.StringVisitable; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.List; +import java.util.stream.Stream; + +@Mixin(value = RenderHelper.class, remap = false) +public abstract class RenderHelperMixin { + @WrapMethod(method = "renderTooltip", require = 0) + private void renderTooltip(DrawContext drawContext, List> elements, int x, int y, TextRenderer textRenderer, ItemStack stack, Operation original) { + List components = elements.stream().flatMap(e -> e.map( + text -> { + if (text instanceof MutableText mutableText) { + MutableText firstOfChain = mutableText.getFirstOfChain(); + if (firstOfChain != null) return Stream.of(new AlignedTooltipComponent(firstOfChain)); + else if (mutableText.getAlignedText() != null) return Stream.of(new AlignedTooltipComponent(mutableText)); + } + return textRenderer.wrapLines(text, 400).stream().map(TooltipComponent::of); + }, + tooltipComponent -> Stream.of(this.createClientTooltipComponent(tooltipComponent))) + ).toList(); + ((DrawContextInvoker) drawContext).invokeDrawTooltip(textRenderer, components, x, y, HoveredTooltipPositioner.INSTANCE); + } + + @Shadow + protected abstract TooltipComponent createClientTooltipComponent(TooltipData tooltipComponent); +} diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index a5fdea6530..f8b065e2f7 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -58,7 +58,8 @@ "discordipc.ConnectionMixin", "jgit.SystemReaderMixin", "rei.ScreenOverlayImplFabricMixin", - "emi.FakeScreenMixin" + "emi.FakeScreenMixin", + "jei.RenderHelperMixin" ], "injectors": { "defaultRequire": 1 From 6e55af71e5642d725e416671bbd8301ebfb2230f Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:52:14 +0300 Subject: [PATCH 8/9] Revamp the aligned text logic and use OrderedText for the actual rendering --- .../skyblocker/injected/AlignedText.java | 6 +- .../skyblocker/mixins/DrawContextMixin.java | 34 +--------- .../skyblocker/mixins/MutableTextMixin.java | 64 +++++++++++++++++-- .../skyblocker/mixins/TextHandlerMixin.java | 63 ++++++++++++++++++ .../mixins/emi/FakeScreenMixin.java | 32 ---------- .../mixins/jei/RenderHelperMixin.java | 35 ++-------- .../rei/ScreenOverlayImplFabricMixin.java | 26 ++++---- .../item/tooltip/adders/AvgBinTooltip.java | 2 +- .../adders/EstimatedItemValueTooltip.java | 6 +- .../utils/render/gui/AlignedOrderedText.java | 26 ++++++++ .../render/gui/AlignedTooltipComponent.java | 54 ---------------- src/main/resources/skyblocker.accesswidener | 2 + src/main/resources/skyblocker.mixins.json | 2 +- 13 files changed, 178 insertions(+), 174 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/mixins/TextHandlerMixin.java delete mode 100644 src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java create mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedOrderedText.java delete mode 100644 src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java diff --git a/src/main/java/de/hysky/skyblocker/injected/AlignedText.java b/src/main/java/de/hysky/skyblocker/injected/AlignedText.java index 0c40565a85..4ea84939ef 100644 --- a/src/main/java/de/hysky/skyblocker/injected/AlignedText.java +++ b/src/main/java/de/hysky/skyblocker/injected/AlignedText.java @@ -1,6 +1,5 @@ package de.hysky.skyblocker.injected; -import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; import net.minecraft.text.MutableText; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -36,7 +35,6 @@ public interface AlignedText { * @param xOffset The x offset to apply to the given {@code text}, * relative to the start of the text object this method is called upon. * @return The {@code text} object passed in, for chaining purposes - * @see AlignedTooltipComponent */ default @NotNull MutableText align(@NotNull MutableText text, int xOffset) { return text; @@ -47,12 +45,14 @@ public interface AlignedText { } /** - * @return The x offset to apply to the text, or {@link Integer#MIN_VALUE } if there's no aligned text + * @return The x offset to apply to the text, or {@link Integer#MIN_VALUE } if there's no aligned text or 0 if first of the chain */ default int getXOffset() { return Integer.MIN_VALUE; } + default void setXOffset(int xOffset) {} + default MutableText getFirstOfChain() { return null; } diff --git a/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java b/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java index 7326ffdd9a..b58db8e42c 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/DrawContextMixin.java @@ -4,47 +4,15 @@ import com.llamalad7.mixinextras.sugar.Local; import de.hysky.skyblocker.skyblock.item.ItemCooldowns; import de.hysky.skyblocker.utils.Utils; -import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.tooltip.OrderedTextTooltipComponent; -import net.minecraft.client.gui.tooltip.TooltipComponent; import net.minecraft.item.ItemStack; -import net.minecraft.text.MutableText; -import net.minecraft.text.Text; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import java.util.ArrayList; -import java.util.List; - @Mixin(DrawContext.class) public abstract class DrawContextMixin { - @ModifyExpressionValue(method = "drawItemInSlot(Lnet/minecraft/client/font/TextRenderer;Lnet/minecraft/item/ItemStack;IILjava/lang/String;)V", - at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/Item;F)F")) + @ModifyExpressionValue(method = "drawCooldownProgress", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/ItemStack;F)F")) private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local(argsOnly = true) ItemStack stack) { return Utils.isOnSkyblock() && ItemCooldowns.isOnCooldown(stack) ? ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent() : cooldownProgress; } - - @SuppressWarnings("unchecked") //R is an ArrayList. - @ModifyExpressionValue(method = "drawTooltip(Lnet/minecraft/client/font/TextRenderer;Ljava/util/List;Ljava/util/Optional;II)V", at = @At(value = "INVOKE", target = "Ljava/util/stream/Stream;collect(Ljava/util/stream/Collector;)Ljava/lang/Object;")) - private R skyblocker$alignedTooltip(R original, @Local(argsOnly = true) List list) { - if (!Utils.isOnSkyblock()) return original; - List result = new ArrayList<>(); - - for (Text text : list) { - if (text instanceof MutableText mutableText) { - MutableText firstOfChain = mutableText.getFirstOfChain(); - if (firstOfChain != null) result.add(new AlignedTooltipComponent(firstOfChain)); - else if (mutableText.getAlignedText() != null) result.add(new AlignedTooltipComponent(mutableText)); - else result.add(new OrderedTextTooltipComponent(mutableText.asOrderedText())); - } else result.add(new OrderedTextTooltipComponent(text.asOrderedText())); - } - - return (R) result; - } - - @ModifyExpressionValue(method = "drawCooldownProgress", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/player/ItemCooldownManager;getCooldownProgress(Lnet/minecraft/item/ItemStack;F)F")) - private float skyblocker$modifyItemCooldown(float cooldownProgress, @Local(argsOnly = true) ItemStack stack) { - return Utils.isOnSkyblock() && ItemCooldowns.isOnCooldown(stack) ? ItemCooldowns.getItemCooldownEntry(stack).getRemainingCooldownPercent() : cooldownProgress; - } } diff --git a/src/main/java/de/hysky/skyblocker/mixins/MutableTextMixin.java b/src/main/java/de/hysky/skyblocker/mixins/MutableTextMixin.java index 7c531ba649..993eb02123 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/MutableTextMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/MutableTextMixin.java @@ -1,14 +1,27 @@ package de.hysky.skyblocker.mixins; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import de.hysky.skyblocker.injected.AlignedText; +import de.hysky.skyblocker.utils.render.gui.AlignedOrderedText; import net.minecraft.text.MutableText; +import net.minecraft.text.OrderedText; +import net.minecraft.text.StringVisitable; +import net.minecraft.text.Text; +import net.minecraft.util.Language; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; +import org.spongepowered.asm.mixin.injection.At; + +import java.util.ArrayList; +import java.util.List; @Mixin(MutableText.class) -public abstract class MutableTextMixin implements AlignedText { +public abstract class MutableTextMixin implements AlignedText, Text { //There's an implicit linked list here, where each text has a reference to the next one. //With the addition of firstOfChain it becomes a doubly linked list with the caveat of the reference of each text being the first of the chain rather than the previous element. //There's no need for a real doubly linked list with references to the previous elements, as the only operation that needs to be done is to get the first element of the chain to render the whole chain properly. @@ -17,16 +30,25 @@ public abstract class MutableTextMixin implements AlignedText { private MutableText alignedWith = null; @Unique private int xOffset = Integer.MIN_VALUE; - @Unique + // Null if this is the first of the chain, not null otherwise & always points to the first of the aligned text chain + @Unique private MutableText firstOfChain = null; + @Unique + Logger logger = LoggerFactory.getLogger("Skyblocker Mutable Text"); + @Override public @NotNull MutableText align(@NotNull MutableText text, int xOffset) { this.alignedWith = text; - this.xOffset = xOffset; - if (firstOfChain == null) text.setFirstOfChain((MutableText) (Object) this); - else text.setFirstOfChain(firstOfChain); + if (firstOfChain == null) { + text.setFirstOfChain((MutableText) (Object) this); + text.setXOffset(xOffset); + this.xOffset = 0; + } else { + text.setFirstOfChain(firstOfChain); + text.setXOffset(xOffset); + } return text; } @@ -40,6 +62,11 @@ public int getXOffset() { return xOffset; } + @Override + public void setXOffset(int xOffset) { + this.xOffset = xOffset; + } + @Override public MutableText getFirstOfChain() { return firstOfChain; @@ -49,4 +76,31 @@ public MutableText getFirstOfChain() { public void setFirstOfChain(MutableText text) { firstOfChain = text; } + + @WrapOperation(method = "asOrderedText", at = @At(target = "Lnet/minecraft/util/Language;reorder(Lnet/minecraft/text/StringVisitable;)Lnet/minecraft/text/OrderedText;", value = "INVOKE")) + private OrderedText skyblocker$asOrderedText(Language instance, StringVisitable visitable, Operation original) { + if (visitable instanceof MutableText mutableText) { + MutableText tmp = mutableText.getFirstOfChain(); + if (tmp != null) { + List segments = new ArrayList<>(); + while (tmp != null) { + segments.add(new AlignedOrderedText.Segment(original.call(instance, tmp), tmp.getXOffset())); + tmp = tmp.getAlignedText(); + } + return new AlignedOrderedText(segments); + } else { // This is the first of the chain + tmp = mutableText.getAlignedText(); + if (tmp != null) { + List segments = new ArrayList<>(); + segments.add(new AlignedOrderedText.Segment(original.call(instance, mutableText), 0)); + while (tmp != null) { + segments.add(new AlignedOrderedText.Segment(original.call(instance, tmp), tmp.getXOffset())); + tmp = tmp.getAlignedText(); + } + return new AlignedOrderedText(segments); + } + } + } + return original.call(instance, visitable); + } } diff --git a/src/main/java/de/hysky/skyblocker/mixins/TextHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/TextHandlerMixin.java new file mode 100644 index 0000000000..e6e35894bd --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/mixins/TextHandlerMixin.java @@ -0,0 +1,63 @@ +package de.hysky.skyblocker.mixins; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import de.hysky.skyblocker.utils.render.gui.AlignedOrderedText; +import net.minecraft.client.font.TextHandler; +import net.minecraft.text.*; +import org.apache.commons.lang3.mutable.MutableFloat; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.List; +import java.util.function.BiConsumer; + +@Mixin(TextHandler.class) +public abstract class TextHandlerMixin { + @Final + @Shadow + TextHandler.WidthRetriever widthRetriever; + + @WrapOperation(method = "getWidth(Lnet/minecraft/text/OrderedText;)F", at = @At(value = "INVOKE", target = "Lnet/minecraft/text/OrderedText;accept(Lnet/minecraft/text/CharacterVisitor;)Z")) + private boolean skyblocker$getWidth(OrderedText instance, CharacterVisitor characterVisitor, Operation original, @Local MutableFloat mutableFloat) { + if (instance instanceof AlignedOrderedText alignedOrderedText) { + MutableFloat width = new MutableFloat(); + List segments = alignedOrderedText.segments(); + float tmp = 0; + for (AlignedOrderedText.Segment segment : segments) { + tmp = segment.xOffset(); + if (width.addAndGet(-tmp) < 0) width.setValue(tmp); // If the offset is greater than the current width, set the width to the offset to not allow clipping between segments + segment.text().accept((index, style, codePoint) -> { // This is a copied version of the original operation, but the width addition is done to our MutableFloat rather than the original + width.add(this.widthRetriever.getWidth(codePoint, style)); + return true; + }); + } + + mutableFloat.setValue(width.getValue()); + return true; + } + return original.call(instance, characterVisitor); + } + + @Inject(method = "wrapLines(Lnet/minecraft/text/StringVisitable;ILnet/minecraft/text/Style;Ljava/util/function/BiConsumer;)V", at = @At("HEAD"), cancellable = true) + private void skyblocker$wrapLines(StringVisitable text, int maxWidth, Style style, BiConsumer lineConsumer, CallbackInfo ci) { + if (text instanceof MutableText mutableText) { + switch (mutableText.getXOffset()) { + case 0 -> { + lineConsumer.accept(mutableText, false); + ci.cancel(); + } + case Integer.MIN_VALUE -> {} + default -> { + lineConsumer.accept(mutableText.getFirstOfChain(), false); + ci.cancel(); + } + } + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java deleted file mode 100644 index 70976f7a2d..0000000000 --- a/src/main/java/de/hysky/skyblocker/mixins/emi/FakeScreenMixin.java +++ /dev/null @@ -1,32 +0,0 @@ -package de.hysky.skyblocker.mixins.emi; - -import com.llamalad7.mixinextras.injector.ModifyReceiver; -import com.llamalad7.mixinextras.sugar.Local; -import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; -import dev.emi.emi.screen.FakeScreen; -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.tooltip.OrderedTextTooltipComponent; -import net.minecraft.client.gui.tooltip.TooltipComponent; -import net.minecraft.item.ItemStack; -import net.minecraft.text.MutableText; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import java.util.List; -import java.util.stream.Collector; -import java.util.stream.Stream; - -@Mixin(value = FakeScreen.class) -public class FakeScreenMixin { - @ModifyReceiver(method = "getTooltipComponentListFromItem", at = @At(target = "Ljava/util/stream/Stream;collect(Ljava/util/stream/Collector;)Ljava/lang/Object;", value = "INVOKE"), require = 0) - private Stream skyblocker$alignedTooltip(Stream instance, Collector> arCollector, @Local(argsOnly = true) ItemStack stack) { - return Screen.getTooltipFromItem(MinecraftClient.getInstance(), stack).stream().map(text -> { - if (text instanceof MutableText mutableText) { - MutableText firstOfChain = mutableText.getFirstOfChain(); - if (firstOfChain != null) return new AlignedTooltipComponent(firstOfChain); - else if (mutableText.getAlignedText() != null) return new AlignedTooltipComponent(mutableText); - } return new OrderedTextTooltipComponent(text.asOrderedText()); - }); - } -} diff --git a/src/main/java/de/hysky/skyblocker/mixins/jei/RenderHelperMixin.java b/src/main/java/de/hysky/skyblocker/mixins/jei/RenderHelperMixin.java index 2e2abc695a..b0889fa691 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/jei/RenderHelperMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/jei/RenderHelperMixin.java @@ -1,43 +1,22 @@ package de.hysky.skyblocker.mixins.jei; -import com.llamalad7.mixinextras.injector.wrapmethod.WrapMethod; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.mojang.datafixers.util.Either; -import de.hysky.skyblocker.mixins.accessors.DrawContextInvoker; -import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import mezz.jei.fabric.platform.RenderHelper; import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.DrawContext; -import net.minecraft.client.gui.tooltip.HoveredTooltipPositioner; -import net.minecraft.client.gui.tooltip.TooltipComponent; -import net.minecraft.item.ItemStack; -import net.minecraft.item.tooltip.TooltipData; import net.minecraft.text.MutableText; +import net.minecraft.text.OrderedText; import net.minecraft.text.StringVisitable; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; import java.util.List; -import java.util.stream.Stream; @Mixin(value = RenderHelper.class, remap = false) public abstract class RenderHelperMixin { - @WrapMethod(method = "renderTooltip", require = 0) - private void renderTooltip(DrawContext drawContext, List> elements, int x, int y, TextRenderer textRenderer, ItemStack stack, Operation original) { - List components = elements.stream().flatMap(e -> e.map( - text -> { - if (text instanceof MutableText mutableText) { - MutableText firstOfChain = mutableText.getFirstOfChain(); - if (firstOfChain != null) return Stream.of(new AlignedTooltipComponent(firstOfChain)); - else if (mutableText.getAlignedText() != null) return Stream.of(new AlignedTooltipComponent(mutableText)); - } - return textRenderer.wrapLines(text, 400).stream().map(TooltipComponent::of); - }, - tooltipComponent -> Stream.of(this.createClientTooltipComponent(tooltipComponent))) - ).toList(); - ((DrawContextInvoker) drawContext).invokeDrawTooltip(textRenderer, components, x, y, HoveredTooltipPositioner.INSTANCE); + @WrapOperation(method = "lambda$renderTooltip$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/font/TextRenderer;wrapLines(Lnet/minecraft/text/StringVisitable;I)Ljava/util/List;"), require = 0) + private static List skyblocker$renderTooltip(TextRenderer instance, StringVisitable text, int width, Operation> original) { + if (text instanceof MutableText mutableText && mutableText.getXOffset() != Integer.MIN_VALUE) return List.of(mutableText.asOrderedText()); + return original.call(instance, text, width); } - - @Shadow - protected abstract TooltipComponent createClientTooltipComponent(TooltipData tooltipComponent); } diff --git a/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java b/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java index f9d0bf8f5e..a4a7acb2cb 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java @@ -1,25 +1,23 @@ package de.hysky.skyblocker.mixins.rei; -import de.hysky.skyblocker.utils.render.gui.AlignedTooltipComponent; -import me.shedaniel.rei.api.client.gui.widgets.Tooltip; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; import me.shedaniel.rei.impl.client.gui.fabric.ScreenOverlayImplFabric; -import net.minecraft.client.gui.tooltip.TooltipComponent; +import net.minecraft.client.font.TextHandler; import net.minecraft.text.MutableText; +import net.minecraft.text.StringVisitable; +import net.minecraft.text.Style; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -import java.util.stream.Stream; +import java.util.List; -@Mixin(value = ScreenOverlayImplFabric.class, remap = false) +@Mixin(value = ScreenOverlayImplFabric.class) public class ScreenOverlayImplFabricMixin { - @Inject(method = "lambda$renderTooltipInner$0", at = @At(target = "Lme/shedaniel/rei/api/client/gui/widgets/Tooltip$Entry;isText()Z", value = "INVOKE", shift = At.Shift.AFTER), cancellable = true, require = 0) - private static void renderTooltipInner(Tooltip.Entry component, CallbackInfoReturnable> cir) { - if (!(component.getAsText() instanceof MutableText mutableText)) return; - MutableText firstOfChain = mutableText.getFirstOfChain(); - if (firstOfChain != null) cir.setReturnValue(Stream.of(new AlignedTooltipComponent(firstOfChain))); - else if (mutableText.getAlignedText() != null) cir.setReturnValue(Stream.of(new AlignedTooltipComponent(mutableText))); - // The last branch of defaulting back to OrderedTextTooltipComponent is already handled by REI, so we can just omit it here + @WrapOperation(method = "lambda$renderTooltipInner$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/font/TextHandler;wrapLines(Lnet/minecraft/text/StringVisitable;ILnet/minecraft/text/Style;)Ljava/util/List;"), require = 1) + private static List renderTooltipInner(TextHandler instance, StringVisitable text, int maxWidth, Style style, Operation> original) { + if (text instanceof MutableText mutableText && mutableText.getXOffset() != Integer.MIN_VALUE) return List.of(); + + return original.call(instance, text, maxWidth, style); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java index b794ba5474..80f8fa98d7 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/AvgBinTooltip.java @@ -47,7 +47,7 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List } if (type == Average.THREE_DAY || type == Average.BOTH) { lines.add( - Text.literal(String.format("%-19s", "3 Day Avg. Price:")) + Text.literal("3 Day Avg. Price:") .formatted(Formatting.GOLD) .align(TooltipInfoType.THREE_DAY_AVERAGE.getData().containsKey(neuName) ? ItemTooltip.getCoinsMessage(TooltipInfoType.THREE_DAY_AVERAGE.getData().getDouble(neuName), stack.getCount()) diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EstimatedItemValueTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EstimatedItemValueTooltip.java index 0e14266b28..864ba990da 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EstimatedItemValueTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/adders/EstimatedItemValueTooltip.java @@ -24,9 +24,9 @@ public void addToTooltip(@Nullable Slot focusedSlot, ItemStack stack, List NetworthResult result = NetworthCalculator.getItemNetworth(stack); if (result.price() > 0) { - lines.add(Text.literal(String.format("%-20s", "Est. Item Value:")) - .formatted(Formatting.GOLD) - .align(ItemTooltip.getCoinsMessage(result.price(), stack.getCount(), true), 100)); + lines.add(Text.literal("Est. Item Value:") + .formatted(Formatting.GOLD) + .align(ItemTooltip.getCoinsMessage(result.price(), stack.getCount(), true), 100)); } } diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedOrderedText.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedOrderedText.java new file mode 100644 index 0000000000..ca17cb8c9b --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedOrderedText.java @@ -0,0 +1,26 @@ +package de.hysky.skyblocker.utils.render.gui; + +import net.minecraft.client.font.TextRenderer; +import net.minecraft.text.CharacterVisitor; +import net.minecraft.text.OrderedText; + +import java.util.List; + +public record AlignedOrderedText(List segments) implements OrderedText { + @Override + public boolean accept(CharacterVisitor visitor) { + if (!(visitor instanceof TextRenderer.Drawer drawer)) return true; + float initialX = drawer.x; + float lastX = 0; + for (Segment segment : segments) { + float xOffset = lastX > segment.xOffset ? lastX : segment.xOffset; + drawer.x = initialX + xOffset; + boolean accepted = segment.text.accept(visitor); + if (!accepted) return false; + lastX = drawer.x - initialX; + } + return true; + } + + public record Segment(OrderedText text, int xOffset) {} +} diff --git a/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java b/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java deleted file mode 100644 index acfe96978c..0000000000 --- a/src/main/java/de/hysky/skyblocker/utils/render/gui/AlignedTooltipComponent.java +++ /dev/null @@ -1,54 +0,0 @@ -package de.hysky.skyblocker.utils.render.gui; - -import net.minecraft.client.font.TextRenderer; -import net.minecraft.client.gui.tooltip.TooltipComponent; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.text.MutableText; -import org.joml.Matrix4f; - -/** - * This class is used to display text at a certain x offset after the current text in tooltips. - * Example: - *

- * List lines; //Assuming you have a list of lines for the tooltip
- * lines.add(Text.literal("Left")
- *               .align(Text.literal("Right"), 50);
- * This will result in {@code Left<50px - the width of the "Left" text>Right} (in the same line) being rendered. - */ -public class AlignedTooltipComponent implements TooltipComponent { - private final MutableText text; - - public AlignedTooltipComponent(MutableText text) { - this.text = text; - } - - //Same as the original OrderedTextTooltipComponent - @Override - public int getHeight() { - return 10; - } - - @Override - public int getWidth(TextRenderer textRenderer) { - MutableText tmpText = this.text; - int width = 0; - while (tmpText != null) { - int offset = tmpText.getXOffset(); - //If the offset would cause the following text to overlap with the previous text, the width of the previous text is used instead to append the following text to the previous text - width += offset != Integer.MIN_VALUE ? Math.max(textRenderer.getWidth(tmpText), tmpText.getXOffset()) : textRenderer.getWidth(tmpText); - tmpText = tmpText.getAlignedText(); - } - return width; - } - - @Override - public void drawText(TextRenderer textRenderer, int x, int y, Matrix4f matrix, VertexConsumerProvider.Immediate vertexConsumers) { - MutableText tmpText = this.text; - int tmpX = x; - while (tmpText != null) { - textRenderer.draw(tmpText, tmpX, y, -1, true, matrix, vertexConsumers, TextRenderer.TextLayerType.NORMAL, 0, 15728880); - tmpX += Math.max(textRenderer.getWidth(tmpText), tmpText.getXOffset()); - tmpText = tmpText.getAlignedText(); - } - } -} diff --git a/src/main/resources/skyblocker.accesswidener b/src/main/resources/skyblocker.accesswidener index ba86af126f..99f3243169 100644 --- a/src/main/resources/skyblocker.accesswidener +++ b/src/main/resources/skyblocker.accesswidener @@ -20,3 +20,5 @@ extendable method net/minecraft/client/gui/screen/recipebook/RecipeBookWidget re extendable method net/minecraft/client/gui/screen/recipebook/RecipeBookWidget refreshTabButtons (Z)V extendable method net/minecraft/client/gui/screen/recipebook/RecipeBookWidget refreshSearchResults ()V extendable method net/minecraft/client/gui/screen/recipebook/RecipeBookWidget triggerPirateSpeakEasterEgg (Ljava/lang/String;)V +accessible class net/minecraft/client/font/TextRenderer$Drawer +accessible field net/minecraft/client/font/TextRenderer$Drawer x F diff --git a/src/main/resources/skyblocker.mixins.json b/src/main/resources/skyblocker.mixins.json index f8b065e2f7..7b87535674 100644 --- a/src/main/resources/skyblocker.mixins.json +++ b/src/main/resources/skyblocker.mixins.json @@ -38,6 +38,7 @@ "ScoreboardMixin", "SignEditScreenMixin", "SocialInteractionsPlayerListWidgetMixin", + "TextHandlerMixin", "WindowMixin", "WorldRendererMixin", "YggdrasilMinecraftSessionServiceMixin", @@ -58,7 +59,6 @@ "discordipc.ConnectionMixin", "jgit.SystemReaderMixin", "rei.ScreenOverlayImplFabricMixin", - "emi.FakeScreenMixin", "jei.RenderHelperMixin" ], "injectors": { From 8b059750c605b791f95301ccc7ad4d627121bf69 Mon Sep 17 00:00:00 2001 From: Rime <81419447+Emirlol@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:54:20 +0300 Subject: [PATCH 9/9] Change require back to 0 after debugging --- .../skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java b/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java index a4a7acb2cb..dc37887dd5 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/rei/ScreenOverlayImplFabricMixin.java @@ -14,7 +14,7 @@ @Mixin(value = ScreenOverlayImplFabric.class) public class ScreenOverlayImplFabricMixin { - @WrapOperation(method = "lambda$renderTooltipInner$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/font/TextHandler;wrapLines(Lnet/minecraft/text/StringVisitable;ILnet/minecraft/text/Style;)Ljava/util/List;"), require = 1) + @WrapOperation(method = "lambda$renderTooltipInner$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/font/TextHandler;wrapLines(Lnet/minecraft/text/StringVisitable;ILnet/minecraft/text/Style;)Ljava/util/List;"), require = 0) private static List renderTooltipInner(TextHandler instance, StringVisitable text, int maxWidth, Style style, Operation> original) { if (text instanceof MutableText mutableText && mutableText.getXOffset() != Integer.MIN_VALUE) return List.of();