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 --- .../java/me/xmrvizzy/skyblocker/SkyblockerMod.java | 2 + .../skyblock/dungeon/secrets/DungeonSecrets.java | 90 ++++++++++++++++++++++ 2 files changed, 92 insertions(+) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java (limited to 'src/main/java/me') diff --git a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java index 3d1427a2..b8c8cb9e 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java +++ b/src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java @@ -11,6 +11,7 @@ import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonBlaze; import me.xmrvizzy.skyblocker.skyblock.dungeon.DungeonMap; import me.xmrvizzy.skyblocker.skyblock.dungeon.LividColor; import me.xmrvizzy.skyblocker.skyblock.dungeon.TicTacToe; +import me.xmrvizzy.skyblocker.skyblock.dungeon.secrets.DungeonSecrets; import me.xmrvizzy.skyblocker.skyblock.dwarven.DwarvenHud; import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorDyeColors; import me.xmrvizzy.skyblocker.skyblock.item.CustomArmorTrims; @@ -88,6 +89,7 @@ public class SkyblockerMod implements ClientModInitializer { FishingHelper.init(); TabHud.init(); DungeonMap.init(); + DungeonSecrets.init(); TheRift.init(); TitleContainer.init(); OcclusionCulling.init(); 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 --- .../skyblocker/config/SkyblockerConfig.java | 2 ++ .../skyblock/dungeon/secrets/DungeonSecrets.java | 36 +++++++++++++++------- 2 files changed, 27 insertions(+), 11 deletions(-) (limited to 'src/main/java/me') diff --git a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java index e6961819..c7b84b25 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java +++ b/src/main/java/me/xmrvizzy/skyblocker/config/SkyblockerConfig.java @@ -407,6 +407,8 @@ public class SkyblockerConfig implements ConfigData { } public static class Dungeons { + public boolean secretWaypoints = true; + public boolean noLoadSecretWaypoints = false; @ConfigEntry.Gui.Tooltip() public boolean croesusHelper = true; public boolean enableMap = true; 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') 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') 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') 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') 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.of(dungeonsURL.getPath()); + if ("jar".equals(dungeonsURL.getProtocol())) { + try { + dungeonsDir = FileSystems.getFileSystem(dungeonsURL.toURI()).getPath(DUNGEONS_DATA_DIR); + } catch (URISyntaxException e) { + LOGGER.error("Failed to load dungeon secrets, unable to open dungeon rooms data directory", e); + return; + } + } int resourcePathIndex = dungeonsDir.toString().indexOf(DUNGEONS_DATA_DIR); try (DirectoryStream dungeons = Files.newDirectoryStream(dungeonsDir, Files::isDirectory)) { for (Path dungeon : dungeons) { @@ -100,7 +88,7 @@ public class DungeonSecrets { 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()))); + dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.debug("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); } @@ -129,12 +117,12 @@ public class DungeonSecrets { //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); + LOGGER.debug("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()); + LOGGER.debug("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); -- cgit From 58616218d5d0c5359bdc1d84f226a0a1b08a71b2 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sun, 16 Jul 2023 16:13:22 +0800 Subject: Add room position utils and physical room position parsing --- .../skyblock/dungeon/secrets/DungeonMapUtils.java | 129 +++++++++++++++++---- .../skyblock/dungeon/secrets/DungeonSecrets.java | 18 ++- .../skyblocker/skyblock/dungeon/secrets/Room.java | 27 +++++ 3 files changed, 147 insertions(+), 27 deletions(-) create mode 100644 src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java (limited to 'src/main/java/me') 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 index 9d1c7ccb..4ce9be06 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java @@ -3,50 +3,135 @@ 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 net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.joml.RoundingMode; import org.joml.Vector2i; +import org.joml.Vector2ic; 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) { + @Nullable + private static Vector2i getMapPlayerPos(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 new Vector2i((icon.getX() >> 1) + 64, (icon.getZ() >> 1) + 64); } } return null; } - public static int getRoomWidth(MapState map, Vector2i entrancePos) { + @Nullable + public static Vector2ic getMapEntrancePos(MapState map) { + Vector2i mapPos = getMapPlayerPos(map); + if (!isEntranceColor(getColor(map, mapPos))) { + return null; + } + // noinspection StatementWithEmptyBody, DataFlowIssue + while (isEntranceColor(getColor(map, mapPos.sub(1, 0)))) { + } + //noinspection StatementWithEmptyBody + while (isEntranceColor(getColor(map, mapPos.sub(0, 1)))) { + } + return mapPos; + } + + public static int getMapRoomWidth(MapState map, Vector2ic entrancePos) { int i = 0; - while (getColor(map, entrancePos.x + i, entrancePos.y) == ENTRANCE_COLOR) { - i++; + //noinspection StatementWithEmptyBody + while (isEntranceColor(getColor(map, entrancePos.x() + i++, entrancePos.y()))) { } return i; } + /** + * Gets the map position of the top left corner of the room the player is in. + * + * @param map the map + * @param entrancePos the map position of the top left corner of the entrance + * @param mapRoomWidth the width of a room on the map + * @return the map position of the top left corner of the room the player is in + * @implNote {@code mapPos} is shifted by 2 so room borders are evenly split. + * {@code mapPos} is then shifted by {@code offset} to align the top left most room at (0, 0) + * so subtracting the modulo will give the top left corner of the room shifted by {@code offset}. + * Finally, {@code mapPos} is shifted back by {@code offset} to its intended position. + */ + @Nullable + public static Vector2ic getMapRoomPos(MapState map, Vector2ic entrancePos, int mapRoomWidth) { + int mapRoomWidthWithGap = mapRoomWidth + 4; + Vector2i mapPos = getMapPlayerPos(map); + if (mapPos == null) { + return null; + } + Vector2ic offset = new Vector2i(entrancePos.x() % mapRoomWidthWithGap, entrancePos.y() % mapRoomWidthWithGap); + return mapPos.add(2, 2).sub(offset).sub(mapPos.x() % mapRoomWidthWithGap, mapPos.y() % mapRoomWidthWithGap).add(offset); + } + + /** + * Gets the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room. + * + * @param physicalEntrancePos the physical position of the northwest corner of the entrance room + * @param mapEntrancePos the map position of the top left corner of the entrance room + * @param mapRoomWidth the width of a room on the map + * @param physicalPos the physical position of the northwest corner of the room + * @return the map position of the top left corner of the room corresponding to the physical position of the northwest corner of a room + */ + public static Vector2ic getMapPosFromPhysical(Vector2ic physicalEntrancePos, Vector2ic mapEntrancePos, int mapRoomWidth, Vector2ic physicalPos) { + return new Vector2i(physicalPos).sub(physicalEntrancePos).div(32).mul(mapRoomWidth + 4).add(mapEntrancePos); + } + + @Nullable + public static Vector2ic getPhysicalEntrancePos(MapState map, @NotNull Vec3d playerPos) { + if (isEntranceColor(getColor(map, getMapPlayerPos(map)))) { + return getPhysicalRoomPos(playerPos); + } + return null; + } + + /** + * Gets the physical position of the northwest corner of the room the player is in. Hypixel Skyblock Dungeons are aligned to a 32 by 32 blocks grid, allowing corners to be calculated through math. + * + * @param playerPos the position of the player + * @return the physical position of the northwest corner of the room the player is in + * @implNote {@code physicalPos} is shifted by 0.5 so room borders are evenly split. + * {@code physicalPos} is further shifted by 8 because Hypixel offset dungeons by 8 blocks in Skyblock 0.12.3. + * Subtracting the modulo gives the northwest corner of the room shifted by 8. Finally, {@code physicalPos} is shifted back by 8 to its intended position. + */ + @NotNull + public static Vector2ic getPhysicalRoomPos(@NotNull Vec3d playerPos) { + Vector2i physicalPos = new Vector2i(playerPos.getX() + 8.5, playerPos.getZ() + 8.5, RoundingMode.TRUNCATE); + return physicalPos.sub(MathHelper.floorMod(physicalPos.x(), 32), MathHelper.floorMod(physicalPos.y(), 32)).sub(8, 8); + } + + /** + * Gets the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room. + * + * @param mapEntrancePos the map position of the top left corner of the entrance room + * @param mapRoomWidth the width of a room on the map + * @param physicalEntrancePos the physical position of the northwest corner of the entrance room + * @param mapPos the map position of the top left corner of the room + * @return the physical position of the northwest corner of the room corresponding to the map position of the top left corner of a room + */ + public static Vector2ic getPhysicalPosFromMap(Vector2ic mapEntrancePos, int mapRoomWidth, Vector2ic physicalEntrancePos, Vector2ic mapPos) { + return new Vector2i(mapPos).sub(mapEntrancePos).div(mapRoomWidth + 4).mul(32).add(physicalEntrancePos); + } + + private static byte getColor(MapState map, @Nullable Vector2ic pos) { + return pos == null ? -1 : getColor(map, pos.x(), pos.y()); + } + 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)]; } + + private static boolean isEntranceColor(byte color) { + return color == Room.RoomType.ENTRANCE.color; + } } 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 17446db7..01274ae4 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 @@ -9,9 +9,10 @@ import net.minecraft.item.FilledMapItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; import net.minecraft.item.map.MapState; +import net.minecraft.text.Text; import net.minecraft.util.Identifier; import org.jetbrains.annotations.Nullable; -import org.joml.Vector2i; +import org.joml.Vector2ic; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -43,8 +44,10 @@ public class DungeonSecrets { private static JsonObject waypointsJson; @Nullable private static CompletableFuture roomsLoaded; - private static Vector2i mapEntrancePos; + private static Vector2ic mapEntrancePos; private static int mapRoomWidth; + private static Vector2ic physicalEntrancePos; + private static Room currentRoom; public static boolean isRoomsLoaded() { return roomsLoaded != null && roomsLoaded.isDone(); @@ -106,7 +109,7 @@ public class DungeonSecrets { 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())); + roomsLoaded = CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("[Skyblocker] 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(Path roomShape, int resourcePathIndex) { @@ -146,11 +149,16 @@ public class DungeonSecrets { if (map == null) { return; } - if (mapEntrancePos == null && (mapEntrancePos = DungeonMapUtils.getEntrancePos(map)) == null) { + if (mapEntrancePos == null && (mapEntrancePos = DungeonMapUtils.getMapEntrancePos(map)) == null) { return; } - if (mapRoomWidth == 0 && (mapRoomWidth = DungeonMapUtils.getRoomWidth(map, mapEntrancePos)) == 0) { + if (mapRoomWidth == 0 && (mapRoomWidth = DungeonMapUtils.getMapRoomWidth(map, mapEntrancePos)) == 0) { return; } + if (physicalEntrancePos == null && (physicalEntrancePos = DungeonMapUtils.getPhysicalEntrancePos(map, client.player.getPos())) == null) { + client.player.sendMessage(Text.translatable("skyblocker.dungeons.secrets.physicalEntranceNotFound")); + return; + } + LOGGER.info("[Skyblocker] Detected dungeon with map room width {} and entrance at map pos {} and physical pos {}", mapRoomWidth, mapEntrancePos, physicalEntrancePos); } } diff --git a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java new file mode 100644 index 00000000..0ee7d7f4 --- /dev/null +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java @@ -0,0 +1,27 @@ +package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; + +import net.minecraft.block.MapColor; + +public class Room { + private final RoomType type; + private String name; + + public Room(RoomType type) { + this.type = type; + } + + public enum RoomType { + ENTRANCE(MapColor.DARK_GREEN.getRenderColorByte(MapColor.Brightness.HIGH)), + ROOM(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.LOWEST)), + PUZZLE(MapColor.MAGENTA.getRenderColorByte(MapColor.Brightness.HIGH)), + MINIBOSS(MapColor.YELLOW.getRenderColorByte(MapColor.Brightness.HIGH)), + FAIRY(MapColor.PINK.getRenderColorByte(MapColor.Brightness.HIGH)), + BLOOD(MapColor.BRIGHT_RED.getRenderColorByte(MapColor.Brightness.HIGH)), + UNKNOWN(MapColor.GRAY.getRenderColorByte(MapColor.Brightness.NORMAL)); + final byte color; + + RoomType(byte color) { + this.color = color; + } + } +} -- cgit From ad5dac4b45a86c11e11cace97b314c3cc38cbf23 Mon Sep 17 00:00:00 2001 From: Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> Date: Sun, 16 Jul 2023 18:58:44 +0800 Subject: Implement room --- .../skyblock/dungeon/secrets/DungeonSecrets.java | 52 ++++++++++++++++++---- .../skyblocker/skyblock/dungeon/secrets/Room.java | 15 ++++++- 2 files changed, 57 insertions(+), 10 deletions(-) (limited to 'src/main/java/me') 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 01274ae4..c74898ff 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 @@ -5,6 +5,7 @@ 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.client.network.ClientPlayerEntity; import net.minecraft.item.FilledMapItem; import net.minecraft.item.ItemStack; import net.minecraft.item.Items; @@ -32,7 +33,7 @@ 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_DATA = new HashMap<>(); /** * Maps the block identifier string to a custom numeric block id used in dungeon rooms data. * @@ -44,9 +45,19 @@ public class DungeonSecrets { private static JsonObject waypointsJson; @Nullable