From 73fad7064fb47d9d79ccdb3bab5988df1b9eea6f Mon Sep 17 00:00:00 2001 From: CraftyOldMiner <85420839+CraftyOldMiner@users.noreply.github.com> Date: Mon, 7 Mar 2022 21:44:17 -0600 Subject: Wishing Compass Solver, Metal Detector Solver improvements, add /neudiag, other misc changes (#90) --- .../miscfeatures/CrystalMetalDetectorSolver.java | 439 +++++++++++++++++---- 1 file changed, 363 insertions(+), 76 deletions(-) (limited to 'src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java') diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java index 4586d190..27c334ad 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java @@ -2,31 +2,119 @@ package io.github.moulberry.notenoughupdates.miscfeatures; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.core.util.render.RenderUtils; +import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; +import io.github.moulberry.notenoughupdates.util.NEUDebugLogger; import io.github.moulberry.notenoughupdates.util.SBInfo; import net.minecraft.client.Minecraft; -import net.minecraft.util.*; +import net.minecraft.entity.item.EntityArmorStand; +import net.minecraft.util.BlockPos; +import net.minecraft.util.ChatComponentText; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.IChatComponent; +import net.minecraft.util.Vec3; +import net.minecraft.util.Vec3i; -import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.stream.Collectors; public class CrystalMetalDetectorSolver { private static final Minecraft mc = Minecraft.getMinecraft(); + private static BlockPos prevPlayerPos; private static double prevDistToTreasure = 0; - private static List possibleBlocks = new ArrayList<>(); - private static final List locations = new ArrayList<>(); - + private static HashSet possibleBlocks = new HashSet<>(); + private static final HashMap evaluatedPlayerPositions = new HashMap<>(); + private static BlockPos blockPosIfLastSolutionInvalid; private static Boolean chestRecentlyFound = false; private static long chestLastFoundMillis = 0; + private static final HashSet openedChestPositions = new HashSet<>(); + + // Keeper and Mines of Divan center location info + private static Vec3i minesCenter; + private static boolean visitKeeperMessagePrinted = false; + private static String KEEPER_OF_STRING = "Keeper of "; + private static String DIAMOND_STRING = "diamond"; + private static String LAPIS_STRING = "lapis"; + private static String EMERALD_STRING = "emerald"; + private static String GOLD_STRING = "gold"; + private static final HashMap keeperOffsets = new HashMap() {{ + put(DIAMOND_STRING, new Vec3i(33,0,3)); + put(LAPIS_STRING, new Vec3i(-33,0,-3)); + put(EMERALD_STRING, new Vec3i(-3,0,33)); + put(GOLD_STRING, new Vec3i(3,0,-33)); + }}; + + // Chest offsets from center + private static final HashSet knownChestOffsets = new HashSet<>(Arrays.asList( + -10171958951910L, // x=-38, y=-22, z=26 + 10718829084646L, // x=38, y=-22, z=-26 + -10721714765806L, // x=-40, y=-22, z=18 + -10996458455018L, // x=-41, y=-20, z=22 + -1100920913904L, // x=-5, y=-21, z=16 + 11268584898530L, // x=40, y=-22, z=-30 + -11271269253148L, // x=-42, y=-20, z=-28 + -11546281377832L, // x=-43, y=-22, z=-40 + 11818542038999L, // x=42, y=-19, z=-41 + 12093285728240L, // x=43, y=-21, z=-16 + -1409286164L, // x=-1, y=-22, z=-20 + 1922736062492L, // x=6, y=-21, z=28 + 2197613969419L, // x=7, y=-21, z=11 + 2197613969430L, // x=7, y=-21, z=22 + -3024999153708L, // x=-12, y=-21, z=-44 + 3571936395295L, // x=12, y=-22, z=31 + 3572003504106L, // x=12, y=-22, z=-22 + 3572003504135L, // x=12, y=-21, z=7 + 3572070612949L, // x=12, y=-21, z=-43 + -3574822076373L, // x=-14, y=-21, z=43 + -3574822076394L, // x=-14, y=-21, z=22 + -4399455797228L, // x=-17, y=-21, z=20 + -5224156626944L, // x=-20, y=-22, z=0 + 548346527764L, // x=1, y=-21, z=20 + 5496081743901L, // x=19, y=-22, z=29 + 5770959650816L, // x=20, y=-22, z=0 + 5771093868518L, // x=20, y=-21, z=-26 + -6048790347736L, // x=-23, y=-22, z=40 + 6320849682418L, // x=22, y=-21, z=-14 + -6323668254708L, // x=-24, y=-22, z=12 + 6595593371674L, // x=23, y=-22, z=26 + 6595660480473L, // x=23, y=-22, z=-39 + 6870471278619L, // x=24, y=-22, z=27 + 7145349185553L, // x=25, y=-22, z=17 + 8244995030996L, // x=29, y=-21, z=-44 + -8247679385612L, // x=-31, y=-21, z=-12 + -8247679385640L, // x=-31, y=-21, z=-40 + 8519872937959L, // x=30, y=-21, z=-25 + -8522557292584L, // x=-32, y=-21, z=-40 + -9622068920278L, // x=-36, y=-20, z=42 + -9896946827278L, // x=-37, y=-21, z=-14 + -9896946827286L // x=-37, y=-21, z=-22 + )); public static void process(IChatComponent message) { + if (SBInfo.getInstance().getLocation() == null || + !NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled || + !SBInfo.getInstance().getLocation().equals("crystal_hollows") || + !message.getUnformattedText().contains("TREASURE: ")) { + return; + } + + locateMinesCenterIfNeeded(); + + double distToTreasure = Double.parseDouble(message + .getUnformattedText() + .split("TREASURE: ")[1].split("m")[0].replaceAll("(?!\\.)\\D", "")); + // Delay to keep old chest location from being treated as the new chest location if (chestRecentlyFound) { long currentTimeMillis = System.currentTimeMillis(); if (chestLastFoundMillis == 0) { chestLastFoundMillis = currentTimeMillis; return; - } else if (currentTimeMillis - chestLastFoundMillis < 1000) { + } else if (currentTimeMillis - chestLastFoundMillis < 1000 && distToTreasure < 5.0) { return; } @@ -34,80 +122,239 @@ public class CrystalMetalDetectorSolver { chestRecentlyFound = false; } - if (SBInfo.getInstance().getLocation() != null && SBInfo.getInstance().getLocation().equals("crystal_hollows") - && message.getUnformattedText().contains("TREASURE: ")) { - double distToTreasure = Double.parseDouble(message - .getUnformattedText() - .split("TREASURE: ")[1].split("m")[0].replaceAll("(?!\\.)\\D", "")); - if (NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled && prevDistToTreasure == distToTreasure && - prevPlayerPos.getX() == mc.thePlayer.getPosition().getX() && - prevPlayerPos.getY() == mc.thePlayer.getPosition().getY() && - prevPlayerPos.getZ() == mc.thePlayer.getPosition().getZ() && !locations.contains(mc.thePlayer.getPosition())) { - if (possibleBlocks.size() == 0) { - locations.add(mc.thePlayer.getPosition()); - for (int zOffset = (int) Math.floor(-distToTreasure); zOffset <= Math.ceil(distToTreasure); zOffset++) { - for (int y = 65; y <= 75; y++) { - double calculatedDist = 0; - int xOffset = 0; - while (calculatedDist < distToTreasure) { - BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) + xOffset, - y, Math.floor(mc.thePlayer.posZ) + zOffset - ); - calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && !possibleBlocks.contains(pos) && - treasureAllowed(pos) && mc.theWorld. - getBlockState(pos.add(0, 1, 0)).getBlock().getRegistryName().equals("minecraft:air")) { - possibleBlocks.add(pos); - } - xOffset++; - } - xOffset = 0; - calculatedDist = 0; - while (calculatedDist < distToTreasure) { - BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) - xOffset, - y, Math.floor(mc.thePlayer.posZ) + zOffset - ); - calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && !possibleBlocks.contains(pos) && - treasureAllowed(pos) && mc.theWorld. - getBlockState(pos.add(0, 1, 0)).getBlock().getRegistryName().equals("minecraft:air")) { - possibleBlocks.add(pos); - } - xOffset++; + if (prevDistToTreasure == distToTreasure && + prevPlayerPos.equals(mc.thePlayer.getPosition()) && + !evaluatedPlayerPositions.keySet().contains(mc.thePlayer.getPosition())) { + if (possibleBlocks.size() == 0) { + evaluatedPlayerPositions.put(mc.thePlayer.getPosition(), distToTreasure); + for (int zOffset = (int) Math.floor(-distToTreasure); zOffset <= Math.ceil(distToTreasure); zOffset++) { + for (int y = 65; y <= 75; y++) { + double calculatedDist = 0; + int xOffset = 0; + while (calculatedDist < distToTreasure) { + BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) + xOffset, + y, Math.floor(mc.thePlayer.posZ) + zOffset); + calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); + if (round(calculatedDist, 1) == distToTreasure && treasureAllowed(pos)) { + possibleBlocks.add(pos); } + xOffset++; } - } - sendMessage(); - } else if (possibleBlocks.size() != 1) { - locations.add(mc.thePlayer.getPosition()); - List temp = new ArrayList<>(); - for (BlockPos pos : possibleBlocks) { - if (round(getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { - temp.add(pos); + xOffset = 0; + calculatedDist = 0; + while (calculatedDist < distToTreasure) { + BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) - xOffset, + y, Math.floor(mc.thePlayer.posZ) + zOffset); + calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); + if (round(calculatedDist, 1) == distToTreasure && treasureAllowed(pos)) { + possibleBlocks.add(pos); + } + xOffset++; } } - possibleBlocks = temp; - sendMessage(); - } else { - BlockPos pos = possibleBlocks.get(0); - if (Math.abs(distToTreasure - (getPlayerPos().distanceTo(new Vec3(pos)))) > 5) { - mc.thePlayer.addChatMessage(new ChatComponentText( - EnumChatFormatting.RED + "[NEU] Previous solution is invalid.")); - reset(false); + } + + checkAndDisplaySolutionState(); + } else if (possibleBlocks.size() != 1) { + evaluatedPlayerPositions.put(mc.thePlayer.getPosition().getImmutable(), distToTreasure); + HashSet temp = new HashSet<>(); + for (BlockPos pos : possibleBlocks) { + if (round(getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { + temp.add(pos); } } + possibleBlocks = temp; + checkAndDisplaySolutionState(); + } else { + BlockPos pos = possibleBlocks.iterator().next(); + if (Math.abs(distToTreasure - (getPlayerPos().distanceTo(new Vec3(pos)))) > 5) { + mc.thePlayer.addChatMessage(new ChatComponentText( + EnumChatFormatting.RED + "[NEU] Previous solution is invalid.")); + blockPosIfLastSolutionInvalid = pos.getImmutable(); + logDiagnosticData(false); + resetSolution(false); + } } + } + + prevPlayerPos = mc.thePlayer.getPosition().getImmutable(); + prevDistToTreasure = distToTreasure; + } + + private static void checkForSingleKnownLocationMatch() { + if (minesCenter == BlockPos.NULL_VECTOR || possibleBlocks.size() < 2) { + return; + } - prevPlayerPos = mc.thePlayer.getPosition(); - prevDistToTreasure = distToTreasure; + HashSet temp = possibleBlocks.stream() + .filter(block -> knownChestOffsets.contains(block.subtract(minesCenter).toLong())) + .collect(Collectors.toCollection(HashSet::new)); + if (temp.size() == 1) { + possibleBlocks = temp; + NEUDebugLogger.log(NEUDebugFlag.METAL, "Known location identified."); + } else if (temp.size() > 1) { + NEUDebugLogger.log(NEUDebugFlag.METAL, temp.size() + " known locations identified:"); } } - public static void reset(Boolean chestFound) { + private static String getFriendlyBlockPositions(Collection positions) { + if (!NEUDebugLogger.isFlagEnabled(NEUDebugFlag.METAL) || positions.size() == 0) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + for (BlockPos blockPos : positions) { + sb.append("Absolute: " + blockPos.toString()); + if (minesCenter != Vec3i.NULL_VECTOR) { + BlockPos relativeOffset = blockPos.subtract(minesCenter); + sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); + } + sb.append("\n"); + } + + return sb.toString(); + } + + private static String getFriendlyEvaluatedPositions(HashMap positions) { + if (!NEUDebugLogger.isFlagEnabled(NEUDebugFlag.METAL) || positions.size() == 0) { + return ""; + } + + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + for (BlockPos blockPos : positions.keySet()) { + sb.append("Absolute: " + blockPos.toString()); + if (minesCenter != Vec3i.NULL_VECTOR) { + BlockPos relativeOffset = blockPos.subtract(minesCenter); + sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); + } + + sb.append(" Distance: "); + sb.append(positions.get(blockPos)); + + sb.append("\n"); + } + + return sb.toString(); + } + + public static void logDiagnosticData(boolean outputAlways) { + if (SBInfo.getInstance().getLocation() == null) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] This command is not available outside SkyBlock")); + return; + } + + if (!NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled) + { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Metal Detector Solver is not enabled.")); + return; + } + + if (!outputAlways && !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.METAL)) { + return; + } + + boolean originalDebugFlag = !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.add(NEUDebugFlag.METAL); + + StringBuilder diagsMessage = new StringBuilder(); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Mines Center: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((minesCenter == Vec3i.NULL_VECTOR) ? "" : minesCenter.toString()); + diagsMessage.append("\n"); + + diagsMessage.append("\n"); + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Previous Player Position: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((prevPlayerPos == null) ? "" : prevPlayerPos.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Previous Distance To Treasure: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((prevDistToTreasure == 0) ? "" : prevDistToTreasure); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Last Solution Invalid Position: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((blockPosIfLastSolutionInvalid == null) ? "" : blockPosIfLastSolutionInvalid.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Current Possible Blocks: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(possibleBlocks.size()); + diagsMessage.append(getFriendlyBlockPositions(possibleBlocks)); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Evaluated player positions: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(evaluatedPlayerPositions.size()); + diagsMessage.append(getFriendlyEvaluatedPositions(evaluatedPlayerPositions)); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Chest locations not on known list:\n"); + diagsMessage.append(EnumChatFormatting.WHITE); + if (minesCenter != Vec3i.NULL_VECTOR) { + HashSet locationsNotOnKnownList = openedChestPositions + .stream() + .filter(block -> !knownChestOffsets.contains(block.subtract(minesCenter).toLong())) + .map(block -> block.subtract(minesCenter)) + .collect(Collectors.toCollection(HashSet::new)); + if (locationsNotOnKnownList.size() > 0) { + for (BlockPos blockPos : locationsNotOnKnownList) { + diagsMessage.append(String.format( + "%dL,\t\t// x=%d, y=%d, z=%d", + blockPos.toLong(), + blockPos.getX(), + blockPos.getY(), + blockPos.getZ() + )); + } + } + } else { + diagsMessage.append(""); + } + + NEUDebugLogger.log(NEUDebugFlag.METAL, diagsMessage.toString()); + + if (!originalDebugFlag) { + NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.remove(NEUDebugFlag.METAL); + } + } + + public static void resetSolution(Boolean chestFound) { + if (chestFound) { + blockPosIfLastSolutionInvalid = null; + prevPlayerPos = null; + prevDistToTreasure = 0; + if (possibleBlocks.size() == 1) { + openedChestPositions.add(possibleBlocks.iterator().next().getImmutable()); + } + } + chestRecentlyFound = chestFound; possibleBlocks.clear(); - locations.clear(); + evaluatedPlayerPositions.clear(); + } + + public static void initWorld() { + minesCenter = Vec3i.NULL_VECTOR; + visitKeeperMessagePrinted = false; + blockPosIfLastSolutionInvalid = null; + openedChestPositions.clear(); + prevDistToTreasure = 0; + prevPlayerPos = null; + resetSolution(false); } public static void render(float partialTicks) { @@ -117,10 +364,10 @@ public class CrystalMetalDetectorSolver { SBInfo.getInstance().location.equals("Mines of Divan")) { if (possibleBlocks.size() == 1) { - BlockPos block = possibleBlocks.get(0); + BlockPos block = possibleBlocks.iterator().next(); RenderUtils.renderBeaconBeam(block.add(0, 1, 0), beaconRGB, 1.0f, partialTicks); - RenderUtils.renderWayPoint("Treasure", possibleBlocks.get(0).add(0, 2.5, 0), partialTicks); + RenderUtils.renderWayPoint("Treasure", possibleBlocks.iterator().next().add(0, 2.5, 0), partialTicks); } else if (possibleBlocks.size() > 1 && NotEnoughUpdates.INSTANCE.config.mining.metalDetectorShowPossible) { for (BlockPos block : possibleBlocks) { RenderUtils.renderBeaconBeam(block.add(0, 1, 0), beaconRGB, 1.0f, partialTicks); @@ -130,19 +377,55 @@ public class CrystalMetalDetectorSolver { } } + private static void locateMinesCenterIfNeeded() { + if (minesCenter != Vec3i.NULL_VECTOR) { + return; + } + + List keeperEntities = mc.theWorld.getEntities(EntityArmorStand.class, (entity) -> { + if (!entity.hasCustomName()) return false; + if (entity.getCustomNameTag().contains(KEEPER_OF_STRING)) return true; + return false; + }); + + if (keeperEntities.size() == 0) { + if (!visitKeeperMessagePrinted) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] Approach a Keeper while holding the metal detector to enable faster treasure hunting.")); + visitKeeperMessagePrinted = true; + } + return; + } + + EntityArmorStand keeperEntity = keeperEntities.get(0); + String keeperName = keeperEntity.getCustomNameTag(); + NEUDebugLogger.log(NEUDebugFlag.METAL,"Locating center using Keeper: " + + EnumChatFormatting.WHITE + keeperEntity); + String keeperType = keeperName.substring(keeperName.indexOf(KEEPER_OF_STRING) + KEEPER_OF_STRING.length()); + minesCenter = keeperEntity.getPosition().add(keeperOffsets.get(keeperType.toLowerCase())); + NEUDebugLogger.log(NEUDebugFlag.METAL,"Mines center: " + + EnumChatFormatting.WHITE + minesCenter.toString()); + mc.thePlayer.addChatMessage(new ChatComponentText( + EnumChatFormatting.YELLOW + "[NEU] Faster treasure hunting is now enabled based on Keeper location.")); + } + private static double round(double value, int precision) { int scale = (int) Math.pow(10, precision); return (double) Math.round(value * scale) / scale; } - private static void sendMessage() { + private static void checkAndDisplaySolutionState() { + if (possibleBlocks.size() == 0) { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[NEU] Failed to find a solution.")); + logDiagnosticData(false); + resetSolution(false); + return; + } + + checkForSingleKnownLocationMatch(); if (possibleBlocks.size() > 1) { - mc.thePlayer.addChatMessage(new ChatComponentText( - EnumChatFormatting.YELLOW + "[NEU] Need another position to find solution. Possible blocks: " - + possibleBlocks.size())); - } else if (possibleBlocks.size() == 0) { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[NEU] Failed to find solution.")); - reset(false); + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] Need another position to find solution. Possible blocks: " + possibleBlocks.size())); } else { mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "[NEU] Found solution.")); } @@ -157,12 +440,16 @@ public class CrystalMetalDetectorSolver { } private static boolean treasureAllowed(BlockPos pos) { - return mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:gold_block") || + boolean airAbove = mc.theWorld. + getBlockState(pos.add(0, 1, 0)).getBlock().getRegistryName().equals("minecraft:air"); + boolean allowedBlockType = mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:gold_block") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:prismarine") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:chest") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:stained_glass") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:stained_glass_pane") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:wool") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:stained_hardened_clay"); + boolean knownOffset = knownChestOffsets.contains(pos.subtract(minesCenter).toLong()); + return airAbove & (knownOffset | allowedBlockType); } } -- cgit From e202e4adf979073921455083f5e737bc4fcf5f14 Mon Sep 17 00:00:00 2001 From: CraftyOldMiner <85420839+CraftyOldMiner@users.noreply.github.com> Date: Thu, 24 Mar 2022 14:55:20 -0500 Subject: Refactor Hollows solvers, add tests, add Vec3Comparable, fix bugs (#95) --- .../miscfeatures/CrystalMetalDetectorSolver.java | 450 +++++++++++++-------- 1 file changed, 274 insertions(+), 176 deletions(-) (limited to 'src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java') diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java index 27c334ad..f43ebda2 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java @@ -1,6 +1,7 @@ package io.github.moulberry.notenoughupdates.miscfeatures; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; +import io.github.moulberry.notenoughupdates.core.util.Vec3Comparable; import io.github.moulberry.notenoughupdates.core.util.render.RenderUtils; import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; import io.github.moulberry.notenoughupdates.util.NEUDebugLogger; @@ -11,7 +12,6 @@ import net.minecraft.util.BlockPos; import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; -import net.minecraft.util.Vec3; import net.minecraft.util.Vec3i; import java.util.Arrays; @@ -22,25 +22,35 @@ import java.util.List; import java.util.stream.Collectors; public class CrystalMetalDetectorSolver { + enum SolutionState { + NOT_STARTED, + MULTIPLE, + MULTIPLE_KNOWN, + FOUND, + FOUND_KNOWN, + FAILED, + INVALID, + } + private static final Minecraft mc = Minecraft.getMinecraft(); - private static BlockPos prevPlayerPos; - private static double prevDistToTreasure = 0; + private static Vec3Comparable prevPlayerPos; + private static double prevDistToTreasure; private static HashSet possibleBlocks = new HashSet<>(); - private static final HashMap evaluatedPlayerPositions = new HashMap<>(); - private static BlockPos blockPosIfLastSolutionInvalid; - private static Boolean chestRecentlyFound = false; - private static long chestLastFoundMillis = 0; + private static final HashMap evaluatedPlayerPositions = new HashMap<>(); + private static boolean chestRecentlyFound; + private static long chestLastFoundMillis; private static final HashSet openedChestPositions = new HashSet<>(); // Keeper and Mines of Divan center location info private static Vec3i minesCenter; - private static boolean visitKeeperMessagePrinted = false; - private static String KEEPER_OF_STRING = "Keeper of "; - private static String DIAMOND_STRING = "diamond"; - private static String LAPIS_STRING = "lapis"; - private static String EMERALD_STRING = "emerald"; - private static String GOLD_STRING = "gold"; + private static boolean debugDoNotUseCenter = false; + private static boolean visitKeeperMessagePrinted; + private static final String KEEPER_OF_STRING = "Keeper of "; + private static final String DIAMOND_STRING = "diamond"; + private static final String LAPIS_STRING = "lapis"; + private static final String EMERALD_STRING = "emerald"; + private static final String GOLD_STRING = "gold"; private static final HashMap keeperOffsets = new HashMap() {{ put(DIAMOND_STRING, new Vec3i(33,0,3)); put(LAPIS_STRING, new Vec3i(-33,0,-3)); @@ -94,6 +104,14 @@ public class CrystalMetalDetectorSolver { -9896946827286L // x=-37, y=-21, z=-22 )); + static Predicate treasureAllowedPredicate = CrystalMetalDetectorSolver::treasureAllowed; + static SolutionState currentState = SolutionState.NOT_STARTED; + static SolutionState previousState = SolutionState.NOT_STARTED; + + public interface Predicate { + boolean check(BlockPos blockPos); + } + public static void process(IChatComponent message) { if (SBInfo.getInstance().getLocation() == null || !NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled || @@ -102,7 +120,7 @@ public class CrystalMetalDetectorSolver { return; } - locateMinesCenterIfNeeded(); + boolean centerNewlyDiscovered = locateMinesCenterIfNeeded(); double distToTreasure = Double.parseDouble(message .getUnformattedText() @@ -122,20 +140,64 @@ public class CrystalMetalDetectorSolver { chestRecentlyFound = false; } - if (prevDistToTreasure == distToTreasure && - prevPlayerPos.equals(mc.thePlayer.getPosition()) && - !evaluatedPlayerPositions.keySet().contains(mc.thePlayer.getPosition())) { + SolutionState originalState = currentState; + int originalCount = possibleBlocks.size(); + Vec3Comparable adjustedPlayerPos = getPlayerPosAdjustedForEyeHeight(); + findPossibleSolutions(distToTreasure, adjustedPlayerPos, centerNewlyDiscovered); + if (currentState != originalState || originalCount != possibleBlocks.size()) { + switch (currentState) { + case FOUND_KNOWN: + NEUDebugLogger.log(NEUDebugFlag.METAL, "Known location identified."); + // falls through + case FOUND: + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "[NEU] Found solution.")); + if (NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.METAL) && + (previousState == SolutionState.INVALID || previousState == SolutionState.FAILED)) { + NEUDebugLogger.log(NEUDebugFlag.METAL, + EnumChatFormatting.AQUA + "Solution coordinates: " + + EnumChatFormatting.WHITE + possibleBlocks.iterator().next().toString()); + } + break; + case INVALID: + mc.thePlayer.addChatMessage(new ChatComponentText( + EnumChatFormatting.RED + "[NEU] Previous solution is invalid.")); + logDiagnosticData(false); + resetSolution(false); + break; + case FAILED: + mc.thePlayer.addChatMessage(new ChatComponentText( + EnumChatFormatting.RED + "[NEU] Failed to find a solution.")); + logDiagnosticData(false); + resetSolution(false); + break; + case MULTIPLE_KNOWN: + NEUDebugLogger.log(NEUDebugFlag.METAL, "Multiple known locations identified:"); + // falls through + case MULTIPLE: + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + + "[NEU] Need another position to find solution. Possible blocks: " + possibleBlocks.size())); + break; + default: + throw new IllegalStateException("Metal detector is in invalid state"); + } + } + } + + static void findPossibleSolutions(double distToTreasure, Vec3Comparable playerPos, boolean centerNewlyDiscovered) { + if (prevDistToTreasure == distToTreasure && prevPlayerPos.equals(playerPos) && + !evaluatedPlayerPositions.containsKey(playerPos)) { + evaluatedPlayerPositions.put(playerPos, distToTreasure); if (possibleBlocks.size() == 0) { - evaluatedPlayerPositions.put(mc.thePlayer.getPosition(), distToTreasure); for (int zOffset = (int) Math.floor(-distToTreasure); zOffset <= Math.ceil(distToTreasure); zOffset++) { for (int y = 65; y <= 75; y++) { double calculatedDist = 0; int xOffset = 0; while (calculatedDist < distToTreasure) { - BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) + xOffset, - y, Math.floor(mc.thePlayer.posZ) + zOffset); - calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && treasureAllowed(pos)) { + BlockPos pos = new BlockPos(Math.floor(playerPos.xCoord) + xOffset, + y, Math.floor(playerPos.zCoord) + zOffset + ); + calculatedDist = playerPos.distanceTo(new Vec3Comparable(pos).addVector(0D, 1D, 0D)); + if (round(calculatedDist, 1) == distToTreasure && treasureAllowedPredicate.check(pos)) { possibleBlocks.add(pos); } xOffset++; @@ -143,10 +205,11 @@ public class CrystalMetalDetectorSolver { xOffset = 0; calculatedDist = 0; while (calculatedDist < distToTreasure) { - BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) - xOffset, - y, Math.floor(mc.thePlayer.posZ) + zOffset); - calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && treasureAllowed(pos)) { + BlockPos pos = new BlockPos(Math.floor(playerPos.xCoord) - xOffset, + y, Math.floor(playerPos.zCoord) + zOffset + ); + calculatedDist = playerPos.distanceTo(new Vec3Comparable(pos).addVector(0D, 1D, 0D)); + if (round(calculatedDist, 1) == distToTreasure && treasureAllowedPredicate.check(pos)) { possibleBlocks.add(pos); } xOffset++; @@ -154,48 +217,33 @@ public class CrystalMetalDetectorSolver { } } - checkAndDisplaySolutionState(); + updateSolutionState(); } else if (possibleBlocks.size() != 1) { - evaluatedPlayerPositions.put(mc.thePlayer.getPosition().getImmutable(), distToTreasure); HashSet temp = new HashSet<>(); for (BlockPos pos : possibleBlocks) { - if (round(getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { + if (round(playerPos.distanceTo(new Vec3Comparable(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { temp.add(pos); } } possibleBlocks = temp; - checkAndDisplaySolutionState(); + updateSolutionState(); } else { BlockPos pos = possibleBlocks.iterator().next(); - if (Math.abs(distToTreasure - (getPlayerPos().distanceTo(new Vec3(pos)))) > 5) { - mc.thePlayer.addChatMessage(new ChatComponentText( - EnumChatFormatting.RED + "[NEU] Previous solution is invalid.")); - blockPosIfLastSolutionInvalid = pos.getImmutable(); - logDiagnosticData(false); - resetSolution(false); + if (Math.abs(distToTreasure - (playerPos.distanceTo(new Vec3Comparable(pos)))) > 5) { + currentState = SolutionState.INVALID; } } + } else if (centerNewlyDiscovered && possibleBlocks.size() > 1) { + updateSolutionState(); } - prevPlayerPos = mc.thePlayer.getPosition().getImmutable(); + prevPlayerPos = playerPos; prevDistToTreasure = distToTreasure; } - private static void checkForSingleKnownLocationMatch() { - if (minesCenter == BlockPos.NULL_VECTOR || possibleBlocks.size() < 2) { - return; - } - - HashSet temp = possibleBlocks.stream() - .filter(block -> knownChestOffsets.contains(block.subtract(minesCenter).toLong())) - .collect(Collectors.toCollection(HashSet::new)); - if (temp.size() == 1) { - possibleBlocks = temp; - NEUDebugLogger.log(NEUDebugFlag.METAL, "Known location identified."); - } else if (temp.size() > 1) { - NEUDebugLogger.log(NEUDebugFlag.METAL, temp.size() + " known locations identified:"); - } + public static void setDebugDoNotUseCenter(boolean val) { + debugDoNotUseCenter = val; } private static String getFriendlyBlockPositions(Collection positions) { @@ -206,10 +254,13 @@ public class CrystalMetalDetectorSolver { StringBuilder sb = new StringBuilder(); sb.append("\n"); for (BlockPos blockPos : positions) { - sb.append("Absolute: " + blockPos.toString()); + sb.append("Absolute: "); + sb.append(blockPos.toString()); if (minesCenter != Vec3i.NULL_VECTOR) { BlockPos relativeOffset = blockPos.subtract(minesCenter); - sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); + sb.append(", Relative: "); + sb.append(relativeOffset.toString() ); + sb.append(" (" + relativeOffset.toLong() + ")"); } sb.append("\n"); } @@ -217,22 +268,23 @@ public class CrystalMetalDetectorSolver { return sb.toString(); } - private static String getFriendlyEvaluatedPositions(HashMap positions) { - if (!NEUDebugLogger.isFlagEnabled(NEUDebugFlag.METAL) || positions.size() == 0) { + private static String getFriendlyEvaluatedPositions() { + if (!NEUDebugLogger.isFlagEnabled(NEUDebugFlag.METAL) || evaluatedPlayerPositions.size() == 0) { return ""; } StringBuilder sb = new StringBuilder(); sb.append("\n"); - for (BlockPos blockPos : positions.keySet()) { - sb.append("Absolute: " + blockPos.toString()); + for (Vec3Comparable vec : evaluatedPlayerPositions.keySet()) { + sb.append("Absolute: " + vec.toString()); if (minesCenter != Vec3i.NULL_VECTOR) { - BlockPos relativeOffset = blockPos.subtract(minesCenter); + BlockPos positionBlockPos = new BlockPos(vec); + BlockPos relativeOffset = positionBlockPos.subtract(minesCenter); sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); } sb.append(" Distance: "); - sb.append(positions.get(blockPos)); + sb.append(evaluatedPlayerPositions.get(vec)); sb.append("\n"); } @@ -240,101 +292,8 @@ public class CrystalMetalDetectorSolver { return sb.toString(); } - public static void logDiagnosticData(boolean outputAlways) { - if (SBInfo.getInstance().getLocation() == null) { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] This command is not available outside SkyBlock")); - return; - } - - if (!NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled) - { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + - "[NEU] Metal Detector Solver is not enabled.")); - return; - } - - if (!outputAlways && !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.METAL)) { - return; - } - - boolean originalDebugFlag = !NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.add(NEUDebugFlag.METAL); - - StringBuilder diagsMessage = new StringBuilder(); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Mines Center: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((minesCenter == Vec3i.NULL_VECTOR) ? "" : minesCenter.toString()); - diagsMessage.append("\n"); - - diagsMessage.append("\n"); - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Previous Player Position: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((prevPlayerPos == null) ? "" : prevPlayerPos.toString()); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Previous Distance To Treasure: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((prevDistToTreasure == 0) ? "" : prevDistToTreasure); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Last Solution Invalid Position: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append((blockPosIfLastSolutionInvalid == null) ? "" : blockPosIfLastSolutionInvalid.toString()); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Current Possible Blocks: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(possibleBlocks.size()); - diagsMessage.append(getFriendlyBlockPositions(possibleBlocks)); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Evaluated player positions: "); - diagsMessage.append(EnumChatFormatting.WHITE); - diagsMessage.append(evaluatedPlayerPositions.size()); - diagsMessage.append(getFriendlyEvaluatedPositions(evaluatedPlayerPositions)); - diagsMessage.append("\n"); - - diagsMessage.append(EnumChatFormatting.AQUA); - diagsMessage.append("Chest locations not on known list:\n"); - diagsMessage.append(EnumChatFormatting.WHITE); - if (minesCenter != Vec3i.NULL_VECTOR) { - HashSet locationsNotOnKnownList = openedChestPositions - .stream() - .filter(block -> !knownChestOffsets.contains(block.subtract(minesCenter).toLong())) - .map(block -> block.subtract(minesCenter)) - .collect(Collectors.toCollection(HashSet::new)); - if (locationsNotOnKnownList.size() > 0) { - for (BlockPos blockPos : locationsNotOnKnownList) { - diagsMessage.append(String.format( - "%dL,\t\t// x=%d, y=%d, z=%d", - blockPos.toLong(), - blockPos.getX(), - blockPos.getY(), - blockPos.getZ() - )); - } - } - } else { - diagsMessage.append(""); - } - - NEUDebugLogger.log(NEUDebugFlag.METAL, diagsMessage.toString()); - - if (!originalDebugFlag) { - NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.remove(NEUDebugFlag.METAL); - } - } - public static void resetSolution(Boolean chestFound) { if (chestFound) { - blockPosIfLastSolutionInvalid = null; prevPlayerPos = null; prevDistToTreasure = 0; if (possibleBlocks.size() == 1) { @@ -345,15 +304,18 @@ public class CrystalMetalDetectorSolver { chestRecentlyFound = chestFound; possibleBlocks.clear(); evaluatedPlayerPositions.clear(); + previousState = currentState; + currentState = SolutionState.NOT_STARTED; } public static void initWorld() { minesCenter = Vec3i.NULL_VECTOR; visitKeeperMessagePrinted = false; - blockPosIfLastSolutionInvalid = null; openedChestPositions.clear(); + chestLastFoundMillis = 0; prevDistToTreasure = 0; prevPlayerPos = null; + currentState = SolutionState.NOT_STARTED; resetSolution(false); } @@ -377,15 +339,14 @@ public class CrystalMetalDetectorSolver { } } - private static void locateMinesCenterIfNeeded() { + private static boolean locateMinesCenterIfNeeded() { if (minesCenter != Vec3i.NULL_VECTOR) { - return; + return false; } List keeperEntities = mc.theWorld.getEntities(EntityArmorStand.class, (entity) -> { if (!entity.hasCustomName()) return false; - if (entity.getCustomNameTag().contains(KEEPER_OF_STRING)) return true; - return false; + return entity.getCustomNameTag().contains(KEEPER_OF_STRING); }); if (keeperEntities.size() == 0) { @@ -394,7 +355,7 @@ public class CrystalMetalDetectorSolver { "[NEU] Approach a Keeper while holding the metal detector to enable faster treasure hunting.")); visitKeeperMessagePrinted = true; } - return; + return false; } EntityArmorStand keeperEntity = keeperEntities.get(0); @@ -407,6 +368,11 @@ public class CrystalMetalDetectorSolver { EnumChatFormatting.WHITE + minesCenter.toString()); mc.thePlayer.addChatMessage(new ChatComponentText( EnumChatFormatting.YELLOW + "[NEU] Faster treasure hunting is now enabled based on Keeper location.")); + return true; + } + + public static void setMinesCenter(BlockPos center) { + minesCenter = center; } private static double round(double value, int precision) { @@ -414,42 +380,174 @@ public class CrystalMetalDetectorSolver { return (double) Math.round(value * scale) / scale; } - private static void checkAndDisplaySolutionState() { + private static void updateSolutionState() { + previousState = currentState; + if (possibleBlocks.size() == 0) { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + "[NEU] Failed to find a solution.")); - logDiagnosticData(false); - resetSolution(false); + currentState = SolutionState.FAILED; return; } - checkForSingleKnownLocationMatch(); - if (possibleBlocks.size() > 1) { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "[NEU] Need another position to find solution. Possible blocks: " + possibleBlocks.size())); - } else { - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "[NEU] Found solution.")); + if (possibleBlocks.size() == 1) { + currentState = SolutionState.FOUND; + return; } + + // Narrow solutions using known locations if the mines center is known + if (minesCenter.equals(BlockPos.NULL_VECTOR) || debugDoNotUseCenter) { + currentState = SolutionState.MULTIPLE; + return; + } + + HashSet temp = + possibleBlocks.stream() + .filter(block -> knownChestOffsets.contains(block.subtract(minesCenter).toLong())) + .collect(Collectors.toCollection(HashSet::new)); + if (temp.size() == 0) { + currentState = SolutionState.MULTIPLE; + return; + } + + if (temp.size() == 1) { + possibleBlocks = temp; + currentState = SolutionState.FOUND_KNOWN; + return; + + } + + currentState = SolutionState.MULTIPLE_KNOWN; + } + + public static BlockPos getSolution() { + if (CrystalMetalDetectorSolver.possibleBlocks.size() != 1) { + return BlockPos.ORIGIN; + } + + return CrystalMetalDetectorSolver.possibleBlocks.stream().iterator().next(); } - private static Vec3 getPlayerPos() { - return new Vec3( + private static Vec3Comparable getPlayerPosAdjustedForEyeHeight() { + return new Vec3Comparable( mc.thePlayer.posX, mc.thePlayer.posY + (mc.thePlayer.getEyeHeight() - mc.thePlayer.getDefaultEyeHeight()), mc.thePlayer.posZ ); } - private static boolean treasureAllowed(BlockPos pos) { - boolean airAbove = mc.theWorld. - getBlockState(pos.add(0, 1, 0)).getBlock().getRegistryName().equals("minecraft:air"); - boolean allowedBlockType = mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:gold_block") || + static boolean isKnownOffset(BlockPos pos) { + return knownChestOffsets.contains(pos.subtract(minesCenter).toLong()); + } + + static boolean isAllowedBlockType(BlockPos pos) { + return mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:gold_block") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:prismarine") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:chest") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:stained_glass") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:stained_glass_pane") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:wool") || mc.theWorld.getBlockState(pos).getBlock().getRegistryName().equals("minecraft:stained_hardened_clay"); - boolean knownOffset = knownChestOffsets.contains(pos.subtract(minesCenter).toLong()); - return airAbove & (knownOffset | allowedBlockType); + } + + static boolean isAirAbove(BlockPos pos) { + return mc.theWorld. + getBlockState(pos.add(0, 1, 0)).getBlock().getRegistryName().equals("minecraft:air"); + } + + private static boolean treasureAllowed(BlockPos pos) { + boolean airAbove = isAirAbove(pos); + boolean allowedBlockType = isAllowedBlockType(pos); + return isKnownOffset(pos) || (airAbove && allowedBlockType); + } + + static private String getDiagnosticMessage() { + StringBuilder diagsMessage = new StringBuilder(); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Mines Center: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((minesCenter.equals(Vec3i.NULL_VECTOR)) ? "" : minesCenter.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Current Solution State: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(currentState.name()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Previous Solution State: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(previousState.name()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Previous Player Position: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((prevPlayerPos == null) ? "" : prevPlayerPos.toString()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Previous Distance To Treasure: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append((prevDistToTreasure == 0) ? "" : prevDistToTreasure); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Current Possible Blocks: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(possibleBlocks.size()); + diagsMessage.append(getFriendlyBlockPositions(possibleBlocks)); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Evaluated player positions: "); + diagsMessage.append(EnumChatFormatting.WHITE); + diagsMessage.append(evaluatedPlayerPositions.size()); + diagsMessage.append(getFriendlyEvaluatedPositions()); + diagsMessage.append("\n"); + + diagsMessage.append(EnumChatFormatting.AQUA); + diagsMessage.append("Chest locations not on known list:\n"); + diagsMessage.append(EnumChatFormatting.WHITE); + if (minesCenter != Vec3i.NULL_VECTOR) { + HashSet locationsNotOnKnownList = openedChestPositions + .stream() + .filter(block -> !knownChestOffsets.contains(block.subtract(minesCenter).toLong())) + .map(block -> block.subtract(minesCenter)) + .collect(Collectors.toCollection(HashSet::new)); + if (locationsNotOnKnownList.size() > 0) { + for (BlockPos blockPos : locationsNotOnKnownList) { + diagsMessage.append(String.format( + "%dL,\t\t// x=%d, y=%d, z=%d", + blockPos.toLong(), + blockPos.getX(), + blockPos.getY(), + blockPos.getZ() + )); + } + } + } else { + diagsMessage.append(""); + } + + return diagsMessage.toString(); + } + + public static void logDiagnosticData(boolean outputAlways) { + if (!SBInfo.getInstance().checkForSkyblockLocation()) { + return; + } + + if (!NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled) + { + mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.RED + + "[NEU] Metal Detector Solver is not enabled.")); + return; + } + + boolean metalDebugFlagSet = NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.METAL); + if (outputAlways || metalDebugFlagSet) { + NEUDebugLogger.logAlways(getDiagnosticMessage()); + } } } -- cgit From 5e7a8a02fe836c1e635faf067fbe8913549bc0e0 Mon Sep 17 00:00:00 2001 From: Walker Selby Date: Sun, 27 Mar 2022 12:13:08 -0500 Subject: Add NBT Data to Profile Viewer Tabs and XP Bars (#94) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Fixed what hypxiel broke in dungeons * Added Longswords to Tools Tab (Itemlist) * Set max cata level to 70 * ups * why is it now E * YEP the cap is 99 not 70 * long cata xp BatChest + remove crash check because hypixel profile is null and i havent fixed that yet * Added checks for chat messages for dungeon win overlay - i think ive got them all but 4Shrug * Added an option to alert you if you put something for too much onto ah (default 50%) + Lowest bin alert triggers if lowest bin isnt found * IMPORTANT * fix dungeon page in /pv * show minions from coop in /pv * (BREAKING CHANGE) remove whitespace in changelog * fix crash with minion check * initial draft * tooltip * important changes * progress related things progress bar for community goals total collected points and personal goals display * only show bingo tab on Bingo profiles * make goals display gold when completed * Added MM7 button to dungeon preset * wart go not stonks * fix percents and added stack size * use code nopo in fortnite shop — Today at 17:29 The capitalisation * config option for always showing bingo page * 🙂 * movble pv tabs * 2.1.md 🙂 * oops * der Kabel * import * Added powder amount to level up perk * Made it only show if the perk isnt maxed / level 0 * fix /pv crash when pet is not in repo * 2.1.md 🙂 * web editor my beloved * Add NBT Data to Profile Viewer Tabs and XP Bars Add NBT Data Support for Resources in Tabs Add NBT Data Support for Resources in XP Bars Optimize renderTabs function Optimize mouseClicked Function Optimize ProfileViewerPage Enum * Update Style Co-authored-by: NopoTheGamer <40329022+NopoTheGamer@users.noreply.github.com> * Update GuiProfileViewer Rebased on pvbingo Updated formatting with IntelliJ reformatting tool (hopefully that matches now, let me know if you want me to try again but I think I did it right) * dungeons and pv bingo tab (#93) * add custom skull loading back (#96) * Refactor Hollows solvers, add tests, add Vec3Comparable, fix bugs (#95) * Rebase on master * Fix conflicts and rebase on master Add changes to change log * Oops Co-authored-by: nopo Co-authored-by: jani270 Co-authored-by: Lulonaut Co-authored-by: NopoTheGamer <40329022+NopoTheGamer@users.noreply.github.com> Co-authored-by: Lulonaut <67191924+Lulonaut@users.noreply.github.com> Co-authored-by: CraftyOldMiner <85420839+CraftyOldMiner@users.noreply.github.com> --- .../miscfeatures/CrystalMetalDetectorSolver.java | 450 ++++++++------------- 1 file changed, 176 insertions(+), 274 deletions(-) (limited to 'src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java') diff --git a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java index f43ebda2..27c334ad 100644 --- a/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java +++ b/src/main/java/io/github/moulberry/notenoughupdates/miscfeatures/CrystalMetalDetectorSolver.java @@ -1,7 +1,6 @@ package io.github.moulberry.notenoughupdates.miscfeatures; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; -import io.github.moulberry.notenoughupdates.core.util.Vec3Comparable; import io.github.moulberry.notenoughupdates.core.util.render.RenderUtils; import io.github.moulberry.notenoughupdates.options.customtypes.NEUDebugFlag; import io.github.moulberry.notenoughupdates.util.NEUDebugLogger; @@ -12,6 +11,7 @@ import net.minecraft.util.BlockPos; import net.minecraft.util.ChatComponentText; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.IChatComponent; +import net.minecraft.util.Vec3; import net.minecraft.util.Vec3i; import java.util.Arrays; @@ -22,35 +22,25 @@ import java.util.List; import java.util.stream.Collectors; public class CrystalMetalDetectorSolver { - enum SolutionState { - NOT_STARTED, - MULTIPLE, - MULTIPLE_KNOWN, - FOUND, - FOUND_KNOWN, - FAILED, - INVALID, - } - private static final Minecraft mc = Minecraft.getMinecraft(); - private static Vec3Comparable prevPlayerPos; - private static double prevDistToTreasure; + private static BlockPos prevPlayerPos; + private static double prevDistToTreasure = 0; private static HashSet possibleBlocks = new HashSet<>(); - private static final HashMap evaluatedPlayerPositions = new HashMap<>(); - private static boolean chestRecentlyFound; - private static long chestLastFoundMillis; + private static final HashMap evaluatedPlayerPositions = new HashMap<>(); + private static BlockPos blockPosIfLastSolutionInvalid; + private static Boolean chestRecentlyFound = false; + private static long chestLastFoundMillis = 0; private static final HashSet openedChestPositions = new HashSet<>(); // Keeper and Mines of Divan center location info private static Vec3i minesCenter; - private static boolean debugDoNotUseCenter = false; - private static boolean visitKeeperMessagePrinted; - private static final String KEEPER_OF_STRING = "Keeper of "; - private static final String DIAMOND_STRING = "diamond"; - private static final String LAPIS_STRING = "lapis"; - private static final String EMERALD_STRING = "emerald"; - private static final String GOLD_STRING = "gold"; + private static boolean visitKeeperMessagePrinted = false; + private static String KEEPER_OF_STRING = "Keeper of "; + private static String DIAMOND_STRING = "diamond"; + private static String LAPIS_STRING = "lapis"; + private static String EMERALD_STRING = "emerald"; + private static String GOLD_STRING = "gold"; private static final HashMap keeperOffsets = new HashMap() {{ put(DIAMOND_STRING, new Vec3i(33,0,3)); put(LAPIS_STRING, new Vec3i(-33,0,-3)); @@ -104,14 +94,6 @@ public class CrystalMetalDetectorSolver { -9896946827286L // x=-37, y=-21, z=-22 )); - static Predicate treasureAllowedPredicate = CrystalMetalDetectorSolver::treasureAllowed; - static SolutionState currentState = SolutionState.NOT_STARTED; - static SolutionState previousState = SolutionState.NOT_STARTED; - - public interface Predicate { - boolean check(BlockPos blockPos); - } - public static void process(IChatComponent message) { if (SBInfo.getInstance().getLocation() == null || !NotEnoughUpdates.INSTANCE.config.mining.metalDetectorEnabled || @@ -120,7 +102,7 @@ public class CrystalMetalDetectorSolver { return; } - boolean centerNewlyDiscovered = locateMinesCenterIfNeeded(); + locateMinesCenterIfNeeded(); double distToTreasure = Double.parseDouble(message .getUnformattedText() @@ -140,64 +122,20 @@ public class CrystalMetalDetectorSolver { chestRecentlyFound = false; } - SolutionState originalState = currentState; - int originalCount = possibleBlocks.size(); - Vec3Comparable adjustedPlayerPos = getPlayerPosAdjustedForEyeHeight(); - findPossibleSolutions(distToTreasure, adjustedPlayerPos, centerNewlyDiscovered); - if (currentState != originalState || originalCount != possibleBlocks.size()) { - switch (currentState) { - case FOUND_KNOWN: - NEUDebugLogger.log(NEUDebugFlag.METAL, "Known location identified."); - // falls through - case FOUND: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + "[NEU] Found solution.")); - if (NotEnoughUpdates.INSTANCE.config.hidden.debugFlags.contains(NEUDebugFlag.METAL) && - (previousState == SolutionState.INVALID || previousState == SolutionState.FAILED)) { - NEUDebugLogger.log(NEUDebugFlag.METAL, - EnumChatFormatting.AQUA + "Solution coordinates: " + - EnumChatFormatting.WHITE + possibleBlocks.iterator().next().toString()); - } - break; - case INVALID: - mc.thePlayer.addChatMessage(new ChatComponentText( - EnumChatFormatting.RED + "[NEU] Previous solution is invalid.")); - logDiagnosticData(false); - resetSolution(false); - break; - case FAILED: - mc.thePlayer.addChatMessage(new ChatComponentText( - EnumChatFormatting.RED + "[NEU] Failed to find a solution.")); - logDiagnosticData(false); - resetSolution(false); - break; - case MULTIPLE_KNOWN: - NEUDebugLogger.log(NEUDebugFlag.METAL, "Multiple known locations identified:"); - // falls through - case MULTIPLE: - mc.thePlayer.addChatMessage(new ChatComponentText(EnumChatFormatting.YELLOW + - "[NEU] Need another position to find solution. Possible blocks: " + possibleBlocks.size())); - break; - default: - throw new IllegalStateException("Metal detector is in invalid state"); - } - } - } - - static void findPossibleSolutions(double distToTreasure, Vec3Comparable playerPos, boolean centerNewlyDiscovered) { - if (prevDistToTreasure == distToTreasure && prevPlayerPos.equals(playerPos) && - !evaluatedPlayerPositions.containsKey(playerPos)) { - evaluatedPlayerPositions.put(playerPos, distToTreasure); + if (prevDistToTreasure == distToTreasure && + prevPlayerPos.equals(mc.thePlayer.getPosition()) && + !evaluatedPlayerPositions.keySet().contains(mc.thePlayer.getPosition())) { if (possibleBlocks.size() == 0) { + evaluatedPlayerPositions.put(mc.thePlayer.getPosition(), distToTreasure); for (int zOffset = (int) Math.floor(-distToTreasure); zOffset <= Math.ceil(distToTreasure); zOffset++) { for (int y = 65; y <= 75; y++) { double calculatedDist = 0; int xOffset = 0; while (calculatedDist < distToTreasure) { - BlockPos pos = new BlockPos(Math.floor(playerPos.xCoord) + xOffset, - y, Math.floor(playerPos.zCoord) + zOffset - ); - calculatedDist = playerPos.distanceTo(new Vec3Comparable(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && treasureAllowedPredicate.check(pos)) { + BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) + xOffset, + y, Math.floor(mc.thePlayer.posZ) + zOffset); + calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); + if (round(calculatedDist, 1) == distToTreasure && treasureAllowed(pos)) { possibleBlocks.add(pos); } xOffset++; @@ -205,11 +143,10 @@ public class CrystalMetalDetectorSolver { xOffset = 0; calculatedDist = 0; while (calculatedDist < distToTreasure) { - BlockPos pos = new BlockPos(Math.floor(playerPos.xCoord) - xOffset, - y, Math.floor(playerPos.zCoord) + zOffset - ); - calculatedDist = playerPos.distanceTo(new Vec3Comparable(pos).addVector(0D, 1D, 0D)); - if (round(calculatedDist, 1) == distToTreasure && treasureAllowedPredicate.check(pos)) { + BlockPos pos = new BlockPos(Math.floor(mc.thePlayer.posX) - xOffset, + y, Math.floor(mc.thePlayer.posZ) + zOffset); + calculatedDist = getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)); + if (round(calculatedDist, 1) == distToTreasure && treasureAllowed(pos)) { possibleBlocks.add(pos); } xOffset++; @@ -217,33 +154,48 @@ public class CrystalMetalDetectorSolver { } } - updateSolutionState(); + checkAndDisplaySolutionState(); } else if (possibleBlocks.size() != 1) { + evaluatedPlayerPositions.put(mc.thePlayer.getPosition().getImmutable(), distToTreasure); HashSet temp = new HashSet<>(); for (BlockPos pos : possibleBlocks) { - if (round(playerPos.distanceTo(new Vec3Comparable(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { + if (round(getPlayerPos().distanceTo(new Vec3(pos).addVector(0D, 1D, 0D)), 1) == distToTreasure) { temp.add(pos); } } possibleBlocks = temp; - updateSolutionState(); + checkAndDisplaySolutionState(); } else { BlockPos pos = possibleBlocks.iterator().next(); - if (Math.abs(distToTreasure - (playerPos.distanceTo(new Vec3Comparable(pos)))) > 5) { - currentState = SolutionState.INVALID; + if (Math.abs(distToTreasure - (getPlayerPos().distanceTo(new Vec3(pos)))) > 5) { + mc.thePlayer.addChatMessage(new ChatComponentText( + EnumChatFormatting.RED + "[NEU] Previous solution is invalid.")); + blockPosIfLastSolutionInvalid = pos.getImmutable(); + logDiagnosticData(false); + resetSolution(false); } } - } else if (centerNewlyDiscovered && possibleBlocks.size() > 1) { - updateSolutionState(); } - prevPlayerPos = playerPos; + prevPlayerPos = mc.thePlayer.getPosition().getImmutable(); prevDistToTreasure = distToTreasure; } - public static void setDebugDoNotUseCenter(boolean val) { - debugDoNotUseCenter = val; + private static void checkForSingleKnownLocationMatch() { + if (minesCenter == BlockPos.NULL_VECTOR || possibleBlocks.size() < 2) { + return; + } + + HashSet temp = possibleBlocks.stream() + .filter(block -> knownChestOffsets.contains(block.subtract(minesCenter).toLong())) + .collect(Collectors.toCollection(HashSet::new)); + if (temp.size() == 1) { + possibleBlocks = temp; + NEUDebugLogger.log(NEUDebugFlag.METAL, "Known location identified."); + } else if (temp.size() > 1) { + NEUDebugLogger.log(NEUDebugFlag.METAL, temp.size() + " known locations identified:"); + } } private static String getFriendlyBlockPositions(Collection positions) { @@ -254,13 +206,10 @@ public class CrystalMetalDetectorSolver { StringBuilder sb = new StringBuilder(); sb.append("\n"); for (BlockPos blockPos : positions) { - sb.append("Absolute: "); - sb.append(blockPos.toString()); + sb.append("Absolute: " + blockPos.toString()); if (minesCenter != Vec3i.NULL_VECTOR) { BlockPos relativeOffset = blockPos.subtract(minesCenter); - sb.append(", Relative: "); - sb.append(relativeOffset.toString() ); - sb.append(" (" + relativeOffset.toLong() + ")"); + sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); } sb.append("\n"); } @@ -268,23 +217,22 @@ public class CrystalMetalDetectorSolver { return sb.toString(); } - private static String getFriendlyEvaluatedPositions() { - if (!NEUDebugLogger.isFlagEnabled(NEUDebugFlag.METAL) || evaluatedPlayerPositions.size() == 0) { + private static String getFriendlyEvaluatedPositions(HashMap positions) { + if (!NEUDebugLogger.isFlagEnabled(NEUDebugFlag.METAL) || positions.size() == 0) { return ""; } StringBuilder sb = new StringBuilder(); sb.append("\n"); - for (Vec3Comparable vec : evaluatedPlayerPositions.keySet()) { - sb.append("Absolute: " + vec.toString()); + for (BlockPos blockPos : positions.keySet()) { + sb.append("Absolute: " + blockPos.toString()); if (minesCenter != Vec3i.NULL_VECTOR) { - BlockPos positionBlockPos = new BlockPos(vec); - BlockPos relativeOffset = positionBlockPos.subtract(minesCenter); + BlockPos relativeOffset = blockPos.subtract(minesCenter); sb.append(", Relative: " + relativeOffset.toString() + " (" + relativeOffset.toLong() + ")"); } sb.append(" Distance: "); - sb.append(evaluatedPlayerPositions.get(vec)); + sb.append(positions.get(blockPos)); sb.appe