diff options
| author | olim88 <bobq4582@gmail.com> | 2025-06-07 20:25:52 +0100 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-06-07 15:25:52 -0400 |
| commit | 47770e4d7fb54c3c4aff54f8481277672ceec533 (patch) | |
| tree | 4f3c7b95c7b6295d1fa2cf3e74bcf444869463a6 /src/main/java/de | |
| parent | 3106ee6c992736ef7a315125455545914de8d7c6 (diff) | |
| download | Skyblocker-47770e4d7fb54c3c4aff54f8481277672ceec533.tar.gz Skyblocker-47770e4d7fb54c3c4aff54f8481277672ceec533.tar.bz2 Skyblocker-47770e4d7fb54c3c4aff54f8481277672ceec533.zip | |
Item pickup Widget (#1295)
* basic inventory diff
* add getting items from sacks
* clean up code and add config
* add doc strings
* fix weird formatting
* overide shouldUpdateBeforeRendering to be true
* Change injection point to ScreenHandler and clean up lang
* Clean up ItemPickupWidget
* Fix player inventory out of bounds crash
* fix showing changes when in an inventory
without this the items of gui's and chests get added to the widget
* fix lobby change code
* Delay after location detected
---------
Co-authored-by: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com>
Diffstat (limited to 'src/main/java/de')
6 files changed, 308 insertions, 6 deletions
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java index 7e20f8ff..2682a40d 100644 --- a/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java +++ b/src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java @@ -3,8 +3,10 @@ package de.hysky.skyblocker.config.categories; import de.hysky.skyblocker.config.ConfigUtils; import de.hysky.skyblocker.config.SkyblockerConfig; import de.hysky.skyblocker.config.configs.UIAndVisualsConfig; +import de.hysky.skyblocker.skyblock.ItemPickupWidget; import de.hysky.skyblocker.skyblock.TeleportOverlay; import de.hysky.skyblocker.skyblock.fancybars.StatusBarsConfigScreen; +import de.hysky.skyblocker.skyblock.fishing.FishingHudWidget; import de.hysky.skyblocker.skyblock.item.slottext.SlotTextManager; import de.hysky.skyblocker.skyblock.item.slottext.SlotTextMode; import de.hysky.skyblocker.skyblock.tabhud.config.WidgetsConfigurationScreen; @@ -374,9 +376,9 @@ public class UIAndVisualsCategory { .name(Text.translatable("skyblocker.config.uiAndVisuals.teleportOverlay.teleportOverlayColor")) .binding(defaults.uiAndVisuals.teleportOverlay.teleportOverlayColor, () -> config.uiAndVisuals.teleportOverlay.teleportOverlayColor, - newValue -> { + newValue -> { config.uiAndVisuals.teleportOverlay.teleportOverlayColor = newValue; - TeleportOverlay.configCallback(newValue); + TeleportOverlay.configCallback(newValue); }) .controller(opt -> ColorControllerBuilder.create(opt).allowAlpha(true)) .build()) @@ -699,6 +701,41 @@ public class UIAndVisualsCategory { .build() ) + //item pickup widget + .group(OptionGroup.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.itemPickup")) + .collapsed(true) + .option(ButtonOption.createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.itemPickup.hud.screen")) + .text(Text.translatable("text.skyblocker.open")) + .action((screen, opt) -> MinecraftClient.getInstance().setScreen(new WidgetsConfigurationScreen(Location.HUB, ItemPickupWidget.getInstance().getInternalID(), screen))) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.itemPickup.sackNotifications")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.itemPickup.sackNotifications.@Tooltip"))) + .binding(defaults.uiAndVisuals.itemPickup.sackNotifications, + () -> config.uiAndVisuals.itemPickup.sackNotifications, + newValue -> config.uiAndVisuals.itemPickup.sackNotifications = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Boolean>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.itemPickup.showItemName")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.itemPickup.showItemName.@Tooltip"))) + .binding(defaults.uiAndVisuals.itemPickup.showItemName, + () -> config.uiAndVisuals.itemPickup.showItemName, + newValue -> config.uiAndVisuals.itemPickup.showItemName = newValue) + .controller(ConfigUtils::createBooleanController) + .build()) + .option(Option.<Integer>createBuilder() + .name(Text.translatable("skyblocker.config.uiAndVisuals.itemPickup.lifeTime")) + .description(OptionDescription.of(Text.translatable("skyblocker.config.uiAndVisuals.itemPickup.lifeTime.@Tooltip"))) + .binding(defaults.uiAndVisuals.itemPickup.lifeTime, + () -> config.uiAndVisuals.itemPickup.lifeTime, + newValue -> config.uiAndVisuals.itemPickup.lifeTime = newValue) + .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(1, 10).step(1)) + .build()) + .build() + ) .build(); } diff --git a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java index 081d6a4d..99665366 100644 --- a/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java +++ b/src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java @@ -2,6 +2,7 @@ package de.hysky.skyblocker.config.configs; import de.hysky.skyblocker.skyblock.item.slottext.SlotTextMode; import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; +import de.hysky.skyblocker.utils.Location; import de.hysky.skyblocker.utils.waypoint.Waypoint; import dev.isxander.yacl3.config.v2.api.SerialEntry; import it.unimi.dsi.fastutil.objects.Object2BooleanOpenHashMap; @@ -92,6 +93,9 @@ public class UIAndVisualsConfig { @SerialEntry public HealthBars healthBars = new HealthBars(); + @SerialEntry + public ItemPickup itemPickup = new ItemPickup(); + public static class ChestValue { @SerialEntry public boolean enableChestValue = true; @@ -453,4 +457,21 @@ public class UIAndVisualsConfig { @SerialEntry public Color emptyBarColor = new Color(0xFF0000); } + + public static class ItemPickup { + @SerialEntry + public boolean enabled = false; + + @SerialEntry + public boolean sackNotifications = false; + + @SerialEntry + public boolean showItemName = true; + + @SerialEntry + public int lifeTime = 3; + + + + } } diff --git a/src/main/java/de/hysky/skyblocker/mixins/ScreenHandlerMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ScreenHandlerMixin.java index a6eccb91..559be4ad 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ScreenHandlerMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ScreenHandlerMixin.java @@ -1,6 +1,7 @@ package de.hysky.skyblocker.mixins; import de.hysky.skyblocker.skyblock.InventorySearch; +import de.hysky.skyblocker.skyblock.ItemPickupWidget; import net.minecraft.item.ItemStack; import net.minecraft.screen.ScreenHandler; import org.spongepowered.asm.mixin.Mixin; @@ -10,10 +11,11 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ScreenHandler.class) public class ScreenHandlerMixin { - @Inject(method = "setStackInSlot", at = @At("RETURN")) + @Inject(method = "setStackInSlot", at = @At("HEAD")) private void onSetStackInSlot(int slot, int revision, ItemStack stack, CallbackInfo ci) { if (InventorySearch.isSearching()) { InventorySearch.refreshSlot(slot); } + ItemPickupWidget.getInstance().onItemPickup(slot, stack); } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/ItemPickupWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/ItemPickupWidget.java new file mode 100644 index 00000000..18e5e7a6 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/ItemPickupWidget.java @@ -0,0 +1,235 @@ +package de.hysky.skyblocker.skyblock; + +import de.hysky.skyblocker.annotations.RegisterWidget; +import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.events.SkyblockEvents; +import de.hysky.skyblocker.skyblock.itemlist.ItemRepository; +import de.hysky.skyblocker.skyblock.tabhud.config.WidgetsConfigurationScreen; +import de.hysky.skyblocker.skyblock.tabhud.util.Ico; +import de.hysky.skyblocker.skyblock.tabhud.widget.ComponentBasedWidget; +import de.hysky.skyblocker.utils.Formatters; +import de.hysky.skyblocker.utils.Location; +import de.hysky.skyblocker.utils.NEURepoManager; +import de.hysky.skyblocker.utils.scheduler.Scheduler; +import io.github.moulberry.repo.data.NEUItem; +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.fabricmc.fabric.api.client.message.v1.ClientReceiveMessageEvents; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.text.HoverEvent; +import net.minecraft.text.Text; +import net.minecraft.util.Formatting; + +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +@RegisterWidget +public class ItemPickupWidget extends ComponentBasedWidget { + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + private static final int LOBBY_CHANGE_DELAY = 60; + private static final String SACKS_MESSAGE_START = "[Sacks]"; + private static final Pattern CHANGE_REGEX = Pattern.compile("([+-])([\\d,]+) (.+) \\((.+)\\)"); + + private static ItemPickupWidget instance; + + private boolean changingLobby; + + private final Object2ObjectOpenHashMap<String, ChangeData> addedCount = new Object2ObjectOpenHashMap<>(); + private final Object2ObjectOpenHashMap<String, ChangeData> removedCount = new Object2ObjectOpenHashMap<>(); + + public ItemPickupWidget() { + super(Text.literal("Items"), Formatting.AQUA.getColorValue(), "Item Pickup"); + instance = this; + + ClientReceiveMessageEvents.GAME.register(instance::onChatMessage); + ClientPlayConnectionEvents.JOIN.register((_handler, _sender, _client) -> changingLobby = true); + // Make changingLobby true for a short period while the player loads into a new lobby and their items are loading + SkyblockEvents.LOCATION_CHANGE.register(location -> Scheduler.INSTANCE.schedule(() -> changingLobby = false, LOBBY_CHANGE_DELAY)); + } + + public static ItemPickupWidget getInstance() { + return instance; + } + + /** + * Searches the NEU REPO for the item linked to the name + */ + private static ItemStack getItem(String itemName) { + return NEURepoManager.NEU_REPO.getItems().getItems() + .values().stream() + .filter(item -> Formatting.strip(item.getDisplayName()).equals(itemName)) + .findFirst() + .map(NEUItem::getSkyblockItemId) + .map(ItemRepository::getItemStack) + .orElse(new ItemStack(Items.BARRIER)); + } + + /** + * Checks chat messages for a stack update message, then finds the items linked to it + */ + private void onChatMessage(Text message, boolean overlay) { + if (!Formatting.strip(message.getString()).startsWith(SACKS_MESSAGE_START)) return; + if (!SkyblockerConfigManager.get().uiAndVisuals.itemPickup.sackNotifications) return; + HoverEvent hoverEvent = message.getSiblings().getFirst().getStyle().getHoverEvent(); + if (hoverEvent == null || hoverEvent.getAction() != HoverEvent.Action.SHOW_TEXT) return; + String hoverMessage = ((HoverEvent.ShowText) hoverEvent).value().getString(); + + Matcher matcher = CHANGE_REGEX.matcher(hoverMessage); + while (matcher.find()) { + + ItemStack item = getItem(matcher.group(3)); + //positive + int existingCount = 0; + if (matcher.group(1).equals("+")) { + if (addedCount.containsKey(item.getNeuName())) { + existingCount = addedCount.get(item.getNeuName()).amount; + } + addedCount.put(item.getNeuName(), new ChangeData(item, existingCount + Formatters.parseNumber(matcher.group(2)).intValue(), System.currentTimeMillis())); + } + //negative + else if (matcher.group(1).equals("-")) { + if (removedCount.containsKey(item.getNeuName())) { + existingCount = removedCount.get(item.getNeuName()).amount; + } + removedCount.put(item.getNeuName(), new ChangeData(item, existingCount - Formatters.parseNumber(matcher.group(2)).intValue(), System.currentTimeMillis())); + } + } + } + + @Override + protected boolean shouldUpdateBeforeRendering() { + return true; + } + + @Override + public void updateContent() { + if (MinecraftClient.getInstance().currentScreen instanceof WidgetsConfigurationScreen) { + addSimpleIcoText(Ico.BONE, "Bone ", Formatting.GREEN, "+64"); + return; + } + //add each diff item to the widget + //add positive changes + for (String item : addedCount.keySet()) { + ChangeData entry = addedCount.get(item); + String itemName = checkNextItem(entry); + if (itemName == null) { + addedCount.remove(item); + continue; + } + addSimpleIcoText(entry.item, itemName, Formatting.GREEN, Formatters.DIFF_NUMBERS.format(entry.amount)); + } + //add negative changes + for (String item : removedCount.keySet()) { + ChangeData entry = removedCount.get(item); + String itemName = checkNextItem(entry); + if (itemName == null) { + removedCount.remove(item); + continue; + } + addSimpleIcoText(entry.item, itemName, Formatting.RED, Formatters.DIFF_NUMBERS.format(entry.amount)); + } + } + + /** + * Checks if the ChangeData has expired and if not, returns the item name for the entry + * + * @param entry ChangeData to check + * @return formatted name from ChangeData + */ + private String checkNextItem(ChangeData entry) { + //check the item has not expired + if (entry.lastChange + SkyblockerConfigManager.get().uiAndVisuals.itemPickup.lifeTime * 1000L < System.currentTimeMillis()) { + return null; + } + //return the formatted name for the item based on user settings + return SkyblockerConfigManager.get().uiAndVisuals.itemPickup.showItemName ? entry.item.getName().getString() + " " : " "; + } + + @Override + public boolean shouldRender(Location location) { + if (super.shouldRender(location)) { + //render if enabled + if (SkyblockerConfigManager.get().uiAndVisuals.itemPickup.enabled) { + //render if there are items in history + return !addedCount.isEmpty() || !removedCount.isEmpty(); + } + } + return false; + } + + @Override + public Set<Location> availableLocations() { + return Set.of(Location.values()); + } + + @Override + public void setEnabledIn(Location location, boolean enabled) { + SkyblockerConfigManager.get().uiAndVisuals.itemPickup.enabled = enabled; + } + + @Override + public boolean isEnabledIn(Location location) { + return SkyblockerConfigManager.get().uiAndVisuals.itemPickup.enabled; + } + + /** + * When the client receives a slot change packet, see what has changed in the inventory and add to the counts + */ + public void onItemPickup(int slot, ItemStack newStack) { + //if just changed a lobby, don't read item as this is just going to be all the player's items + if (changingLobby || CLIENT.player == null) return; + //make sure there is not an inventory open + if (CLIENT.currentScreen != null) return; + + //if the slot is below 9, it is a slot that we do not care about + //if the slot is equals to or above 45, it is not in the player's inventory + if (slot < 9 || slot >= 45) return; + //hotbar slots are at the end of the ids instead of at the start like in the inventory main stacks, so we convert to that indexing + if (slot >= 36) { + slot = slot - 36; + } + //find what used to be in the slot + ItemStack oldStack = CLIENT.player.getInventory().getMainStacks().get(slot); + + //work out the number of items changed + int existingCount = 0; + int countDiff = newStack.getCount() - oldStack.getCount(); + + //if item being removed completely + if (newStack.getItem() == Items.AIR) { + // don't count air being changed somehow + if (oldStack.getItem() == Items.AIR) { + return; + } + + if (removedCount.containsKey(oldStack.getNeuName())) { + existingCount = removedCount.get(oldStack.getNeuName()).amount; + } + removedCount.put(oldStack.getNeuName(), new ChangeData(oldStack, existingCount - oldStack.getCount(), System.currentTimeMillis())); + return; + } + + //if there are more items than before + if (countDiff > 0) { + //see if there is already a change for this type of item + if (addedCount.containsKey(newStack.getNeuName())) { + existingCount = addedCount.get(newStack.getNeuName()).amount; + } + addedCount.put(newStack.getNeuName(), new ChangeData(newStack, existingCount + countDiff, System.currentTimeMillis())); + + } + //if there are fewer items than before + else if (countDiff < 0) { + //see if there is already a change for this type of item + if (removedCount.containsKey(newStack.getNeuName())) { + existingCount = removedCount.get(newStack.getNeuName()).amount; + } + removedCount.put(newStack.getNeuName(), new ChangeData(newStack, existingCount + countDiff, System.currentTimeMillis())); + } + } + + private record ChangeData(ItemStack item, int amount, long lastChange) {} +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComponentBasedWidget.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComponentBasedWidget.java index 607b321d..eb709f1e 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComponentBasedWidget.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComponentBasedWidget.java @@ -5,7 +5,7 @@ import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.skyblock.tabhud.screenbuilder.ScreenBuilder; import de.hysky.skyblocker.skyblock.tabhud.util.PlayerListManager; import de.hysky.skyblocker.skyblock.tabhud.widget.component.Component; -import de.hysky.skyblocker.skyblock.tabhud.widget.component.IcoTextComponent; +import de.hysky.skyblocker.skyblock.tabhud.widget.component.Components; import de.hysky.skyblocker.skyblock.tabhud.widget.component.PlainTextComponent; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; @@ -92,12 +92,12 @@ public abstract class ComponentBasedWidget extends HudWidget { */ public final void addSimpleIcoText(ItemStack ico, String string, Formatting fmt, int idx) { Text txt = simpleEntryText(idx, string, fmt); - this.addComponent(new IcoTextComponent(ico, txt)); + this.addComponent(Components.iconTextComponent(ico, txt)); } public final void addSimpleIcoText(ItemStack ico, String string, Formatting fmt, String content) { Text txt = simpleEntryText(content, string, fmt); - this.addComponent(new IcoTextComponent(ico, txt)); + this.addComponent(Components.iconTextComponent(ico, txt)); } @Override diff --git a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Components.java b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Components.java index 2a1ce26e..2cc9770c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Components.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Components.java @@ -8,6 +8,13 @@ import net.minecraft.text.Text; import org.jetbrains.annotations.Range; public class Components { + public static Component iconTextComponent(ItemStack icon, Text text) { + return switch (SkyblockerConfigManager.get().uiAndVisuals.tabHud.style) { + case MINIMAL, SIMPLE -> new PlainTextComponent(text); + case CLASSIC, FANCY -> new IcoTextComponent(icon, text); + }; + } + /** * Returns a progress component based on the configured style. * |
