aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAaron <51387595+AzureAaron@users.noreply.github.com>2024-06-16 16:08:01 -0400
committerAaron <51387595+AzureAaron@users.noreply.github.com>2024-06-18 16:54:00 -0400
commitcdfcdf9d5e9cbdad30c591d1b58d4259a1fa3a38 (patch)
tree2132e766de013b45b4cedcb33c0c32a73d832ed9 /src
parent163905a63d840a4dd17b29bb53c6e7be0bf55c03 (diff)
downloadSkyblocker-cdfcdf9d5e9cbdad30c591d1b58d4259a1fa3a38.tar.gz
Skyblocker-cdfcdf9d5e9cbdad30c591d1b58d4259a1fa3a38.tar.bz2
Skyblocker-cdfcdf9d5e9cbdad30c591d1b58d4259a1fa3a38.zip
Track Museum Item Donations
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java29
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java113
-rw-r--r--src/main/resources/assets/skyblocker/lang/en_us.json5
3 files changed, 127 insertions, 20 deletions
diff --git a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
index a7685ffc..7fdb5738 100644
--- a/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
+++ b/src/main/java/de/hysky/skyblocker/mixins/HandledScreenMixin.java
@@ -11,6 +11,7 @@ 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;
@@ -248,17 +249,27 @@ public abstract class HandledScreenMixin<T extends ScreenHandler> extends Screen
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 -> {}
}
if (currentSolver != null) {
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<String, Object2ObjectOpenHashMap<String, ProfileMuseumData>> 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<Void> loaded;
public static void init() {
ClientLifecycleEvents.CLIENT_STARTED.register(MuseumItemCache::load);
+ ClientCommandRegistrationCallback.EVENT.register(MuseumItemCache::registerCommands);
+ }
+
+ private static void registerCommands(CommandDispatcher<FabricClientCommandSource> 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<Slot> 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<String, ProfileMuseumData> 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<String, ProfileMuseumData> 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<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 record ProfileMuseumData(long lastUpdated, ObjectOpenHashSet<String> collectedItemIds) {
+ 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 MAX_AGE = 86_400_000;
+ private static final long TIME_BETWEEN_RESYNCING_ALLOWED = 172_800_000L;
private static final Codec<ProfileMuseumData> 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
diff --git a/src/main/resources/assets/skyblocker/lang/en_us.json b/src/main/resources/assets/skyblocker/lang/en_us.json
index d03e2a33..a73d523d 100644
--- a/src/main/resources/assets/skyblocker/lang/en_us.json
+++ b/src/main/resources/assets/skyblocker/lang/en_us.json
@@ -661,6 +661,11 @@
"skyblocker.updateRepository.failed": "§cUpdating the local repository failed. See logs for details.",
"skyblocker.updateRepository.error": "§cEncountered an error while deleting and updating the local repository. Try again in a moment or remove the files manually and restart the game. See logs for details.",
+ "skyblocker.museum.attemptingResync": "Attempting to resync your museum item donations...",
+ "skyblocker.museum.cannotResync": "Cannot resync your museum item donations! Note that you can only do this once every two days.",
+ "skyblocker.museum.resyncSuccess": "Successfully resynced your museum item donations!",
+ "skyblocker.museum.resyncFailure": "Failed to resync your museum item donations!",
+
"skyblocker.dungeons.secrets.physicalEntranceNotFound": "§cDungeon Entrance Room coordinates not found. Please go back to the green Entrance Room.",
"skyblocker.dungeons.secrets.markSecretFound": "§rMarked secret #%d as found.",
"skyblocker.dungeons.secrets.markSecretMissing": "§rMarked secret #%d as missing.",