diff options
author | Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> | 2023-08-31 20:21:30 -0400 |
---|---|---|
committer | Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> | 2023-08-31 20:40:44 -0400 |
commit | aa2abcf644bf58adb3330e87d870ce56a09ce3f5 (patch) | |
tree | f9e9114f9b0031ce61dcfba05a030453f540d6b1 /src/test/java/me/xmrvizzy/skyblocker | |
parent | fea919794608128b6c436f1a64d1be38e935241d (diff) | |
parent | 1370097922f19815e14bdfd8c9e606cde2bc8f39 (diff) | |
download | Skyblocker-aa2abcf644bf58adb3330e87d870ce56a09ce3f5.tar.gz Skyblocker-aa2abcf644bf58adb3330e87d870ce56a09ce3f5.tar.bz2 Skyblocker-aa2abcf644bf58adb3330e87d870ce56a09ce3f5.zip |
Merge branch 'master' into utils-cleanup
# Conflicts:
# src/main/java/me/xmrvizzy/skyblocker/SkyblockerMod.java
# src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/DungeonBlaze.java
# src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHud.java
# src/main/java/me/xmrvizzy/skyblocker/skyblock/dwarven/DwarvenHudConfigScreen.java
# src/main/java/me/xmrvizzy/skyblocker/utils/RenderHelper.java
Diffstat (limited to 'src/test/java/me/xmrvizzy/skyblocker')
-rw-r--r-- | src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java | 165 | ||||
-rw-r--r-- | src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/RoomTest.java | 13 |
2 files changed, 178 insertions, 0 deletions
diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java new file mode 100644 index 00000000..c103bb3d --- /dev/null +++ b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java @@ -0,0 +1,165 @@ +package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; + +import net.minecraft.datafixer.fix.ItemIdFix; +import net.minecraft.datafixer.fix.ItemInstanceTheFlatteningFix; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +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.DeflaterOutputStream; +import java.util.zip.InflaterInputStream; + +/** + * Utility class to convert the old dungeon rooms data from Dungeon Rooms Mod to a new format. + * The new format is similar to <a href="https://quantizr.github.io/posts/how-it-works/">DRM's format</a>, but uses ints instead of longs and a custom numeric block id to store the block states. + * The first byte is the x position, the second byte is the y position, the third byte is the z position, and the fourth byte is the custom numeric block id. + * Use {@link DungeonSecrets#NUMERIC_ID} to get the custom numeric block id of a block. + * Run this manually when updating dungeon rooms data with DRM's data in {@code src/test/resources/assets/skyblocker/dungeons/dungeonrooms}. + */ +public class DungeonRoomsDFU { + private static final Logger LOGGER = LoggerFactory.getLogger(DungeonRoomsDFU.class); + private static final String DUNGEONS_DATA_DIR = "/assets/skyblocker/dungeons"; + private static final String DUNGEON_ROOMS_DATA_DIR = DUNGEONS_DATA_DIR + "/dungeonrooms"; + private static final HashMap<String, HashMap<String, HashMap<String, long[]>>> OLD_ROOMS = new HashMap<>(); + private static final HashMap<String, HashMap<String, HashMap<String, int[]>>> ROOMS = new HashMap<>(); + + public static void main(String[] args) { + load().join(); + updateRooms(); + save().join(); + } + + private static CompletableFuture<Void> load() { + List<CompletableFuture<Void>> dungeonFutures = new ArrayList<>(); + URL dungeonsURL = DungeonRoomsDFU.class.getResource(DUNGEON_ROOMS_DATA_DIR); + if (dungeonsURL == null) { + LOGGER.error("Failed to load dungeon secrets, unable to find dungeon rooms data directory"); + return CompletableFuture.completedFuture(null); + } + Path dungeonsDir = Path.of(dungeonsURL.getPath()); + int resourcePathIndex = dungeonsDir.toString().indexOf(DUNGEON_ROOMS_DATA_DIR); + try (DirectoryStream<Path> dungeons = Files.newDirectoryStream(dungeonsDir, Files::isDirectory)) { + for (Path dungeon : dungeons) { + try (DirectoryStream<Path> roomShapes = Files.newDirectoryStream(dungeon, Files::isDirectory)) { + List<CompletableFuture<Void>> roomShapeFutures = new ArrayList<>(); + HashMap<String, HashMap<String, long[]>> roomShapesMap = new HashMap<>(); + for (Path roomShape : roomShapes) { + roomShapeFutures.add(CompletableFuture.supplyAsync(() -> readRooms(roomShape, resourcePathIndex)).thenAccept(rooms -> roomShapesMap.put(roomShape.getFileName().toString().toLowerCase(), rooms))); + } + OLD_ROOMS.put(dungeon.getFileName().toString().toLowerCase(), 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); + } + } + } catch (IOException e) { + LOGGER.error("Failed to load dungeon secrets", e); + } + return CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Loaded dungeon secrets for {} dungeon(s), {} room shapes, and {} rooms total", OLD_ROOMS.size(), OLD_ROOMS.values().stream().mapToInt(HashMap::size).sum(), OLD_ROOMS.values().stream().map(HashMap::values).flatMap(Collection::stream).mapToInt(HashMap::size).sum())); + } + + private static HashMap<String, long[]> readRooms(Path roomShape, int resourcePathIndex) { + try (DirectoryStream<Path> rooms = Files.newDirectoryStream(roomShape, Files::isRegularFile)) { + HashMap<String, long[]> roomsData = new HashMap<>(); + for (Path room : rooms) { + String name = room.getFileName().toString(); + //noinspection DataFlowIssue + try (ObjectInputStream in = new ObjectInputStream(new InflaterInputStream(DungeonRoomsDFU.class.getResourceAsStream(room.toString().substring(resourcePathIndex))))) { + roomsData.put(name.substring(0, name.length() - 9).toLowerCase(), (long[]) 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); + } + return null; + } + + private static void updateRooms() { + for (Map.Entry<String, HashMap<String, HashMap<String, long[]>>> oldDungeon : OLD_ROOMS.entrySet()) { + HashMap<String, HashMap<String, int[]>> dungeon = new HashMap<>(); + for (Map.Entry<String, HashMap<String, long[]>> oldRoomShape : oldDungeon.getValue().entrySet()) { + HashMap<String, int[]> roomShape = new HashMap<>(); + for (Map.Entry<String, long[]> oldRoomEntry : oldRoomShape.getValue().entrySet()) { + roomShape.put(oldRoomEntry.getKey(), updateRoom(oldRoomEntry.getValue())); + } + dungeon.put(oldRoomShape.getKey(), roomShape); + } + ROOMS.put(oldDungeon.getKey(), dungeon); + } + } + + private static int[] updateRoom(long[] oldRoom) { + int[] room = new int[oldRoom.length]; + for (int i = 0; i < oldRoom.length; i++) { + room[i] = updateBlock(oldRoom[i]); + } + // Technically not needed, as the long array should be sorted already. + Arrays.sort(room); + return room; + } + + /** + * Updates the block state from Dungeon Rooms Mod's format to the new format explained in {@link DungeonRoomsDFU}. + * + * @param oldBlock the old block state in DRM's format + * @return the new block state in the new format + */ + private static int updateBlock(long oldBlock) { + short x = (short) (oldBlock >> 48 & 0xFFFF); + short y = (short) (oldBlock >> 32 & 0xFFFF); + short z = (short) (oldBlock >> 16 & 0xFFFF); + // Blocks should be within the range 0 to 256, since a dungeon room is at most around 128 blocks long and around 150 blocks tall. + if (x < 0 || x > 0xFF || y < 0 || y > 0xFF || z < 0 || z > 0xFF) { + throw new IllegalArgumentException("Invalid block: " + oldBlock); + } + short oldId = (short) (oldBlock & 0xFFFF); + // Get the new id for the block. + String newId = ItemInstanceTheFlatteningFix.getItem(ItemIdFix.fromId(oldId / 100), oldId % 100); + if (newId == null) { + newId = ItemIdFix.fromId(oldId / 100); + } + return x << 24 | y << 16 | z << 8 | DungeonSecrets.NUMERIC_ID.getByte(newId); + } + + private static CompletableFuture<Void> save() { + List<CompletableFuture<Void>> dungeonFutures = new ArrayList<>(); + for (Map.Entry<String, HashMap<String, HashMap<String, int[]>>> dungeon : ROOMS.entrySet()) { + Path dungeonDir = Path.of("out", "dungeons", dungeon.getKey()); + List<CompletableFuture<Void>> roomShapeFutures = new ArrayList<>(); + for (Map.Entry<String, HashMap<String, int[]>> roomShape : dungeon.getValue().entrySet()) { + Path roomShapeDir = dungeonDir.resolve(roomShape.getKey()); + roomShapeFutures.add(CompletableFuture.runAsync(() -> saveRooms(roomShapeDir, roomShape))); + } + dungeonFutures.add(CompletableFuture.allOf(roomShapeFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Saved dungeon secrets for dungeon {} with {} room shapes and {} rooms total", dungeon.getKey(), dungeon.getValue().size(), dungeon.getValue().values().stream().mapToInt(HashMap::size).sum()))); + } + return CompletableFuture.allOf(dungeonFutures.toArray(CompletableFuture[]::new)).thenRun(() -> LOGGER.info("Saved 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 void saveRooms(Path roomShapeDir, Map.Entry<String, HashMap<String, int[]>> roomShape) { + try { + Files.createDirectories(roomShapeDir); + } catch (IOException e) { + LOGGER.error("Failed to save dungeon secrets: failed to create dungeon secrets room shape directory " + roomShapeDir, e); + } + for (Map.Entry<String, int[]> room : roomShape.getValue().entrySet()) { + try (ObjectOutputStream out = new ObjectOutputStream(new DeflaterOutputStream(Files.newOutputStream(roomShapeDir.resolve(room.getKey() + ".skeleton"))))) { + out.writeObject(room.getValue()); + LOGGER.info("Saved dungeon secrets room {}", room.getKey()); + } catch (IOException e) { + LOGGER.error("Failed to save dungeon secrets room " + room.getKey(), e); + } + } + LOGGER.info("Saved dungeon secrets room shape {} with {} rooms", roomShape.getKey(), roomShape.getValue().size()); + } +} diff --git a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/RoomTest.java b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/RoomTest.java new file mode 100644 index 00000000..b704037c --- /dev/null +++ b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/RoomTest.java @@ -0,0 +1,13 @@ +package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class RoomTest { + @Test + void onChatMessage() { + Assertions.assertFalse(Room.isAllSecretsFound("§10,000/10,000❤ §a5,000§a❈ Defense §b2,000/2,000✎ Mana §70/1 Secrets")); + Assertions.assertTrue(Room.isAllSecretsFound("§1,000,000/10,000❤ §3+1,000.5 Combat (33.33%) §b4,000/2,000✎ Mana §710/10 Secrets")); + Assertions.assertTrue(Room.isAllSecretsFound("§1,000,000/10,000❤ §b-25 Mana (§6Instant Transmission§b) §b2,000/2,000✎ Mana §710/1 Secrets")); + } +} |