diff options
author | Kevin <92656833+kevinthegreat1@users.noreply.github.com> | 2023-10-28 00:50:38 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-10-28 00:50:38 -0400 |
commit | 9edc4849a387aef3ac53d43573cfe53f5e06a5eb (patch) | |
tree | 3b0059dee9f41691e8332aafbf27f276379bebde | |
parent | 158782dabf00a009d7a431f20d4cb2227ffc4298 (diff) | |
parent | 1cbf455f1b065db83a9b2f433a68fb99d0647494 (diff) | |
download | Skyblocker-9edc4849a387aef3ac53d43573cfe53f5e06a5eb.tar.gz Skyblocker-9edc4849a387aef3ac53d43573cfe53f5e06a5eb.tar.bz2 Skyblocker-9edc4849a387aef3ac53d43573cfe53f5e06a5eb.zip |
Merge pull request #389 from AzureAaron/museum-cache
Museum Item Cache
-rw-r--r-- | build.gradle | 5 | ||||
-rw-r--r-- | gradle.properties | 2 | ||||
-rw-r--r-- | src/main/java/de/hysky/skyblocker/SkyblockerMod.java | 1 | ||||
-rw-r--r-- | src/main/java/de/hysky/skyblocker/skyblock/TeleportOverlay.java | 7 | ||||
-rw-r--r-- | src/main/java/de/hysky/skyblocker/skyblock/item/MuseumItemCache.java | 146 | ||||
-rw-r--r-- | src/main/java/de/hysky/skyblocker/utils/Http.java | 8 | ||||
-rw-r--r-- | src/main/java/de/hysky/skyblocker/utils/Utils.java | 3 | ||||
-rw-r--r-- | src/main/java/de/hysky/skyblocker/utils/render/RenderHelper.java | 43 |
8 files changed, 189 insertions, 26 deletions
diff --git a/build.gradle b/build.gradle index fde768d5..72432203 100644 --- a/build.gradle +++ b/build.gradle @@ -59,11 +59,6 @@ dependencies { modCompileOnly "dev.emi:emi-fabric:${project.emi_version}:api" modLocalRuntime "dev.emi:emi-fabric:${project.emi_version}" - // Renderer (https://github.com/0x3C50/Renderer) - include modImplementation("com.github.0x3C50:Renderer:${project.renderer_version}") { - exclude group: "io.github.ladysnake" exclude module: "satin" - } - include modImplementation("meteordevelopment:discord-ipc:1.1") // Mixin Extras (https://github.com/LlamaLad7/MixinExtras) diff --git a/gradle.properties b/gradle.properties index 42529261..6c364898 100644 --- a/gradle.properties +++ b/gradle.properties @@ -20,8 +20,6 @@ mod_menu_version = 8.0.0 rei_version = 13.0.666 ## EMI (https://modrinth.com/mod/emi/versions) emi_version = 1.0.22+1.20.2 -## Renderer (https://github.com/0x3C50/Renderer) -renderer_version = master-SNAPSHOT # Minecraft and Related Libraries ## Mixin Extras (https://github.com/LlamaLad7/MixinExtras) 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) { |