aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKevinthegreat <92656833+kevinthegreat1@users.noreply.github.com>2023-07-24 11:33:16 +0800
committerKevinthegreat <92656833+kevinthegreat1@users.noreply.github.com>2023-08-30 22:49:52 -0400
commit1f40dff278681277cee7605e0aadd1812577b31a (patch)
tree58fa316dc3b779938b322fbb3bbb04abb629d3db /src
parent2452ce3afafec1f9230a0cbfe384cd97d93e2d72 (diff)
downloadSkyblocker-1f40dff278681277cee7605e0aadd1812577b31a.tar.gz
Skyblocker-1f40dff278681277cee7605e0aadd1812577b31a.tar.bz2
Skyblocker-1f40dff278681277cee7605e0aadd1812577b31a.zip
Refactor room matching
Diffstat (limited to 'src')
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonMapUtils.java11
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonSecrets.java7
-rw-r--r--src/main/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/Room.java108
-rw-r--r--src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java2
4 files changed, 78 insertions, 50 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) {
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
index 613f642c..7260bff8 100644
--- a/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java
+++ b/src/test/java/me/xmrvizzy/skyblocker/skyblock/dungeon/secrets/DungeonRoomsDFU.java
@@ -129,7 +129,7 @@ public class DungeonRoomsDFU {
if (newId == null) {
newId = ItemIdFix.fromId(oldId / 100);
}
- return x << 24 | y << 16 | z << 8 | DungeonSecrets.NUMERIC_ID.get(newId);
+ return x << 24 | y << 16 | z << 8 | DungeonSecrets.NUMERIC_ID.getByte(newId);
}
private static CompletableFuture<Void> save() {