diff --git a/src/main/java/baritone/behavior/impl/MineBehavior.java b/src/main/java/baritone/behavior/impl/MineBehavior.java index 185cacd2..bbd1c594 100644 --- a/src/main/java/baritone/behavior/impl/MineBehavior.java +++ b/src/main/java/baritone/behavior/impl/MineBehavior.java @@ -19,8 +19,10 @@ package baritone.behavior.impl; import baritone.api.event.events.TickEvent; import baritone.behavior.Behavior; +import baritone.chunk.CachedChunk; import baritone.chunk.ChunkPacker; import baritone.chunk.WorldProvider; +import baritone.chunk.WorldScanner; import baritone.pathing.goals.Goal; import baritone.pathing.goals.GoalComposite; import baritone.pathing.goals.GoalTwoBlocks; @@ -55,21 +57,7 @@ public class MineBehavior extends Behavior { if (mining == null) { return; } - List locs = new ArrayList<>(); - for (String m : mining) { - locs.addAll(WorldProvider.INSTANCE.getCurrentWorld().cache.getLocationsOf(m, 1, 1)); - } - BlockPos playerFeet = playerFeet(); - locs.sort(Comparator.comparingDouble(playerFeet::distanceSq)); - - // remove any that are within loaded chunks that aren't actually what we want - locs.removeAll(locs.stream() - .filter(pos -> !(world().getChunk(pos) instanceof EmptyChunk)) - .filter(pos -> !mining.contains(ChunkPacker.blockToString(BlockStateInterface.get(pos).getBlock()).toLowerCase())) - .collect(Collectors.toList())); - if (locs.size() > 30) { - locs = locs.subList(0, 30); - } + List locs = scanFor(mining, 64); if (locs.isEmpty()) { displayChatMessageRaw("No locations for " + mining + " known, cancelling"); cancel(); @@ -79,6 +67,31 @@ public class MineBehavior extends Behavior { PathingBehavior.INSTANCE.path(); } + public static List scanFor(List mining, int max) { + List locs = new ArrayList<>(); + List uninteresting = new ArrayList<>(); + for (String m : mining) { + if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(ChunkPacker.stringToBlock(m))) { + locs.addAll(WorldProvider.INSTANCE.getCurrentWorld().cache.getLocationsOf(m, 1, 1)); + } else { + uninteresting.add(m); + } + } + locs.addAll(WorldScanner.INSTANCE.scanLoadedChunks(uninteresting, max)); + BlockPos playerFeet = MineBehavior.INSTANCE.playerFeet(); + locs.sort(Comparator.comparingDouble(playerFeet::distanceSq)); + + // remove any that are within loaded chunks that aren't actually what we want + locs.removeAll(locs.stream() + .filter(pos -> !(MineBehavior.INSTANCE.world().getChunk(pos) instanceof EmptyChunk)) + .filter(pos -> !mining.contains(ChunkPacker.blockToString(BlockStateInterface.get(pos).getBlock()).toLowerCase())) + .collect(Collectors.toList())); + if (locs.size() > max) { + locs = locs.subList(0, max); + } + return locs; + } + public void mine(String... mining) { this.mining = mining == null || mining.length == 0 ? null : new ArrayList<>(Arrays.asList(mining)); } diff --git a/src/main/java/baritone/chunk/WorldScanner.java b/src/main/java/baritone/chunk/WorldScanner.java new file mode 100644 index 00000000..62e20944 --- /dev/null +++ b/src/main/java/baritone/chunk/WorldScanner.java @@ -0,0 +1,92 @@ +/* + * This file is part of Baritone. + * + * Baritone is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Baritone is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Baritone. If not, see . + */ + +package baritone.chunk; + +import baritone.utils.Helper; +import net.minecraft.block.Block; +import net.minecraft.block.state.IBlockState; +import net.minecraft.client.multiplayer.ChunkProviderClient; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.chunk.BlockStateContainer; +import net.minecraft.world.chunk.Chunk; +import net.minecraft.world.chunk.storage.ExtendedBlockStorage; + +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Collectors; + +public enum WorldScanner implements Helper { + INSTANCE; + + public List scanLoadedChunks(List blockTypes, int max) { + List asBlocks = blockTypes.stream().map(ChunkPacker::stringToBlock).collect(Collectors.toList()); + if (asBlocks.contains(null)) { + throw new IllegalStateException("Invalid block name should have been caught earlier: " + blockTypes.toString()); + } + LinkedList res = new LinkedList<>(); + ChunkProviderClient chunkProvider = world().getChunkProvider(); + + int playerChunkX = playerFeet().getX() >> 4; + int playerChunkZ = playerFeet().getZ() >> 4; + + int searchRadius = 0; + while (true) { + boolean allUnloaded = true; + for (int xoff = -searchRadius; xoff <= searchRadius; xoff++) { + for (int zoff = -searchRadius; zoff <= searchRadius; zoff++) { + int distance = xoff * xoff + zoff * zoff; + if (distance != searchRadius) { + continue; + } + int chunkX = xoff + playerChunkX; + int chunkZ = zoff + playerChunkZ; + Chunk chunk = chunkProvider.getLoadedChunk(chunkX, chunkZ); + if (chunk == null) { + continue; + } + allUnloaded = false; + ExtendedBlockStorage[] chunkInternalStorageArray = chunk.getBlockStorageArray(); + for (int y0 = 0; y0 < 16; y0++) { + ExtendedBlockStorage extendedblockstorage = chunkInternalStorageArray[y0]; + if (extendedblockstorage == null) { + continue; + } + BlockStateContainer bsc = extendedblockstorage.getData(); + for (int x = 0; x < 16; x++) { + for (int y = 0; y < 16; y++) { + for (int z = 0; z < 16; z++) { + IBlockState state = bsc.get(x, y, z); + if (asBlocks.contains(state.getBlock())) { + res.add(new BlockPos(chunkX * 16 + x, y0 * 16 + y, chunkZ * 16 + z)); + } + } + } + } + } + } + } + if (allUnloaded) { + return res; + } + if (res.size() >= max) { + return res; + } + searchRadius++; + } + } +} diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index 5bc75d3f..16dabf4f 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -38,10 +38,8 @@ import baritone.utils.pathing.BetterBlockPos; import net.minecraft.block.Block; import net.minecraft.entity.Entity; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.chunk.EmptyChunk; import java.util.*; -import java.util.stream.Collectors; public class ExampleBaritoneControl extends Behavior { public static ExampleBaritoneControl INSTANCE = new ExampleBaritoneControl(); @@ -188,9 +186,16 @@ public class ExampleBaritoneControl extends Behavior { return; } if (msg.toLowerCase().startsWith("mine")) { - String blockType = msg.toLowerCase().substring(4).trim(); - MineBehavior.INSTANCE.mine(blockType.split(" ")); - displayChatMessageRaw("Started mining blocks of type " + Arrays.toString(blockType.split(" "))); + String[] blockTypes = msg.toLowerCase().substring(4).trim().split(" "); + for (String s : blockTypes) { + if (ChunkPacker.stringToBlock(s) == null) { + displayChatMessageRaw(s + " isn't a valid block name"); + event.cancel(); + return; + } + } + MineBehavior.INSTANCE.mine(blockTypes); + displayChatMessageRaw("Started mining blocks of type " + Arrays.toString(blockTypes)); event.cancel(); return; } @@ -235,21 +240,14 @@ public class ExampleBaritoneControl extends Behavior { String mining = waypointType; //displayChatMessageRaw("Not a valid tag. Tags are: " + Arrays.asList(Waypoint.Tag.values()).toString().toLowerCase()); event.cancel(); - List locs = new ArrayList<>(WorldProvider.INSTANCE.getCurrentWorld().cache.getLocationsOf(mining, 1, 1)); - if (locs.isEmpty()) { + if (ChunkPacker.stringToBlock(mining) == null) { displayChatMessageRaw("No locations for " + mining + " known, cancelling"); return; } - BlockPos playerFeet = playerFeet(); - locs.sort(Comparator.comparingDouble(playerFeet::distanceSq)); - - // remove any that are within loaded chunks that aren't actually what we want - locs.removeAll(locs.stream() - .filter(pos -> !(world().getChunk(pos) instanceof EmptyChunk)) - .filter(pos -> !ChunkPacker.blockToString(BlockStateInterface.get(pos).getBlock()).equalsIgnoreCase(mining)) - .collect(Collectors.toList())); - if (locs.size() > 30) { - locs = locs.subList(0, 30); + List locs = MineBehavior.scanFor(Arrays.asList(mining), 64); + if (locs.isEmpty()) { + displayChatMessageRaw("No locations for " + mining + " known, cancelling"); + return; } PathingBehavior.INSTANCE.setGoal(new GoalComposite(locs.stream().map(GoalGetToBlock::new).toArray(Goal[]::new))); PathingBehavior.INSTANCE.path();