diff options
author | Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> | 2023-07-24 11:33:16 +0800 |
---|---|---|
committer | Kevinthegreat <92656833+kevinthegreat1@users.noreply.github.com> | 2023-08-30 22:49:52 -0400 |
commit | 1f40dff278681277cee7605e0aadd1812577b31a (patch) | |
tree | 58fa316dc3b779938b322fbb3bbb04abb629d3db /src/main/java | |
parent | 2452ce3afafec1f9230a0cbfe384cd97d93e2d72 (diff) | |
download | Skyblocker-1f40dff278681277cee7605e0aadd1812577b31a.tar.gz Skyblocker-1f40dff278681277cee7605e0aadd1812577b31a.tar.bz2 Skyblocker-1f40dff278681277cee7605e0aadd1812577b31a.zip |
Refactor room matching
Diffstat (limited to 'src/main/java')
3 files changed, 77 insertions, 49 deletions
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 dc785084..c9603809 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 @@ -1,5 +1,6 @@ package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; +import it.unimi.dsi.fastutil.ints.IntSortedSet; import net.minecraft.block.MapColor; import net.minecraft.item.map.MapIcon; import net.minecraft.item.map.MapState; @@ -131,12 +132,12 @@ public class DungeonMapUtils { return new Vector2i(mapPos).sub(mapEntrancePos).div(mapRoomSize + 4).mul(32).add(physicalEntrancePos); } - public static Vector2ic getPhysicalCornerPos(Room.Direction direction, SortedSet<Integer> segmentsX, SortedSet<Integer> segmentsY) { + public static Vector2ic getPhysicalCornerPos(Room.Direction direction, IntSortedSet segmentsX, IntSortedSet segmentsY) { return switch (direction) { - case NW -> new Vector2i(segmentsX.first(), segmentsY.first()); - case NE -> new Vector2i(segmentsX.last() + 30, segmentsY.first()); - case SW -> new Vector2i(segmentsX.first(), segmentsY.last() + 30); - case SE -> new Vector2i(segmentsX.last() + 30, segmentsY.last() + 30); + case NW -> new Vector2i(segmentsX.firstInt(), segmentsY.firstInt()); + case NE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.firstInt()); + case SW -> new Vector2i(segmentsX.firstInt(), segmentsY.lastInt() + 30); + case SE -> new Vector2i(segmentsX.lastInt() + 30, segmentsY.lastInt() + 30); }; } 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 04e30d0e..bcc26f78 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 @@ -1,6 +1,8 @@ package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; import com.google.gson.JsonObject; +import it.unimi.dsi.fastutil.objects.Object2ByteMap; +import it.unimi.dsi.fastutil.objects.Object2ByteOpenHashMap; import me.xmrvizzy.skyblocker.SkyblockerMod; import me.xmrvizzy.skyblocker.config.SkyblockerConfig; import me.xmrvizzy.skyblocker.utils.Utils; @@ -45,7 +47,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<String, Byte> NUMERIC_ID = Map.ofEntries( + protected static final Object2ByteMap<String> NUMERIC_ID = new Object2ByteOpenHashMap<>(Map.ofEntries( Map.entry("minecraft:stone", (byte) 1), Map.entry("minecraft:diorite", (byte) 2), Map.entry("minecraft:polished_diorite", (byte) 3), @@ -67,7 +69,7 @@ public class DungeonSecrets { 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 @@ -221,6 +223,7 @@ public class DungeonSecrets { if (room != null && currentRoom != room) { currentRoom = room; } + currentRoom.update(); } /** 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 index ff397098..6eabc027 100644 --- a/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java +++ b/src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java @@ -1,12 +1,16 @@ package me.xmrvizzy.skyblocker.skyblock.dungeon.secrets; -import com.google.common.collect.ImmutableSortedSet; +import it.unimi.dsi.fastutil.ints.IntRBTreeSet; +import it.unimi.dsi.fastutil.ints.IntSortedSet; +import it.unimi.dsi.fastutil.ints.IntSortedSets; import net.minecraft.block.MapColor; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.world.ClientWorld; import net.minecraft.registry.Registries; import net.minecraft.util.math.BlockPos; +import org.apache.commons.lang3.tuple.MutableTriple; +import org.apache.commons.lang3.tuple.Triple; import org.joml.Vector2i; import org.joml.Vector2ic; @@ -14,51 +18,42 @@ import java.util.*; import java.util.concurrent.CompletableFuture; public class Room { + public static final String UNKNOWN_NAME = "Unknown"; private final Type type; private final Set<Vector2ic> segments; - private final SortedSet<Integer> segmentsX; - private final SortedSet<Integer> segmentsY; private final Shape shape; - private final HashMap<String, int[]> roomsData; - private final HashMap<Direction, List<String>> possibleRooms = new HashMap<>(); - private final Set<BlockPos> checkedBlocks = new HashSet<>(); + private HashMap<String, int[]> roomsData; + private List<MutableTriple<Direction, Vector2ic, List<String>>> possibleRooms = new ArrayList<>(); + private Set<BlockPos> checkedBlocks = new HashSet<>(); private CompletableFuture<Void> findRoom; private String name; private Direction direction; + private Vector2ic corner; public Room(Type type, Vector2ic... physicalPositions) { this.type = type; segments = Set.of(physicalPositions); - ImmutableSortedSet.Builder<Integer> segmentsXBuilder = ImmutableSortedSet.naturalOrder(); - ImmutableSortedSet.Builder<Integer> segmentsYBuilder = ImmutableSortedSet.naturalOrder(); - for (Vector2ic physicalPos : physicalPositions) { - segmentsXBuilder.add(physicalPos.x()); - segmentsYBuilder.add(physicalPos.y()); - } - segmentsX = segmentsXBuilder.build(); - segmentsY = segmentsYBuilder.build(); - shape = getShape(); + IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray())); + IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray())); + shape = getShape(segmentsX, segmentsY); roomsData = DungeonSecrets.ROOMS_DATA.get("catacombs").get(shape.shape); List<String> possibleDirectionRooms = new ArrayList<>(roomsData.keySet()); - for (Direction direction : getPossibleDirections()) { - this.possibleRooms.replace(direction, possibleDirectionRooms); + for (Direction direction : getPossibleDirections(segmentsX, segmentsY)) { + possibleRooms.add(MutableTriple.of(direction, DungeonMapUtils.getPhysicalCornerPos(direction, segmentsX, segmentsY), possibleDirectionRooms)); } + DungeonSecrets.LOGGER.info("Created {}", this); // TODO change to debug } public Type getType() { return type; } - public boolean containsSegment(Vector2ic segment) { - return segments.contains(segment); - } - @Override public String toString() { - return "Room{type=" + type + ", name='" + name + "'" + ", segments=" + Arrays.toString(segments.toArray()) + "}"; + return "Room{type=" + type + ", shape=" + shape + ", name='" + name + "', direction=" + direction + ", corner=" + corner + ", segments=" + Arrays.toString(segments.toArray()) + "}"; } - private Shape getShape() { + private Shape getShape(IntSortedSet segmentsX, IntSortedSet segmentsY) { int segmentsSize = segments.size(); if (segmentsSize == 1) { return Shape.ONE_BY_ONE; @@ -81,7 +76,7 @@ public class Room { throw new IllegalArgumentException("There are no matching room shapes with this set of physical positions: " + Arrays.toString(segments.toArray())); } - private Direction[] getPossibleDirections() { + private Direction[] getPossibleDirections(IntSortedSet segmentsX, IntSortedSet segmentsY) { return switch (shape) { case ONE_BY_ONE, TWO_BY_TWO -> Direction.values(); case ONE_BY_TWO, ONE_BY_THREE, ONE_BY_FOUR -> { @@ -93,13 +88,13 @@ public class Room { throw new IllegalStateException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray())); } case L_SHAPE -> { - if (!segments.contains(new Vector2i(segmentsX.first(), segmentsY.first()))) { + if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.firstInt()))) { yield new Direction[]{Direction.SW}; - } else if (!segments.contains(new Vector2i(segmentsX.first(), segmentsY.last()))) { + } else if (!segments.contains(new Vector2i(segmentsX.firstInt(), segmentsY.lastInt()))) { yield new Direction[]{Direction.SE}; - } else if (!segments.contains(new Vector2i(segmentsX.last(), segmentsY.first()))) { + } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.firstInt()))) { yield new Direction[]{Direction.NW}; - } else if (!segments.contains(new Vector2i(segmentsX.last(), segmentsY.last()))) { + } else if (!segments.contains(new Vector2i(segmentsX.lastInt(), segmentsY.lastInt()))) { yield new Direction[]{Direction.NE}; } throw new IllegalArgumentException("Shape " + shape.shape + " does not match segments: " + Arrays.toString(segments.toArray())); @@ -109,7 +104,7 @@ public class Room { public void update() { // Logical AND has higher precedence than logical OR - if (name != null && direction != null || findRoom != null && !findRoom.isDone()) { + if (name != null || !DungeonSecrets.isRoomsLoaded() || findRoom != null && !findRoom.isDone()) { return; } MinecraftClient client = MinecraftClient.getInstance(); @@ -122,6 +117,7 @@ public class Room { long startTime = System.currentTimeMillis(); for (BlockPos pos : BlockPos.iterate(player.getBlockPos().add(-5, -5, -5), player.getBlockPos().add(5, 5, 5))) { if (checkedBlocks.add(pos) && checkBlock(world, pos)) { + reset(); break; } } @@ -131,28 +127,34 @@ public class Room { } private boolean checkBlock(ClientWorld world, BlockPos pos) { - for (Map.Entry<Direction, List<String>> directionRooms : possibleRooms.entrySet()) { - Direction direction = directionRooms.getKey(); - BlockPos relative = DungeonMapUtils.actualToRelative(DungeonMapUtils.getPhysicalCornerPos(direction, segmentsX, segmentsY), direction, pos); - int block = posIdToInt(relative, DungeonSecrets.NUMERIC_ID.get(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString())); + byte id = DungeonSecrets.NUMERIC_ID.getByte(Registries.BLOCK.getId(world.getBlockState(pos).getBlock()).toString()); + if (id == 0) { + return false; + } + for (MutableTriple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) { + Direction direction = directionRooms.getLeft(); + BlockPos relative = DungeonMapUtils.actualToRelative(directionRooms.getMiddle(), direction, pos); + int block = posIdToInt(relative, id); List<String> possibleDirectionRooms = new ArrayList<>(); - for (String room : directionRooms.getValue()) { + for (String room : directionRooms.getRight()) { if (Arrays.binarySearch(roomsData.get(room), block) >= 0) { possibleDirectionRooms.add(room); } } - possibleRooms.put(direction, possibleDirectionRooms); + directionRooms.setRight(possibleDirectionRooms); } - int matchingRoomsSize = possibleRooms.values().stream().mapToInt(Collection::size).sum(); + int matchingRoomsSize = possibleRooms.stream().map(Triple::getRight).mapToInt(Collection::size).sum(); if (matchingRoomsSize == 0) { DungeonSecrets.LOGGER.warn("[Skyblocker] No dungeon room matches after checking {} block(s)", checkedBlocks.size()); + name = UNKNOWN_NAME; return true; } else if (matchingRoomsSize == 1) { - for (Map.Entry<Direction, List<String>> directionRoomEntry : possibleRooms.entrySet()) { - if (directionRoomEntry.getValue().size() == 1) { - name = directionRoomEntry.getValue().get(0); - direction = directionRoomEntry.getKey(); + for (Triple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) { + if (directionRooms.getRight().size() == 1) { + name = directionRooms.getRight().get(0); + direction = directionRooms.getLeft(); + corner = directionRooms.getMiddle(); } } DungeonSecrets.LOGGER.info("[Skyblocker] Room {} matched after checking {} block(s)", name, checkedBlocks.size()); // TODO change to debug @@ -167,8 +169,25 @@ public class Room { return pos.getX() << 24 | pos.getY() << 16 | pos.getZ() << 8 | id; } + /** + * Resets fields after room matching completes, where either a room is found or none matched. + * These fields are no longer needed and are discarded to save memory. + */ + private void reset() { + roomsData = null; + possibleRooms = null; + checkedBlocks = null; + } + public enum Type { - ENTRANCE(MapColor.DARK_GREEN.getRenderColorByte(MapColor.Brightness.HIGH)), ROOM(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.LOWEST)), PUZZLE(MapColor.MAGENTA.getRenderColorByte(MapColor.Brightness.HIGH)), TRAP(MapColor.ORANGE.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)); + ENTRANCE(MapColor.DARK_GREEN.getRenderColorByte(MapColor.Brightness.HIGH)), + ROOM(MapColor.ORANGE.getRenderColorByte(MapColor.Brightness.LOWEST)), + PUZZLE(MapColor.MAGENTA.getRenderColorByte(MapColor.Brightness.HIGH)), + TRAP(MapColor.ORANGE.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; Type(byte color) { @@ -177,7 +196,12 @@ public class Room { } public enum Shape { - ONE_BY_ONE("1x1"), ONE_BY_TWO("1x2"), ONE_BY_THREE("1x3"), ONE_BY_FOUR("1x4"), L_SHAPE("L-shape"), TWO_BY_TWO("2x2"); + ONE_BY_ONE("1x1"), + ONE_BY_TWO("1x2"), + ONE_BY_THREE("1x3"), + ONE_BY_FOUR("1x4"), + L_SHAPE("L-shape"), + TWO_BY_TWO("2x2"); final String shape; Shape(String shape) { |