From cdfcdf9d5e9cbdad30c591d1b58d4259a1fa3a38 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sun, 16 Jun 2024 16:08:01 -0400 Subject: Track Museum Item Donations --- .../skyblocker/skyblock/item/MuseumItemCache.java | 113 +++++++++++++++++++-- 1 file changed, 102 insertions(+), 11 deletions(-) (limited to 'src/main/java/de/hysky/skyblocker/skyblock') 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 c78724ca..11e8ea9c 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java @@ -1,22 +1,36 @@ package de.hysky.skyblocker.skyblock.item; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; + import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import com.mojang.brigadier.Command; +import com.mojang.brigadier.CommandDispatcher; import com.mojang.serialization.Codec; import com.mojang.serialization.JsonOps; import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.util.UndashedUuid; import de.hysky.skyblocker.SkyblockerMod; +import de.hysky.skyblocker.utils.Constants; import de.hysky.skyblocker.utils.Http; import de.hysky.skyblocker.utils.Http.ApiResponse; +import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.Utils; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; +import net.fabricmc.fabric.api.client.command.v2.ClientCommandRegistrationCallback; +import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.minecraft.client.MinecraftClient; +import net.minecraft.command.CommandRegistryAccess; +import net.minecraft.item.ItemStack; import net.minecraft.nbt.*; +import net.minecraft.screen.slot.Slot; +import net.minecraft.text.Text; +import net.minecraft.util.collection.DefaultedList; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,11 +50,29 @@ public class MuseumItemCache { private static final Path CACHE_FILE = SkyblockerMod.CONFIG_DIR.resolve("museum_item_cache.json"); private static final Map> MUSEUM_ITEM_CACHE = new Object2ObjectOpenHashMap<>(); private static final String ERROR_LOG_TEMPLATE = "[Skyblocker] Failed to refresh museum item data for profile {}"; + public static final String DONATION_CONFIRMATION_SCREEN_TITLE = "Confirm Donation"; + private static final int CONFIRM_DONATION_BUTTON_SLOT = 20; private static CompletableFuture loaded; public static void init() { ClientLifecycleEvents.CLIENT_STARTED.register(MuseumItemCache::load); + ClientCommandRegistrationCallback.EVENT.register(MuseumItemCache::registerCommands); + } + + private static void registerCommands(CommandDispatcher dispatcher, CommandRegistryAccess registryAccess) { + dispatcher.register(literal(SkyblockerMod.NAMESPACE) + .then(literal("museum") + .then(literal("resync") + .executes(context -> { + if (tryResync(context.getSource())) { + context.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.museum.attemptingResync"))); + } else { + context.getSource().sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.museum.cannotResync"))); + } + + return Command.SINGLE_SUCCESS; + })))); } private static void load(MinecraftClient client) { @@ -67,7 +99,35 @@ public class MuseumItemCache { }); } + public static void handleClick(Slot slot, int slotId, DefaultedList slots) { + if (slotId == CONFIRM_DONATION_BUTTON_SLOT) { + //Slots 0 to 17 can have items, well not all but thats the general range + for (int i = 0; i < 17; i++) { + ItemStack stack = slots.get(i).getStack(); + + if (!stack.isEmpty()) { + String itemId = ItemUtils.getItemId(stack); + String profileId = Utils.getProfileId(); + + if (!itemId.isEmpty() && !profileId.isEmpty()) { + String uuid = getUndashedUuid(MinecraftClient.getInstance()); + //Be safe about access to avoid NPEs + Map playerData = MUSEUM_ITEM_CACHE.computeIfAbsent(uuid, _uuid -> new Object2ObjectOpenHashMap<>()); + playerData.putIfAbsent(profileId, ProfileMuseumData.EMPTY); + + playerData.get(profileId).collectedItemIds().add(itemId); + save(); + } + } + } + } + } + private static void updateData4ProfileMember(String uuid, String profileId) { + updateData4ProfileMember(uuid, profileId, null); + } + + private static void updateData4ProfileMember(String uuid, String profileId, FabricClientCommandSource source) { CompletableFuture.runAsync(() -> { try (ApiResponse response = Http.sendHypixelRequest("skyblock/museum", "?profile=" + profileId)) { //The request was successful @@ -103,58 +163,89 @@ public class MuseumItemCache { MUSEUM_ITEM_CACHE.get(uuid).put(profileId, new ProfileMuseumData(System.currentTimeMillis(), itemIds)); save(); + if (source != null) source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.museum.resyncSuccess"))); + LOGGER.info("[Skyblocker] Successfully updated museum item cache for profile {}", profileId); } else { //If the player's Museum API is disabled putEmpty(uuid, profileId); + if (source != null) source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.museum.resyncFailure"))); + LOGGER.warn(ERROR_LOG_TEMPLATE + " because the Museum API is disabled!", profileId); } } else { //If the request returns a non 200 status code putEmpty(uuid, profileId); + if (source != null) source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.museum.resyncFailure"))); + LOGGER.error(ERROR_LOG_TEMPLATE + " because a non 200 status code was encountered! Status Code: {}", profileId, response.statusCode()); } } catch (Exception e) { //If an exception was somehow thrown putEmpty(uuid, profileId); + if (source != null) source.sendFeedback(Constants.PREFIX.get().append(Text.translatable("skyblocker.museum.resyncFailure"))); + LOGGER.error(ERROR_LOG_TEMPLATE, profileId, e); } }); } private static void putEmpty(String uuid, String profileId) { - MUSEUM_ITEM_CACHE.get(uuid).put(profileId, new ProfileMuseumData(System.currentTimeMillis(), ObjectOpenHashSet.of())); + //Only put new data if they didn't have any before + if (!MUSEUM_ITEM_CACHE.get(uuid).containsKey(profileId)) { + MUSEUM_ITEM_CACHE.get(uuid).put(profileId, new ProfileMuseumData(System.currentTimeMillis(), ObjectOpenHashSet.of())); + } + save(); } + private static boolean tryResync(FabricClientCommandSource source) { + String uuid = getUndashedUuid(source.getClient()); + 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 + if (loaded.isDone() && !profileId.isEmpty() && MUSEUM_ITEM_CACHE.containsKey(uuid) && MUSEUM_ITEM_CACHE.get(uuid).containsKey(profileId) && MUSEUM_ITEM_CACHE.get(uuid).get(profileId).canResync()) { + updateData4ProfileMember(uuid, profileId, source); + + return true; + } + + return false; + } + /** - * The cache is ticked upon switching skyblock servers + * 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) { - if (loaded.isDone()) { - String uuid = UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull()); + String uuid = getUndashedUuid(MinecraftClient.getInstance()); + + if (loaded.isDone() && (!MUSEUM_ITEM_CACHE.containsKey(uuid) || !MUSEUM_ITEM_CACHE.getOrDefault(uuid, new Object2ObjectOpenHashMap<>()).containsKey(profileId))) { Map playerData = MUSEUM_ITEM_CACHE.computeIfAbsent(uuid, _uuid -> new Object2ObjectOpenHashMap<>()); playerData.putIfAbsent(profileId, ProfileMuseumData.EMPTY); - if (playerData.get(profileId).stale()) updateData4ProfileMember(uuid, profileId); + updateData4ProfileMember(uuid, profileId); } } public static boolean hasItemInMuseum(String id) { - String uuid = UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull()); + String uuid = getUndashedUuid(MinecraftClient.getInstance()); ObjectOpenHashSet 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 record ProfileMuseumData(long lastUpdated, ObjectOpenHashSet collectedItemIds) { + private static String getUndashedUuid(MinecraftClient client) { + return UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull()); + } + + private record ProfileMuseumData(long lastResync, ObjectOpenHashSet collectedItemIds) { private static final ProfileMuseumData EMPTY = new ProfileMuseumData(0L, null); - private static final long MAX_AGE = 86_400_000; + private static final long TIME_BETWEEN_RESYNCING_ALLOWED = 172_800_000L; private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( - Codec.LONG.fieldOf("lastUpdated").forGetter(ProfileMuseumData::lastUpdated), + Codec.LONG.fieldOf("lastResync").forGetter(ProfileMuseumData::lastResync), Codec.STRING.listOf() .xmap(ObjectOpenHashSet::new, ObjectArrayList::new) .fieldOf("collectedItemIds") @@ -165,8 +256,8 @@ public class MuseumItemCache { .xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new) ).xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new); - private boolean stale() { - return System.currentTimeMillis() > lastUpdated + MAX_AGE; + private boolean canResync() { + return this.lastResync + TIME_BETWEEN_RESYNCING_ALLOWED < System.currentTimeMillis(); } } } \ No newline at end of file -- cgit From 685b2da9745e9f9e0d23e1fce39e0c6bcb6ca699 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sun, 16 Jun 2024 17:56:52 -0400 Subject: Pet Cache --- .../java/de/hysky/skyblocker/SkyblockerMod.java | 1 + .../skyblocker/mixins/HandledScreenMixin.java | 7 + .../de/hysky/skyblocker/skyblock/PetCache.java | 148 +++++++++++++++++++++ .../skyblocker/skyblock/item/ItemCooldowns.java | 44 +++--- .../skyblocker/skyblock/item/MuseumItemCache.java | 12 +- src/main/java/de/hysky/skyblocker/utils/Utils.java | 6 + 6 files changed, 185 insertions(+), 33 deletions(-) create mode 100644 src/main/java/de/hysky/skyblocker/skyblock/PetCache.java (limited to 'src/main/java/de/hysky/skyblocker/skyblock') 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 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> 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 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 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>> 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 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 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 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 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()); + } } -- cgit From 677044d628ee090b8079c06b8341c4996a9428a0 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sun, 16 Jun 2024 17:57:54 -0400 Subject: 10 minute resync allow time for museum cache --- src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main/java/de/hysky/skyblocker/skyblock') 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 49df5b78..50982d29 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java @@ -239,7 +239,7 @@ public class MuseumItemCache { private record ProfileMuseumData(long lastResync, ObjectOpenHashSet collectedItemIds) { private static final ProfileMuseumData EMPTY = new ProfileMuseumData(0L, null); - private static final long TIME_BETWEEN_RESYNCING_ALLOWED = 172_800_000L; + private static final long TIME_BETWEEN_RESYNCING_ALLOWED = 600_000L; private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( Codec.LONG.fieldOf("lastResync").forGetter(ProfileMuseumData::lastResync), Codec.STRING.listOf() -- cgit From d20243d7a4785b5640be6d9c45cb5e2f1faa4e17 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Wed, 19 Jun 2024 02:42:03 -0400 Subject: Add missing null check --- src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main/java/de/hysky/skyblocker/skyblock') 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 8f15b20b..93d29714 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/ItemCooldowns.java @@ -47,7 +47,7 @@ public class ItemCooldowns { public static void updateCooldown() { PetInfo pet = PetCache.getCurrentPet(); - if (pet.tier().equals("LEGENDARY")) { + if (pet != null && pet.tier().equals("LEGENDARY")) { monkeyExp = pet.exp(); monkeyLevel = 0; -- cgit From a750329670683eabc6701f4fead32c2c9e601915 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:19:18 -0400 Subject: Implement new item-specific LBIN pricing --- .../de/hysky/skyblocker/mixins/ItemStackMixin.java | 51 ++++++++++++++++++++-- .../de/hysky/skyblocker/skyblock/PetCache.java | 2 +- .../skyblock/item/tooltip/ItemTooltip.java | 5 +++ 3 files changed, 53 insertions(+), 5 deletions(-) (limited to 'src/main/java/de/hysky/skyblocker/skyblock') diff --git a/src/main/java/de/hysky/skyblocker/mixins/ItemStackMixin.java b/src/main/java/de/hysky/skyblocker/mixins/ItemStackMixin.java index 2154c4b5..3abbfbcd 100644 --- a/src/main/java/de/hysky/skyblocker/mixins/ItemStackMixin.java +++ b/src/main/java/de/hysky/skyblocker/mixins/ItemStackMixin.java @@ -1,10 +1,12 @@ package de.hysky.skyblocker.mixins; -import com.google.gson.JsonObject; +import com.google.gson.JsonParser; import com.llamalad7.mixinextras.injector.ModifyReturnValue; -import de.hysky.skyblocker.SkyblockerMod; +import com.mojang.serialization.JsonOps; + import de.hysky.skyblocker.config.SkyblockerConfigManager; import de.hysky.skyblocker.injected.SkyblockerStack; +import de.hysky.skyblocker.skyblock.PetCache.PetInfo; import de.hysky.skyblocker.skyblock.item.tooltip.ItemTooltip; import de.hysky.skyblocker.utils.ItemUtils; import de.hysky.skyblocker.utils.Utils; @@ -165,6 +167,7 @@ public abstract class ItemStackMixin implements ComponentHolder, SkyblockerStack } // Transformation to API format. + //TODO future - remove this and just handle it directly for the NEU id conversion because this whole system is confusing and hard to follow if (customData.contains("is_shiny")) { return "ISSHINY_" + customDataString; } @@ -178,12 +181,14 @@ public abstract class ItemStackMixin implements ComponentHolder, SkyblockerStack return "ENCHANTMENT_" + enchant.toUpperCase(Locale.ENGLISH) + "_" + enchants.getInt(enchant); } } + case "PET" -> { if (customData.contains("petInfo")) { - JsonObject petInfo = SkyblockerMod.GSON.fromJson(customData.getString("petInfo"), JsonObject.class); - return "LVL_1_" + petInfo.get("tier").getAsString() + "_" + petInfo.get("type").getAsString(); + PetInfo petInfo = PetInfo.CODEC.parse(JsonOps.INSTANCE, JsonParser.parseString(customData.getString("petInfo"))).getOrThrow(); + return "LVL_1_" + petInfo.tier() + "_" + petInfo.type(); } } + case "POTION" -> { String enhanced = customData.contains("enhanced") ? "_ENHANCED" : ""; String extended = customData.contains("extended") ? "_EXTENDED" : ""; @@ -193,6 +198,7 @@ public abstract class ItemStackMixin implements ComponentHolder, SkyblockerStack + enhanced + extended + splash).toUpperCase(Locale.ENGLISH); } } + case "RUNE" -> { if (customData.contains("runes")) { NbtCompound runes = customData.getCompound("runes"); @@ -201,6 +207,7 @@ public abstract class ItemStackMixin implements ComponentHolder, SkyblockerStack return rune.toUpperCase(Locale.ENGLISH) + "_RUNE_" + runes.getInt(rune); } } + case "ATTRIBUTE_SHARD" -> { if (customData.contains("attributes")) { NbtCompound shards = customData.getCompound("attributes"); @@ -209,6 +216,42 @@ public abstract class ItemStackMixin implements ComponentHolder, SkyblockerStack return customDataString + "-" + shard.toUpperCase(Locale.ENGLISH) + "_" + shards.getInt(shard); } } + + case "NEW_YEAR_CAKE" -> { + return customDataString + "_" + customData.getInt("new_years_cake"); + } + + case "PARTY_HAT_CRAB", "PARTY_HAT_CRAB_ANIMATED", "BALLOON_HAT_2024" -> { + return customDataString + "_" + customData.getString("party_hat_color").toUpperCase(Locale.ENGLISH); + } + + case "PARTY_HAT_SLOTH" -> { + return customDataString + "_" + customData.getString("party_hat_emoji").toUpperCase(Locale.ENGLISH); + } + + case "CRIMSON_HELMET", "CRIMSON_CHESTPLATE", "CRIMSON_LEGGINGS", "CRIMSON_BOOTS" -> { + NbtCompound attributes = customData.getCompound("attributes"); + + if (attributes.contains("magic_find") && attributes.contains("veteran")) { + return customDataString + "-MAGIC_FIND-VETERAN"; + } + } + + case "AURORA_HELMET", "AURORA_CHESTPLATE", "AURORA_LEGGINGS", "AURORA_BOOTS" -> { + NbtCompound attributes = customData.getCompound("attributes"); + + if (attributes.contains("mana_pool") && attributes.contains("mana_regeneration")) { + return customDataString + "-MANA_POOL-MANA_REGENERATION"; + } + } + + case "TERROR_HELMET", "TERROR_CHESTPLATE", "TERROR_LEGGINGS", "TERROR_BOOTS" -> { + NbtCompound attributes = customData.getCompound("attributes"); + + if (attributes.contains("lifeline") && attributes.contains("mana_pool")) { + return customDataString + "-LIFELINE-MANA_POOL"; + } + } } return customDataString; } diff --git a/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java b/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java index 179e4ed3..19714e41 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java @@ -135,7 +135,7 @@ public class PetCache { } public record PetInfo(String type, double exp, String tier, String uuid) { - private static final Codec CODEC = RecordCodecBuilder.create(instance -> instance.group( + public static final Codec 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), diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java index 2f5408a1..cc3d2099 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/item/tooltip/ItemTooltip.java @@ -52,6 +52,11 @@ public class ItemTooltip { case "POTION" -> apiId = ""; case "ATTRIBUTE_SHARD" -> apiId = id + "+" + apiId.replace("SHARD-", "").replaceAll("_(?!.*_)", ";"); + case "NEW_YEAR_CAKE" -> apiId = id + "+" + apiId.replace("NEW_YEAR_CAKE_", ""); + case "PARTY_HAT_CRAB_ANIMATED" -> apiId = "PARTY_HAT_CRAB_" + apiId.replace("PARTY_HAT_CRAB_ANIMATED_", "") + "_ANIMATED"; + case "CRIMSON_HELMET", "CRIMSON_CHESTPLATE", "CRIMSON_LEGGINGS", "CRIMSON_BOOTS", + "AURORA_HELMET", "AURORA_CHESTPLATE", "AURORA_LEGGINGS", "AURORA_BOOTS", + "TERROR_HELMET", "TERROR_CHESTPLATE", "TERROR_LEGGINGS", "TERROR_BOOTS" -> apiId = id; default -> apiId = apiId.replace(":", "-"); } return apiId; -- cgit From b9771e13634b7f7c32e46d07fbaa9aecd58a5976 Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:31:47 -0400 Subject: Apparently a pet's uuid is an optional field .-. --- src/main/java/de/hysky/skyblocker/skyblock/PetCache.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/main/java/de/hysky/skyblocker/skyblock') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java b/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java index 19714e41..d8cd6e48 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/PetCache.java @@ -6,6 +6,7 @@ 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; @@ -113,7 +114,7 @@ public class PetCache { Object2ObjectOpenHashMap playerData = CACHED_PETS.computeIfAbsent(Utils.getUndashedUuid(), _uuid -> new Object2ObjectOpenHashMap<>()); //Handle deselecting pets - if (clicked && getCurrentPet() != null && getCurrentPet().uuid().equals(petInfo.uuid())) { + if (clicked && getCurrentPet() != null && getCurrentPet().uuid().orElse("").equals(petInfo.uuid().orElse(""))) { playerData.remove(profileId); } else { playerData.put(profileId, petInfo); @@ -134,12 +135,12 @@ public class PetCache { 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) { + public record PetInfo(String type, double exp, String tier, Optional uuid) { public static final Codec 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)) + Codec.STRING.optionalFieldOf("uuid").forGetter(PetInfo::uuid)) .apply(instance, PetInfo::new)); private static final Codec>> SERIALIZATION_CODEC = Codec.unboundedMap(Codec.STRING, Codec.unboundedMap(Codec.STRING, CODEC).xmap(Object2ObjectOpenHashMap::new, Object2ObjectOpenHashMap::new) -- cgit From 6fbe98c27696bc13bbb8dd88b6e7372ab9d16dfe Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Thu, 20 Jun 2024 16:40:34 -0400 Subject: Fix recipe book flipping two pages at a time --- src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListTab.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/main/java/de/hysky/skyblocker/skyblock') diff --git a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListTab.java b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListTab.java index 89d41290..10e12ace 100644 --- a/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListTab.java +++ b/src/main/java/de/hysky/skyblocker/skyblock/itemlist/ItemListTab.java @@ -78,7 +78,8 @@ public class ItemListTab extends ItemListWidget.TabContainerWidget { return true; } else if (results != null) { this.searchField.setFocused(false); - this.results.mouseClicked(mouseX, mouseY, button); + + return this.results.mouseClicked(mouseX, mouseY, button); } return false; -- cgit