aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de/hysky/skyblocker/skyblock/item
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/skyblock/item')
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/BackpackPreview.java235
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java15
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java23
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/CustomItemNames.java11
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java9
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java144
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java30
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/BackpackPreview.java204
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/CompactorDeletorPreview.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/item/CompactorDeletorPreview.java)6
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/CompactorPreviewTooltipComponent.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java)2
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ExoticTooltip.java96
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java (renamed from src/main/java/de/hysky/skyblocker/skyblock/item/PriceInfoTooltip.java)285
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java145
13 files changed, 764 insertions, 441 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/BackpackPreview.java b/src/main/java/de/hysky/skyblocker/skyblock/item/BackpackPreview.java
deleted file mode 100644
index 122ffe9b..00000000
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/BackpackPreview.java
+++ /dev/null
@@ -1,235 +0,0 @@
-package de.hysky.skyblocker.skyblock.item;
-
-import com.mojang.blaze3d.systems.RenderSystem;
-import de.hysky.skyblocker.SkyblockerMod;
-import de.hysky.skyblocker.utils.Utils;
-import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
-import net.fabricmc.loader.api.FabricLoader;
-import net.minecraft.client.MinecraftClient;
-import net.minecraft.client.font.TextRenderer;
-import net.minecraft.client.gui.DrawContext;
-import net.minecraft.client.gui.screen.Screen;
-import net.minecraft.client.gui.screen.ingame.HandledScreen;
-import net.minecraft.client.util.math.MatrixStack;
-import net.minecraft.entity.player.PlayerEntity;
-import net.minecraft.inventory.Inventory;
-import net.minecraft.item.ItemStack;
-import net.minecraft.nbt.*;
-import net.minecraft.util.Identifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public class BackpackPreview {
- private static final Logger LOGGER = LoggerFactory.getLogger(BackpackPreview.class);
- private static final Identifier TEXTURE = new Identifier(SkyblockerMod.NAMESPACE, "textures/gui/inventory_background.png");
- private static final Pattern ECHEST_PATTERN = Pattern.compile("Ender Chest.*\\((\\d+)/\\d+\\)");
- private static final Pattern BACKPACK_PATTERN = Pattern.compile("Backpack.*\\(Slot #(\\d+)\\)");
- private static final int STORAGE_SIZE = 27;
-
- private static final Inventory[] storage = new Inventory[STORAGE_SIZE];
- private static final boolean[] dirty = new boolean[STORAGE_SIZE];
-
- private static String loaded = ""; // uuid + sb profile currently loaded
- private static Path save_dir = null;
-
- public static void init() {
- ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
- if (screen instanceof HandledScreen<?> handledScreen) {
- updateStorage(handledScreen);
- }
- });
- }
-
- public static void tick() {
- Utils.update(); // force update isOnSkyblock to prevent crash on disconnect
- if (Utils.isOnSkyblock()) {
- // save all dirty storages
- saveStorage();
- // update save dir based on uuid and sb profile
- String uuid = MinecraftClient.getInstance().getSession().getUuidOrNull().toString().replaceAll("-", "");
- String profile = Utils.getProfile();
- if (profile != null && !profile.isEmpty()) {
- save_dir = FabricLoader.getInstance().getConfigDir().resolve("skyblocker/backpack-preview/" + uuid + "/" + profile);
- save_dir.toFile().mkdirs();
- if (loaded.equals(uuid + "/" + profile)) {
- // mark currently opened storage as dirty
- if (MinecraftClient.getInstance().currentScreen != null) {
- String title = MinecraftClient.getInstance().currentScreen.getTitle().getString();
- int index = getStorageIndexFromTitle(title);
- if (index != -1) dirty[index] = true;
- }
- } else {
- // load storage again because uuid/profile changed
- loaded = uuid + "/" + profile;
- loadStorage();
- }
- }
- }
- }
-
- public static void loadStorage() {
- assert (save_dir != null);
- for (int index = 0; index < STORAGE_SIZE; ++index) {
- storage[index] = null;
- dirty[index] = false;
- File file = save_dir.resolve(index + ".nbt").toFile();
- if (file.isFile()) {
- try {
- NbtCompound root = NbtIo.read(file);
- storage[index] = new DummyInventory(root);
- } catch (Exception e) {
- LOGGER.error("Failed to load backpack preview file: " + file.getName(), e);
- }
- }
- }
- }
-
- private static void saveStorage() {
- assert (save_dir != null);
- for (int index = 0; index < STORAGE_SIZE; ++index) {
- if (dirty[index]) {
- if (storage[index] != null) {
- try {
- NbtCompound root = new NbtCompound();
- NbtList list = new NbtList();
- for (int i = 9; i < storage[index].size(); ++i) {
- ItemStack stack = storage[index].getStack(i);
- NbtCompound item = new NbtCompound();
- if (stack.isEmpty()) {
- item.put("id", NbtString.of("minecraft:air"));
- item.put("Count", NbtInt.of(1));
- } else {
- item.put("id", NbtString.of(stack.getItem().toString()));
- item.put("Count", NbtInt.of(stack.getCount()));
- item.put("tag", stack.getNbt());
- }
- list.add(item);
- }
- root.put("list", list);
- root.put("size", NbtInt.of(storage[index].size() - 9));
- NbtIo.write(root, save_dir.resolve(index + ".nbt").toFile());
- dirty[index] = false;
- } catch (Exception e) {
- LOGGER.error("Failed to save backpack preview file: " + index + ".nbt", e);
- }
- }
- }
- }
- }
-
- public static void updateStorage(HandledScreen<?> screen) {
- String title = screen.getTitle().getString();
- int index = getStorageIndexFromTitle(title);
- if (index != -1) {
- storage[index] = screen.getScreenHandler().slots.get(0).inventory;
- dirty[index] = true;
- }
- }
-
- public static boolean renderPreview(DrawContext context, int index, int mouseX, int mouseY) {
- if (index >= 9 && index < 18) index -= 9;
- else if (index >= 27 && index < 45) index -= 18;
- else return false;
-
- if (storage[index] == null) return false;
- int rows = (storage[index].size() - 9) / 9;
-
- Screen screen = MinecraftClient.getInstance().currentScreen;
- if (screen == null) return false;
- int x = mouseX + 184 >= screen.width ? mouseX - 188 : mouseX + 8;
- int y = Math.max(0, mouseY - 16);
-
- RenderSystem.disableDepthTest();
- RenderSystem.setShaderTexture(0, TEXTURE);
- context.drawTexture(TEXTURE, x, y, 0, 0, 176, 7);
- for (int i = 0; i < rows; ++i) {
- context.drawTexture(TEXTURE, x, y + i * 18 + 7, 0, 7, 176, 18);
- }
- context.drawTexture(TEXTURE, x, y + rows * 18 + 7, 0, 25, 176, 7);
- RenderSystem.enableDepthTest();
-
- MatrixStack matrices = context.getMatrices();
- TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
- for (int i = 9; i < storage[index].size(); ++i) {
- int itemX = x + (i - 9) % 9 * 18 + 8;
- int itemY = y + (i - 9) / 9 * 18 + 8;
- matrices.push();
- matrices.translate(0, 0, 200);
- context.drawItem(storage[index].getStack(i), itemX, itemY);
- context.drawItemInSlot(textRenderer, storage[index].getStack(i), itemX, itemY);
- matrices.pop();
- }
-
- return true;
- }
-
- private static int getStorageIndexFromTitle(String title) {
- Matcher echest = ECHEST_PATTERN.matcher(title);
- if (echest.find()) return Integer.parseInt(echest.group(1)) - 1;
- Matcher backpack = BACKPACK_PATTERN.matcher(title);
- if (backpack.find()) return Integer.parseInt(backpack.group(1)) + 8;
- return -1;
- }
-}
-
-class DummyInventory implements Inventory {
- private final List<ItemStack> stacks;
-
- public DummyInventory(NbtCompound root) {
- stacks = new ArrayList<>(root.getInt("size") + 9);
- for (int i = 0; i < 9; ++i) stacks.add(ItemStack.EMPTY);
- root.getList("list", NbtCompound.COMPOUND_TYPE).forEach(item ->
- stacks.add(ItemStack.fromNbt((NbtCompound) item))
- );
- }
-
- @Override
- public int size() {
- return stacks.size();
- }
-
- @Override
- public boolean isEmpty() {
- return false;
- }
-
- @Override
- public ItemStack getStack(int slot) {
- return stacks.get(slot);
- }
-
- @Override
- public ItemStack removeStack(int slot, int amount) {
- return null;
- }
-
- @Override
- public ItemStack removeStack(int slot) {
- return null;
- }
-
- @Override
- public void setStack(int slot, ItemStack stack) {
- stacks.set(slot, stack);
- }
-
- @Override
- public void markDirty() {
- }
-
- @Override
- public boolean canPlayerUse(PlayerEntity player) {
- return false;
- }
-
- @Override
- public void clear() {
- }
-}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java
index 1496c90f..509f79b7 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorDyeColors.java
@@ -4,6 +4,7 @@ import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.Utils;
import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap;
@@ -34,7 +35,7 @@ public class CustomArmorDyeColors {
ItemStack heldItem = source.getPlayer().getMainHandStack();
if (hex != null && !isHexadecimalColor(hex)) {
- source.sendError(Text.translatable("skyblocker.customDyeColors.invalidHex"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.invalidHex")));
return Command.SINGLE_SUCCESS;
}
@@ -49,24 +50,24 @@ public class CustomArmorDyeColors {
if (customDyeColors.containsKey(itemUuid)) {
customDyeColors.removeInt(itemUuid);
SkyblockerConfigManager.save();
- source.sendFeedback(Text.translatable("skyblocker.customDyeColors.removed"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.removed")));
} else {
- source.sendFeedback(Text.translatable("skyblocker.customDyeColors.neverHad"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.neverHad")));
}
} else {
customDyeColors.put(itemUuid, Integer.decode("0x" + hex.replace("#", "")).intValue());
SkyblockerConfigManager.save();
- source.sendFeedback(Text.translatable("skyblocker.customDyeColors.added"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.added")));
}
} else {
- source.sendError(Text.translatable("skyblocker.customDyeColors.noItemUuid"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.noItemUuid")));
}
} else {
- source.sendError(Text.translatable("skyblocker.customDyeColors.notDyeable"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.notDyeable")));
return Command.SINGLE_SUCCESS;
}
} else {
- source.sendError(Text.translatable("skyblocker.customDyeColors.unableToSetColor"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customDyeColors.unableToSetColor")));
}
return Command.SINGLE_SUCCESS;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java
index 9242d47b..cec84b38 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomArmorTrims.java
@@ -3,8 +3,12 @@ package de.hysky.skyblocker.skyblock.item;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.suggestion.SuggestionProvider;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
+
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.events.SkyblockEvents;
+import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.Utils;
import dev.isxander.yacl3.config.v2.api.SerialEntry;
@@ -105,38 +109,43 @@ public class CustomArmorTrims {
if (customArmorTrims.containsKey(itemUuid)) {
customArmorTrims.remove(itemUuid);
SkyblockerConfigManager.save();
- source.sendFeedback(Text.translatable("skyblocker.customArmorTrims.removed"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customArmorTrims.removed")));
} else {
- source.sendFeedback(Text.translatable("skyblocker.customArmorTrims.neverHad"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customArmorTrims.neverHad")));
}
} else {
// Ensure that the material & trim are valid
ArmorTrimId trimId = new ArmorTrimId(material, pattern);
if (TRIMS_CACHE.get(trimId) == null) {
- source.sendError(Text.translatable("skyblocker.customArmorTrims.invalidMaterialOrPattern"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customArmorTrims.invalidMaterialOrPattern")));
return Command.SINGLE_SUCCESS;
}
customArmorTrims.put(itemUuid, trimId);
SkyblockerConfigManager.save();
- source.sendFeedback(Text.translatable("skyblocker.customArmorTrims.added"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customArmorTrims.added")));
}
} else {
- source.sendError(Text.translatable("skyblocker.customArmorTrims.noItemUuid"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customArmorTrims.noItemUuid")));
}
} else {
- source.sendError(Text.translatable("skyblocker.customArmorTrims.notAnArmorPiece"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customArmorTrims.notAnArmorPiece")));
return Command.SINGLE_SUCCESS;
}
} else {
- source.sendError(Text.translatable("skyblocker.customArmorTrims.unableToSetTrim"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customArmorTrims.unableToSetTrim")));
}
return Command.SINGLE_SUCCESS;
}
public record ArmorTrimId(@SerialEntry Identifier material, @SerialEntry Identifier pattern) implements Pair<Identifier, Identifier> {
+ public static final Codec<ArmorTrimId> CODEC = RecordCodecBuilder.create(instance -> instance.group(
+ Identifier.CODEC.fieldOf("material").forGetter(ArmorTrimId::material),
+ Identifier.CODEC.fieldOf("pattern").forGetter(ArmorTrimId::pattern))
+ .apply(instance, ArmorTrimId::new));
+
@Override
public Identifier left() {
return material();
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomItemNames.java b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomItemNames.java
index b6213eb6..e47444cf 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/CustomItemNames.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/CustomItemNames.java
@@ -3,6 +3,7 @@ package de.hysky.skyblocker.skyblock.item;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.Utils;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
@@ -42,9 +43,9 @@ public class CustomItemNames {
//Remove custom item name when the text argument isn't passed
customItemNames.remove(itemUuid);
SkyblockerConfigManager.save();
- source.sendFeedback(Text.translatable("skyblocker.customItemNames.removed"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customItemNames.removed")));
} else {
- source.sendFeedback(Text.translatable("skyblocker.customItemNames.neverHad"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customItemNames.neverHad")));
}
} else {
//If the text is provided then set the item's custom name to it
@@ -55,13 +56,13 @@ public class CustomItemNames {
customItemNames.put(itemUuid, text);
SkyblockerConfigManager.save();
- source.sendFeedback(Text.translatable("skyblocker.customItemNames.added"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.customItemNames.added")));
}
} else {
- source.sendError(Text.translatable("skyblocker.customItemNames.noItemUuid"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customItemNames.noItemUuid")));
}
} else {
- source.sendError(Text.translatable("skyblocker.customItemNames.unableToSetName"));
+ source.sendError(Constants.PREFIX.get().append(Text.translatable("skyblocker.customItemNames.unableToSetName")));
}
return Command.SINGLE_SUCCESS;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java
index ff88ef8d..2d929c28 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemProtection.java
@@ -3,6 +3,7 @@ package de.hysky.skyblocker.skyblock.item;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.Utils;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
@@ -44,18 +45,18 @@ public class ItemProtection {
protectedItems.add(itemUuid);
SkyblockerConfigManager.save();
- source.sendFeedback(Text.translatable("skyblocker.itemProtection.added", heldItem.getName()));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.itemProtection.added", heldItem.getName())));
} else {
protectedItems.remove(itemUuid);
SkyblockerConfigManager.save();
- source.sendFeedback(Text.translatable("skyblocker.itemProtection.removed", heldItem.getName()));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.itemProtection.removed", heldItem.getName())));
}
} else {
- source.sendFeedback(Text.translatable("skyblocker.itemProtection.noItemUuid"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.itemProtection.noItemUuid")));
}
} else {
- source.sendFeedback(Text.translatable("skyblocker.itemProtection.unableToProtect"));
+ source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.itemProtection.unableToProtect")));
}
return Command.SINGLE_SUCCESS;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java b/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java
new file mode 100644
index 00000000..ac9b1bf0
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java
@@ -0,0 +1,144 @@
+package de.hysky.skyblocker.skyblock.item;
+
+import java.io.ByteArrayInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.util.Base64;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+import com.mojang.util.UndashedUuid;
+
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.utils.Http;
+import de.hysky.skyblocker.utils.Http.ApiResponse;
+import de.hysky.skyblocker.utils.Utils;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtElement;
+import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtList;
+import net.minecraft.util.Util;
+
+public class MuseumItemCache {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MuseumItemCache.class);
+ private static final Path CACHE_FILE = SkyblockerMod.CONFIG_DIR.resolve("museum_item_cache.json");
+ private static final Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, ProfileMuseumData>> MUSEUM_ITEM_CACHE = new Object2ObjectOpenHashMap<>();
+ private static final Type MAP_TYPE = new TypeToken<Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, ProfileMuseumData>>>() {}.getType();
+
+ private static CompletableFuture<Void> loaded;
+
+ public static void init() {
+ ClientLifecycleEvents.CLIENT_STARTED.register(MuseumItemCache::load);
+ }
+
+ private static void load(MinecraftClient client) {
+ loaded = CompletableFuture.runAsync(() -> {
+ try (BufferedReader reader = Files.newBufferedReader(CACHE_FILE)) {
+ Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, ProfileMuseumData>> cachedData = SkyblockerMod.GSON.fromJson(reader, MAP_TYPE);
+
+ MUSEUM_ITEM_CACHE.putAll(cachedData);
+ LOGGER.info("[Skyblocker] Loaded museum items cache");
+ } catch (NoSuchFileException ignored) {
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to load cached museum items", e);
+ }
+ });
+ }
+
+ private static void save() {
+ CompletableFuture.runAsync(() -> {
+ try (BufferedWriter writer = Files.newBufferedWriter(CACHE_FILE)) {
+ SkyblockerMod.GSON.toJson(MUSEUM_ITEM_CACHE, writer);
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to save cached museum items!", e);
+ }
+ });
+ }
+
+ private static void updateData4ProfileMember(String uuid, String profileId) {
+ CompletableFuture.runAsync(() -> {
+ try (ApiResponse response = Http.sendHypixelRequest("skyblock/museum", "?profile=" + profileId)) {
+ //The request was successful
+ if (response.ok()) {
+ JsonObject profileData = JsonParser.parseString(response.content()).getAsJsonObject();
+ JsonObject memberData = profileData.get("members").getAsJsonObject().get(uuid).getAsJsonObject();
+
+ //We call them sets because it could either be a singular item or an entire armour set
+ Map<String, JsonElement> donatedSets = memberData.get("items").getAsJsonObject().asMap();
+
+ //Set of all found item ids on profile
+ ObjectOpenHashSet<String> itemIds = new ObjectOpenHashSet<>();
+
+ for (Map.Entry<String, JsonElement> donatedSet : donatedSets.entrySet()) {
+ //Item is plural here because the nbt is a list
+ String itemsData = donatedSet.getValue().getAsJsonObject().get("items").getAsJsonObject().get("data").getAsString();
+ NbtList items = NbtIo.readCompressed(new ByteArrayInputStream(Base64.getDecoder().decode(itemsData))).getList("i", NbtElement.COMPOUND_TYPE);
+
+ for (int i = 0; i < items.size(); i++) {
+ NbtCompound tag = items.getCompound(i).getCompound("tag");
+
+ if (tag.contains("ExtraAttributes")) {
+ NbtCompound extraAttributes = tag.getCompound("ExtraAttributes");
+
+ if (extraAttributes.contains("id")) itemIds.add(extraAttributes.getString("id"));
+ }
+ }
+ }
+
+ MUSEUM_ITEM_CACHE.get(uuid).put(profileId, new ProfileMuseumData(System.currentTimeMillis(), itemIds));
+ save();
+
+ LOGGER.info("[Skyblocker] Successfully updated museum item cache for profile {}", profileId);
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Failed to refresh museum item data for profile {}", profileId, e);
+ }
+ });
+ }
+
+ /**
+ * The cache is ticked upon switching skyblock servers
+ */
+ public static void tick(String profileId) {
+ if (loaded.isDone()) {
+ String uuid = UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull());
+ Object2ObjectOpenHashMap<String, ProfileMuseumData> playerData = MUSEUM_ITEM_CACHE.computeIfAbsent(uuid, uuid1 -> Util.make(new Object2ObjectOpenHashMap<>(), map -> {
+ map.put(profileId, ProfileMuseumData.EMPTY);
+ }));
+
+ if (playerData.get(profileId).stale()) updateData4ProfileMember(uuid, profileId);
+ }
+ }
+
+ public static boolean hasItemInMuseum(String id) {
+ String uuid = UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull());
+ ObjectOpenHashSet<String> collectedItemIds = MUSEUM_ITEM_CACHE.get(uuid).get(Utils.getProfileId()).collectedItemIds();
+
+ return collectedItemIds != null && collectedItemIds.contains(id);
+ }
+
+ private record ProfileMuseumData(long lastUpdated, ObjectOpenHashSet<String> collectedItemIds) {
+ private static final ProfileMuseumData EMPTY = new ProfileMuseumData(0L, null);
+ private static final long MAX_AGE = 86_400_000;
+
+ private boolean stale() {
+ return System.currentTimeMillis() > lastUpdated + MAX_AGE;
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java b/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java
index d4e6a0df..38121ea3 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/WikiLookup.java
@@ -1,23 +1,25 @@
package de.hysky.skyblocker.skyblock.item;
-import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;
import de.hysky.skyblocker.utils.ItemUtils;
-import de.hysky.skyblocker.utils.Utils;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
-import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.util.InputUtil;
+import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.screen.slot.Slot;
import net.minecraft.text.Text;
import net.minecraft.util.Util;
import org.lwjgl.glfw.GLFW;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import java.util.concurrent.CompletableFuture;
public class WikiLookup {
+ private static final Logger LOGGER = LoggerFactory.getLogger(WikiLookup.class);
public static KeyBinding wikiLookup;
- static final MinecraftClient client = MinecraftClient.getInstance();
- static String id;
+ private static String id;
public static void init() {
wikiLookup = KeyBindingHelper.registerKeyBinding(new KeyBinding(
@@ -28,22 +30,22 @@ public class WikiLookup {
));
}
- public static String getSkyblockId(Slot slot) {
+ public static void getSkyblockId(Slot slot) {
//Grabbing the skyblock NBT data
ItemUtils.getItemIdOptional(slot.getStack()).ifPresent(newId -> id = newId);
- return id;
}
- public static void openWiki(Slot slot) {
- if (Utils.isOnSkyblock()) {
- id = getSkyblockId(slot);
+ public static void openWiki(Slot slot, PlayerEntity player) {
+ if (SkyblockerConfigManager.get().general.wikiLookup.enableWikiLookup) {
+ getSkyblockId(slot);
try {
- String wikiLink = ItemRegistry.getWikiLink(id);
+ String wikiLink = ItemRepository.getWikiLink(id, player);
CompletableFuture.runAsync(() -> Util.getOperatingSystem().open(wikiLink));
} catch (IndexOutOfBoundsException | IllegalStateException e) {
- e.printStackTrace();
- if (client.player != null)
- client.player.sendMessage(Text.of("Error while retrieving wiki article..."), false);
+ LOGGER.error("[Skyblocker] Error while retrieving wiki article...", e);
+ if (player != null) {
+ player.sendMessage(Text.of("[Skyblocker] Error while retrieving wiki article, see logs..."), false);
+ }
}
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/BackpackPreview.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/BackpackPreview.java
new file mode 100644
index 00000000..5627b56d
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/BackpackPreview.java
@@ -0,0 +1,204 @@
+package de.hysky.skyblocker.skyblock.item.tooltip;
+
+import com.mojang.blaze3d.systems.RenderSystem;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds;
+import de.hysky.skyblocker.utils.Utils;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.client.font.TextRenderer;
+import net.minecraft.client.gui.DrawContext;
+import net.minecraft.client.gui.screen.Screen;
+import net.minecraft.client.gui.screen.ingame.HandledScreen;
+import net.minecraft.client.util.math.MatrixStack;
+import net.minecraft.inventory.Inventory;
+import net.minecraft.inventory.SimpleInventory;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtInt;
+import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtList;
+import net.minecraft.util.Identifier;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.nio.file.Path;
+import java.util.Objects;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class BackpackPreview {
+ private static final Logger LOGGER = LoggerFactory.getLogger(BackpackPreview.class);
+ private static final Identifier TEXTURE = new Identifier("textures/gui/container/generic_54.png");
+ private static final Pattern ECHEST_PATTERN = Pattern.compile("Ender Chest.*\\((\\d+)/\\d+\\)");
+ private static final Pattern BACKPACK_PATTERN = Pattern.compile("Backpack.*\\(Slot #(\\d+)\\)");
+ private static final int STORAGE_SIZE = 27;
+
+ private static final Storage[] storages = new Storage[STORAGE_SIZE];
+
+ /**
+ * The profile id of the currently loaded backpack preview.
+ */
+ private static String loaded;
+ private static Path saveDir;
+
+ public static void init() {
+ ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> {
+ if (screen instanceof HandledScreen<?> handledScreen) {
+ ScreenEvents.remove(screen).register(screen1 -> updateStorage(handledScreen));
+ }
+ });
+ }
+
+ public static void tick() {
+ Utils.update(); // force update isOnSkyblock to prevent crash on disconnect
+ if (Utils.isOnSkyblock()) {
+ // save all dirty storages
+ saveStorages();
+ // update save dir based on sb profile id
+ String id = MinecraftClient.getInstance().getSession().getUuidOrNull().toString().replaceAll("-", "") + "/" + Utils.getProfileId();
+ if (!id.equals(loaded)) {
+ saveDir = SkyblockerMod.CONFIG_DIR.resolve("backpack-preview/" + id);
+ //noinspection ResultOfMethodCallIgnored
+ saveDir.toFile().mkdirs();
+ // load storage again because profile id changed
+ loaded = id;
+ loadStorages();
+ }
+ }
+ }
+
+ private static void loadStorages() {
+ for (int index = 0; index < STORAGE_SIZE; ++index) {
+ storages[index] = null;
+ File storageFile = saveDir.resolve(index + ".nbt").toFile();
+ if (storageFile.isFile()) {
+ try {
+ storages[index] = Storage.fromNbt(Objects.requireNonNull(NbtIo.read(storageFile)));
+ } catch (Exception e) {
+ LOGGER.error("Failed to load backpack preview file: " + storageFile.getName(), e);
+ }
+ }
+ }
+ }
+
+ private static void saveStorages() {
+ for (int index = 0; index < STORAGE_SIZE; ++index) {
+ if (storages[index] != null && storages[index].dirty) {
+ saveStorage(index);
+ }
+ }
+ }
+
+ private static void saveStorage(int index) {
+ try {
+ NbtIo.write(storages[index].toNbt(), saveDir.resolve(index + ".nbt").toFile());
+ storages[index].markClean();
+ } catch (Exception e) {
+ LOGGER.error("Failed to save backpack preview file: " + index + ".nbt", e);
+ }
+ }
+
+ private static void updateStorage(HandledScreen<?> handledScreen) {
+ String title = handledScreen.getTitle().getString();
+ int index = getStorageIndexFromTitle(title);
+ if (index != -1) {
+ storages[index] = new Storage(handledScreen.getScreenHandler().slots.get(0).inventory, title, true);
+ }
+ }
+
+ public static boolean renderPreview(DrawContext context, Screen screen, int index, int mouseX, int mouseY) {
+ if (index >= 9 && index < 18) index -= 9;
+ else if (index >= 27 && index < 45) index -= 18;
+ else return false;
+
+ if (storages[index] == null) return false;
+ int rows = (storages[index].size() - 9) / 9;
+
+ int x = mouseX + 184 >= screen.width ? mouseX - 188 : mouseX + 8;
+ int y = Math.max(0, mouseY - 16);
+
+ MatrixStack matrices = context.getMatrices();
+ matrices.push();
+ matrices.translate(0f, 0f, 400f);
+
+ RenderSystem.enableDepthTest();
+ context.drawTexture(TEXTURE, x, y, 0, 0, 176, rows * 18 + 17);
+ context.drawTexture(TEXTURE, x, y + rows * 18 + 17, 0, 215, 176, 7);
+
+ TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
+ context.drawText(textRenderer, storages[index].name, x + 8, y + 6, 0x404040, false);
+
+ matrices.translate(0f, 0f, 200f);
+ for (int i = 9; i < storages[index].size(); ++i) {
+ ItemStack currentStack = storages[index].getStack(i);
+ int itemX = x + (i - 9) % 9 * 18 + 8;
+ int itemY = y + (i - 9) / 9 * 18 + 18;
+
+ if (SkyblockerConfigManager.get().general.itemInfoDisplay.itemRarityBackgrounds) {
+ ItemRarityBackgrounds.tryDraw(currentStack, context, itemX, itemY);
+ }
+
+ context.drawItem(currentStack, itemX, itemY);
+ context.drawItemInSlot(textRenderer, currentStack, itemX, itemY);
+ }
+
+ matrices.pop();
+
+ return true;
+ }
+
+ private static int getStorageIndexFromTitle(String title) {
+ Matcher echest = ECHEST_PATTERN.matcher(title);
+ if (echest.find()) return Integer.parseInt(echest.group(1)) - 1;
+ Matcher backpack = BACKPACK_PATTERN.matcher(title);
+ if (backpack.find()) return Integer.parseInt(backpack.group(1)) + 8;
+ return -1;
+ }
+
+ static class Storage {
+ private final Inventory inventory;
+ private final String name;
+ private boolean dirty;
+
+ private Storage(Inventory inventory, String name, boolean dirty) {
+ this.inventory = inventory;
+ this.name = name;
+ this.dirty = dirty;
+ }
+
+ private int size() {
+ return inventory.size();
+ }
+
+ private ItemStack getStack(int index) {
+ return inventory.getStack(index);
+ }
+
+ private void markClean() {
+ dirty = false;
+ }
+
+ @NotNull
+ private static Storage fromNbt(NbtCompound root) {
+ SimpleInventory inventory = new SimpleInventory(root.getList("list", NbtCompound.COMPOUND_TYPE).stream().map(NbtCompound.class::cast).map(ItemStack::fromNbt).toArray(ItemStack[]::new));
+ return new Storage(inventory, root.getString("name"), false);
+ }
+
+ @NotNull
+ private NbtCompound toNbt() {
+ NbtCompound root = new NbtCompound();
+ NbtList list = new NbtList();
+ for (int i = 0; i < size(); ++i) {
+ list.add(getStack(i).writeNbt(new NbtCompound()));
+ }
+ root.put("list", list);
+ root.put("size", NbtInt.of(size()));
+ root.putString("name", name);
+ return root;
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorDeletorPreview.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/CompactorDeletorPreview.java
index 7f5b96b9..9cf0356b 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorDeletorPreview.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/CompactorDeletorPreview.java
@@ -1,7 +1,7 @@
-package de.hysky.skyblocker.skyblock.item;
+package de.hysky.skyblocker.skyblock.item.tooltip;
import de.hysky.skyblocker.mixin.accessor.DrawContextInvoker;
-import de.hysky.skyblocker.skyblock.itemlist.ItemRegistry;
+import de.hysky.skyblocker.skyblock.itemlist.ItemRepository;
import de.hysky.skyblocker.utils.ItemUtils;
import it.unimi.dsi.fastutil.ints.IntIntPair;
import it.unimi.dsi.fastutil.ints.IntObjectPair;
@@ -43,7 +43,7 @@ public class CompactorDeletorPreview {
NbtCompound extraAttributes = ItemUtils.getExtraAttributes(stack);
if (extraAttributes == null) return false;
// Get the slots and their items from the nbt, which is in the format personal_compact_<slot_number> or personal_deletor_<slot_number>
- List<IntObjectPair<ItemStack>> slots = extraAttributes.getKeys().stream().filter(slot -> slot.contains(type.toLowerCase().substring(0, 7))).map(slot -> IntObjectPair.of(Integer.parseInt(slot.substring(17)), ItemRegistry.getItemStack(extraAttributes.getString(slot)))).toList();
+ List<IntObjectPair<ItemStack>> slots = extraAttributes.getKeys().stream().filter(slot -> slot.contains(type.toLowerCase().substring(0, 7))).map(slot -> IntObjectPair.of(Integer.parseInt(slot.substring(17)), ItemRepository.getItemStack(extraAttributes.getString(slot)))).toList();
List<TooltipComponent> components = tooltips.stream().map(Text::asOrderedText).map(TooltipComponent::of).collect(Collectors.toList());
IntIntPair dimensions = DIMENSIONS.getOrDefault(size, DEFAULT_DIMENSION);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/CompactorPreviewTooltipComponent.java
index 513d7d72..22498c02 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/CompactorPreviewTooltipComponent.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/CompactorPreviewTooltipComponent.java
@@ -1,4 +1,4 @@
-package de.hysky.skyblocker.skyblock.item;
+package de.hysky.skyblocker.skyblock.item.tooltip;
import de.hysky.skyblocker.SkyblockerMod;
import it.unimi.dsi.fastutil.ints.IntIntPair;
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ExoticTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ExoticTooltip.java
new file mode 100644
index 00000000..66d94890
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ExoticTooltip.java
@@ -0,0 +1,96 @@
+package de.hysky.skyblocker.skyblock.item.tooltip;
+
+import de.hysky.skyblocker.utils.Constants;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.text.MutableText;
+import net.minecraft.text.Text;
+import net.minecraft.util.Formatting;
+import net.minecraft.util.StringIdentifiable;
+
+public class ExoticTooltip {
+ public static String getExpectedHex(String id) {
+ String color = TooltipInfoType.COLOR.getData().get(id).getAsString();
+ if (color != null) {
+ String[] RGBValues = color.split(",");
+ return String.format("%02X%02X%02X", Integer.parseInt(RGBValues[0]), Integer.parseInt(RGBValues[1]), Integer.parseInt(RGBValues[2]));
+ } else {
+ ItemTooltip.LOGGER.warn("[Skyblocker Exotics] No expected color data found for id {}", id);
+ return null;
+ }
+ }
+
+ public static boolean isException(String id, String hex) {
+ if (id.startsWith("LEATHER") || id.equals("GHOST_BOOTS") || Constants.SEYMOUR_IDS.contains(id)) {
+ return true;
+ }
+ if (id.startsWith("RANCHER")) {
+ return Constants.RANCHERS.contains(hex);
+ }
+ if (id.contains("ADAPTIVE_CHESTPLATE")) {
+ return Constants.ADAPTIVE_CHEST.contains(hex);
+ } else if (id.contains("ADAPTIVE")) {
+ return Constants.ADAPTIVE.contains(hex);
+ }
+ if (id.startsWith("REAPER")) {
+ return Constants.REAPER.contains(hex);
+ }
+ if (id.startsWith("FAIRY")) {
+ return Constants.FAIRY_HEXES.contains(hex);
+ }
+ if (id.startsWith("CRYSTAL")) {
+ return Constants.CRYSTAL_HEXES.contains(hex);
+ }
+ if (id.contains("SPOOK")) {
+ return Constants.SPOOK.contains(hex);
+ }
+ return false;
+ }
+
+ public static DyeType checkDyeType(String hex) {
+ if (Constants.CRYSTAL_HEXES.contains(hex)) {
+ return DyeType.CRYSTAL;
+ }
+ if (Constants.FAIRY_HEXES.contains(hex)) {
+ return DyeType.FAIRY;
+ }
+ if (Constants.OG_FAIRY_HEXES.contains(hex)) {
+ return DyeType.OG_FAIRY;
+ }
+ if (Constants.SPOOK.contains(hex)) {
+ return DyeType.SPOOK;
+ }
+ if (Constants.GLITCHED.contains(hex)) {
+ return DyeType.GLITCHED;
+ }
+ return DyeType.EXOTIC;
+ }
+
+ public static boolean intendedDyed(NbtCompound ItemData) {
+ return ItemData.getCompound("ExtraAttributes").contains("dye_item");
+ }
+
+ public enum DyeType implements StringIdentifiable {
+ CRYSTAL("crystal", Formatting.AQUA),
+ FAIRY("fairy", Formatting.LIGHT_PURPLE),
+ OG_FAIRY("og_fairy", Formatting.DARK_PURPLE),
+ SPOOK("spook", Formatting.RED),
+ GLITCHED("glitched", Formatting.BLUE),
+ EXOTIC("exotic", Formatting.GOLD);
+ private final String name;
+ private final Formatting formatting;
+
+ DyeType(String name, Formatting formatting) {
+ this.name = name;
+ this.formatting = formatting;
+ }
+
+ @Override
+ public String asString() {
+ return name;
+ }
+
+ public MutableText getTranslatedText() {
+ return Text.translatable("skyblocker.exotic." + name).formatted(formatting);
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/PriceInfoTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java
index 0f84deea..e050aff5 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/item/PriceInfoTooltip.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java
@@ -1,10 +1,10 @@
-package de.hysky.skyblocker.skyblock.item;
+package de.hysky.skyblocker.skyblock.item.tooltip;
-import com.google.gson.Gson;
import com.google.gson.JsonObject;
+import de.hysky.skyblocker.SkyblockerMod;
import de.hysky.skyblocker.config.SkyblockerConfig;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
-import de.hysky.skyblocker.utils.Http;
+import de.hysky.skyblocker.utils.Constants;
import de.hysky.skyblocker.utils.ItemUtils;
import de.hysky.skyblocker.utils.Utils;
import de.hysky.skyblocker.utils.scheduler.Scheduler;
@@ -12,36 +12,25 @@ import net.minecraft.client.MinecraftClient;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtElement;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.net.http.HttpHeaders;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.CompletableFuture;
-public class PriceInfoTooltip {
- private static final Logger LOGGER = LoggerFactory.getLogger(PriceInfoTooltip.class.getName());
+public class ItemTooltip {
+ protected static final Logger LOGGER = LoggerFactory.getLogger(ItemTooltip.class.getName());
private static final MinecraftClient client = MinecraftClient.getInstance();
- private static JsonObject npcPricesJson;
- private static JsonObject bazaarPricesJson;
- private static JsonObject oneDayAvgPricesJson;
- private static JsonObject threeDayAvgPricesJson;
- private static JsonObject lowestPricesJson;
- private static JsonObject isMuseumJson;
- private static JsonObject motesPricesJson;
- private static volatile boolean nullMsgSend = false;
- private final static Gson gson = new Gson();
- private static final Map<String, String> apiAddresses;
- private static long npcHash = 0;
- private static long museumHash = 0;
- private static long motesHash = 0;
-
- public static void onInjectTooltip(ItemStack stack, TooltipContext context, List<Text> lines) {
+ protected static final SkyblockerConfig.ItemTooltip config = SkyblockerConfigManager.get().general.itemTooltip;
+ private static volatile boolean sentNullWarning = false;
+
+ public static void getTooltip(ItemStack stack, TooltipContext context, List<Text> lines) {
if (!Utils.isOnSkyblock() || client.player == null) return;
String name = getInternalNameFromNBT(stack, false);
@@ -54,65 +43,47 @@ public class PriceInfoTooltip {
neuName = internalID;
}
+ if (lines.isEmpty()) {
+ return;
+ }
+
int count = stack.getCount();
boolean bazaarOpened = lines.stream().anyMatch(each -> each.getString().contains("Buy price:") || each.getString().contains("Sell price:"));
- if (SkyblockerConfigManager.get().general.itemTooltip.enableNPCPrice) {
- if (npcPricesJson == null) {
- nullWarning();
- } else if (npcPricesJson.has(internalID)) {
- lines.add(Text.literal(String.format("%-21s", "NPC Price:"))
- .formatted(Formatting.YELLOW)
- .append(getCoinsMessage(npcPricesJson.get(internalID).getAsDouble(), count)));
- }
- }
-
- if (SkyblockerConfigManager.get().general.itemTooltip.enableMotesPrice && Utils.isInTheRift()) {
- if (motesPricesJson == null) {
- nullWarning();
- } else if (motesPricesJson.has(internalID)) {
- lines.add(Text.literal(String.format("%-20s", "Motes Price:"))
- .formatted(Formatting.LIGHT_PURPLE)
- .append(getMotesMessage(motesPricesJson.get(internalID).getAsInt(), count)));
- }
+ if (TooltipInfoType.NPC.isTooltipEnabledAndHasOrNullWarning(internalID)) {
+ lines.add(Text.literal(String.format("%-21s", "NPC Price:"))
+ .formatted(Formatting.YELLOW)
+ .append(getCoinsMessage(TooltipInfoType.NPC.getData().get(internalID).getAsDouble(), count)));
}
boolean bazaarExist = false;
- if (SkyblockerConfigManager.get().general.itemTooltip.enableBazaarPrice && !bazaarOpened) {
- if (bazaarPricesJson == null) {
- nullWarning();
- } else if (bazaarPricesJson.has(name)) {
- JsonObject getItem = bazaarPricesJson.getAsJsonObject(name);
- lines.add(Text.literal(String.format("%-18s", "Bazaar buy Price:"))
- .formatted(Formatting.GOLD)
- .append(getItem.get("buyPrice").isJsonNull()
- ? Text.literal("No data").formatted(Formatting.RED)
- : getCoinsMessage(getItem.get("buyPrice").getAsDouble(), count)));
- lines.add(Text.literal(String.format("%-19s", "Bazaar sell Price:"))
- .formatted(Formatting.GOLD)
- .append(getItem.get("sellPrice").isJsonNull()
- ? Text.literal("No data").formatted(Formatting.RED)
- : getCoinsMessage(getItem.get("sellPrice").getAsDouble(), count)));
- bazaarExist = true;
- }
+ if (TooltipInfoType.BAZAAR.isTooltipEnabledAndHasOrNullWarning(name) && !bazaarOpened) {
+ JsonObject getItem = TooltipInfoType.BAZAAR.getData().getAsJsonObject(name);
+ lines.add(Text.literal(String.format("%-18s", "Bazaar buy Price:"))
+ .formatted(Formatting.GOLD)
+ .append(getItem.get("buyPrice").isJsonNull()
+ ? Text.literal("No data").formatted(Formatting.RED)
+ : getCoinsMessage(getItem.get("buyPrice").getAsDouble(), count)));
+ lines.add(Text.literal(String.format("%-19s", "Bazaar sell Price:"))
+ .formatted(Formatting.GOLD)
+ .append(getItem.get("sellPrice").isJsonNull()
+ ? Text.literal("No data").formatted(Formatting.RED)
+ : getCoinsMessage(getItem.get("sellPrice").getAsDouble(), count)));
+ bazaarExist = true;
}
// bazaarOpened & bazaarExist check for lbin, because Skytils keeps some bazaar item data in lbin api
boolean lbinExist = false;
- if (SkyblockerConfigManager.get().general.itemTooltip.enableLowestBIN && !bazaarOpened && !bazaarExist) {
- if (lowestPricesJson == null) {
- nullWarning();
- } else if (lowestPricesJson.has(name)) {
- lines.add(Text.literal(String.format("%-19s", "Lowest BIN Price:"))
- .formatted(Formatting.GOLD)
- .append(getCoinsMessage(lowestPricesJson.get(name).getAsDouble(), count)));
- lbinExist = true;
- }
+ if (TooltipInfoType.LOWEST_BINS.isTooltipEnabledAndHasOrNullWarning(name) && !bazaarOpened && !bazaarExist) {
+ lines.add(Text.literal(String.format("%-19s", "Lowest BIN Price:"))
+ .formatted(Formatting.GOLD)
+ .append(getCoinsMessage(TooltipInfoType.LOWEST_BINS.getData().get(name).getAsDouble(), count)));
+ lbinExist = true;
}
if (SkyblockerConfigManager.get().general.itemTooltip.enableAvgBIN) {
- if (threeDayAvgPricesJson == null || oneDayAvgPricesJson == null) {
+ if (TooltipInfoType.ONE_DAY_AVERAGE.getData() == null || TooltipInfoType.THREE_DAY_AVERAGE.getData() == null) {
nullWarning();
} else {
/*
@@ -142,16 +113,16 @@ public class PriceInfoTooltip {
}
if (!neuName.isEmpty() && lbinExist) {
- SkyblockerConfig.Average type = SkyblockerConfigManager.get().general.itemTooltip.avg;
+ SkyblockerConfig.Average type = config.avg;
// "No data" line because of API not keeping old data, it causes NullPointerException
if (type == SkyblockerConfig.Average.ONE_DAY || type == SkyblockerConfig.Average.BOTH) {
lines.add(
Text.literal(String.format("%-19s", "1 Day Avg. Price:"))
.formatted(Formatting.GOLD)
- .append(oneDayAvgPricesJson.get(neuName) == null
+ .append(TooltipInfoType.ONE_DAY_AVERAGE.getData().get(neuName) == null
? Text.literal("No data").formatted(Formatting.RED)
- : getCoinsMessage(oneDayAvgPricesJson.get(neuName).getAsDouble(), count)
+ : getCoinsMessage(TooltipInfoType.ONE_DAY_AVERAGE.getData().get(neuName).getAsDouble(), count)
)
);
}
@@ -159,9 +130,9 @@ public class PriceInfoTooltip {
lines.add(
Text.literal(String.format("%-19s", "3 Day Avg. Price:"))
.formatted(Formatting.GOLD)
- .append(threeDayAvgPricesJson.get(neuName) == null
+ .append(TooltipInfoType.THREE_DAY_AVERAGE.getData().get(neuName) == null
? Text.literal("No data").formatted(Formatting.RED)
- : getCoinsMessage(threeDayAvgPricesJson.get(neuName).getAsDouble(), count)
+ : getCoinsMessage(TooltipInfoType.THREE_DAY_AVERAGE.getData().get(neuName).getAsDouble(), count)
)
);
}
@@ -169,35 +140,68 @@ public class PriceInfoTooltip {
}
}
- if (SkyblockerConfigManager.get().general.itemTooltip.enableMuseumDate && !bazaarOpened) {
- if (isMuseumJson == null) {
- nullWarning();
- } else {
- String timestamp = getTimestamp(stack);
-
- if (isMuseumJson.has(internalID)) {
- String itemCategory = isMuseumJson.get(internalID).getAsString();
- String format = switch (itemCategory) {
- case "Weapons" -> "%-18s";
- case "Armor" -> "%-19s";
- default -> "%-20s";
- };
- lines.add(Text.literal(String.format(format, "Museum: (" + itemCategory + ")"))
- .formatted(Formatting.LIGHT_PURPLE)
- .append(Text.literal(timestamp).formatted(Formatting.RED)));
- } else if (!timestamp.isEmpty()) {
- lines.add(Text.literal(String.format("%-21s", "Obtained: "))
- .formatted(Formatting.LIGHT_PURPLE)
- .append(Text.literal(timestamp).formatted(Formatting.RED)));
+ if (TooltipInfoType.MOTES.isTooltipEnabledAndHasOrNullWarning(internalID)) {
+ lines.add(Text.literal(String.format("%-20s", "Motes Price:"))
+ .formatted(Formatting.LIGHT_PURPLE)
+ .append(getMotesMessage(TooltipInfoType.MOTES.getData().get(internalID).getAsInt(), count)));
+ }
+
+ if (TooltipInfoType.MUSEUM.isTooltipEnabled() && !bazaarOpened) {
+ String timestamp = getTimestamp(stack);
+
+ if (TooltipInfoType.MUSEUM.hasOrNullWarning(internalID)) {
+ String itemCategory = TooltipInfoType.MUSEUM.getData().get(internalID).getAsString();
+ String format = switch (itemCategory) {
+ case "Weapons" -> "%-18s";
+ case "Armor" -> "%-19s";
+ default -> "%-20s";
+ };
+ lines.add(Text.literal(String.format(format, "Museum: (" + itemCategory + ")"))
+ .formatted(Formatting.LIGHT_PURPLE)
+ .append(Text.literal(timestamp).formatted(Formatting.RED)));
+ } else if (!timestamp.isEmpty()) {
+ lines.add(Text.literal(String.format("%-21s", "Obtained: "))
+ .formatted(Formatting.LIGHT_PURPLE)
+ .append(Text.literal(timestamp).formatted(Formatting.RED)));
+ }
+ }
+
+ if (TooltipInfoType.COLOR.isTooltipEnabledAndHasOrNullWarning(internalID) && stack.getNbt() != null) {
+ final NbtElement color = stack.getNbt().getCompound("display").get("color");
+
+ if (color != null) {
+ String colorHex = String.format("%06X", Integer.parseInt(color.asString()));
+ String expectedHex = ExoticTooltip.getExpectedHex(internalID);
+
+ boolean correctLine = false;
+ for (Text text : lines) {
+ String existingTooltip = text.getString() + " ";
+ if (existingTooltip.startsWith("Color: ")) {
+ correctLine = true;
+
+ addExoticTooltip(lines, internalID, stack.getNbt(), colorHex, expectedHex, existingTooltip);
+ break;
+ }
+ }
+
+ if (!correctLine) {
+ addExoticTooltip(lines, internalID, stack.getNbt(), colorHex, expectedHex, "");
}
}
}
}
- private static void nullWarning() {
- if (!nullMsgSend && client.player != null) {
- client.player.sendMessage(Text.translatable("skyblocker.itemTooltip.nullMessage"), false);
- nullMsgSend = true;
+ private static void addExoticTooltip(List<Text> lines, String internalID, NbtCompound nbt, String colorHex, String expectedHex, String existingTooltip) {
+ if (expectedHex != null && !colorHex.equalsIgnoreCase(expectedHex) && !ExoticTooltip.isException(internalID, colorHex) && !ExoticTooltip.intendedDyed(nbt)) {
+ final ExoticTooltip.DyeType type = ExoticTooltip.checkDyeType(colorHex);
+ lines.add(1, Text.literal(existingTooltip + Formatting.DARK_GRAY + "(").append(type.getTranslatedText()).append(Formatting.DARK_GRAY + ")"));
+ }
+ }
+
+ public static void nullWarning() {
+ if (!sentNullWarning && client.player != null) {
+ client.player.sendMessage(Constants.PREFIX.get().append(Text.translatable("skyblocker.itemTooltip.nullMessage")), false);
+ sentNullWarning = true;
}
}
@@ -233,6 +237,7 @@ public class PriceInfoTooltip {
return "";
}
+ // TODO What in the world is this?
public static String getInternalNameFromNBT(ItemStack stack, boolean internalIDOnly) {
NbtCompound ea = ItemUtils.getExtraAttributes(stack);
@@ -261,7 +266,7 @@ public class PriceInfoTooltip {
}
case "PET" -> {
if (ea.contains("petInfo")) {
- JsonObject petInfo = gson.fromJson(ea.getString("petInfo"), JsonObject.class);
+ JsonObject petInfo = SkyblockerMod.GSON.fromJson(ea.getString("petInfo"), JsonObject.class);
return "LVL_1_" + petInfo.get("tier").getAsString() + "_" + petInfo.get("type").getAsString();
}
}
@@ -339,86 +344,36 @@ public class PriceInfoTooltip {
public static void init() {
Scheduler.INSTANCE.scheduleCyclic(() -> {
if (!Utils.isOnSkyblock() && 0 < minute++) {
- nullMsgSend = false;
+ sentNullWarning = false;
return;
}
List<CompletableFuture<Void>> futureList = new ArrayList<>();
- if (SkyblockerConfigManager.get().general.itemTooltip.enableAvgBIN) {
- SkyblockerConfig.Average type = SkyblockerConfigManager.get().general.itemTooltip.avg;
-
- if (type == SkyblockerConfig.Average.BOTH || oneDayAvgPricesJson == null || threeDayAvgPricesJson == null || minute % 5 == 0) {
- futureList.add(CompletableFuture.runAsync(() -> {
- oneDayAvgPricesJson = downloadPrices("1 day avg");
- threeDayAvgPricesJson = downloadPrices("3 day avg");
- }));
+
+ TooltipInfoType.NPC.downloadIfEnabled(futureList);
+ TooltipInfoType.BAZAAR.downloadIfEnabled(futureList);
+ TooltipInfoType.LOWEST_BINS.downloadIfEnabled(futureList);
+
+ if (config.enableAvgBIN) {
+ SkyblockerConfig.Average type = config.avg;
+
+ if (type == SkyblockerConfig.Average.BOTH || TooltipInfoType.ONE_DAY_AVERAGE.getData() == null || TooltipInfoType.THREE_DAY_AVERAGE.getData() == null || minute % 5 == 0) {
+ TooltipInfoType.ONE_DAY_AVERAGE.download(futureList);
+ TooltipInfoType.THREE_DAY_AVERAGE.download(futureList);
} else if (type == SkyblockerConfig.Average.ONE_DAY) {
- futureList.add(CompletableFuture.runAsync(() -> oneDayAvgPricesJson = downloadPrices("1 day avg")));
+ TooltipInfoType.ONE_DAY_AVERAGE.download(futureList);
} else if (type == SkyblockerConfig.Average.THREE_DAY) {
- futureList.add(CompletableFuture.runAsync(() -> threeDayAvgPricesJson = downloadPrices("3 day avg")));
+ TooltipInfoType.THREE_DAY_AVERAGE.download(futureList);
}
}
- if (SkyblockerConfigManager.get().general.itemTooltip.enableLowestBIN || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator)
- futureList.add(CompletableFuture.runAsync(() -> lowestPricesJson = downloadPrices("lowest bins")));
-
- if (SkyblockerConfigManager.get().general.itemTooltip.enableBazaarPrice || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator)
- futureList.add(CompletableFuture.runAsync(() -> bazaarPricesJson = downloadPrices("bazaar")));
-
- if (SkyblockerConfigManager.get().general.itemTooltip.enableNPCPrice && npcPricesJson == null)
- futureList.add(CompletableFuture.runAsync(() -> npcPricesJson = downloadPrices("npc")));
-
- if (SkyblockerConfigManager.get().general.itemTooltip.enableMuseumDate && isMuseumJson == null)
- futureList.add(CompletableFuture.runAsync(() -> isMuseumJson = downloadPrices("museum")));
- if (SkyblockerConfigManager.get().general.itemTooltip.enableMotesPrice && motesPricesJson == null)
- futureList.add(CompletableFuture.runAsync(() -> motesPricesJson = downloadPrices("motes")));
+ TooltipInfoType.MOTES.downloadIfEnabled(futureList);
+ TooltipInfoType.MUSEUM.downloadIfEnabled(futureList);
+ TooltipInfoType.COLOR.downloadIfEnabled(futureList);
minute++;
- CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0]))
- .whenComplete((unused, throwable) -> nullMsgSend = false);
+ CompletableFuture.allOf(futureList.toArray(CompletableFuture[]::new))
+ .whenComplete((unused, throwable) -> sentNullWarning = false);
}, 1200, true);
}
-
- private static JsonObject downloadPrices(String type) {
- try {
- String url = apiAddresses.get(type);
-
- if (type.equals("npc") || type.equals("museum") || type.equals("motes")) {
- HttpHeaders headers = Http.sendHeadRequest(url);
- long combinedHash = Http.getEtag(headers).hashCode() + Http.getLastModified(headers).hashCode();
-
- switch (type) {
- case "npc": if (npcHash == combinedHash) return npcPricesJson; else npcHash = combinedHash;
- case "museum": if (museumHash == combinedHash) return isMuseumJson; else museumHash = combinedHash;
- case "motes": if (motesHash == combinedHash) return motesPricesJson; else motesHash = combinedHash;
- }
- }
-
- String apiResponse = Http.sendGetRequest(url);
-
- return new Gson().fromJson(apiResponse, JsonObject.class);
- } catch (Exception e) {
- LOGGER.warn("[Skyblocker] Failed to download " + type + " prices!", e);
- return null;
- }
- }
-
- public static JsonObject getBazaarPrices() {
- return bazaarPricesJson;
- }
-
- public static JsonObject getLBINPrices() {
- return lowestPricesJson;
- }
-
- static {
- apiAddresses = new HashMap<>();
- apiAddresses.put("1 day avg", "https://moulberry.codes/auction_averages_lbin/1day.json");
- apiAddresses.put("3 day avg", "https://moulberry.codes/auction_averages_lbin/3day.json");
- apiAddresses.put("bazaar", "https://hysky.de/api/bazaar");
- apiAddresses.put("lowest bins", "https://hysky.de/api/auctions/lowestbins");
- apiAddresses.put("npc", "https://hysky.de/api/npcprice");
- apiAddresses.put("museum", "https://hysky.de/api/museum");
- apiAddresses.put("motes", "https://hysky.de/api/motesprice");
- }
} \ No newline at end of file
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
new file mode 100644
index 00000000..086fcb00
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/TooltipInfoType.java
@@ -0,0 +1,145 @@
+package de.hysky.skyblocker.skyblock.item.tooltip;
+
+import com.google.gson.JsonObject;
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.config.SkyblockerConfig;
+import de.hysky.skyblocker.config.SkyblockerConfigManager;
+import de.hysky.skyblocker.utils.Http;
+import de.hysky.skyblocker.utils.Utils;
+import org.jetbrains.annotations.Nullable;
+
+import java.net.http.HttpHeaders;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.function.Predicate;
+
+public enum TooltipInfoType implements Runnable {
+ NPC("https://hysky.de/api/npcprice", itemTooltip -> itemTooltip.enableNPCPrice, true),
+ BAZAAR("https://hysky.de/api/bazaar", itemTooltip -> itemTooltip.enableBazaarPrice || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator, itemTooltip -> itemTooltip.enableBazaarPrice, false),
+ LOWEST_BINS("https://hysky.de/api/auctions/lowestbins", itemTooltip -> itemTooltip.enableLowestBIN || SkyblockerConfigManager.get().locations.dungeons.dungeonChestProfit.enableProfitCalculator, 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),
+ MOTES("https://hysky.de/api/motesprice", itemTooltip -> itemTooltip.enableMotesPrice, itemTooltip -> itemTooltip.enableMotesPrice && Utils.isInTheRift(), true),
+ MUSEUM("https://hysky.de/api/museum", itemTooltip -> itemTooltip.enableMuseumDate, true),
+ COLOR("https://hysky.de/api/color", itemTooltip -> itemTooltip.enableExoticTooltip, true);
+
+ private final String address;
+ private final Predicate<SkyblockerConfig.ItemTooltip> dataEnabled;
+ private final Predicate<SkyblockerConfig.ItemTooltip> tooltipEnabled;
+ private JsonObject data;
+ private final boolean cacheable;
+ private long hash;
+
+ /**
+ * @param address the address to download the data from
+ * @param enabled the predicate to check if the data should be downloaded and the tooltip should be shown
+ * @param cacheable whether the data should be cached
+ */
+ TooltipInfoType(String address, Predicate<SkyblockerConfig.ItemTooltip> enabled, boolean cacheable) {
+ this(address, enabled, enabled, null, cacheable);
+ }
+
+ /**
+ * @param address the address to download the data from
+ * @param dataEnabled the predicate to check if data should be downloaded
+ * @param tooltipEnabled the predicate to check if the tooltip should be shown
+ * @param cacheable whether the data should be cached
+ */
+ TooltipInfoType(String address, Predicate<SkyblockerConfig.ItemTooltip> dataEnabled, Predicate<SkyblockerConfig.ItemTooltip> tooltipEnabled, boolean cacheable) {
+ this(address, dataEnabled, tooltipEnabled, null, cacheable);
+ }
+
+ /**
+ * @param address the address to download the data from
+ * @param dataEnabled the predicate to check if data should be downloaded
+ * @param tooltipEnabled the predicate to check if the tooltip should be shown
+ * @param data the data
+ * @param cacheable whether the data should be cached
+ */
+ TooltipInfoType(String address, Predicate<SkyblockerConfig.ItemTooltip> dataEnabled, Predicate<SkyblockerConfig.ItemTooltip> tooltipEnabled, @Nullable JsonObject data, boolean cacheable) {
+ this.address = address;
+ this.dataEnabled = dataEnabled;
+ this.tooltipEnabled = tooltipEnabled;
+ this.data = data;
+ this.cacheable = cacheable;
+ }
+
+ /**
+ * @return whether the data should be downloaded
+ */
+ private boolean isDataEnabled() {
+ return dataEnabled.test(ItemTooltip.config);
+ }
+
+ /**
+ * @return whether the tooltip should be shown
+ */
+ public boolean isTooltipEnabled() {
+ return tooltipEnabled.test(ItemTooltip.config);
+ }
+
+ public JsonObject getData() {
+ return data;
+ }
+
+ /**
+ * Checks if the data has the given member name and sends a warning message if data is null.
+ *
+ * @param memberName the member name to check
+ * @return whether the data has the given member name or not
+ */
+ public boolean hasOrNullWarning(String memberName) {
+ if (data == null) {
+ ItemTooltip.nullWarning();
+ return false;
+ } else return data.has(memberName);
+ }
+
+ /**
+ * Checks if the tooltip is enabled and the data has the given member name and sends a warning message if data is null.
+ *
+ * @param memberName the member name to check
+ * @return whether the tooltip is enabled and the data has the given member name or not
+ */
+ public boolean isTooltipEnabledAndHasOrNullWarning(String memberName) {
+ return isTooltipEnabled() && hasOrNullWarning(memberName);
+ }
+
+ /**
+ * Downloads the data if it is enabled.
+ *
+ * @param futureList the list to add the future to
+ */
+ public void downloadIfEnabled(List<CompletableFuture<Void>> futureList) {
+ if (isDataEnabled()) {
+ download(futureList);
+ }
+ }
+
+ /**
+ * Downloads the data.
+ *
+ * @param futureList the list to add the future to
+ */
+ public void download(List<CompletableFuture<Void>> futureList) {
+ futureList.add(CompletableFuture.runAsync(this));
+ }
+
+ /**
+ * Downloads the data.
+ */
+ @Override
+ public void run() {
+ try {
+ if (cacheable) {
+ HttpHeaders headers = Http.sendHeadRequest(address);
+ long hash = Http.getEtag(headers).hashCode() + Http.getLastModified(headers).hashCode();
+ if (this.hash == hash) return;
+ else this.hash = hash;
+ }
+ data = SkyblockerMod.GSON.fromJson(Http.sendGetRequest(address), JsonObject.class);
+ } catch (Exception e) {
+ ItemTooltip.LOGGER.warn("[Skyblocker] Failed to download " + this + " prices!", e);
+ }
+ }
+}