aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKevinthegreat <92656833+kevinthegreat1@users.noreply.github.com>2023-11-10 22:55:56 -0500
committerKevinthegreat <92656833+kevinthegreat1@users.noreply.github.com>2023-11-11 01:57:24 -0500
commitd23c1acb7e416b67d40982e6e50968e1c23cf799 (patch)
treef11fc8ffca99ac2dc389732e32c3537959572d59 /src
parent7a94d9380acb69006340e53db12faad8f13ab828 (diff)
downloadSkyblocker-d23c1acb7e416b67d40982e6e50968e1c23cf799.tar.gz
Skyblocker-d23c1acb7e416b67d40982e6e50968e1c23cf799.tar.bz2
Skyblocker-d23c1acb7e416b67d40982e6e50968e1c23cf799.zip
Fix room matching and secret index parsing bug
Diffstat (limited to 'src')
-rw-r--r--src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java114
-rw-r--r--src/main/resources/assets/skyblocker/dungeons/secretlocations.json4
2 files changed, 71 insertions, 47 deletions
diff --git a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java
index ecfcf496..c9b32be9 100644
--- a/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java
+++ b/src/main/java/de/hysky/skyblocker/skyblock/dungeon/secrets/Room.java
@@ -41,6 +41,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Room {
+ private static final Pattern SECRET_INDEX = Pattern.compile("^(\\d+)");
private static final Pattern SECRETS = Pattern.compile("ยง7(\\d{1,2})/(\\d{1,2}) Secrets");
@NotNull
private final Type type;
@@ -70,11 +71,12 @@ public class Room {
private int doubleCheckBlocks;
/**
* Represents the matching state of the room with the following possible values:
- * <li>{@link TriState#DEFAULT} means that the room has not been checked, is being processed, or does not {@link Type#needsScanning() need to be processed}.
- * <li>{@link TriState#FALSE} means that the room has been checked and there is no match.
- * <li>{@link TriState#TRUE} means that the room has been checked and there is a match.
+ * <li>{@link MatchState#MATCHING} means that the room has not been checked, is being processed, or does not {@link Type#needsScanning() need to be processed}.</li>
+ * <li>{@link MatchState#DOUBLE_CHECKING} means that the room has a unique match and is being double checked.</li>
+ * <li>{@link MatchState#MATCHED} means that the room has a unique match ans has been double checked.</li>
+ * <li>{@link MatchState#FAILED} means that the room has been checked and there is no match.</li>
*/
- private TriState matched = TriState.DEFAULT;
+ private MatchState matchState = MatchState.MATCHING;
private Table<Integer, BlockPos, SecretWaypoint> secretWaypoints;
private String name;
private Direction direction;
@@ -96,7 +98,7 @@ public class Room {
}
public boolean isMatched() {
- return matched == TriState.TRUE;
+ return matchState == MatchState.DOUBLE_CHECKING || matchState == MatchState.MATCHED;
}
/**
@@ -108,7 +110,7 @@ public class Room {
@Override
public String toString() {
- return "Room{type=" + type + ", shape=" + shape + ", matched=" + matched + ", segments=" + Arrays.toString(segments.toArray()) + "}";
+ return "Room{type=" + type + ", shape=" + shape + ", matchState=" + matchState + ", segments=" + Arrays.toString(segments.toArray()) + "}";
}
@NotNull
@@ -208,6 +210,7 @@ public class Room {
/**
* Removes a custom waypoint relative to this room from {@link DungeonSecrets#customWaypoints} and all existing instances of this room.
+ *
* @param pos the position of the secret waypoint relative to this room
* @return the removed secret waypoint or {@code null} if there was no secret waypoint at the given position
*/
@@ -223,6 +226,7 @@ public class Room {
/**
* Removes a custom waypoint relative to this room from this instance of the room.
+ *
* @param secretIndex the index of the secret waypoint
* @param relativePos the position of the secret waypoint relative to this room
*/
@@ -237,7 +241,7 @@ public class Room {
* This method returns immediately if any of the following conditions are met:
* <ul>
* <li> The room does not need to be scanned and matched. (When the room is not of type {@link Type.ROOM}, {@link Type.PUZZLE}, or {@link Type.TRAP}. See {@link Type#needsScanning()}) </li>
- * <li> The room has been matched or failed to match and is on cooldown. See {@link #matched}. </li>
+ * <li> The room has been matched or failed to match and is on cooldown. See {@link #matchState}. </li>
* <li> {@link #findRoom The previous update} has not completed. </li>
* </ul>
* Then this method tries to match this room through:
@@ -251,7 +255,7 @@ public class Room {
@SuppressWarnings("JavadocReference")
protected void update() {
// Logical AND has higher precedence than logical OR
- if (!type.needsScanning() || matched != TriState.DEFAULT || !DungeonSecrets.isRoomsLoaded() || findRoom != null && !findRoom.isDone()) {
+ if (!type.needsScanning() || matchState != MatchState.MATCHING && matchState != MatchState.DOUBLE_CHECKING || !DungeonSecrets.isRoomsLoaded() || findRoom != null && !findRoom.isDone()) {
return;
}
MinecraftClient client = MinecraftClient.getInstance();
@@ -266,6 +270,9 @@ public class Room {
break;
}
}
+ }).exceptionally(e -> {
+ DungeonSecrets.LOGGER.error("[Skyblocker Dungeon Secrets] Encountered an unknown error while matching room {}", this, e);
+ return null;
});
}
@@ -297,16 +304,24 @@ public class Room {
* </ul>
* <li> If there are no matching rooms left: </li>
* <ul>
- * <li> Terminate matching by setting {@link #matched} to {@link TriState#FALSE}. </li>
+ * <li> Terminate matching by setting {@link #matchState} to {@link TriState#FALSE}. </li>
* <li> Schedule another matching attempt in 50 ticks (2.5 seconds). </li>
* <li> Reset {@link #possibleRooms} and {@link #checkedBlocks} with {@link #reset()}. </li>
* <li> Return {@code true} </li>
* </ul>
* <li> If there are exactly one room matching: </li>
* <ul>
- * <li> Call {@link #roomMatched()}. </li>
- * <li> Discard the no longer needed fields to save memory. </li>
- * <li> Return {@code true} </li>
+ * <li> If {@link #matchState} is {@link MatchState#MATCHING}: </li>
+ * <ul>
+ * <li> Call {@link #roomMatched()}. </li>
+ * <li> Return {@code false}. </li>
+ * </ul>
+ * <li> If {@link #matchState} is {@link MatchState#DOUBLE_CHECKING}: </li>
+ * <ul>
+ * <li> Set the match state to {@link MatchState#MATCHED}. </li>
+ * <li> Discard the no longer needed fields to save memory. </li>
+ * <li> Return {@code true}. </li>
+ * </ul>
* </ul>
* <li> Return {@code false} </li>
* </ul>
@@ -320,40 +335,39 @@ public class Room {
if (id == 0) {
return false;
}
- for (MutableTriple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) {
+ possibleRooms.removeIf(directionRooms -> {
int block = posIdToInt(DungeonMapUtils.actualToRelative(directionRooms.getLeft(), directionRooms.getMiddle(), pos), id);
- List<String> possibleDirectionRooms = new ArrayList<>();
- for (String room : directionRooms.getRight()) {
- if (Arrays.binarySearch(roomsData.get(room), block) >= 0) {
- possibleDirectionRooms.add(room);
- }
- }
- directionRooms.setRight(possibleDirectionRooms);
- }
+ directionRooms.getRight().removeIf(room -> Arrays.binarySearch(roomsData.get(room), block) < 0);
+ return directionRooms.getRight().isEmpty();
+ });
int matchingRoomsSize = possibleRooms.stream().map(Triple::getRight).mapToInt(Collection::size).sum();
if (matchingRoomsSize == 0) {
// If no rooms match, reset the fields and scan again after 50 ticks.
- matched = TriState.FALSE;
- DungeonSecrets.LOGGER.warn("[Skyblocker] No dungeon room matches after checking {} block(s)", checkedBlocks.size());
- Scheduler.INSTANCE.schedule(() -> matched = TriState.DEFAULT, 50);
+ DungeonSecrets.LOGGER.warn("[Skyblocker Dungeon Secrets] No dungeon room matched after checking {} block(s) including double checking {} block(s)", checkedBlocks.size(), doubleCheckBlocks + 1);
+ Scheduler.INSTANCE.schedule(() -> matchState = MatchState.MATCHING, 50);
reset();
return true;
- } else if (matchingRoomsSize == 1 && ++doubleCheckBlocks >= 10) {
- // If one room matches, load the secrets for that room and discard the no longer needed fields.
- for (Triple<Direction, Vector2ic, List<String>> directionRooms : possibleRooms) {
- if (directionRooms.getRight().size() == 1) {
- name = directionRooms.getRight().get(0);
- direction = directionRooms.getLeft();
- physicalCornerPos = directionRooms.getMiddle();
- roomMatched();
- discard();
- return true;
- }
+ } else if (matchingRoomsSize == 1) {
+ if (matchState == MatchState.MATCHING) {
+ // If one room matches, load the secrets for that room and set state to double-checking.
+ assert possibleRooms.size() == 1;
+ Triple<Direction, Vector2ic, List<String>> directionRoom = possibleRooms.get(0);
+ assert directionRoom.getRight().size() == 1;
+ name = directionRoom.getRight().get(0);
+ direction = directionRoom.getLeft();
+ physicalCornerPos = directionRoom.getMiddle();
+ roomMatched();
+ return false;
+ } else if (matchState == MatchState.DOUBLE_CHECKING && ++doubleCheckBlocks >= 10) {
+ // If double-checked, set state to matched and discard the no longer needed fields.
+ DungeonSecrets.LOGGER.info("[Skyblocker Dungeon Secrets] Room {} matched after checking {} block(s) including double checking {} block(s)", name, checkedBlocks.size(), doubleCheckBlocks);
+ discard();
+ return true;
}
- return false; // This should never happen, we just checked that there is one possible room, and the return true in the loop should activate
+ return false;
} else {
- DungeonSecrets.LOGGER.debug("[Skyblocker] {} room(s) remaining after checking {} block(s)", matchingRoomsSize, checkedBlocks.size());
+ DungeonSecrets.LOGGER.debug("[Skyblocker Dungeon Secrets] {} room(s) remaining after checking {} block(s)", matchingRoomsSize, checkedBlocks.size());
return false;
}
}
@@ -371,7 +385,7 @@ public class Room {
/**
* Loads the secret waypoints for the room from {@link DungeonSecrets#waypointsJson} once it has been matched
- * and sets {@link #matched} to {@link TriState#TRUE}.
+ * and sets {@link #matchState} to {@link MatchState#DOUBLE_CHECKING}.
*
* @param directionRooms the direction, position, and name of the room
*/
@@ -381,25 +395,30 @@ public class Room {
for (JsonElement waypointElement : DungeonSecrets.getRoomWaypoints(name)) {
JsonObject waypoint = waypointElement.getAsJsonObject();
String secretName = waypoint.get("secretName").getAsString();
- int secretIndex = Integer.parseInt(secretName.substring(0, Character.isDigit(secretName.charAt(1)) ? 2 : 1));
+ Matcher secretIndexMatcher = SECRET_INDEX.matcher(secretName);
+ int secretIndex = secretIndexMatcher.find() ? Integer.parseInt(secretIndexMatcher.group(1)) : 0;
BlockPos pos = DungeonMapUtils.relativeToActual(direction, physicalCornerPos, waypoint);
secretWaypoints.put(secretIndex, pos, new SecretWaypoint(secretIndex, waypoint, secretName, pos));
}
DungeonSecrets.getCustomWaypoints(name).values().forEach(this::addCustomWaypoint);
- matched = TriState.TRUE;
-
- DungeonSecrets.LOGGER.info("[Skyblocker] Room {} matched after checking {} block(s)", name, checkedBlocks.size());
+ matchState = MatchState.DOUBLE_CHECKING;
+ DungeonSecrets.LOGGER.info("[Skyblocker Dungeon Secrets] Room {} matched after checking {} block(s), starting double checking", name, checkedBlocks.size());
}
/**
* Resets fields for another round of matching after room matching fails.
*/
private void reset() {
+ matchState = MatchState.FAILED;
IntSortedSet segmentsX = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::x).toArray()));
IntSortedSet segmentsY = IntSortedSets.unmodifiable(new IntRBTreeSet(segments.stream().mapToInt(Vector2ic::y).toArray()));
possibleRooms = getPossibleRooms(segmentsX, segmentsY);
checkedBlocks = new HashSet<>();
doubleCheckBlocks = 0;
+ secretWaypoints = null;
+ name = null;
+ direction = null;
+ physicalCornerPos = null;
}
/**
@@ -407,6 +426,7 @@ public class Room {
* These fields are no longer needed and are discarded to save memory.
*/
private void discard() {
+ matchState = MatchState.MATCHED;
roomsData = null;
possibleRooms = null;
checkedBlocks = null;
@@ -473,7 +493,7 @@ public class Room {
BlockState state = world.getBlockState(hitResult.getBlockPos());
if (state.isOf(Blocks.CHEST) || state.isOf(Blocks.PLAYER_HEAD) || state.isOf(Blocks.PLAYER_WALL_HEAD)) {
secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::needsInteraction).findAny()
- .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} interaction, setting secret #{} as found", secretWaypoint.category, secretWaypoint.secretIndex));
+ .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker Dungeon Secrets] Detected {} interaction, setting secret #{} as found", secretWaypoint.category, secretWaypoint.secretIndex));
} else if (state.isOf(Blocks.LEVER)) {
secretWaypoints.column(hitResult.getBlockPos()).values().stream().filter(SecretWaypoint::isLever).forEach(SecretWaypoint::setFound);
}
@@ -491,7 +511,7 @@ public class Room {
return;
}
secretWaypoints.values().stream().filter(SecretWaypoint::needsItemPickup).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(collector))).filter(SecretWaypoint.getRangePredicate(collector))
- .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} picked up a {} from a {} secret, setting secret #{} as found", collector.getName().getString(), itemEntity.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex));
+ .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker Dungeon Secrets] Detected {} picked up a {} from a {} secret, setting secret #{} as found", collector.getName().getString(), itemEntity.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex));
}
/**
@@ -502,7 +522,7 @@ public class Room {
*/
protected void onBatRemoved(AmbientEntity bat) {
secretWaypoints.values().stream().filter(SecretWaypoint::isBat).min(Comparator.comparingDouble(SecretWaypoint.getSquaredDistanceToFunction(bat)))
- .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker] Detected {} killed for a {} secret, setting secret #{} as found", bat.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex));
+ .ifPresent(secretWaypoint -> onSecretFound(secretWaypoint, "[Skyblocker Dungeon Secrets] Detected {} killed for a {} secret, setting secret #{} as found", bat.getName().getString(), secretWaypoint.category, secretWaypoint.secretIndex));
}
/**
@@ -575,4 +595,8 @@ public class Room {
public enum Direction {
NW, NE, SW, SE
}
+
+ public enum MatchState {
+ MATCHING, DOUBLE_CHECKING, MATCHED, FAILED
+ }
}
diff --git a/src/main/resources/assets/skyblocker/dungeons/secretlocations.json b/src/main/resources/assets/skyblocker/dungeons/secretlocations.json
index a0a97c67..02e19632 100644
--- a/src/main/resources/assets/skyblocker/dungeons/secretlocations.json
+++ b/src/main/resources/assets/skyblocker/dungeons/secretlocations.json
@@ -2173,7 +2173,7 @@
},
{
"secretName":"3 - Lever 2",
- "category":"Lever",
+ "category":"lever",
"x":31,
"y":53,
"z":24
@@ -2375,7 +2375,7 @@
},
{
"secretName":"2 - Item",
- "category":"Item",
+ "category":"item",
"x":27,
"y":56,
"z":19