Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Api changes the Aaron of the Azure color made #783

Merged
merged 10 commits into from
Jun 22, 2024
3 changes: 2 additions & 1 deletion src/main/java/de/hysky/skyblocker/SkyblockerMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -173,9 +173,10 @@ public void onInitializeClient() {
VisitorHelper.init();
ItemRarityBackgrounds.init();
MuseumItemCache.init();
PetCache.init();
SecretsTracker.init();
ApiAuthentication.init();
ApiUtils.init();
ProfileUtils.init();
Debug.init();
Kuudra.init();
RenderHelper.init();
Expand Down
36 changes: 27 additions & 9 deletions src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,15 @@
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;
import de.hysky.skyblocker.skyblock.experiment.UltrasequencerSolver;
import de.hysky.skyblocker.skyblock.garden.VisitorHelper;
import de.hysky.skyblocker.skyblock.item.ItemProtection;
import de.hysky.skyblocker.skyblock.item.ItemRarityBackgrounds;
import de.hysky.skyblocker.skyblock.item.MuseumItemCache;
import de.hysky.skyblocker.skyblock.item.WikiLookup;
import de.hysky.skyblocker.skyblock.item.slottext.SlotText;
import de.hysky.skyblocker.skyblock.item.slottext.SlotTextManager;
Expand All @@ -36,6 +38,7 @@
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;
Expand Down Expand Up @@ -248,17 +251,32 @@ protected HandledScreenMixin(Text title) {
ci.cancel();
return;
}
if (this.handler instanceof GenericContainerScreenHandler genericContainerScreenHandler && genericContainerScreenHandler.getRows() == 6) {
VisitorHelper.onSlotClick(slot, slotId, title, genericContainerScreenHandler.getSlot(13).getStack());

// Prevent selling to NPC shops
ItemStack sellStack = this.handler.slots.get(49).getStack();
if (sellStack.getName().getString().equals("Sell Item") || ItemUtils.getLoreLineIf(sellStack, text -> text.contains("buyback")) != null) {
if (slotId != 49 && ItemProtection.isItemProtected(stack)) {
ci.cancel();
return;

switch (this.handler) {
case GenericContainerScreenHandler genericContainerScreenHandler when genericContainerScreenHandler.getRows() == 6 -> {
VisitorHelper.onSlotClick(slot, slotId, title, genericContainerScreenHandler.getSlot(13).getStack());

// Prevent selling to NPC shops
ItemStack sellStack = this.handler.slots.get(49).getStack();
if (sellStack.getName().getString().equals("Sell Item") || ItemUtils.getLoreLineIf(sellStack, text -> text.contains("buyback")) != null) {
if (slotId != 49 && ItemProtection.isItemProtected(stack)) {
ci.cancel();
return;
}
}
}

case GenericContainerScreenHandler genericContainerScreenHandler when title.equals(MuseumItemCache.DONATION_CONFIRMATION_SCREEN_TITLE) -> {
//Museum Item Cache donation tracking
MuseumItemCache.handleClick(slot, slotId, genericContainerScreenHandler.slots);
}

case null, default -> {}
}

//Pet Caching
if (button == GLFW.GLFW_MOUSE_BUTTON_LEFT && title.startsWith("Pets")) {
PetCache.handlePetEquip(slot, slotId);
}

if (currentSolver != null) {
Expand Down
51 changes: 47 additions & 4 deletions src/main/java/de/hysky/skyblocker/mixins/ItemStackMixin.java
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -165,6 +167,7 @@ public String getNeuName() {
}

// 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;
}
Expand All @@ -178,12 +181,14 @@ public String getNeuName() {
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" : "";
Expand All @@ -193,6 +198,7 @@ public String getNeuName() {
+ enhanced + extended + splash).toUpperCase(Locale.ENGLISH);
}
}

case "RUNE" -> {
if (customData.contains("runes")) {
NbtCompound runes = customData.getCompound("runes");
Expand All @@ -201,6 +207,7 @@ public String getNeuName() {
return rune.toUpperCase(Locale.ENGLISH) + "_RUNE_" + runes.getInt(rune);
}
}

case "ATTRIBUTE_SHARD" -> {
if (customData.contains("attributes")) {
NbtCompound shards = customData.getCompound("attributes");
Expand All @@ -209,6 +216,42 @@ public String getNeuName() {
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;
}
Expand Down
149 changes: 149 additions & 0 deletions src/main/java/de/hysky/skyblocker/skyblock/PetCache.java
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading