From fa5fe9b64407f4dedfa2d37b833ec60ff567f29d Mon Sep 17 00:00:00 2001 From: Tas <103238549+0xTas@users.noreply.github.com> Date: Tue, 8 Oct 2024 18:41:02 -0700 Subject: [PATCH] Fix BetterTooltips crashing over items with the HIDE_TOOLTIP flag (#4913) --- .../events/game/ItemStackTooltipEvent.java | 48 +++++++++++++++++- .../modules/render/BetterTooltips.java | 50 ++++++++++++------- 2 files changed, 79 insertions(+), 19 deletions(-) diff --git a/src/main/java/meteordevelopment/meteorclient/events/game/ItemStackTooltipEvent.java b/src/main/java/meteordevelopment/meteorclient/events/game/ItemStackTooltipEvent.java index c0cf9c5f97..9a5bbf8d21 100644 --- a/src/main/java/meteordevelopment/meteorclient/events/game/ItemStackTooltipEvent.java +++ b/src/main/java/meteordevelopment/meteorclient/events/game/ItemStackTooltipEvent.java @@ -5,9 +5,55 @@ package meteordevelopment.meteorclient.events.game; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.item.ItemStack; import net.minecraft.text.Text; import java.util.List; -public record ItemStackTooltipEvent(ItemStack itemStack, List<Text> list) {} +public class ItemStackTooltipEvent { + private final ItemStack itemStack; + private List<Text> list; + + public ItemStackTooltipEvent(ItemStack itemStack, List<Text> list) { + this.itemStack = itemStack; + this.list = list; + } + + public List<Text> list() { + return list; + } + + public ItemStack itemStack() { + return itemStack; + } + + public void appendStart(Text text) { + copyIfImmutable(); + int index = list.isEmpty() ? 0 : 1; + list.add(index, text); + } + + public void appendEnd(Text text) { + copyIfImmutable(); + list.add(text); + } + + public void append(int index, Text text) { + copyIfImmutable(); + list.add(index, text); + } + + public void set(int index, Text text) { + copyIfImmutable(); + list.set(index, text); + } + + private void copyIfImmutable() { + // ItemStack#getTooltip can sometimes return List.of(), which is immutable. + // Some modules like BetterTooltips try to modify that list anyway, which causes a crash if we don't replace it. + if (List.of().getClass().getSuperclass().isInstance(list)) { + list = new ObjectArrayList<>(list); + } + } +} diff --git a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/BetterTooltips.java b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/BetterTooltips.java index 9336b51568..64a8c345d7 100644 --- a/src/main/java/meteordevelopment/meteorclient/systems/modules/render/BetterTooltips.java +++ b/src/main/java/meteordevelopment/meteorclient/systems/modules/render/BetterTooltips.java @@ -253,6 +253,13 @@ public BetterTooltips() { @EventHandler private void appendTooltip(ItemStackTooltipEvent event) { + // Hide hidden (empty) tooltips unless the tooltip hide flag setting is true. + if (!tooltip.get() && event.list().isEmpty()) { + // Hold-to-preview tooltip text is always added when needed. + appendPreviewTooltipText(event, false); + return; + } + // Status effects if (statusEffects.get()) { if (event.itemStack().getItem() == Items.SUSPICIOUS_STEW) { @@ -260,13 +267,13 @@ private void appendTooltip(ItemStackTooltipEvent event) { if (stewEffectsComponent != null) { for (StewEffect effectTag : stewEffectsComponent.effects()) { StatusEffectInstance effect = new StatusEffectInstance(effectTag.effect(), effectTag.duration(), 0); - event.list().add(1, getStatusText(effect)); + event.appendStart(getStatusText(effect)); } } } else { FoodComponent food = event.itemStack().get(DataComponentTypes.FOOD); if (food != null) { - food.effects().forEach(e -> event.list().add(1, getStatusText(e.effect()))); + food.effects().forEach(e -> event.appendStart(getStatusText(e.effect()))); } } } @@ -277,12 +284,12 @@ private void appendTooltip(ItemStackTooltipEvent event) { BlockStateComponent blockStateComponent = event.itemStack().get(DataComponentTypes.BLOCK_STATE); if (blockStateComponent != null) { String level = blockStateComponent.properties().get("honey_level"); - event.list().add(1, Text.literal(String.format("%sHoney level: %s%s%s.", Formatting.GRAY, Formatting.YELLOW, level, Formatting.GRAY))); + event.appendStart(Text.literal(String.format("%sHoney level: %s%s%s.", Formatting.GRAY, Formatting.YELLOW, level, Formatting.GRAY))); } List<BeehiveBlockEntity.BeeData> bees = event.itemStack().get(DataComponentTypes.BEES); if (bees != null) { - event.list().add(1, Text.literal(String.format("%sBees: %s%d%s.", Formatting.GRAY, Formatting.YELLOW, bees.size(), Formatting.GRAY))); + event.appendStart(Text.literal(String.format("%sBees: %s%d%s.", Formatting.GRAY, Formatting.YELLOW, bees.size(), Formatting.GRAY))); } } } @@ -300,25 +307,14 @@ private void appendTooltip(ItemStackTooltipEvent event) { if (byteCount >= 1024) count = String.format("%.2f kb", byteCount / (float) 1024); else count = String.format("%d bytes", byteCount); - event.list().add(Text.literal(count).formatted(Formatting.GRAY)); + event.appendEnd(Text.literal(count).formatted(Formatting.GRAY)); } catch (Exception e) { - event.list().add(Text.literal("Error getting bytes.").formatted(Formatting.RED)); + event.appendEnd(Text.literal("Error getting bytes.").formatted(Formatting.RED)); } } // Hold to preview tooltip - if ((shulkers.get() && !previewShulkers() && Utils.hasItems(event.itemStack())) - || (event.itemStack().getItem() == Items.ENDER_CHEST && echest.get() && !previewEChest()) - || (event.itemStack().getItem() == Items.FILLED_MAP && maps.get() && !previewMaps()) - || (event.itemStack().getItem() == Items.WRITABLE_BOOK && books.get() && !previewBooks()) - || (event.itemStack().getItem() == Items.WRITTEN_BOOK && books.get() && !previewBooks()) - || (event.itemStack().getItem() instanceof EntityBucketItem && entitiesInBuckets.get() && !previewEntities()) - || (event.itemStack().getItem() instanceof BannerItem && banners.get() && !previewBanners()) - || (event.itemStack().getItem() instanceof BannerPatternItem && banners.get() && !previewBanners()) - || (event.itemStack().getItem() == Items.SHIELD && banners.get() && !previewBanners())) { - event.list().add(Text.literal("")); - event.list().add(Text.literal("Hold " + Formatting.YELLOW + keybind + Formatting.RESET + " to preview")); - } + appendPreviewTooltipText(event, true); } @EventHandler @@ -408,6 +404,24 @@ public void applyCompactShulkerTooltip(ItemStack shulkerItem, List<Text> tooltip } } + private void appendPreviewTooltipText(ItemStackTooltipEvent event, boolean spacer) { + if (!isPressed() && ( + shulkers.get() && Utils.hasItems(event.itemStack()) + || (event.itemStack().getItem() == Items.ENDER_CHEST && echest.get()) + || (event.itemStack().getItem() == Items.FILLED_MAP && maps.get()) + || (event.itemStack().getItem() == Items.WRITABLE_BOOK && books.get()) + || (event.itemStack().getItem() == Items.WRITTEN_BOOK && books.get()) + || (event.itemStack().getItem() instanceof EntityBucketItem && entitiesInBuckets.get()) + || (event.itemStack().getItem() instanceof BannerItem && banners.get()) + || (event.itemStack().getItem() instanceof BannerPatternItem && banners.get()) + || (event.itemStack().getItem() == Items.SHIELD && banners.get()) + )) { + // we don't want to add the spacer if the tooltip is hidden + if (spacer) event.appendEnd(Text.literal("")); + event.appendEnd(Text.literal("Hold " + Formatting.YELLOW + keybind + Formatting.RESET + " to preview")); + } + } + private MutableText getStatusText(StatusEffectInstance effect) { MutableText text = Text.translatable(effect.getTranslationKey()); if (effect.getAmplifier() != 0) {