From 301f40c7a97d862006770a6b27ff11a2677dcb3e Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Sat, 26 Aug 2023 23:46:14 -0400 Subject: Fix Croesus Helper (#257) --- .../java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java index 44c8803f..def1eb76 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/CroesusHelper.java @@ -25,7 +25,7 @@ public class CroesusHelper extends ContainerSolver { List highlights = new ArrayList<>(); for (Map.Entry entry : slots.entrySet()) { ItemStack stack = entry.getValue(); - if (stack != null && stack.getNbt() != null && stack.getNbt().toString().contains("No more Chests to open!")) { + if (stack != null && stack.getNbt() != null && stack.getNbt().toString().contains("Opened Chest:")) { highlights.add(ColorHighlight.gray(entry.getKey())); } } -- cgit From 7bb19de683c70aeccea710d53ca3b13974a1c95d Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Wed, 30 Aug 2023 00:32:20 -0400 Subject: Tic Tac Toe Solver! (#253) * Tic Tac Toe Solver! * alpha-beta-pruning * Small changes --------- Co-authored-by: Yasin --- .../skyblocker/skyblock/dungeon/TicTacToe.java | 138 +++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java new file mode 100644 index 00000000..f5d55d2c --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/TicTacToe.java @@ -0,0 +1,138 @@ +package me.xmrvizzy.skyblocker.skyblock.dungeon; + +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import me.xmrvizzy.skyblocker.config.SkyblockerConfig; +import me.xmrvizzy.skyblocker.utils.RenderUtils; +import me.xmrvizzy.skyblocker.utils.Utils; +import me.xmrvizzy.skyblocker.utils.color.QuadColor; +import me.xmrvizzy.skyblocker.utils.tictactoe.TicTacToeUtils; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; +import net.minecraft.block.Block; +import net.minecraft.block.Blocks; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.decoration.ItemFrameEntity; +import net.minecraft.item.FilledMapItem; +import net.minecraft.item.map.MapState; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Box; +import net.minecraft.util.math.Direction; + +/** + * Thanks to Danker for a reference implementation! + */ +public class TicTacToe { + private static final Logger LOGGER = LoggerFactory.getLogger(TicTacToe.class); + private static final QuadColor RED = QuadColor.single(1.0f, 0.0f, 0.0f, 1f); + private static Box nextBestMoveToMake = null; + + public static void init() { + WorldRenderEvents.BEFORE_DEBUG_RENDER.register(TicTacToe::solutionRenderer); + } + + public static void tick() { + MinecraftClient client = MinecraftClient.getInstance(); + ClientWorld world = client.world; + ClientPlayerEntity player = client.player; + + nextBestMoveToMake = null; + + if (world == null || player == null || !Utils.isInDungeons()) return; + + //Search within 21 blocks for item frames that contain maps + Box searchBox = new Box(player.getX() - 21, player.getY() - 21, player.getZ() - 21, player.getX() + 21, player.getY() + 21, player.getZ() + 21); + List itemFramesThatHoldMaps = world.getEntitiesByClass(ItemFrameEntity.class, searchBox, ItemFrameEntity::containsMap); + + try { + //Only attempt to solve if its the player's turn + if (itemFramesThatHoldMaps.size() != 9 && itemFramesThatHoldMaps.size() % 2 == 1) { + char[][] board = new char[3][3]; + BlockPos leftmostRow = null; + int sign = 1; + char facing = 'X'; + + for (ItemFrameEntity itemFrame : itemFramesThatHoldMaps) { + MapState mapState = world.getMapState(FilledMapItem.getMapName(itemFrame.getMapId().getAsInt())); + + if (mapState == null) continue; + + int column = 0, row; + sign = 1; + + //Find position of the item frame relative to where it is on the tic tac toe board + if (itemFrame.getHorizontalFacing() == Direction.SOUTH || itemFrame.getHorizontalFacing() == Direction.WEST) sign = -1; + BlockPos itemFramePos = BlockPos.ofFloored(itemFrame.getX(), itemFrame.getY(), itemFrame.getZ()); + + for (int i = 2; i >= 0; i--) { + int realI = i * sign; + BlockPos blockPos = itemFramePos; + + if (itemFrame.getX() % 0.5 == 0) { + blockPos = itemFramePos.add(realI, 0, 0); + } else if (itemFrame.getZ() % 0.5 == 0) { + blockPos = itemFramePos.add(0, 0, realI); + facing = 'Z'; + } + + Block block = world.getBlockState(blockPos).getBlock(); + if (block == Blocks.AIR || block == Blocks.STONE_BUTTON) { + leftmostRow = blockPos; + column = i; + + break; + } + } + + //Determine the row of the item frame + if (itemFrame.getY() == 72.5) { + row = 0; + } else if (itemFrame.getY() == 71.5) { + row = 1; + } else if (itemFrame.getY() == 70.5) { + row = 2; + } else { + continue; + } + + + //Get the color of the middle pixel of the map which determines whether its X or O + int middleColor = mapState.colors[8256] & 255; + + if (middleColor == 114) { + board[row][column] = 'X'; + } else if (middleColor == 33) { + board[row][column] = 'O'; + } + + int bestMove = TicTacToeUtils.getBestMove(board) - 1; + + if (leftmostRow != null) { + double drawX = facing == 'X' ? leftmostRow.getX() - sign * (bestMove % 3) : leftmostRow.getX(); + double drawY = 72 - (double) (bestMove / 3); + double drawZ = facing == 'Z' ? leftmostRow.getZ() - sign * (bestMove % 3) : leftmostRow.getZ(); + + nextBestMoveToMake = new Box(drawX, drawY, drawZ, drawX + 1, drawY + 1, drawZ + 1); + } + } + } + } catch (Exception e) { + LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while determining a tic tac toe solution!", e); + } + } + + private static void solutionRenderer(WorldRenderContext context) { + try { + if (SkyblockerConfig.get().locations.dungeons.solveTicTacToe && nextBestMoveToMake != null) { + RenderUtils.drawBoxOutline(nextBestMoveToMake, RED, 5); + } + } catch (Exception e) { + LOGGER.error("[Skyblocker Tic Tac Toe] Encountered an exception while rendering the tic tac toe solution!", e); + } + } +} -- cgit From 44819b6eda32ec3dee637396c94f62bf0bcb0adc Mon Sep 17 00:00:00 2001 From: Aaron <51387595+AzureAaron@users.noreply.github.com> Date: Wed, 30 Aug 2023 21:26:21 -0400 Subject: Starred Mob Glow (#260) --- .../skyblock/dungeon/StarredMobGlow.java | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java new file mode 100644 index 00000000..b45242f9 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/StarredMobGlow.java @@ -0,0 +1,51 @@ +package me.xmrvizzy.skyblocker.skyblock.dungeon; + +import java.util.List; + +import me.xmrvizzy.skyblocker.utils.Utils; +import me.xmrvizzy.skyblocker.utils.culling.OcclusionCulling; +import net.minecraft.entity.Entity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.predicate.entity.EntityPredicates; +import net.minecraft.util.math.Box; + +public class StarredMobGlow { + + public static boolean shouldMobGlow(Entity entity) { + Box box = entity.getBoundingBox(); + + if (Utils.isInDungeons() && !entity.isInvisible() && OcclusionCulling.isVisible(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)) { + //Minibosses + if (entity instanceof PlayerEntity) { + switch (entity.getName().getString()) { + case "Lost Adventurer": return true; + case "Shadow Assassin": return true; + case "Diamond Guy": return true; + } + } + + //Regular Mobs + if (!(entity instanceof ArmorStandEntity)) { + Box searchBox = box.expand(0, 2, 0); + List armorStands = entity.getWorld().getEntitiesByClass(ArmorStandEntity.class, searchBox, EntityPredicates.NOT_MOUNTED); + + if (!armorStands.isEmpty() && armorStands.get(0).getName().getString().contains("✯")) return true; + } + } + + return false; + } + + public static int getGlowColor(Entity entity) { + if (entity instanceof PlayerEntity) { + switch (entity.getName().getString()) { + case "Lost Adventurer": return 0xfee15c; + case "Shadow Assassin": return 0x5b2cb2; + case "Diamond Guy": return 0x57c2f7; + } + } + + return 0xf57738; + } +} -- cgit From fbe97597633db943821efec5f8a522c595241381 Mon Sep 17 00:00:00 2001 From: Yasin Date: Thu, 31 Aug 2023 04:40:05 +0200 Subject: Improve DungeonBlaze.java (#254) improve DungeonBlaze.java * add javadoc * using ArmorStandEntity instead iterating every Entity * reorganize large methods into clearer multiple smaller methods * defined outlinecolor as constants --- .../skyblocker/skyblock/dungeon/DungeonBlaze.java | 153 +++++++++++++-------- 1 file changed, 98 insertions(+), 55 deletions(-) (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java index d0dcf1e1..c3e12c3d 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java @@ -9,8 +9,10 @@ import me.xmrvizzy.skyblocker.utils.color.QuadColor; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderContext; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.Entity; +import net.minecraft.entity.decoration.ArmorStandEntity; +import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.util.math.Box; import net.minecraft.util.math.Vec3d; import org.slf4j.Logger; @@ -20,92 +22,133 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; +/** + * This class provides functionality to render outlines around Blaze entities + */ public class DungeonBlaze { private static final Logger LOGGER = LoggerFactory.getLogger(DungeonBlaze.class.getName()); - private static final float[] WHITE_COLOR_COMPONENTS = { 1.0f, 1.0f, 1.0f }; - static Entity highestBlaze = null; - static Entity lowestBlaze = null; - static Entity nextHighestBlaze = null; - static Entity nextLowestBlaze = null; - static boolean renderHooked = false; - + private static final float[] WHITE_COLOR_COMPONENTS = {1.0f, 1.0f, 1.0f}; + private static final QuadColor outlineColorGreen = QuadColor.single(0.0F, 1.0f, 0.0f, 1f); + private static final QuadColor outlineColorWhite = QuadColor.single(1.0f, 1.0f, 1.0f, 1.0f); + + private static ArmorStandEntity highestBlaze = null; + private static ArmorStandEntity lowestBlaze = null; + private static ArmorStandEntity nextHighestBlaze = null; + private static ArmorStandEntity nextLowestBlaze = null; + private static boolean renderHooked = false; + + /** + * Updates the state of Blaze entities and triggers the rendering process if necessary. + */ public static void update() { ClientWorld world = MinecraftClient.getInstance().world; - if (world == null || !Utils.isInDungeons()) return; - if (!renderHooked){ - + ClientPlayerEntity player = MinecraftClient.getInstance().player; + if (world == null || player == null || !Utils.isInDungeons()) return; + if (!renderHooked) { WorldRenderEvents.BEFORE_DEBUG_RENDER.register(DungeonBlaze::blazeRenderer); renderHooked = true; } - Iterable entities = world.getEntities(); - List> blazes = new ArrayList<>(); + List> blazes = getBlazesInWorld(world, player); + sortBlazes(blazes); + updateBlazeEntities(blazes); + } - for (Entity entity : entities) { - String blazeName = entity.getName().getString(); - + /** + * Retrieves Blaze entities in the world and parses their health information. + * + * @param world The client world to search for Blaze entities. + * @return A list of Blaze entities and their associated health. + */ + private static List> getBlazesInWorld(ClientWorld world, ClientPlayerEntity player) { + List> blazes = new ArrayList<>(); + for (ArmorStandEntity blaze : world.getEntitiesByClass(ArmorStandEntity.class, player.getBoundingBox().expand(500D), EntityPredicates.NOT_MOUNTED)) { + String blazeName = blaze.getName().getString(); if (blazeName.contains("Blaze") && blazeName.contains("/")) { try { int health = Integer.parseInt(blazeName.substring(blazeName.indexOf("/") + 1, blazeName.length() - 1)); - - blazes.add(ObjectIntPair.of(entity, health)); - } catch (NumberFormatException ex) { - ex.printStackTrace(); + blazes.add(ObjectIntPair.of(blaze, health)); + } catch (NumberFormatException e) { + handleException(e); } } } + return blazes; + } - // Order the blazes in the list from the lowest health to the highest health + /** + * Sorts the Blaze entities based on their health values. + * + * @param blazes The list of Blaze entities to be sorted. + */ + private static void sortBlazes(List> blazes) { blazes.sort(Comparator.comparingInt(ObjectIntPair::rightInt)); + } - // Ensure that there are blazes in the list + /** + * Updates information about Blaze entities based on sorted list. + * + * @param blazes The sorted list of Blaze entities with associated health values. + */ + private static void updateBlazeEntities(List> blazes) { if (!blazes.isEmpty()) { lowestBlaze = blazes.get(0).left(); - int highestIndex = blazes.size() - 1; highestBlaze = blazes.get(highestIndex).left(); - - // If there's more than 1 blaze if (blazes.size() > 1) { - nextLowestBlaze = blazes.get(1).left(); - nextHighestBlaze = blazes.get(highestIndex - 1).left(); + nextLowestBlaze = blazes.get(1).left(); + nextHighestBlaze = blazes.get(highestIndex - 1).left(); } } - } + + /** + * Renders outlines for Blaze entities based on health and position. + * + * @param wrc The WorldRenderContext used for rendering. + */ public static void blazeRenderer(WorldRenderContext wrc) { - QuadColor outlineColorRed = QuadColor.single( 0.0F, 1.0F, 0.0F, 1f); - QuadColor outlineColorGreen = QuadColor.single(1.0F, 0.0F, 0.0F, 1f); - QuadColor outlineColorWhite = QuadColor.single(1.0f, 1.0f, 1.0f, 1.0f); - try { - if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfig.get().locations.dungeons.blazesolver){ - /* Outline */ + if (highestBlaze != null && lowestBlaze != null && highestBlaze.isAlive() && lowestBlaze.isAlive() && SkyblockerConfig.get().locations.dungeons.blazesolver) { if (highestBlaze.getY() < 69) { - Box blaze = highestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); - RenderUtils.drawBoxOutline(blaze, outlineColorRed, 5f); - - if (nextHighestBlaze != null && nextHighestBlaze.isAlive() && nextHighestBlaze != highestBlaze) { - Box nextBlaze = nextHighestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); - RenderUtils.drawBoxOutline(nextBlaze, outlineColorWhite, 5f); - RenderHelper.renderLinesFromPoints(wrc, new Vec3d[] { blaze.getCenter(), nextBlaze.getCenter() }, WHITE_COLOR_COMPONENTS, 1f, 5f); - } + renderBlazeOutline(highestBlaze, nextHighestBlaze, wrc); } - - /* Outline */ if (lowestBlaze.getY() > 69) { - Box blaze = lowestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); - RenderUtils.drawBoxOutline(blaze, outlineColorRed, 5f); - - if (nextLowestBlaze != null && nextLowestBlaze.isAlive() && nextLowestBlaze != lowestBlaze) { - Box nextBlaze = nextLowestBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); - RenderUtils.drawBoxOutline(nextBlaze, outlineColorWhite, 5f); - RenderHelper.renderLinesFromPoints(wrc, new Vec3d[] { blaze.getCenter(), nextBlaze.getCenter() }, WHITE_COLOR_COMPONENTS, 1f, 5f); - } + renderBlazeOutline(lowestBlaze, nextLowestBlaze, wrc); } } } catch (Exception e) { - LOGGER.warn("[Skyblocker BlazeRenderer] " + e); - e.printStackTrace(); + handleException(e); } } -} \ No newline at end of file + + /** + * Renders outlines for Blaze entities and connections between them. + * + * @param blaze The Blaze entity for which to render an outline. + * @param nextBlaze The next Blaze entity for connection rendering. + * @param wrc The WorldRenderContext used for rendering. + */ + private static void renderBlazeOutline(ArmorStandEntity blaze, ArmorStandEntity nextBlaze, WorldRenderContext wrc) { + Box blazeBox = blaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); + RenderUtils.drawBoxOutline(blazeBox, DungeonBlaze.outlineColorGreen, 5f); + + if (nextBlaze != null && nextBlaze.isAlive() && nextBlaze != blaze) { + Box nextBlazeBox = nextBlaze.getBoundingBox().expand(0.3, 0.9, 0.3).offset(0, -1.1, 0); + RenderUtils.drawBoxOutline(nextBlazeBox, DungeonBlaze.outlineColorWhite, 5f); + + Vec3d blazeCenter = blazeBox.getCenter(); + Vec3d nextBlazeCenter = nextBlazeBox.getCenter(); + + RenderHelper.renderLinesFromPoints(wrc, new Vec3d[]{blazeCenter, nextBlazeCenter}, WHITE_COLOR_COMPONENTS, 1f, 5f); + } + } + + /** + * Handles exceptions by logging and printing stack traces. + * + * @param e The exception to handle. + */ + private static void handleException(Exception e) { + LOGGER.warn("[Skyblocker BlazeRenderer] Encountered an unknown exception", e); + } +} -- cgit From 369c6a923bf674566498c940ba05599119446446 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Mon, 10 Jul 2023 12:54:04 +0800 Subject: Add Dungeon Secrets loading --- .../skyblock/dungeon/secrets/DungeonSecrets.java | 90 ++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java new file mode 100644 index 00000000..6e72c1c4 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java @@ -0,0 +1,90 @@ +package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; + +import com.google.gson.JsonObject; +import me.xmrvizzy.skyblocker.SkyblockerMod; +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.Identifier; +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.zip.InflaterInputStream; + +public class DungeonSecrets { + private static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class); + private static final String DUNGEONS_DATA_DIR = "/assets/skyblocker/dungeons"; + private static final HashMap>> ROOMS = new HashMap<>(); + private static JsonObject roomsJson; + private static JsonObject waypointsJson; + @Nullable + private static CompletableFuture roomsLoaded; + + public static boolean isRoomsLoaded() { + return roomsLoaded != null && roomsLoaded.isDone(); + } + + public static void init() { + CompletableFuture.runAsync(() -> { + List> dungeonFutures = Collections.synchronizedList(new ArrayList<>()); + try { + //noinspection DataFlowIssue + File dungeons = new File(SkyblockerMod.class.getResource(DUNGEONS_DATA_DIR).getFile()); + int resourcePathIndex = dungeons.getPath().indexOf(DUNGEONS_DATA_DIR); + //noinspection DataFlowIssue + for (File dungeon : dungeons.listFiles()) { + if (!dungeon.isDirectory()) { + continue; + } + File[] roomShapes = dungeon.listFiles(); + if (roomShapes == null) { + LOGGER.error("Failed to load dungeon secrets for dungeon {}", dungeon.getName()); + continue; + } + ROOMS.put(dungeon.getName(), new HashMap<>()); + List> roomShapeFutures = new ArrayList<>(); + for (File roomShape : roomShapes) { + roomShapeFutures.add(CompletableFuture.supplyAsync(() -> readRooms(roomShape, resourcePathIndex)).thenAccept(rooms -> ROOMS.get(dungeon.getName()).put(roomShape.getName(), rooms))); + } + dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.debug("Loaded dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getName(), ROOMS.get(dungeon.getName()).size(), ROOMS.get(dungeon.getName()).values().stream().mapToInt(HashMap::size).sum()))); + } + } catch (NullPointerException e) { + LOGGER.error("Failed to load dungeon secrets", e); + } + try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) { + roomsJson = SkyblockerMod.GSON.fromJson(roomsReader, JsonObject.class); + waypointsJson = SkyblockerMod.GSON.fromJson(waypointsReader, JsonObject.class); + LOGGER.debug("Loaded dungeon secrets json"); + } catch (IOException e) { + LOGGER.error("Failed to load dungeon secrets json", e); + } + roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", ROOMS.size(), ROOMS.values().stream().mapToInt(HashMap::size).sum(), ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); + }); + } + + private static HashMap readRooms(File roomShape, int resourcePathIndex) { + HashMap data = new HashMap<>(); + File[] rooms = roomShape.listFiles(); + if (rooms == null) { + LOGGER.error("Failed to load dungeon secrets room shape {}", roomShape.getName()); + return data; + } + for (File room : rooms) { + String name = room.getName(); + //noinspection DataFlowIssue + try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(SkyblockerMod.class.getResourceAsStream(room.getPath().substring(resourcePathIndex))))) { + data.put(name.substring(0, name.length() - 9), (long[]) in.readObject()); + LOGGER.debug("Loaded dungeon secrets room {}", name); + } catch (NullPointerException | IOException | ClassNotFoundException e) { + LOGGER.error("Failed to load dungeon secrets room " + name, e); + } + } + LOGGER.debug("Loaded dungeon secrets room shape {} with {} rooms", roomShape.getName(), data.size()); + return data; + } +} -- cgit From 2a13974954867ab519e5e2af8cffc629bbb5de24 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Tue, 11 Jul 2023 10:39:38 +0800 Subject: Add Dungeon Secret Waypoint options --- .../skyblock/dungeon/secrets/DungeonSecrets.java | 36 +++++++++++++++------- 1 file changed, 25 insertions(+), 11 deletions(-) (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java index 6e72c1c4..8be626d2 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java @@ -2,6 +2,7 @@ package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; import com.google.gson.JsonObject; import me.xmrvizzy.skyblocker.SkyblockerMod; +import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import net.minecraft.client.MinecraftClient; import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; @@ -12,7 +13,10 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; -import java.util.*; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.zip.InflaterInputStream; @@ -29,10 +33,17 @@ public class DungeonSecrets { return roomsLoaded != null && roomsLoaded.isDone(); } + /** + * Loads the dungeon secrets asynchronously from {@code /assets/skyblocker/dungeons}. + * Use {@link #isRoomsLoaded()} to check for completion of loading. + */ public static void init() { + if (SkyblockerConfig.get().locations.dungeons.noLoadSecretWaypoints) { + return; + } CompletableFuture.runAsync(() -> { - List> dungeonFutures = Collections.synchronizedList(new ArrayList<>()); try { + List> dungeonFutures = new ArrayList<>(); //noinspection DataFlowIssue File dungeons = new File(SkyblockerMod.class.getResource(DUNGEONS_DATA_DIR).getFile()); int resourcePathIndex = dungeons.getPath().indexOf(DUNGEONS_DATA_DIR); @@ -53,17 +64,20 @@ public class DungeonSecrets { } dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.debug("Loaded dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getName(), ROOMS.get(dungeon.getName()).size(), ROOMS.get(dungeon.getName()).values().stream().mapToInt(HashMap::size).sum()))); } - } catch (NullPointerException e) { + // Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set + dungeonFutures.add(CompletableFuture.runAsync(() -> { + try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) { + roomsJson = SkyblockerMod.GSON.fromJson(roomsReader, JsonObject.class); + waypointsJson = SkyblockerMod.GSON.fromJson(waypointsReader, JsonObject.class); + LOGGER.debug("Loaded dungeon secrets json"); + } catch (Exception e) { + LOGGER.error("Failed to load dungeon secrets json", e); + } + }, MinecraftClient.getInstance())); + roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", ROOMS.size(), ROOMS.values().stream().mapToInt(HashMap::size).sum(), ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); + } catch (Exception e) { LOGGER.error("Failed to load dungeon secrets", e); } - try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) { - roomsJson = SkyblockerMod.GSON.fromJson(roomsReader, JsonObject.class); - waypointsJson = SkyblockerMod.GSON.fromJson(waypointsReader, JsonObject.class); - LOGGER.debug("Loaded dungeon secrets json"); - } catch (IOException e) { - LOGGER.error("Failed to load dungeon secrets json", e); - } - roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", ROOMS.size(), ROOMS.values().stream().mapToInt(HashMap::size).sum(), ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); }); } -- cgit From d54ee77fb2c007b16bcc3d6b77acf008f948596d Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Tue, 11 Jul 2023 21:29:42 +0800 Subject: Add entrance pos and room width parsing --- .../skyblock/dungeon/secrets/DungeonMapUtils.java | 52 ++++++++++ .../skyblock/dungeon/secrets/DungeonSecrets.java | 109 ++++++++++++++------- 2 files changed, 124 insertions(+), 37 deletions(-) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java new file mode 100644 index 00000000..9d1c7ccb --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java @@ -0,0 +1,52 @@ +package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; + +import net.minecraft.block.MapColor; +import net.minecraft.item.map.MapIcon; +import net.minecraft.item.map.MapState; +import org.joml.Vector2i; + +public class DungeonMapUtils { + public static final byte ENTRANCE_COLOR = MapColor.DARK_GREEN.getRenderColorByte(MapColor.Brightness.HIGH); + public static final byte ROOM_COLOR = MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.LOWEST); + public static final byte PUZZLE_COLOR = MapColor.MAGENTA.getRenderColorByte(MapColor.Brightness.HIGH); + public static final byte MINIBOSS_COLOR = MapColor.YELLOW.getRenderColorByte(MapColor.Brightness.HIGH); + public static final byte FAIRY_COLOR = MapColor.PINK.getRenderColorByte(MapColor.Brightness.HIGH); + public static final byte BLOOD_COLOR = MapColor.BRIGHT_RED.getRenderColorByte(MapColor.Brightness.HIGH); + public static final byte UNKNOWN_COLOR = MapColor.GRAY.getRenderColorByte(MapColor.Brightness.NORMAL); + public static final byte BLACK_COLOR = MapColor.BLACK.getRenderColorByte(MapColor.Brightness.LOWEST); + public static final byte WHITE_COLOR = MapColor.WHITE.getRenderColorByte(MapColor.Brightness.HIGH); + + public static Vector2i getEntrancePos(MapState map) { + for (MapIcon icon : map.getIcons()) { + if (icon.getType() == MapIcon.Type.FRAME) { + int x = (icon.getX() >> 1) + 64; + int z = (icon.getZ() >> 1) + 64; + if (getColor(map, x, z) == ENTRANCE_COLOR) { + while (getColor(map, x - 1, z) == ENTRANCE_COLOR) { + x--; + } + while (getColor(map, x, z - 1) == ENTRANCE_COLOR) { + z--; + } + return new Vector2i(x, z); + } + } + } + return null; + } + + public static int getRoomWidth(MapState map, Vector2i entrancePos) { + int i = 0; + while (getColor(map, entrancePos.x + i, entrancePos.y) == ENTRANCE_COLOR) { + i++; + } + return i; + } + + private static byte getColor(MapState map, int x, int z) { + if (x < 0 || z < 0 || x >= 128 || z >= 128) { + return -1; + } + return map.colors[x + (z << 7)]; + } +} diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java index 8be626d2..d293bb88 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java @@ -3,9 +3,15 @@ package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; import com.google.gson.JsonObject; import me.xmrvizzy.skyblocker.SkyblockerMod; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; +import me.xmrvizzy.skyblocker.utils.Utils; import net.minecraft.client.MinecraftClient; +import net.minecraft.item.FilledMapItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.item.map.MapState; import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; +import org.joml.Vector2i; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -21,13 +27,15 @@ import java.util.concurrent.CompletableFuture; import java.util.zip.InflaterInputStream; public class DungeonSecrets { - private static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class); + protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class); private static final String DUNGEONS_DATA_DIR = "/assets/skyblocker/dungeons"; private static final HashMap>> ROOMS = new HashMap<>(); private static JsonObject roomsJson; private static JsonObject waypointsJson; @Nullable private static CompletableFuture roomsLoaded; + private static Vector2i mapEntrancePos; + private static int mapRoomWidth; public static boolean isRoomsLoaded() { return roomsLoaded != null && roomsLoaded.isDone(); @@ -41,44 +49,47 @@ public class DungeonSecrets { if (SkyblockerConfig.get().locations.dungeons.noLoadSecretWaypoints) { return; } - CompletableFuture.runAsync(() -> { - try { - List> dungeonFutures = new ArrayList<>(); - //noinspection DataFlowIssue - File dungeons = new File(SkyblockerMod.class.getResource(DUNGEONS_DATA_DIR).getFile()); - int resourcePathIndex = dungeons.getPath().indexOf(DUNGEONS_DATA_DIR); - //noinspection DataFlowIssue - for (File dungeon : dungeons.listFiles()) { - if (!dungeon.isDirectory()) { - continue; - } - File[] roomShapes = dungeon.listFiles(); - if (roomShapes == null) { - LOGGER.error("Failed to load dungeon secrets for dungeon {}", dungeon.getName()); - continue; - } - ROOMS.put(dungeon.getName(), new HashMap<>()); - List> roomShapeFutures = new ArrayList<>(); - for (File roomShape : roomShapes) { - roomShapeFutures.add(CompletableFuture.supplyAsync(() -> readRooms(roomShape, resourcePathIndex)).thenAccept(rooms -> ROOMS.get(dungeon.getName()).put(roomShape.getName(), rooms))); - } - dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.debug("Loaded dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getName(), ROOMS.get(dungeon.getName()).size(), ROOMS.get(dungeon.getName()).values().stream().mapToInt(HashMap::size).sum()))); + CompletableFuture.runAsync(DungeonSecrets::load); + SkyblockerMod.getInstance().scheduler.scheduleCyclic(DungeonSecrets::update, 10); + } + + private static void load() { + try { + List> dungeonFutures = new ArrayList<>(); + //noinspection DataFlowIssue + File dungeons = new File(SkyblockerMod.class.getResource(DUNGEONS_DATA_DIR).getFile()); + int resourcePathIndex = dungeons.getPath().indexOf(DUNGEONS_DATA_DIR); + //noinspection DataFlowIssue + for (File dungeon : dungeons.listFiles()) { + if (!dungeon.isDirectory()) { + continue; + } + File[] roomShapes = dungeon.listFiles(); + if (roomShapes == null) { + LOGGER.error("Failed to load dungeon secrets for dungeon {}", dungeon.getName()); + continue; + } + ROOMS.put(dungeon.getName(), new HashMap<>()); + List> roomShapeFutures = new ArrayList<>(); + for (File roomShape : roomShapes) { + roomShapeFutures.add(CompletableFuture.supplyAsync(() -> readRooms(roomShape, resourcePathIndex)).thenAccept(rooms -> ROOMS.get(dungeon.getName()).put(roomShape.getName(), rooms))); } - // Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set - dungeonFutures.add(CompletableFuture.runAsync(() -> { - try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) { - roomsJson = SkyblockerMod.GSON.fromJson(roomsReader, JsonObject.class); - waypointsJson = SkyblockerMod.GSON.fromJson(waypointsReader, JsonObject.class); - LOGGER.debug("Loaded dungeon secrets json"); - } catch (Exception e) { - LOGGER.error("Failed to load dungeon secrets json", e); - } - }, MinecraftClient.getInstance())); - roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", ROOMS.size(), ROOMS.values().stream().mapToInt(HashMap::size).sum(), ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); - } catch (Exception e) { - LOGGER.error("Failed to load dungeon secrets", e); + dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.debug("Loaded dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getName(), ROOMS.get(dungeon.getName()).size(), ROOMS.get(dungeon.getName()).values().stream().mapToInt(HashMap::size).sum()))); } - }); + // Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set + dungeonFutures.add(CompletableFuture.runAsync(() -> { + try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) { + roomsJson = SkyblockerMod.GSON.fromJson(roomsReader, JsonObject.class); + waypointsJson = SkyblockerMod.GSON.fromJson(waypointsReader, JsonObject.class); + LOGGER.debug("Loaded dungeon secrets json"); + } catch (Exception e) { + LOGGER.error("Failed to load dungeon secrets json", e); + } + }, MinecraftClient.getInstance())); + roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", ROOMS.size(), ROOMS.values().stream().mapToInt(HashMap::size).sum(), ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); + } catch (Exception e) { + LOGGER.error("Failed to load dungeon secrets", e); + } } private static HashMap readRooms(File roomShape, int resourcePathIndex) { @@ -101,4 +112,28 @@ public class DungeonSecrets { LOGGER.debug("Loaded dungeon secrets room shape {} with {} rooms", roomShape.getName(), data.size()); return data; } + + private static void update() { + if (!SkyblockerConfig.get().locations.dungeons.secretWaypoints || !Utils.isInDungeons()) { + return; + } + MinecraftClient client = MinecraftClient.getInstance(); + if (client.player == null || client.world == null) { + return; + } + ItemStack stack = client.player.getInventory().main.get(8); + if (!stack.isOf(Items.FILLED_MAP)) { + return; + } + MapState map = FilledMapItem.getMapState(FilledMapItem.getMapId(stack), client.world); + if (map == null) { + return; + } + if (mapEntrancePos == null && (mapEntrancePos = DungeonMapUtils.getEntrancePos(map)) == null) { + return; + } + if (mapRoomWidth == 0 && (mapRoomWidth = DungeonMapUtils.getRoomWidth(map, mapEntrancePos)) == 0) { + return; + } + } } -- cgit From a5998f09db291a15b7108dec8a66065fbdc40108 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:06:55 +0800 Subject: Add rooms data updating --- .../skyblock/dungeon/secrets/DungeonSecrets.java | 41 +++++++++++++++++----- 1 file changed, 33 insertions(+), 8 deletions(-) (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java index d293bb88..b689f828 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java @@ -19,17 +19,42 @@ import java.io.BufferedReader; import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.zip.InflaterInputStream; public class DungeonSecrets { protected static final Logger LOGGER = LoggerFactory.getLogger(DungeonSecrets.class); private static final String DUNGEONS_DATA_DIR = "/assets/skyblocker/dungeons"; - private static final HashMap>> ROOMS = new HashMap<>(); + private static final HashMap>> ROOMS = new HashMap<>(); + /** + * Maps the block identifier string to a custom numeric block id used in dungeon rooms data. + * @implNote Not using {@link net.minecraft.registry.Registry#getId(Object) Registry#getId(Block)} and {@link net.minecraft.block.Blocks Blocks} since this is also used by {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU}, which runs outside of Minecraft. + */ + @SuppressWarnings("JavadocReference") + protected static final Map NUMERIC_ID = Map.ofEntries( + Map.entry("minecraft:stone", (byte) 1), + Map.entry("minecraft:diorite", (byte) 2), + Map.entry("minecraft:polished_diorite", (byte) 3), + Map.entry("minecraft:andesite", (byte) 4), + Map.entry("minecraft:polished_andesite", (byte) 5), + Map.entry("minecraft:grass_block", (byte) 6), + Map.entry("minecraft:dirt", (byte) 7), + Map.entry("minecraft:coarse_dirt", (byte) 8), + Map.entry("minecraft:cobblestone", (byte) 9), + Map.entry("minecraft:bedrock", (byte) 10), + Map.entry("minecraft:oak_leaves", (byte) 11), + Map.entry("minecraft:gray_wool", (byte) 12), + Map.entry("minecraft:double_stone_slab", (byte) 13), + Map.entry("minecraft:mossy_cobblestone", (byte) 14), + Map.entry("minecraft:clay", (byte) 15), + Map.entry("minecraft:stone_bricks", (byte) 16), + Map.entry("minecraft:mossy_stone_bricks", (byte) 17), + Map.entry("minecraft:chiseled_stone_bricks", (byte) 18), + Map.entry("minecraft:gray_terracotta", (byte) 19), + Map.entry("minecraft:cyan_terracotta", (byte) 20), + Map.entry("minecraft:black_terracotta", (byte) 21) + ); private static JsonObject roomsJson; private static JsonObject waypointsJson; @Nullable @@ -92,8 +117,8 @@ public class DungeonSecrets { } } - private static HashMap readRooms(File roomShape, int resourcePathIndex) { - HashMap data = new HashMap<>(); + private static HashMap readRooms(File roomShape, int resourcePathIndex) { + HashMap data = new HashMap<>(); File[] rooms = roomShape.listFiles(); if (rooms == null) { LOGGER.error("Failed to load dungeon secrets room shape {}", roomShape.getName()); @@ -103,7 +128,7 @@ public class DungeonSecrets { String name = room.getName(); //noinspection DataFlowIssue try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(SkyblockerMod.class.getResourceAsStream(room.getPath().substring(resourcePathIndex))))) { - data.put(name.substring(0, name.length() - 9), (long[]) in.readObject()); + data.put(name.substring(0, name.length() - 9), (int[]) in.readObject()); LOGGER.debug("Loaded dungeon secrets room {}", name); } catch (NullPointerException | IOException | ClassNotFoundException e) { LOGGER.error("Failed to load dungeon secrets room " + name, e); -- cgit From b81cd1254ddc430120c64b1ddcc95e0537f32789 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Fri, 14 Jul 2023 15:58:59 +0800 Subject: Add rooms data saving and update reading to nio --- .../skyblock/dungeon/secrets/DungeonSecrets.java | 104 +++++++++++---------- 1 file changed, 54 insertions(+), 50 deletions(-) (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java index b689f828..9a9752fb 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java @@ -16,9 +16,12 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; -import java.io.File; import java.io.IOException; import java.io.ObjectInputStream; +import java.net.URL; +import java.nio.file.DirectoryStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.zip.InflaterInputStream; @@ -29,6 +32,7 @@ public class DungeonSecrets { private static final HashMap>> ROOMS = new HashMap<>(); /** * Maps the block identifier string to a custom numeric block id used in dungeon rooms data. + * * @implNote Not using {@link net.minecraft.registry.Registry#getId(Object) Registry#getId(Block)} and {@link net.minecraft.block.Blocks Blocks} since this is also used by {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU}, which runs outside of Minecraft. */ @SuppressWarnings("JavadocReference") @@ -79,63 +83,63 @@ public class DungeonSecrets { } private static void load() { - try { - List> dungeonFutures = new ArrayList<>(); - //noinspection DataFlowIssue - File dungeons = new File(SkyblockerMod.class.getResource(DUNGEONS_DATA_DIR).getFile()); - int resourcePathIndex = dungeons.getPath().indexOf(DUNGEONS_DATA_DIR); - //noinspection DataFlowIssue - for (File dungeon : dungeons.listFiles()) { - if (!dungeon.isDirectory()) { - continue; - } - File[] roomShapes = dungeon.listFiles(); - if (roomShapes == null) { - LOGGER.error("Failed to load dungeon secrets for dungeon {}", dungeon.getName()); - continue; - } - ROOMS.put(dungeon.getName(), new HashMap<>()); - List> roomShapeFutures = new ArrayList<>(); - for (File roomShape : roomShapes) { - roomShapeFutures.add(CompletableFuture.supplyAsync(() -> readRooms(roomShape, resourcePathIndex)).thenAccept(rooms -> ROOMS.get(dungeon.getName()).put(roomShape.getName(), rooms))); + List> dungeonFutures = new ArrayList<>(); + URL dungeonsURL = SkyblockerMod.class.getResource(DUNGEONS_DATA_DIR); + if (dungeonsURL == null) { + LOGGER.error("Failed to load dungeon secrets, unable to find dungeon rooms data directory"); + return; + } + Path dungeonsDir = Path.of(dungeonsURL.getPath()); + int resourcePathIndex = dungeonsDir.toString().indexOf(DUNGEONS_DATA_DIR); + try (DirectoryStream dungeons = Files.newDirectoryStream(dungeonsDir, Files::isDirectory)) { + for (Path dungeon : dungeons) { + try (DirectoryStream roomShapes = Files.newDirectoryStream(dungeon, Files::isDirectory)) { + List> roomShapeFutures = new ArrayList<>(); + HashMap> roomShapesMap = new HashMap<>(); + for (Path roomShape : roomShapes) { + roomShapeFutures.add(CompletableFuture.supplyAsync(() -> readRooms(roomShape, resourcePathIndex)).thenAccept(rooms -> roomShapesMap.put(roomShape.getFileName().toString(), rooms))); + } + ROOMS.put(dungeon.getFileName().toString(), roomShapesMap); + dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getFileName(), roomShapesMap.size(), roomShapesMap.values().stream().mapToInt(HashMap::size).sum()))); + } catch (IOException e) { + LOGGER.error("Failed to load dungeon secrets for dungeon " + dungeon.getFileName(), e); } - dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.debug("Loaded dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getName(), ROOMS.get(dungeon.getName()).size(), ROOMS.get(dungeon.getName()).values().stream().mapToInt(HashMap::size).sum()))); } - // Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set - dungeonFutures.add(CompletableFuture.runAsync(() -> { - try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) { - roomsJson = SkyblockerMod.GSON.fromJson(roomsReader, JsonObject.class); - waypointsJson = SkyblockerMod.GSON.fromJson(waypointsReader, JsonObject.class); - LOGGER.debug("Loaded dungeon secrets json"); - } catch (Exception e) { - LOGGER.error("Failed to load dungeon secrets json", e); - } - }, MinecraftClient.getInstance())); - roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", ROOMS.size(), ROOMS.values().stream().mapToInt(HashMap::size).sum(), ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); - } catch (Exception e) { + } catch (IOException e) { LOGGER.error("Failed to load dungeon secrets", e); } + // Execute with MinecraftClient as executor since we need to wait for MinecraftClient#resourceManager to be set + dungeonFutures.add(CompletableFuture.runAsync(() -> { + try (BufferedReader roomsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/dungeonrooms.json")); BufferedReader waypointsReader = MinecraftClient.getInstance().getResourceManager().openAsReader(new Identifier(SkyblockerMod.NAMESPACE, "dungeons/secretlocations.json"))) { + roomsJson = SkyblockerMod.GSON.fromJson(roomsReader, JsonObject.class); + waypointsJson = SkyblockerMod.GSON.fromJson(waypointsReader, JsonObject.class); + LOGGER.debug("Loaded dungeon secrets json"); + } catch (Exception e) { + LOGGER.error("Failed to load dungeon secrets json", e); + } + }, MinecraftClient.getInstance())); + roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", ROOMS.size(), ROOMS.values().stream().mapToInt(HashMap::size).sum(), ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); } - private static HashMap readRooms(File roomShape, int resourcePathIndex) { - HashMap data = new HashMap<>(); - File[] rooms = roomShape.listFiles(); - if (rooms == null) { - LOGGER.error("Failed to load dungeon secrets room shape {}", roomShape.getName()); - return data; - } - for (File room : rooms) { - String name = room.getName(); - //noinspection DataFlowIssue - try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(SkyblockerMod.class.getResourceAsStream(room.getPath().substring(resourcePathIndex))))) { - data.put(name.substring(0, name.length() - 9), (int[]) in.readObject()); - LOGGER.debug("Loaded dungeon secrets room {}", name); - } catch (NullPointerException | IOException | ClassNotFoundException e) { - LOGGER.error("Failed to load dungeon secrets room " + name, e); + private static HashMap readRooms(Path roomShape, int resourcePathIndex) { + try (DirectoryStream rooms = Files.newDirectoryStream(roomShape, Files::isRegularFile)) { + HashMap roomsData = new HashMap<>(); + for (Path room : rooms) { + String name = room.getFileName().toString(); + //noinspection DataFlowIssue + try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(SkyblockerMod.class.getResourceAsStream(room.toString().substring(resourcePathIndex))))) { + roomsData.put(name.substring(0, name.length() - 9), (int[]) in.readObject()); + LOGGER.info("Loaded dungeon secrets room {}", name); + } catch (NullPointerException | IOException | ClassNotFoundException e) { + LOGGER.error("Failed to load dungeon secrets room " + name, e); + } } + LOGGER.info("Loaded dungeon secrets room shape {} with {} rooms", roomShape.getFileName(), roomsData.size()); + return roomsData; + } catch (IOException e) { + LOGGER.error("Failed to load dungeon secrets room shape " + roomShape.getFileName(), e); } - LOGGER.debug("Loaded dungeon secrets room shape {} with {} rooms", roomShape.getName(), data.size()); - return data; + return null; } private static void update() { -- cgit From b77bb4500870479a5804113a182a99f0358432c6 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sun, 16 Jul 2023 11:02:44 +0800 Subject: Add support for loading in jar --- .../skyblock/dungeon/secrets/DungeonSecrets.java | 40 ++++++++-------------- 1 file changed, 14 insertions(+), 26 deletions(-) (limited to 'src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon') diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java index 9a9752fb..17446db7 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java @@ -18,8 +18,10 @@ import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.IOException; import java.io.ObjectInputStream; +import java.net.URISyntaxException; import java.net.URL; import java.nio.file.DirectoryStream; +import java.nio.file.FileSystems; import java.nio.file.Files; import java.nio.file.Path; import java.util.*; @@ -36,29 +38,7 @@ public class DungeonSecrets { * @implNote Not using {@link net.minecraft.registry.Registry#getId(Object) Registry#getId(Block)} and {@link net.minecraft.block.Blocks Blocks} since this is also used by {@link me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonRoomsDFU DungeonRoomsDFU}, which runs outside of Minecraft. */ @SuppressWarnings("JavadocReference") - protected static final Map NUMERIC_ID = Map.ofEntries( - Map.entry("minecraft:stone", (byte) 1), - Map.entry("minecraft:diorite", (byte) 2), - Map.entry("minecraft:polished_diorite", (byte) 3), - Map.entry("minecraft:andesite", (byte) 4), - Map.entry("minecraft:polished_andesite", (byte) 5), - Map.entry("minecraft:grass_block", (byte) 6), - Map.entry("minecraft:dirt", (byte) 7), - Map.entry("minecraft:coarse_dirt", (byte) 8), - Map.entry("minecraft:cobblestone", (byte) 9), - Map.entry("minecraft:bedrock", (byte) 10), - Map.entry("minecraft:oak_leaves", (byte) 11), - Map.entry("minecraft:gray_wool", (byte) 12), - Map.entry("minecraft:double_stone_slab", (byte) 13), - Map.entry("minecraft:mossy_cobblestone", (byte) 14), - Map.entry("minecraft:clay", (byte) 15), - Map.entry("minecraft:stone_bricks", (byte) 16), - Map.entry("minecraft:mossy_stone_bricks", (byte) 17), - Map.entry("minecraft:chiseled_stone_bricks", (byte) 18), - Map.entry("minecraft:gray_terracotta", (byte) 19), - Map.entry("minecraft:cyan_terracotta", (byte) 20), - Map.entry("minecraft:black_terracotta", (byte) 21) - ); + protected static final Map NUMERIC_ID = Map.ofEntries(Map.entry("minecraft:stone", (byte) 1), Map.entry("minecraft:diorite", (byte) 2), Map.entry("minecraft:polished_diorite", (byte) 3), Map.entry("minecraft:andesite", (byte) 4), Map.entry("minecraft:polished_andesite", (byte) 5), Map.entry("minecraft:grass_block", (byte) 6), Map.entry("minecraft:dirt", (byte) 7), Map.entry("minecraft:coarse_dirt", (byte) 8), Map.entry("minecraft:cobblestone", (byte) 9), Map.entry("minecraft:bedrock", (byte) 10), Map.entry("minecraft:oak_leaves", (byte) 11), Map.entry("minecraft:gray_wool", (byte) 12), Map.entry("minecraft:double_stone_slab", (byte) 13), Map.entry("minecraft:mossy_cobblestone", (byte) 14), Map.entry("minecraft:clay", (byte) 15), Map.entry("minecraft:stone_bricks", (byte) 16), Map.entry("minecraft:mossy_stone_bricks", (byte) 17), Map.entry("minecraft:chiseled_stone_bricks", (byte) 18), Map.entry("minecraft:gray_terracotta", (byte) 19), Map.entry("minecraft:cyan_terracotta", (byte) 20), Map.entry("minecraft:black_terracotta", (byte) 21)); private static JsonObject roomsJson; private static JsonObject waypointsJson; @Nullable @@ -90,6 +70,14 @@ public class DungeonSecrets { return; } Path dungeonsDir = Path