From 71ca871f2a2441813b26e24467b8e75b166f747a Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Thu, 26 Jun 2025 12:07:10 -0400 Subject: Add attribute list for bazaar price support The code for generating the list has been left in so that it can easily be regenerated if necessary. --- .../skyblocker/skyblock/hunting/Attribute.java | 16 ++++ .../skyblocker/skyblock/hunting/Attributes.java | 53 +++++++++++ .../skyblock/hunting/AttributesDebug.java | 103 +++++++++++++++++++++ .../java/de/hysky/skyblocker/utils/ItemUtils.java | 10 +- 4 files changed, 177 insertions(+), 5 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/hunting/Attribute.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/hunting/Attributes.java create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/hunting/AttributesDebug.java (limited to 'src/main/java/de') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attribute.java b/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attribute.java new file mode 100644 index 00000000..4090a571 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attribute.java @@ -0,0 +1,16 @@ +package de.hysky.skyblocker.skyblock.hunting; + +import java.util.List; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; + +public record Attribute(String name, String shardName, String id, String apiId) { + private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + Codec.STRING.fieldOf("name").forGetter(Attribute::name), + Codec.STRING.fieldOf("shardName").forGetter(Attribute::shardName), + Codec.STRING.fieldOf("id").forGetter(Attribute::id), + Codec.STRING.fieldOf("apiId").forGetter(Attribute::apiId) + ).apply(instance, Attribute::new)); + public static final Codec> LIST_CODEC = CODEC.listOf(); +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attributes.java b/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attributes.java new file mode 100644 index 00000000..e24f11bd --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/hunting/Attributes.java @@ -0,0 +1,53 @@ +package de.hysky.skyblocker.skyblock.hunting; + +import java.io.BufferedReader; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; + +import com.google.gson.JsonParser; +import com.mojang.logging.LogUtils; +import com.mojang.serialization.JsonOps; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.annotations.Init; +import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.component.ComponentHolder; +import net.minecraft.component.DataComponentTypes; +import net.minecraft.util.Identifier; + +public class Attributes { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final Identifier ATTRIBUTES_FILE = Identifier.of(SkyblockerMod.NAMESPACE, "hunting/attributes.json"); + private static List attributes = List.of(); + + @Init + public static void init() { + ClientLifecycleEvents.CLIENT_STARTED.register(Attributes::loadShards); + } + + private static void loadShards(MinecraftClient client) { + CompletableFuture.runAsync(() -> { + try (BufferedReader reader = client.getResourceManager().openAsReader(ATTRIBUTES_FILE)) { + attributes = Attribute.LIST_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).getOrThrow(); + } catch (Exception e) { + LOGGER.error("[Skyblocker Attributes] Failed to load attributes.", e); + } + }); + } + + @Nullable + public static Attribute getAttributeFromItemName(ComponentHolder stack) { + if (!stack.contains(DataComponentTypes.CUSTOM_NAME)) return null; + String name = stack.get(DataComponentTypes.CUSTOM_NAME).getString(); + + for (Attribute attribute : attributes) { + if (attribute.shardName().equals(name)) return attribute; + } + + return null; + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/hunting/AttributesDebug.java b/src/main/java/de/hysky/skyblocker/skyblock/hunting/AttributesDebug.java new file mode 100644 index 00000000..1ba4bd72 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/hunting/AttributesDebug.java @@ -0,0 +1,103 @@ +package de.hysky.skyblocker.skyblock.hunting; + +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Locale; +import java.util.concurrent.CompletableFuture; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; + +import com.mojang.logging.LogUtils; +import com.mojang.serialization.JsonOps; + +import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.skyblock.item.tooltip.info.TooltipInfoType; +import de.hysky.skyblocker.utils.ItemUtils; +import de.hysky.skyblocker.utils.RomanNumerals; +import de.hysky.skyblocker.utils.container.ContainerSolver; +import de.hysky.skyblocker.utils.container.ContainerSolverManager; +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents; +import net.fabricmc.fabric.api.client.screen.v1.ScreenKeyboardEvents; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.GenericContainerScreenHandler; + +public class AttributesDebug { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final MinecraftClient CLIENT = MinecraftClient.getInstance(); + private static final List DUMPED_ATTRIBUTES = new ArrayList<>(); + private static final Pattern SOURCE_PATTERN = Pattern.compile("Source: (?[A-za-z ]+) Shard \\((?[CUREL]\\d+)\\)"); + private static final Path ATTRIBUTE_EXPORT_DEST = SkyblockerMod.CONFIG_DIR.resolve("attribute_export.json"); + + //@Init + public static void init() { + ScreenEvents.AFTER_INIT.register((client, screen, scaledWidth, scaledHeight) -> { + ScreenKeyboardEvents.afterKeyPress(screen).register((screen1, key, scancode, modifiers) -> { + if (key == GLFW.GLFW_KEY_G) { + dumpAttributes(); + } else if (key == GLFW.GLFW_KEY_J) { + exportAttributes(); + } + }); + }); + } + + private static void dumpAttributes() { + if (CLIENT.currentScreen instanceof HandledScreen screen && screen.getTitle().getString().equals("Attribute Menu")) { + @SuppressWarnings("unchecked") + Int2ObjectMap slots = ContainerSolverManager.slotMap(screen.getScreenHandler().slots.subList(0, ((HandledScreen) screen).getScreenHandler().getRows() * 9)); + ContainerSolver.trimEdges(slots, 6); + + for (ItemStack stack : slots.values()) { + if (stack.isEmpty()) continue; + + String name = stack.getName().getString(); + Matcher sourceMatcher = ItemUtils.getLoreLineIfMatch(stack, SOURCE_PATTERN); + + //Remove roman numeral from name + List words = new ArrayList<>(Arrays.asList(name.split(" "))); + if (RomanNumerals.isValidRomanNumeral(words.getLast().strip())) { + words.removeLast(); + name = String.join(" ", words); + } + + if (sourceMatcher != null) { + String shardName = sourceMatcher.group("shardName"); + String id = sourceMatcher.group("id"); + String apiIdGuess = "SHARD_" + shardName.replace(' ', '_').toUpperCase(Locale.ENGLISH); + boolean hasDataForId = TooltipInfoType.BAZAAR.getData().containsKey(apiIdGuess); + + //Most attributes follow the format above but some have different ids so this is to catch those ones + if (!hasDataForId) LOGGER.warn("[Skyblocker Attributes Debug] No data found for shard. Shard Name: {}", shardName); + + Attribute attribute = new Attribute(name, shardName, id, apiIdGuess); + DUMPED_ATTRIBUTES.add(attribute); + } else { + LOGGER.warn("[Skyblocker Attributes Debug] Failed to match shard! Name: {}", name); + } + } + } + } + + private static void exportAttributes() { + if (CLIENT.currentScreen instanceof HandledScreen screen && screen.getTitle().getString().equals("Attribute Menu")) { + List copy = DUMPED_ATTRIBUTES.stream().distinct().toList(); + + CompletableFuture.runAsync(() -> { + try { + Files.writeString(ATTRIBUTE_EXPORT_DEST, Attribute.LIST_CODEC.encodeStart(JsonOps.INSTANCE, copy).getOrThrow().toString()); + } catch (Exception e) { + LOGGER.error("[Skyblocker Attributes Debug] Failed to export attributes!", e); + } + }); + } + } +} diff --git a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java index a27ed97a..dc3f3c5a 100644 --- a/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java +++ b/src/main/java/de/hysky/skyblocker/utils/ItemUtils.java @@ -11,6 +11,8 @@ import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.debug.Debug; +import de.hysky.skyblocker.skyblock.hunting.Attribute; +import de.hysky.skyblocker.skyblock.hunting.Attributes; import de.hysky.skyblocker.skyblock.item.PetInfo; import de.hysky.skyblocker.skyblock.item.tooltip.adders.ObtainedDateTooltip; import de.hysky.skyblocker.skyblock.item.tooltip.info.TooltipInfoType; @@ -185,11 +187,9 @@ public final class ItemUtils { } } case "ATTRIBUTE_SHARD" -> { - if (customData.contains("attributes")) { - NbtCompound shards = customData.getCompoundOrEmpty("attributes"); - String shard = shards.getKeys().stream().findFirst().orElse(""); - return id + "-" + shard.toUpperCase(Locale.ENGLISH) + "_" + shards.getInt(shard, 0); - } + Attribute attribute = Attributes.getAttributeFromItemName(itemStack); + + if (attribute != null) return attribute.apiId(); } case "NEW_YEAR_CAKE" -> { return id + "_" + customData.getInt("new_years_cake", 0); -- cgit