aboutsummaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'src/main')
-rw-r--r--src/main/java/de/hysky/skyblocker/SkyblockerMod.java1
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java7
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java146
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Http.java8
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/Utils.java3
-rw-r--r--src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java43
6 files changed, 189 insertions, 19 deletions
diff --git a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
index dc8896cf..5e23e2e2 100644
--- a/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
+++ b/src/main/java/de/hysky/skyblocker/SkyblockerMod.java
@@ -106,6 +106,7 @@ public class SkyblockerMod implements ClientModInitializer {
ItemProtection.init();
CreeperBeams.init();
ItemRarityBackgrounds.init();
+ MuseumItemCache.init();
containerSolverManager.init();
statusBarTracker.init();
Scheduler.INSTANCE.scheduleCyclic(Utils::update, 20);
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java b/src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java
index 5ea5513e..36e25d5c 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java
@@ -1,6 +1,5 @@
package de.hysky.skyblocker.skyblock;
-import com.mojang.blaze3d.systems.RenderSystem;
import de.hysky.skyblocker.config.SkyblockerConfigManager;
import de.hysky.skyblocker.skyblock.item.PriceInfoTooltip;
import de.hysky.skyblocker.utils.ItemUtils;
@@ -103,13 +102,7 @@ public class TeleportOverlay {
@SuppressWarnings("DataFlowIssue")
BlockState state = client.world.getBlockState(pos);
if (!state.isAir() && client.world.getBlockState(pos.up()).isAir() && client.world.getBlockState(pos.up(2)).isAir()) {
- RenderSystem.polygonOffset(-1f, -10f);
- RenderSystem.enablePolygonOffset();
-
RenderHelper.renderFilledIfVisible(wrc, pos, COLOR_COMPONENTS, 0.5f);
-
- RenderSystem.polygonOffset(0f, 0f);
- RenderSystem.disablePolygonOffset();
}
}
}
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java b/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java
new file mode 100644
index 00000000..cb11d702
--- /dev/null
+++ b/src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java
@@ -0,0 +1,146 @@
+package de.hysky.skyblocker.skyblock.item;
+
+import java.io.ByteArrayInputStream;
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.nio.file.Files;
+import java.nio.file.NoSuchFileException;
+import java.nio.file.Path;
+import java.util.Base64;
+import java.util.Map;
+import java.util.concurrent.CompletableFuture;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import com.google.gson.reflect.TypeToken;
+import com.mojang.util.UndashedUuid;
+
+import de.hysky.skyblocker.SkyblockerMod;
+import de.hysky.skyblocker.utils.Http;
+import de.hysky.skyblocker.utils.Http.ApiResponse;
+import de.hysky.skyblocker.utils.Utils;
+import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
+import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
+import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents;
+import net.minecraft.client.MinecraftClient;
+import net.minecraft.nbt.NbtCompound;
+import net.minecraft.nbt.NbtElement;
+import net.minecraft.nbt.NbtIo;
+import net.minecraft.nbt.NbtList;
+import net.minecraft.util.Util;
+
+public class MuseumItemCache {
+ private static final Logger LOGGER = LoggerFactory.getLogger(MuseumItemCache.class);
+ private static final Path CACHE_FILE = SkyblockerMod.CONFIG_DIR.resolve("museum_item_cache.json");
+ private static final Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, ProfileMuseumData>> MUSEUM_ITEM_CACHE = new Object2ObjectOpenHashMap<>();
+ private static final Type MAP_TYPE = new TypeToken<Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, ProfileMuseumData>>>() {}.getType();
+
+ private static CompletableFuture<Void> loaded;
+
+ public static void init() {
+ ClientLifecycleEvents.CLIENT_STARTED.register(MuseumItemCache::load);
+ }
+
+ private static void load(MinecraftClient client) {
+ loaded = CompletableFuture.runAsync(() -> {
+ try (BufferedReader reader = Files.newBufferedReader(CACHE_FILE)) {
+ Object2ObjectOpenHashMap<String, Object2ObjectOpenHashMap<String, ProfileMuseumData>> cachedData = SkyblockerMod.GSON.fromJson(reader, MAP_TYPE);
+
+ MUSEUM_ITEM_CACHE.putAll(cachedData);
+ LOGGER.info("[Skyblocker] Loaded museum items cache");
+ } catch (NoSuchFileException ignored) {
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to load cached museum items", e);
+ }
+ });
+ }
+
+ private static void save() {
+ CompletableFuture.runAsync(() -> {
+ try (BufferedWriter writer = Files.newBufferedWriter(CACHE_FILE)) {
+ SkyblockerMod.GSON.toJson(MUSEUM_ITEM_CACHE, writer);
+ } catch (IOException e) {
+ LOGGER.error("[Skyblocker] Failed to save cached museum items!", e);
+ }
+ });
+ }
+
+ private static void updateData4ProfileMember(String uuid, String profileId) {
+ CompletableFuture.runAsync(() -> {
+ try {
+ ApiResponse response = Http.sendHypixelRequest("skyblock/museum", "?profile=" + profileId);
+
+ //The request was successful
+ if (response.ok()) {
+ JsonObject profileData = JsonParser.parseString(response.content()).getAsJsonObject();
+ JsonObject memberData = profileData.get("members").getAsJsonObject().get(uuid).getAsJsonObject();
+
+ //We call them sets because it could either be a singular item or an entire armour set
+ Map<String, JsonElement> donatedSets = memberData.get("items").getAsJsonObject().asMap();
+
+ //Set of all found item ids on profile
+ ObjectOpenHashSet<String> itemIds = new ObjectOpenHashSet<>();
+
+ for (Map.Entry<String, JsonElement> donatedSet : donatedSets.entrySet()) {
+ //Item is plural here because the nbt is a list
+ String itemsData = donatedSet.getValue().getAsJsonObject().get("items").getAsJsonObject().get("data").getAsString();
+ NbtList items = NbtIo.readCompressed(new ByteArrayInputStream(Base64.getDecoder().decode(itemsData))).getList("i", NbtElement.COMPOUND_TYPE);
+
+ for (int i = 0; i < items.size(); i++) {
+ NbtCompound tag = items.getCompound(i).getCompound("tag");
+
+ if (tag.contains("ExtraAttributes")) {
+ NbtCompound extraAttributes = tag.getCompound("ExtraAttributes");
+
+ if (extraAttributes.contains("id")) itemIds.add(extraAttributes.getString("id"));
+ }
+ }
+ }
+
+ MUSEUM_ITEM_CACHE.get(uuid).put(profileId, new ProfileMuseumData(System.currentTimeMillis(), itemIds));
+ save();
+
+ LOGGER.info("[Skyblocker] Successfully updated museum item cache for profile {}", profileId);
+ }
+ } catch (Exception e) {
+ LOGGER.error("[Skyblocker] Failed to refresh museum item data for profile {}", profileId, e);
+ }
+ });
+ }
+
+ /**
+ * The cache is ticked upon switching skyblock servers
+ */
+ public static void tick(String profileId) {
+ if (loaded.isDone()) {
+ String uuid = UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull());
+ Object2ObjectOpenHashMap<String, ProfileMuseumData> playerData = MUSEUM_ITEM_CACHE.computeIfAbsent(uuid, uuid1 -> Util.make(new Object2ObjectOpenHashMap<>(), map -> {
+ map.put(profileId, ProfileMuseumData.EMPTY);
+ }));
+
+ if (playerData.get(profileId).stale()) updateData4ProfileMember(uuid, profileId);
+ }
+ }
+
+ public static boolean hasItemInMuseum(String id) {
+ String uuid = UndashedUuid.toString(MinecraftClient.getInstance().getSession().getUuidOrNull());
+ ObjectOpenHashSet<String> collectedItemIds = 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 final ProfileMuseumData EMPTY = new ProfileMuseumData(0L, null);
+ private static final long MAX_AGE = 86_400_000;
+
+ private boolean stale() {
+ return System.currentTimeMillis() > lastUpdated + MAX_AGE;
+ }
+ }
+}
diff --git a/src/main/java/de/hysky/skyblocker/utils/Http.java b/src/main/java/de/hysky/skyblocker/utils/Http.java
index c961b56f..eabb02e4 100644
--- a/src/main/java/de/hysky/skyblocker/utils/Http.java
+++ b/src/main/java/de/hysky/skyblocker/utils/Http.java
@@ -51,7 +51,7 @@ public class Http {
InputStream decodedInputStream = getDecodedInputStream(response);
String body = new String(decodedInputStream.readAllBytes());
- return new ApiResponse(body, getCacheStatus(response.headers()));
+ return new ApiResponse(body, response.statusCode(), getCacheStatus(response.headers()));
}
public static HttpHeaders sendHeadRequest(String url) throws IOException, InterruptedException {
@@ -120,7 +120,11 @@ public class Http {
}
//TODO If ever needed, we could just replace cache status with the response headers and go from there
- public record ApiResponse(String content, String cacheStatus) {
+ public record ApiResponse(String content, int statusCode, String cacheStatus) {
+
+ public boolean ok() {
+ return statusCode == 200;
+ }
public boolean cached() {
return cacheStatus.equals("HIT");
diff --git a/src/main/java/de/hysky/skyblocker/utils/Utils.java b/src/main/java/de/hysky/skyblocker/utils/Utils.java
index 1205c71b..ff406b61 100644
--- a/src/main/java/de/hysky/skyblocker/utils/Utils.java
+++ b/src/main/java/de/hysky/skyblocker/utils/Utils.java
@@ -3,6 +3,7 @@ package de.hysky.skyblocker.utils;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import de.hysky.skyblocker.events.SkyblockEvents;
+import de.hysky.skyblocker.skyblock.item.MuseumItemCache;
import de.hysky.skyblocker.skyblock.item.PriceInfoTooltip;
import de.hysky.skyblocker.skyblock.rift.TheRift;
import de.hysky.skyblocker.utils.scheduler.MessageScheduler;
@@ -365,6 +366,8 @@ public class Utils {
if (isOnSkyblock && message.startsWith("Profile ID: ")) {
profileId = message.replace("Profile ID: ", "");
+
+ MuseumItemCache.tick(profileId);
}
return true;
diff --git a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java
index 4cfaef8f..064b564c 100644
--- a/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java
+++ b/src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java
@@ -5,7 +5,6 @@ import de.hysky.skyblocker.mixin.accessor.BeaconBlockEntityRendererInvoker;
import de.hysky.skyblocker.utils.render.culling.OcclusionCulling;
import de.hysky.skyblocker.utils.render.title.Title;
import de.hysky.skyblocker.utils.render.title.TitleContainer;
-import me.x150.renderer.render.Renderer3d;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.font.TextRenderer;
@@ -22,8 +21,6 @@ import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL11;
-import java.awt.*;
-
public class RenderHelper {
private static final Vec3d ONE = new Vec3d(1, 1, 1);
private static final int MAX_OVERWORLD_BUILD_HEIGHT = 319;
@@ -36,20 +33,46 @@ public class RenderHelper {
public static void renderFilledThroughWalls(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) {
if (FrustumUtils.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1)) {
- Renderer3d.renderThroughWalls();
- renderFilled(context, pos, colorComponents, alpha);
- Renderer3d.stopRenderThroughWalls();
+ renderFilled(context, Vec3d.of(pos), ONE, colorComponents, alpha, true);
}
}
public static void renderFilledIfVisible(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) {
if (OcclusionCulling.isVisible(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1)) {
- renderFilled(context, pos, colorComponents, alpha);
+ renderFilled(context, Vec3d.of(pos), ONE, colorComponents, alpha, false);
}
}
-
- private static void renderFilled(WorldRenderContext context, BlockPos pos, float[] colorComponents, float alpha) {
- Renderer3d.renderFilled(context.matrixStack(), new Color(colorComponents[0], colorComponents[1], colorComponents[2], alpha), Vec3d.of(pos), ONE);
+
+ private static void renderFilled(WorldRenderContext context, Vec3d pos, Vec3d dimensions, float[] colorComponents, float alpha, boolean throughWalls) {
+ MatrixStack matrices = context.matrixStack();
+ Vec3d camera = context.camera().getPos();
+ Tessellator tessellator = RenderSystem.renderThreadTesselator();
+ BufferBuilder buffer = tessellator.getBuffer();
+
+ matrices.push();
+ matrices.translate(-camera.x, -camera.y, -camera.z);
+
+ RenderSystem.setShader(GameRenderer::getPositionColorProgram);
+ RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
+ RenderSystem.polygonOffset(-1f, -10f);
+ RenderSystem.enablePolygonOffset();
+ RenderSystem.enableBlend();
+ RenderSystem.defaultBlendFunc();
+ RenderSystem.enableDepthTest();
+ RenderSystem.depthFunc(throughWalls ? GL11.GL_ALWAYS : GL11.GL_LEQUAL);
+ RenderSystem.disableCull();
+
+ buffer.begin(DrawMode.TRIANGLE_STRIP, VertexFormats.POSITION_COLOR);
+ WorldRenderer.renderFilledBox(matrices, buffer, pos.x, pos.y, pos.z, pos.x + dimensions.x, pos.y + dimensions.y, pos.z + dimensions.z, colorComponents[0], colorComponents[1], colorComponents[2], alpha);
+ tessellator.draw();
+
+ matrices.pop();
+ RenderSystem.polygonOffset(0f, 0f);
+ RenderSystem.disablePolygonOffset();
+ RenderSystem.disableBlend();
+ RenderSystem.disableDepthTest();
+ RenderSystem.depthFunc(GL11.GL_LEQUAL);
+ RenderSystem.enableCull();
}
private static void renderBeaconBeam(WorldRenderContext context, BlockPos pos, float[] colorComponents) {