aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/de/hysky/skyblocker/skyblock/PetCache.java')
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/PetCache.java149
1 files changed, 149 insertions, 0 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java b/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java
new file mode 100644
index 00000000..d8cd6e48
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java
@@ -0,0 +1,149 @@
+package de.hysky.skyblocker.skyblock;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.util.concurrent.CompletableFuture;
+import java.util.Optional;
+
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+
+import com.google.gson.JsonParser;
+import com.mojang.logging.LogUtils;
+import com.mojang.serialization.Codec;
+import com.mojang.serialization.JsonOps;
+import com.mojang.serialization.codecs.RecordCodecBuilder;
+
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.utils.ItemUtils;
+import de.hysky.skyblocker.utils.Utils;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import net.fabricmc.fabric.api.client.screen.v1.ScreenEvents;
+import net.minecraft.client.gui.screen.ingame.GenericContainerScreen;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.screen.slot.Slot;
+
+/**
+ * Doesn't work with auto pet right now because thats complicated.
+ *
+ * Want support? Ask the Admins for a Mod API event or open your pets menu.
+ */
+public class PetCache {
+ private static final Logger LOGGER = LogUtils.getLogger();
+ private static final Path FILE = SkyblockerMod.CONFIG_DIR.resolve("pet_cache.json");
+ private static final Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, PetInfo>> CACHED_PETS = new Object2ObjectOpenHashMap<>();
+
+ /**
+ * Used in case the server lags to prevent the screen tick check from overwriting the clicked pet logic
+ */
+ private static boolean shouldLook4Pets;
+
+ public static void init() {
+ load();
+
+ ScreenEvents.BEFORE_INIT.register((_client, screen, _scaledWidth, _scaledHeight) -> {
+ if (Utils.isOnSkyblock() && screen instanceof GenericContainerScreen genericContainerScreen) {
+ if (genericContainerScreen.getTitle().getString().startsWith("Pets")) {
+ shouldLook4Pets = true;
+
+ ScreenEvents.afterTick(screen).register(screen1 -> {
+ if (shouldLook4Pets) {
+ for (Slot slot : genericContainerScreen.getScreenHandler().slots) {
+ ItemStack stack = slot.getStack();
+
+ if (!stack.isEmpty() && ItemUtils.getLoreLineIf(stack, line -> line.equals("Click to despawn!")) != null) {
+ shouldLook4Pets = false;
+ parsePet(stack, false);
+
+ break;
+ }
+ }
+ }
+ });
+ }
+ }
+ });
+ }
+
+ private static void load() {
+ CompletableFuture.runAsync(() -> {
+ try (BufferedReader reader = Files.newBufferedReader(FILE)) {
+ CACHED_PETS.putAll(PetInfo.SERIALIZATION_CODEC.parse(JsonOps.INSTANCE, JsonParser.parseReader(reader)).getOrThrow());
+ } catch (NoSuchFileException ignored) {
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Pet Cache] Failed to load saved pet!", e);
+ }
+ });
+ }
+
+ private static void save() {
+ CompletableFuture.runAsync(() -> {
+ try (BufferedWriter writer = Files.newBufferedWriter(FILE)) {
+ SkyblockerMod.GSON.toJson(PetInfo.SERIALIZATION_CODEC.encodeStart(JsonOps.INSTANCE, CACHED_PETS).getOrThrow(), writer);
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker Pet Cache] Failed to save pet data to the cache!", e);
+ }
+ });
+ }
+
+ public static void handlePetEquip(Slot slot, int slotId) {
+ //Ignore inventory clicks
+ if (slotId >= 0 && slotId <= 53) {
+ ItemStack stack = slot.getStack();
+
+ if (!stack.isEmpty()) parsePet(stack, true);
+ }
+ }
+
+ private static void parsePet(ItemStack stack, boolean clicked) {
+ String id = ItemUtils.getItemId(stack);
+ String profileId = Utils.getProfileId();
+
+ if (id.equals("PET") && !profileId.isEmpty()) {
+ NbtCompound customData = ItemUtils.getCustomData(stack);
+
+ //Should never fail, all pets must have this but you never know with Hypixel
+ try {
+ PetInfo petInfo = PetInfo.CODEC.parse(JsonOps.INSTANCE, JsonParser.parseString(customData.getString("petInfo"))).getOrThrow();
+ shouldLook4Pets = false;
+
+ Object2ObjectOpenHashMap<String, PetInfo> playerData = CACHED_PETS.computeIfAbsent(Utils.getUndashedUuid(), _uuid -> new Object2ObjectOpenHashMap<>());
+
+ //Handle deselecting pets
+ if (clicked && getCurrentPet() != null && getCurrentPet().uuid().orElse("").equals(petInfo.uuid().orElse(""))) {
+ playerData.remove(profileId);
+ } else {
+ playerData.put(profileId, petInfo);
+ }
+
+ save();
+ } catch (Exception e) {
+ LOGGER.error(LogUtils.FATAL_MARKER, "[Skyblocker Pet Cache] Failed to parse pet's pet info!", e);
+ }
+ }
+ }
+
+ @Nullable
+ public static PetInfo getCurrentPet() {
+ String uuid = Utils.getUndashedUuid();
+ String profileId = Utils.getProfileId();
+
+ return CACHED_PETS.containsKey(uuid) && CACHED_PETS.get(uuid).containsKey(profileId) ? CACHED_PETS.get(uuid).get(profileId) : null;
+ }
+
+ public record PetInfo(String type, double exp, String tier, Optional<String> uuid) {
+ public static final Codec<PetInfo> CODEC = RecordCodecBuilder.create(instance -> instance.group(
+ Codec.STRING.fieldOf("type").forGetter(PetInfo::type),
+ Codec.DOUBLE.fieldOf("exp").forGetter(PetInfo::exp),
+ Codec.STRING.fieldOf("tier").forGetter(PetInfo::tier),
+ Codec.STRING.optionalFieldOf("uuid").forGetter(PetInfo::uuid))
+ .apply(instance, PetInfo::new));
+ private static final Codec<Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, PetInfo>>> SERIALIZATION_CODEC = Codec.unboundedMap(Codec.STRING,
+ Codec.unboundedMap(Codec.STRING, CODEC).xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new)
+ ).xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new);
+ }
+}