aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de
diff options
context:
space:
mode:
authorKevin <92656833+kevinthegreat1@users.noreply.github.com>2024-02-14 14:56:55 -0500
committerGitHub <noreply@github.com>2024-02-14 14:56:55 -0500
commit41d02daf5f53a761043ae76f5be484f94ceb0128 (patch)
treee5c5028c11c135c187477ece01ef3b3c3d809e47 /src/main/java/de
parentfd77289b711923ff23c31cc6a26f85fb96e68d4f (diff)
parentcffa68314fc74fbedd97708df5ccb7c098eb638f (diff)
downloadSkyblocker-41d02daf5f53a761043ae76f5be484f94ceb0128.tar.gz
Skyblocker-41d02daf5f53a761043ae76f5be484f94ceb0128.tar.bz2
Skyblocker-41d02daf5f53a761043ae76f5be484f94ceb0128.zip
Merge pull request #537 from olim88/Search-overlays
Search overlays for bz and ah
Diffstat (limited to 'src/main/java/de')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java69
-rw-r--r--src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java54
-rw-r--r--src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java32
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java185
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java347
7 files changed, 663 insertions, 28 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index a438419b..71623ea7 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -24,6 +24,7 @@ import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip;
import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;
import de.hysky.skyblocker.skyblock.quicknav.QuickNav;
import de.hysky.skyblocker.skyblock.rift.TheRift;
+import de.hysky.skyblocker.skyblock.searchOverlay.SearchOverManager;
import de.hysky.skyblocker.skyblock.shortcut.Shortcuts;
import de.hysky.skyblocker.skyblock.special.SpecialEffects;
import de.hysky.skyblocker.skyblock.tabhud.TabHud;
@@ -120,6 +121,7 @@ public class SkyblockerMod implements ClientModInitializer {
FireFreezeStaffTimer.init();
GuardianHealth.init();
TheRift.init();
+ SearchOverManager.init();
TitleContainer.init();
ScreenMaster.init();
DungeonTextures.init();
diff --git a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
index 6411d6e4..175c3bdf 100644
--- a/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
+++ b/src/main/java/de/hysky/skyblocker/config/SkyblockerConfig.java
@@ -241,6 +241,9 @@ public class SkyblockerConfig {
public FlameOverlay flameOverlay = new FlameOverlay();
@SerialEntry
+ public SearchOverlay searchOverlay = new SearchOverlay();
+
+ @SerialEntry
public List<Integer> lockedSlots = new ArrayList<>();
@SerialEntry
@@ -415,6 +418,31 @@ public class SkyblockerConfig {
public Alignment alignment = Alignment.MIDDLE;
}
+ public enum Direction {
+ HORIZONTAL, VERTICAL;
+
+ @Override
+ public String toString() {
+ return switch (this) {
+ case HORIZONTAL -> "Horizontal";
+ case VERTICAL -> "Vertical";
+ };
+ }
+ }
+
+ public enum Alignment {
+ LEFT, RIGHT, MIDDLE;
+
+ @Override
+ public String toString() {
+ return switch (this) {
+ case LEFT -> "Left";
+ case RIGHT -> "Right";
+ case MIDDLE -> "Middle";
+ };
+ }
+ }
+
public static class TeleportOverlay {
@SerialEntry
public boolean enableTeleportOverlays = true;
@@ -443,29 +471,30 @@ public class SkyblockerConfig {
public float flameOpacity = 0f;
}
- public enum Direction {
- HORIZONTAL, VERTICAL;
+ public static class SearchOverlay {
+ @SerialEntry
+ public boolean enableBazaar = true;
- @Override
- public String toString() {
- return switch (this) {
- case HORIZONTAL -> "Horizontal";
- case VERTICAL -> "Vertical";
- };
- }
- }
+ @SerialEntry
+ public boolean enableAuctionHouse = true;
- public enum Alignment {
- LEFT, RIGHT, MIDDLE;
+ @SerialEntry
+ public boolean keepPreviousSearches = false;
- @Override
- public String toString() {
- return switch (this) {
- case LEFT -> "Left";
- case RIGHT -> "Right";
- case MIDDLE -> "Middle";
- };
- }
+ @SerialEntry
+ public int maxSuggestions = 3;
+
+ @SerialEntry
+ public int historyLength = 3;
+
+ @SerialEntry
+ public boolean enableCommands = false;
+
+ @SerialEntry
+ public List<String> bazaarHistory = new ArrayList<>();
+
+ @SerialEntry
+ public List<String> auctionHistory = new ArrayList<>();
}
public static class RichPresence {
diff --git a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
index bb2fdee5..bb333f79 100644
--- a/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
+++ b/src/main/java/de/hysky/skyblocker/config/categories/GeneralCategory.java
@@ -649,6 +649,60 @@ public class GeneralCategory {
.controller(opt -> FloatSliderControllerBuilder.create(opt).range(0.0f, 0.8f).step(0.1f))
.build())
.build())
+
+ //Search overlay
+ .group(OptionGroup.createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay"))
+ .collapsed(true)
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.enableBazaar"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.enableBazaar.@Tooltip")))
+ .binding(defaults.general.searchOverlay.enableBazaar,
+ () -> config.general.searchOverlay.enableBazaar,
+ newValue -> config.general.searchOverlay.enableBazaar = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.enableAuctionHouse"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.enableAuctionHouse.@Tooltip")))
+ .binding(defaults.general.searchOverlay.enableAuctionHouse,
+ () -> config.general.searchOverlay.enableAuctionHouse,
+ newValue -> config.general.searchOverlay.enableAuctionHouse = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.keepPreviousSearches"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.keepPreviousSearches.@Tooltip")))
+ .binding(defaults.general.searchOverlay.keepPreviousSearches,
+ () -> config.general.searchOverlay.keepPreviousSearches,
+ newValue -> config.general.searchOverlay.keepPreviousSearches = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .option(Option.<Integer>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.maxSuggestions"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.maxSuggestions.@Tooltip")))
+ .binding(defaults.general.searchOverlay.maxSuggestions,
+ () -> config.general.searchOverlay.maxSuggestions,
+ newValue -> config.general.searchOverlay.maxSuggestions = newValue)
+ .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(0, 5).step(1))
+ .build())
+ .option(Option.<Integer>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.historyLength"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.historyLength.@Tooltip")))
+ .binding(defaults.general.searchOverlay.historyLength,
+ () -> config.general.searchOverlay.historyLength,
+ newValue -> config.general.searchOverlay.historyLength = newValue)
+ .controller(opt -> IntegerSliderControllerBuilder.create(opt).range(0, 5).step(1))
+ .build())
+ .option(Option.<Boolean>createBuilder()
+ .name(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.enableCommands"))
+ .description(OptionDescription.of(Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.enableCommands.@Tooltip")))
+ .binding(defaults.general.searchOverlay.enableCommands,
+ () -> config.general.searchOverlay.enableCommands,
+ newValue -> config.general.searchOverlay.enableCommands = newValue)
+ .controller(ConfigUtils::createBooleanController)
+ .build())
+ .build())
.build();
}
}
diff --git a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
index 1d54b02c..2a4c38a7 100644
--- a/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixin/ClientPlayerEntityMixin.java
@@ -1,18 +1,20 @@
package de.hysky.skyblocker.mixin;
import com.mojang.authlib.GameProfile;
-
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.skyblock.dungeon.partyfinder.PartyFinderScreen;
import de.hysky.skyblocker.skyblock.item.HotbarSlotLock;
import de.hysky.skyblocker.skyblock.item.ItemProtection;
import de.hysky.skyblocker.skyblock.rift.HealingMelonIndicator;
+import de.hysky.skyblocker.skyblock.searchOverlay.OverlayScreen;
+import de.hysky.skyblocker.skyblock.searchOverlay.SearchOverManager;
import de.hysky.skyblocker.utils.Utils;
import net.minecraft.block.entity.SignBlockEntity;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.AbstractClientPlayerEntity;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.client.world.ClientWorld;
+import net.minecraft.text.Text;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -48,12 +50,28 @@ public abstract class ClientPlayerEntityMixin extends AbstractClientPlayerEntity
}
@Inject(method = "openEditSignScreen", at = @At("HEAD"), cancellable = true)
- public void skyblocker$partyFinderRange(SignBlockEntity sign, boolean front, CallbackInfo callbackInfo) {
- if (PartyFinderScreen.isInKuudraPartyFinder) return;
- if (client.currentScreen instanceof PartyFinderScreen partyFinderScreen && !partyFinderScreen.isAborted()) {
- if (sign.getText(front).getMessage(3, false).getString().toLowerCase().contains("level")) {
- partyFinderScreen.updateSign(sign, front);
- callbackInfo.cancel();
+ public void skyblocker$redirectEditSignScreen(SignBlockEntity sign, boolean front, CallbackInfo callbackInfo) {
+ // Fancy Party Finder
+ if (!PartyFinderScreen.isInKuudraPartyFinder && client.currentScreen instanceof PartyFinderScreen partyFinderScreen && !partyFinderScreen.isAborted() && sign.getText(front).getMessage(3, false).getString().toLowerCase().contains("level")) {
+ partyFinderScreen.updateSign(sign, front);
+ callbackInfo.cancel();
+ return;
+ }
+
+ // Search Overlay
+ if (client.currentScreen != null) {
+ if (SkyblockerConfigManager.get().general.searchOverlay.enableAuctionHouse && client.currentScreen.getTitle().getString().toLowerCase().contains("auction")) {
+ if (sign.getText(front).getMessage(3, false).getString().equalsIgnoreCase("enter query")) {
+ SearchOverManager.updateSign(sign, front, true);
+ client.setScreen(new OverlayScreen(Text.of("")));
+ callbackInfo.cancel();
+ }
+ } else if (SkyblockerConfigManager.get().general.searchOverlay.enableBazaar && client.currentScreen.getTitle().getString().toLowerCase().contains("bazaar")) {
+ if (sign.getText(front).getMessage(3, false).getString().equalsIgnoreCase("enter query")) {
+ SearchOverManager.updateSign(sign, front, false);
+ client.setScreen(new OverlayScreen(Text.of("")));
+ callbackInfo.cancel();
+ }
}
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java
index 9b83f276..fc5c7087 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java
@@ -18,7 +18,7 @@ public enum TooltipInfoType implements Runnable {
BAZAAR("https://hysky.de/api/bazaar", itemTooltip -> itemTooltip.enableBazaarPrice || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator || SkyblockerConfigManager.get().general.chestValue.enableChestValue, itemTooltip -> itemTooltip.enableBazaarPrice, false),
LOWEST_BINS("https://hysky.de/api/auctions/lowestbins", itemTooltip -> itemTooltip.enableLowestBIN || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator || SkyblockerConfigManager.get().general.chestValue.enableChestValue, itemTooltip -> itemTooltip.enableLowestBIN, false),
ONE_DAY_AVERAGE("https://moulberry.codes/auction_averages_lbin/1day.json", itemTooltip -> itemTooltip.enableAvgBIN, false),
- THREE_DAY_AVERAGE("https://moulberry.codes/auction_averages_lbin/3day.json", itemTooltip -> itemTooltip.enableAvgBIN, false),
+ THREE_DAY_AVERAGE("https://moulberry.codes/auction_averages_lbin/3day.json", itemTooltip -> itemTooltip.enableAvgBIN || SkyblockerConfigManager.get().general.searchOverlay.enableAuctionHouse, itemTooltip -> itemTooltip.enableAvgBIN, false),
MOTES("https://hysky.de/api/motesprice", itemTooltip -> itemTooltip.enableMotesPrice, itemTooltip -> itemTooltip.enableMotesPrice && Utils.isInTheRift(), true),
OBTAINED(itemTooltip -> itemTooltip.enableObtainedDate),
MUSEUM("https://hysky.de/api/museum", itemTooltip -> itemTooltip.enableMuseumInfo, true),
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java b/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java
new file mode 100644
index 00000000..e1545c6c
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/OverlayScreen.java
@@ -0,0 +1,185 @@
+package de.hysky.skyblocker.skyblock.searchOverlay;
+
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.widget.ButtonWidget;
+import net.minecraft.client.gui.widget.TextFieldWidget;
+import net.minecraft.item.ItemStack;
+import net.minecraft.text.Style;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.Identifier;
+import org.lwjgl.glfw.GLFW;
+
+import static de.hysky.skyblocker.skyblock.itemlist.ItemRepository.getItemStack;
+
+public class OverlayScreen extends Screen {
+
+ protected static final Identifier SEARCH_ICON_TEXTURE = new Identifier("icon/search");
+ private static final int rowHeight = 20;
+ private TextFieldWidget searchField;
+ private ButtonWidget finishedButton;
+ private ButtonWidget[] suggestionButtons;
+ private ButtonWidget[] historyButtons;
+
+ public OverlayScreen(Text title) {
+ super(title);
+ }
+
+ /**
+ * Creates the layout for the overlay screen.
+ */
+ @Override
+ protected void init() {
+ super.init();
+ int rowWidth = (int) (this.width * 0.4);
+ int startX = (int) (this.width * 0.5) - rowWidth / 2;
+ int startY = (int) ((int) (this.height * 0.5) - (rowHeight * (1 + SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions + 0.75 + SkyblockerConfigManager.get().general.searchOverlay.historyLength)) / 2);
+
+ // Search field
+ this.searchField = new TextFieldWidget(textRenderer, startX, startY, rowWidth - rowHeight, rowHeight, Text.translatable("gui.recipebook.search_hint"));
+ searchField.setText(SearchOverManager.search);
+ searchField.setChangedListener(SearchOverManager::updateSearch);
+ searchField.setMaxLength(30);
+
+ // finish buttons
+ finishedButton = ButtonWidget.builder(Text.literal("").setStyle(Style.EMPTY.withColor(Formatting.GREEN)), a -> close())
+ .position(startX + rowWidth - rowHeight, startY)
+ .size(rowHeight, rowHeight).build();
+
+ // suggested item buttons
+ int rowOffset = rowHeight;
+ int totalSuggestions = SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions;
+ this.suggestionButtons = new ButtonWidget[totalSuggestions];
+ for (int i = 0; i < totalSuggestions; i++) {
+ suggestionButtons[i] = ButtonWidget.builder(Text.literal(SearchOverManager.getSuggestion(i)).setStyle(Style.EMPTY), a -> {
+ SearchOverManager.updateSearch(a.getMessage().getString());
+ close();
+ })
+ .position(startX, startY + rowOffset)
+ .size(rowWidth, rowHeight).build();
+ suggestionButtons[i].visible = false;
+ rowOffset += rowHeight;
+ }
+ // history item buttons
+ rowOffset += (int) (rowHeight * 0.75);
+ int historyLength = SkyblockerConfigManager.get().general.searchOverlay.historyLength;
+ this.historyButtons = new ButtonWidget[historyLength];
+ for (int i = 0; i < historyLength; i++) {
+ String text = SearchOverManager.getHistory(i);
+ if (text != null) {
+ historyButtons[i] = ButtonWidget.builder(Text.literal(text).setStyle(Style.EMPTY), (a) -> {
+ SearchOverManager.updateSearch(a.getMessage().getString());
+ close();
+ })
+ .position(startX, startY + rowOffset)
+ .size(rowWidth, rowHeight).build();
+ rowOffset += rowHeight;
+ } else {
+ break;
+ }
+ }
+
+ //add drawables in order to make tab navigation sensible
+ addDrawableChild(searchField);
+ for (ButtonWidget suggestion : suggestionButtons) {
+ addDrawableChild(suggestion);
+ }
+ for (ButtonWidget historyOption : historyButtons) {
+ if (historyOption != null) {
+ addDrawableChild(historyOption);
+ }
+ }
+ addDrawableChild(finishedButton);
+
+ //focus the search box
+ this.setInitialFocus(searchField);
+ }
+
+ /**
+ * Renders the search icon, label for the history and item Stacks for item names
+ */
+ @Override
+ public void render(DrawContext context, int mouseX, int mouseY, float delta) {
+ super.render(context, mouseX, mouseY, delta);
+ int renderOffset = (rowHeight - 16) / 2;
+ context.drawGuiTexture(SEARCH_ICON_TEXTURE, finishedButton.getX() + renderOffset, finishedButton.getY() + renderOffset, 16, 16);
+ if (historyButtons.length > 0 && historyButtons[0] != null) {
+ context.drawText(textRenderer, Text.translatable("text.autoconfig.skyblocker.option.general.searchOverlay.historyLabel"), historyButtons[0].getX() + renderOffset, historyButtons[0].getY() - rowHeight / 2, 0xFFFFFFFF, true);
+ }
+
+ //draw item stacks and tooltip to buttons
+ for (int i = 0; i < suggestionButtons.length; i++) {
+ drawItemAndTooltip(context, mouseX, mouseY, SearchOverManager.getSuggestionId(i), suggestionButtons[i], renderOffset);
+ }
+ for (int i = 0; i < historyButtons.length; i++) {
+ drawItemAndTooltip(context, mouseX, mouseY, SearchOverManager.getHistoryId(i), historyButtons[i], renderOffset);
+ }
+ }
+
+ /**
+ * Draws the item and tooltip for the given button
+ */
+ private void drawItemAndTooltip(DrawContext context, int mouseX, int mouseY, String id, ButtonWidget button, int renderOffset) {
+ if (id == null || id.isEmpty()) return;
+ ItemStack item = getItemStack(id);
+ if (item == null) return;
+ context.drawItem(item, button.getX() + renderOffset, button.getY() + renderOffset);
+
+ // Draw tooltip
+ if (button.isMouseOver(mouseX, mouseY)) {
+ context.drawItemTooltip(textRenderer, item, mouseX, mouseY);
+ }
+ }
+
+ /**
+ * updates if the suggestions buttons should be visible based on if they have a value
+ */
+ @Override
+ public final void tick() {
+ super.tick();
+ //update suggestion buttons text
+ for (int i = 0; i < SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions; i++) {
+ String text = SearchOverManager.getSuggestion(i);
+ if (!text.isEmpty()) {
+ suggestionButtons[i].visible = true;
+
+ boolean isNewText = !text.equals(suggestionButtons[i].getMessage().getString());
+ if (!isNewText) continue;
+
+ suggestionButtons[i].setMessage(Text.literal(text).setStyle(Style.EMPTY));
+ } else {
+ suggestionButtons[i].visible = false;
+ }
+ }
+ }
+
+ /**
+ * When a key is pressed. If enter key pressed and search box selected close
+ */
+ @Override
+ public boolean keyPressed(int keyCode, int scanCode, int modifiers) {
+ if (keyCode == GLFW.GLFW_KEY_ENTER && searchField.isActive()) {
+ close();
+ return true;
+ }
+ return super.keyPressed(keyCode, scanCode, modifiers);
+ }
+
+ @Override
+ public boolean shouldPause() {
+ return false;
+ }
+
+ /**
+ * Closes the overlay screen and gets the manager to send a packet update about the sign
+ */
+ @Override
+ public void close() {
+ assert this.client != null;
+ assert this.client.player != null;
+ SearchOverManager.pushSearch();
+ super.close();
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java b/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java
new file mode 100644
index 00000000..b2a453a9
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/searchOverlay/SearchOverManager.java
@@ -0,0 +1,347 @@
+package de.hysky.skyblocker.skyblock.searchOverlay;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.mojang.brigadier.Command;
+import com.mojang.brigadier.CommandDispatcher;
+import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.item.tooltip.TooltipInfoType;
+import de.hysky.skyblocker.utils.Http;
+import de.hysky.skyblocker.utils.NEURepoManager;
+import de.hysky.skyblocker.utils.scheduler.MessageScheduler;
+import io.github.moulberry.repo.data.NEUItem;
+import it.unimi.dsi.fastutil.Pair;
+import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback;
+import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
+import net.minecraft.block.entity.SignBlockEntity;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.command.CommandRegistryAccess;
+import net.minecraft.network.packet.c2s.play.UpdateSignC2SPacket;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+
+import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal;
+
+public class SearchOverManager {
+
+ private static final MinecraftClient CLIENT = MinecraftClient.getInstance();
+ private static final Logger LOGGER = LoggerFactory.getLogger("Skyblocker Search Overlay");
+
+ private static final Pattern BAZAAR_ENCHANTMENT_PATTERN = Pattern.compile("ENCHANTMENT_(\\D*)_(\\d+)");
+ private static final Pattern AUCTION_PET_AND_RUNE_PATTERN = Pattern.compile("([A-Z0-9_]+);(\\d+)");
+
+ /**
+ * converts index (in array) +1 to a roman numeral
+ */
+ private static final String[] ROMAN_NUMERALS = new String[]{
+ "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI",
+ "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX"
+ };
+
+ private static @Nullable SignBlockEntity sign = null;
+ private static boolean signFront = true;
+ private static boolean isAuction;
+ private static boolean isCommand;
+
+ protected static String search = "";
+
+ // Use non-final variables and swap them to prevent concurrent modification
+ private static HashSet<String> bazaarItems;
+ private static HashSet<String> auctionItems;
+ private static HashMap<String, String> namesToId;
+
+ public static String[] suggestionsArray = {};
+
+ /**
+ * uses the skyblock api and Moulberry auction to load a list of items in bazaar and auction house
+ */
+ public static void init() {
+ ClientCommandRegistrationCallback.EVENT.register(SearchOverManager::registerSearchCommands);
+ NEURepoManager.runAsyncAfterLoad(SearchOverManager::loadItems);
+ }
+
+ private static void registerSearchCommands(CommandDispatcher<FabricClientCommandSource> dispatcher, CommandRegistryAccess registryAccess) {
+ if (SkyblockerConfigManager.get().general.searchOverlay.enableCommands) {
+ dispatcher.register(literal("ahs").executes(context -> startCommand(true)));
+ dispatcher.register(literal("bzs").executes(context -> startCommand(false)));
+ }
+ }
+
+ private static int startCommand(boolean isAuction) {
+ isCommand = true;
+ SearchOverManager.isAuction = isAuction;
+ search = "";
+ suggestionsArray = new String[]{};
+ CLIENT.send(() -> CLIENT.setScreen(new OverlayScreen(Text.of(""))));
+ return Command.SINGLE_SUCCESS;
+ }
+
+ private static void loadItems() {
+ HashSet<String> bazaarItems = new HashSet<>();
+ HashSet<String> auctionItems = new HashSet<>();
+ HashMap<String, String> namesToId = new HashMap<>();
+
+ //get bazaar items
+ try (Http.ApiResponse response = Http.sendHypixelRequest("skyblock/bazaar", "")) {
+ JsonObject products = JsonParser.parseString(response.content()).getAsJsonObject().get("products").getAsJsonObject();
+ for (Map.Entry<String, JsonElement> entry : products.entrySet()) {
+ if (entry.getValue().isJsonObject()) {
+ JsonObject product = entry.getValue().getAsJsonObject();
+ String id = product.get("product_id").getAsString();
+ int sellVolume = product.get("quick_status").getAsJsonObject().get("sellVolume").getAsInt();
+ if (sellVolume == 0)
+ continue; //do not add items that do not sell e.g. they are not actual in the bazaar
+ Matcher matcher = BAZAAR_ENCHANTMENT_PATTERN.matcher(id);
+ if (matcher.matches()) {//format enchantments
+ //remove ultimate if in name
+ String name = matcher.group(1);
+ if (!name.contains("WISE")) { //only way found to remove ultimate from everything but ultimate wise
+ name = name.replace("ULTIMATE_", "");
+ }
+ name = name.replace("_", " ");
+ name = capitalizeFully(name);
+ int enchantLevel = Integer.parseInt(matcher.group(2));
+ String level = "";
+ if (enchantLevel > 0) {
+ level = ROMAN_NUMERALS[enchantLevel - 1];
+ }
+ bazaarItems.add(name + " " + level);
+ namesToId.put(name + " " + level, matcher.group(1) + ";" + matcher.group(2));
+ continue;
+ }
+ //look up id for name
+ NEUItem neuItem = NEURepoManager.NEU_REPO.getItems().getItemBySkyblockId(id);
+ if (neuItem != null) {
+ String name = Formatting.strip(neuItem.getDisplayName());
+ bazaarItems.add(name);
+ namesToId.put(name, id);
+ continue;
+ }
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Failed to load bazaar item list! ", e);
+ }
+
+ //get auction items
+ try {
+ if (TooltipInfoType.THREE_DAY_AVERAGE.getData() == null) {
+ TooltipInfoType.THREE_DAY_AVERAGE.run();
+ }
+ for (Map.Entry<String, JsonElement> entry : TooltipInfoType.THREE_DAY_AVERAGE.getData().entrySet()) {
+ String id = entry.getKey();
+
+ Matcher matcher = AUCTION_PET_AND_RUNE_PATTERN.matcher(id);
+ if (matcher.find()) {//is a pet or rune convert id to name
+ String name = matcher.group(1).replace("_", " ");
+ name = capitalizeFully(name);
+ auctionItems.add(name);
+ namesToId.put(name, id);
+ continue;
+ }
+ //something else look up in NEU repo.
+ id = id.split("[+-]")[0];
+ NEUItem neuItem = NEURepoManager.NEU_REPO.getItems().getItemBySkyblockId(id);
+ if (neuItem != null) {
+ String name = Formatting.strip(neuItem.getDisplayName());
+ auctionItems.add(name);
+ namesToId.put(name, id);
+ continue;
+ }
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Failed to load auction house item list! ", e);
+ }
+
+ SearchOverManager.bazaarItems = bazaarItems;
+ SearchOverManager.auctionItems = auctionItems;
+ SearchOverManager.namesToId = namesToId;
+ }
+
+ /**
+ * Capitalizes the first letter off every word in a string
+ * @param str string to capitalize
+ */
+ private static String capitalizeFully(String str) {
+ if (str == null || str.isEmpty()) {
+ return str;
+ }
+
+ return Arrays.stream(str.split("\\s+"))
+ .map(t -> t.substring(0, 1).toUpperCase() + t.substring(1).toLowerCase())
+ .collect(Collectors.joining(" "));
+ }
+
+ /**
+ * Receives data when a search is started and resets values
+ * @param sign the sign that is being edited
+ * @param front if it's the front of the sign
+ * @param isAuction if the sign is loaded from the auction house menu or bazaar
+ */
+ public static void updateSign(@NotNull SignBlockEntity sign, boolean front, boolean isAuction) {
+ signFront = front;
+ SearchOverManager.sign = sign;
+ isCommand = false;
+ SearchOverManager.isAuction = isAuction;
+ if (SkyblockerConfigManager.get().general.searchOverlay.keepPreviousSearches) {
+ Text[] messages = SearchOverManager.sign.getText(signFront).getMessages(CLIENT.shouldFilterText());
+ search = messages[0].getString();
+ if (!messages[1].getString().isEmpty()) {
+ if (!search.endsWith(" ")) {
+ search += " ";
+ }
+ search += messages[1].getString();
+ }
+ } else {
+ search = "";
+ }
+ suggestionsArray = new String[]{};
+ }
+
+ /**
+ * Updates the search value and the suggestions based on that value.
+ * @param newValue new search value
+ */
+ protected static void updateSearch(String newValue) {
+ search = newValue;
+ //update the suggestion values
+ int totalSuggestions = SkyblockerConfigManager.get().general.searchOverlay.maxSuggestions;
+ if (newValue.isBlank() || totalSuggestions == 0) return; //do not search for empty value
+ suggestionsArray = (isAuction ? auctionItems : bazaarItems).stream().filter(item -> item.toLowerCase().contains(search.toLowerCase())).limit(totalSuggestions).toArray(String[]::new);
+ }
+
+ /**
+ * Gets the suggestion in the suggestion array at the index
+ * @param index index of suggestion
+ */
+ protected static String getSuggestion(int index) {
+ if (suggestionsArray.length > index && suggestionsArray[index] != null) {
+ return suggestionsArray[index];
+ } else {//there are no suggestions yet
+ return "";
+ }
+ }
+
+ protected static String getSuggestionId(int index) {
+ return namesToId.get(getSuggestion(index));
+ }
+
+ /**
+ * Gets the item name in the history array at the index
+ * @param index index of suggestion
+ */
+ protected static String getHistory(int index) {
+ if (isAuction) {
+ if (SkyblockerConfigManager.get().general.searchOverlay.auctionHistory.size() > index) {
+ return SkyblockerConfigManager.get().general.searchOverlay.auctionHistory.get(index);
+ }
+ } else {
+ if (SkyblockerConfigManager.get().general.searchOverlay.bazaarHistory.size() > index) {
+ return SkyblockerConfigManager.get().general.searchOverlay.bazaarHistory.get(index);
+ }
+ }
+ return null;
+ }
+
+ protected static String getHistoryId(int index) {
+ return namesToId.get(getHistory(index));
+ }
+
+ /**
+ * Add the current search value to the start of the history list and truncate to the max history value and save this to the config
+ */
+ private static void saveHistory() {
+ //save to history
+ SkyblockerConfig.SearchOverlay config = SkyblockerConfigManager.get().general.searchOverlay;
+ if (isAuction) {
+ if (config.auctionHistory.isEmpty() || !config.auctionHistory.get(0).equals(search)) {
+ config.auctionHistory.add(0, search);
+ if (config.auctionHistory.size() > config.historyLength) {
+ config.auctionHistory = config.auctionHistory.subList(0, config.historyLength);
+ }
+ }
+ } else {
+ if (config.bazaarHistory.isEmpty() || !config.bazaarHistory.get(0).equals(search)) {
+ config.bazaarHistory.add(0, search);
+ if (config.bazaarHistory.size() > config.historyLength) {
+ config.bazaarHistory = config.bazaarHistory.subList(0, config.historyLength);
+ }
+ }
+ }
+ SkyblockerConfigManager.save();
+ }
+
+ /**
+ *Saves the current value of ({@link SearchOverManager#search}) then pushes it to a command or sign depending on how the gui was opened
+ */
+ protected static void pushSearch() {
+ //save to history
+ if (!search.isEmpty()) {
+ saveHistory();
+ }
+ if (isCommand) {
+ pushCommand();
+ } else {
+ pushSign();
+ }
+ }
+
+ /**
+ * runs the command to search for the value in ({@link SearchOverManager#search})
+ */
+ private static void pushCommand() {
+ if (search.isEmpty()) return;
+
+ String command;
+ if (isAuction) {
+ command = "/ahSearch " + search;
+ } else {
+ command = "/bz " + search;
+ }
+ MessageScheduler.INSTANCE.sendMessageAfterCooldown(command);
+ }
+
+ /**
+ * pushes the ({@link SearchOverManager#search}) to the sign. It needs to split it over two lines without splitting a word
+ */
+ private static void pushSign() {
+ //splits text into 2 lines max = 30 chars
+ Pair<String, String> split = splitString(search);
+
+ // send packet to update sign
+ if (CLIENT.player != null && sign != null) {
+ Text[] messages = sign.getText(signFront).getMessages(CLIENT.shouldFilterText());
+ CLIENT.player.networkHandler.sendPacket(new UpdateSignC2SPacket(sign.getPos(), signFront,
+ split.left(),
+ split.right(),
+ messages[2].getString(),
+ messages[3].getString()
+ ));
+ }
+ }
+
+ static Pair<String, String> splitString(String s) {
+ if (s.length() <= 15) {
+ return Pair.of(s, "");
+ }
+ int index = s.lastIndexOf(' ', 15);
+ if (index == -1) {
+ return Pair.of(s.substring(0, 15), "");
+ }
+ return Pair.of(s.substring(0, index), s.substring(index + 1, Math.min(index + 16, s.length())).trim());
+ }
+}