diff options
author | Aaron <51387595+AzureAaron@users.noreply.github.com> | 2024-06-16 17:56:52 -0400 |
---|---|---|
committer | Aaron <51387595+AzureAaron@users.noreply.github.com> | 2024-06-18 16:54:00 -0400 |
commit | 685b2da9745e9f9e0d23e1fce39e0c6bcb6ca699 (patch) | |
tree | 7c7c80dd3c160cd38f091a018a63c4ea133440d8 /src/main/java | |
parent | cdfcdf9d5e9cbdad30c591d1b58d4259a1fa3a38 (diff) | |
download | Skyblocker-685b2da9745e9f9e0d23e1fce39e0c6bcb6ca699.tar.gz Skyblocker-685b2da9745e9f9e0d23e1fce39e0c6bcb6ca699.tar.bz2 Skyblocker-685b2da9745e9f9e0d23e1fce39e0c6bcb6ca699.zip |
Pet Cache
Diffstat (limited to 'src/main/java')
6 files changed, 185 insertions, 33 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java index 21790256..d793e73d 100644 --- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java +++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java @@ -173,6 +173,7 @@ public class SkyblockerMod implements ClientModInitializer { VisitorHelper.init(); ItemRarityBackgrounds.init(); MuseumItemCache.init(); + PetCache.init(); SecretsTracker.init(); ApiAuthentication.init(); ApiUtils.init(); diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java index 7fdb5738..f2e3e907 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java @@ -4,6 +4,7 @@ import com.llamalad7.mixinextras.sugar.Local; import com.mojang.blaze3d.systems.RenderSystem; import de.hysky.skyblocker.SkyblockerMod; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.PetCache; import de.hysky.skyblocker.skyblock.experiment.ChronomatronSolver; import de.hysky.skyblocker.skyblock.experiment.ExperimentSolver; import de.hysky.skyblocker.skyblock.experiment.SuperpairsSolver; @@ -37,6 +38,7 @@ import net.minecraft.text.Text; import net.minecraft.util.Identifier; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.lwjgl.glfw.GLFW; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -272,6 +274,11 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen case null, default -> {} } + //Pet Caching + if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT && title.startsWith("Pets")) { + PetCache.handlePetEquip(slot, slotId); + } + if (currentSolver != null) { boolean disallowed = SkyblockerMod.getInstance().containerSolverManager.onSlotClick(slotId, stack); 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..179e4ed3 --- /dev/null +++ b/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java @@ -0,0 +1,148 @@ +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 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().equals(petInfo.uuid())) { + 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, String uuid) { + private 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.fieldOf("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); + } +} diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java index 96c21d22..8f15b20b 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java @@ -1,9 +1,9 @@ package de.hysky.skyblocker.skyblock.item; -import com.google.gson.JsonElement; import de.hysky.skyblocker.config.SkyblockerConfigManager; +import de.hysky.skyblocker.skyblock.PetCache; +import de.hysky.skyblocker.skyblock.PetCache.PetInfo; import de.hysky.skyblocker.utils.ItemUtils; -import de.hysky.skyblocker.utils.ProfileUtils; import net.fabricmc.fabric.api.event.client.player.ClientPlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.UseItemCallback; import net.minecraft.block.BlockState; @@ -36,8 +36,8 @@ public class ItemCooldowns { 561700, 611700, 666700, 726700, 791700, 861700, 936700, 1016700, 1101700, 1191700, 1286700, 1386700, 1496700, 1616700, 1746700, 1886700 }; - public static int monkeyLevel = 1; - public static double monkeyExp = 0; + private static int monkeyLevel = 1; + private static double monkeyExp = 0; public static void init() { ClientPlayerBlockBreakEvents.AFTER.register(ItemCooldowns::afterBlockBreak); @@ -45,30 +45,24 @@ public class ItemCooldowns { } public static void updateCooldown() { - ProfileUtils.updateProfile().thenAccept(player -> { - for (JsonElement pet : player.getAsJsonObject("pets_data").getAsJsonArray("pets")) { - if (!pet.getAsJsonObject().get("type").getAsString().equals("MONKEY")) continue; - if (!pet.getAsJsonObject().get("active").getAsString().equals("true")) continue; - if (pet.getAsJsonObject().get("tier").getAsString().equals("LEGENDARY")) { - monkeyExp = Double.parseDouble(pet.getAsJsonObject().get("exp").getAsString()); - monkeyLevel = 0; - for (int xpLevel : EXPERIENCE_LEVELS) { - if (monkeyExp < xpLevel) { - break; - } else { - monkeyExp -= xpLevel; - monkeyLevel++; - } - } + PetInfo pet = PetCache.getCurrentPet(); + + if (pet.tier().equals("LEGENDARY")) { + monkeyExp = pet.exp(); + + monkeyLevel = 0; + for (int xpLevel : EXPERIENCE_LEVELS) { + if (monkeyExp < xpLevel) { + break; + } else { + monkeyExp -= xpLevel; + monkeyLevel++; } } - }).exceptionally(e -> { - ProfileUtils.LOGGER.error("[Skyblocker Item Cooldown] Failed to get Player Pet Data, is the API Down/Limited?", e); - return null; - }); + } } - private static int getCooldown() { + private static int getCooldown4Foraging() { int baseCooldown = 2000; int monkeyPetCooldownReduction = baseCooldown * monkeyLevel / 200; return baseCooldown - monkeyPetCooldownReduction; @@ -82,7 +76,7 @@ public class ItemCooldowns { if (usedItemId.equals(JUNGLE_AXE_ID) || usedItemId.equals(TREECAPITATOR_ID)) { updateCooldown(); if (!isOnCooldown(JUNGLE_AXE_ID) || !isOnCooldown(TREECAPITATOR_ID)) { - ITEM_COOLDOWNS.put(usedItemId, new CooldownEntry(getCooldown())); + ITEM_COOLDOWNS.put(usedItemId, new CooldownEntry(getCooldown4Foraging())); } } } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java b/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java index 11e8ea9c..49df5b78 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java @@ -110,7 +110,7 @@ public class MuseumItemCache { String profileId = Utils.getProfileId(); if (!itemId.isEmpty() && !profileId.isEmpty()) { - String uuid = getUndashedUuid(MinecraftClient.getInstance()); + String uuid = Utils.getUndashedUuid(); //Be safe about access to avoid NPEs Map<String, ProfileMuseumData> playerData = MUSEUM_ITEM_CACHE.computeIfAbsent(uuid, _uuid -> new Object2ObjectOpenHashMap<>()); playerData.putIfAbsent(profileId, ProfileMuseumData.EMPTY); @@ -203,7 +203,7 @@ public class MuseumItemCache { } private static boolean tryResync(FabricClientCommandSource source) { - String uuid = getUndashedUuid(source.getClient()); + String uuid = Utils.getUndashedUuid(); String profileId = Utils.getProfileId(); //Only allow resyncing if the data is actually present yet, otherwise the player needs to swap servers for the tick method to be called @@ -220,7 +220,7 @@ public class MuseumItemCache { * The cache is ticked upon switching Skyblock servers. Only loads from the API if the profile wasn't cached yet. */ public static void tick(String profileId) { - String uuid = getUndashedUuid(MinecraftClient.getInstance()); + String uuid = Utils.getUndashedUuid(); if (loaded.isDone() && (!MUSEUM_ITEM_CACHE.containsKey(uuid) || !MUSEUM_ITEM_CACHE.getOrDefault(uuid, new Object2ObjectOpenHashMap<>()).containsKey(profileId))) { Map<String, ProfileMuseumData> playerData = MUSEUM_ITEM_CACHE.computeIfAbsent(uuid, _uuid -> new Object2ObjectOpenHashMap<>()); @@ -231,16 +231,12 @@ public class MuseumItemCache { } public static boolean hasItemInMuseum(String id) { - String uuid = getUndashedUuid(MinecraftClient.getInstance()); + String uuid = Utils.getUndashedUuid(); ObjectOpenHashSet<String> collectedItemIds = (!MUSEUM_ITEM_CACHE.containsKey(uuid) || Utils.getProfileId().isBlank() || !MUSEUM_ITEM_CACHE.get(uuid).containsKey(Utils.getProfileId())) ? null : MUSEUM_ITEM_CACHE.get(uuid).get(Utils.getProfileId()).collectedItemIds(); return collectedItemIds != null && collectedItemIds.contains(id); } - private static String getUndashedUuid(MinecraftClient client) { - return UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull()); - } - private record ProfileMuseumData(long lastResync, ObjectOpenHashSet<String> collectedItemIds) { private static final ProfileMuseumData EMPTY = new ProfileMuseumData(0L, null); private static final long TIME_BETWEEN_RESYNCING_ALLOWED = 172_800_000L; diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java index 925879b8..84b3cb9e 100644 --- a/src/main/java/de/hysky/skyblocker/utils/Utils.java +++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java @@ -2,6 +2,8 @@ package de.hysky.skyblocker.utils; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.mojang.util.UndashedUuid; + import de.hysky.skyblocker.events.SkyblockEvents; import de.hysky.skyblocker.mixins.accessors.MessageHandlerAccessor; import de.hysky.skyblocker.skyblock.item.MuseumItemCache; @@ -507,4 +509,8 @@ public class Utils { ((MessageHandlerAccessor) client.getMessageHandler()).invokeAddToChatLog(message, Instant.now()); client.getNarratorManager().narrateSystemMessage(message); } + + public static String getUndashedUuid() { + return UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull()); + } } |