aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de
diff options
context:
space:
mode:
authorolim88 <bobq4582@gmail.com>2025-06-07 20:25:52 +0100
committerGitHub <noreply@github.com>2025-06-07 15:25:52 -0400
commit47770e4d7fb54c3c4aff54f8481277672ceec533 (patch)
tree4f3c7b95c7b6295d1fa2cf3e74bcf444869463a6 /src/main/java/de
parent3106ee6c992736ef7a315125455545914de8d7c6 (diff)
downloadSkyblocker-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')
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/UIAndVisualsCategory.java41
-rw-r--r--src/main/java/de/hysky/skyblocker/config/configs/UIAndVisualsConfig.java21
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/ScreenHandlerMixin.java4
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/ItemPickupWidget.java235
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/ComponentBasedWidget.java6
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/tabhud/widget/component/Components.java7
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.
*