diff --git a/src/main/java/baritone/chunk/CachedChunk.java b/src/main/java/baritone/chunk/CachedChunk.java index f4d56fd2..fca8034d 100644 --- a/src/main/java/baritone/chunk/CachedChunk.java +++ b/src/main/java/baritone/chunk/CachedChunk.java @@ -21,8 +21,10 @@ import baritone.utils.pathing.IBlockTypeAccess; import baritone.utils.pathing.PathingBlockType; import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; +import net.minecraft.init.Blocks; +import net.minecraft.util.math.BlockPos; -import java.util.BitSet; +import java.util.*; /** * @author Brady @@ -30,6 +32,32 @@ import java.util.BitSet; */ public final class CachedChunk implements IBlockTypeAccess { + public static final Set BLOCKS_TO_KEEP_TRACK_OF = Collections.unmodifiableSet(new HashSet() {{ + add(Blocks.DIAMOND_ORE); + add(Blocks.DIAMOND_BLOCK); + //add(Blocks.COAL_ORE); + add(Blocks.COAL_BLOCK); + //add(Blocks.IRON_ORE); + add(Blocks.IRON_BLOCK); + //add(Blocks.GOLD_ORE); + add(Blocks.GOLD_BLOCK); + add(Blocks.EMERALD_ORE); + add(Blocks.EMERALD_BLOCK); + + add(Blocks.ENDER_CHEST); + add(Blocks.FURNACE); + add(Blocks.CHEST); + add(Blocks.END_PORTAL); + add(Blocks.END_PORTAL_FRAME); + add(Blocks.MOB_SPAWNER); + // TODO add all shulker colors + add(Blocks.PORTAL); + add(Blocks.HOPPER); + add(Blocks.BEACON); + add(Blocks.BREWING_STAND); + add(Blocks.SKULL); + }}); + /** * The size of the chunk data in bits. Equal to 16 KiB. *

@@ -66,7 +94,9 @@ public final class CachedChunk implements IBlockTypeAccess { private final int[] heightMap; - CachedChunk(int x, int z, BitSet data, String[] overview) { + private final Map> specialBlockLocations; + + CachedChunk(int x, int z, BitSet data, String[] overview, Map> specialBlockLocations) { validateSize(data); this.x = x; @@ -74,6 +104,7 @@ public final class CachedChunk implements IBlockTypeAccess { this.data = data; this.overview = overview; this.heightMap = new int[256]; + this.specialBlockLocations = specialBlockLocations; calculateHeightMap(); } @@ -82,11 +113,7 @@ public final class CachedChunk implements IBlockTypeAccess { int internalPos = z << 4 | x; if (heightMap[internalPos] == y) { // we have this exact block, it's a surface block - String name = overview[internalPos]; - if (!name.contains(":")) { - name = "minecraft:" + name; - } - IBlockState state = Block.getBlockFromName(name).getDefaultState(); + IBlockState state = ChunkPacker.stringToBlock(overview[internalPos]).getDefaultState(); //System.out.println("Saying that " + x + "," + y + "," + z + " is " + state); return state; } @@ -118,6 +145,21 @@ public final class CachedChunk implements IBlockTypeAccess { return overview; } + public final Map> getRelativeBlocks() { + return specialBlockLocations; + } + + public final LinkedList getAbsoluteBlocks(String blockType) { + if (specialBlockLocations.get(blockType) == null) { + return null; + } + LinkedList res = new LinkedList<>(); + for (BlockPos pos : specialBlockLocations.get(blockType)) { + res.add(new BlockPos(pos.getX() + x * 16, pos.getY(), pos.getZ() + z * 16)); + } + return res; + } + /** * @return Returns the raw packed chunk data as a byte array */ diff --git a/src/main/java/baritone/chunk/CachedRegion.java b/src/main/java/baritone/chunk/CachedRegion.java index d8996fa3..b9e0e4e0 100644 --- a/src/main/java/baritone/chunk/CachedRegion.java +++ b/src/main/java/baritone/chunk/CachedRegion.java @@ -19,12 +19,13 @@ package baritone.chunk; import baritone.utils.pathing.IBlockTypeAccess; import net.minecraft.block.state.IBlockState; +import net.minecraft.util.math.BlockPos; import java.io.*; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.BitSet; +import java.util.*; import java.util.function.Consumer; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; @@ -77,6 +78,25 @@ public final class CachedRegion implements IBlockTypeAccess { return null; } + public final LinkedList getLocationsOf(String block) { + LinkedList res = new LinkedList<>(); + for (int chunkX = 0; chunkX < 32; chunkX++) { + for (int chunkZ = 0; chunkZ < 32; chunkZ++) { + if (chunks[chunkX][chunkZ] == null) { + continue; + } + List locs = chunks[chunkX][chunkZ].getAbsoluteBlocks(block); + if (locs == null) { + continue; + } + for (BlockPos pos : locs) { + res.add(pos); + } + } + } + return res; + } + final void updateCachedChunk(int chunkX, int chunkZ, CachedChunk chunk) { this.chunks[chunkX][chunkZ] = chunk; hasUnsavedChanges = true; @@ -139,6 +159,22 @@ public final class CachedRegion implements IBlockTypeAccess { } } } + for (int z = 0; z < 32; z++) { + for (int x = 0; x < 32; x++) { + if (chunks[x][z] != null) { + Map> locs = chunks[x][z].getRelativeBlocks(); + out.writeShort(locs.entrySet().size()); + for (Map.Entry> entry : locs.entrySet()) { + out.writeUTF(entry.getKey()); + out.writeShort(entry.getValue().size()); + for (BlockPos pos : entry.getValue()) { + out.writeByte((byte) (pos.getZ() << 4 | pos.getX())); + out.writeByte((byte) (pos.getY())); + } + } + } + } + } out.writeInt(~CACHED_REGION_MAGIC); } hasUnsavedChanges = false; @@ -174,6 +210,7 @@ public final class CachedRegion implements IBlockTypeAccess { throw new IOException("Bad magic value " + magic); } CachedChunk[][] tmpCached = new CachedChunk[32][32]; + Map>[][] location = new Map[32][32]; for (int z = 0; z < 32; z++) { for (int x = 0; x < 32; x++) { int isChunkPresent = in.read(); @@ -181,7 +218,12 @@ public final class CachedRegion implements IBlockTypeAccess { case CHUNK_PRESENT: byte[] bytes = new byte[CachedChunk.SIZE_IN_BYTES]; in.readFully(bytes); - tmpCached[x][z] = new CachedChunk(x, z, BitSet.valueOf(bytes), new String[256]); + location[x][z] = new HashMap<>(); + int regionX = this.x; + int regionZ = this.z; + int chunkX = x + 32 * regionX; + int chunkZ = z + 32 * regionZ; + tmpCached[x][z] = new CachedChunk(chunkX, chunkZ, BitSet.valueOf(bytes), new String[256], location[x][z]); break; case CHUNK_NOT_PRESENT: tmpCached[x][z] = null; @@ -200,6 +242,30 @@ public final class CachedRegion implements IBlockTypeAccess { } } } + for (int z = 0; z < 32; z++) { + for (int x = 0; x < 32; x++) { + if (tmpCached[x][z] != null) { + // 16 * 16 * 256 = 65536 so a short is enough + short numSpecialBlockTypes = in.readShort(); + for (int i = 0; i < numSpecialBlockTypes; i++) { + String blockName = in.readUTF(); + ArrayList locs = new ArrayList<>(); + location[x][z].put(blockName, locs); + short numLocations = in.readShort(); + for (int j = 0; j < numLocations; j++) { + byte xz = in.readByte(); + int X = xz & 0x0f; + int Z = (xz >>> 4) & 0x0f; + int Y = (int) in.readByte(); + if (Y < 0) { + Y += 256; + } + locs.add(new BlockPos(X, Y, Z)); + } + } + } + } + } int fileEndMagic = in.readInt(); if (fileEndMagic != ~magic) { throw new IOException("Bad end of file magic"); diff --git a/src/main/java/baritone/chunk/CachedWorld.java b/src/main/java/baritone/chunk/CachedWorld.java index 5374e837..3abcb8b3 100644 --- a/src/main/java/baritone/chunk/CachedWorld.java +++ b/src/main/java/baritone/chunk/CachedWorld.java @@ -21,12 +21,13 @@ import baritone.utils.pathing.IBlockTypeAccess; import it.unimi.dsi.fastutil.longs.Long2ObjectMap; import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; import net.minecraft.block.state.IBlockState; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.chunk.Chunk; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.BitSet; +import java.util.LinkedList; import java.util.concurrent.LinkedBlockingQueue; import java.util.function.Consumer; @@ -84,6 +85,16 @@ public final class CachedWorld implements IBlockTypeAccess { return region.getBlock(x & 511, y, z & 511); } + public final LinkedList getLocationsOf(String block) { + LinkedList res = new LinkedList<>(); + this.cachedRegions.values().forEach(region -> { + if (region != null) + for (BlockPos pos : region.getLocationsOf(block)) + res.add(pos); + }); + return res; + } + private void updateCachedChunk(CachedChunk chunk) { CachedRegion region = getOrCreateRegion(chunk.x >> 5, chunk.z >> 5); region.updateCachedChunk(chunk.x & 31, chunk.z & 31, chunk); @@ -99,6 +110,16 @@ public final class CachedWorld implements IBlockTypeAccess { System.out.println("World save took " + (now - start) + "ms"); } + public final void reloadAllFromDisk() { + long start = System.currentTimeMillis(); + this.cachedRegions.values().forEach(region -> { + if (region != null) + region.load(this.directory); + }); + long now = System.currentTimeMillis(); + System.out.println("World load took " + (now - start) + "ms"); + } + /** * Returns the region at the specified region coordinates * @@ -168,9 +189,7 @@ public final class CachedWorld implements IBlockTypeAccess { } try { Chunk chunk = queue.take(); - BitSet packedChunk = ChunkPacker.createPackedChunk(chunk); - String[] packedOverview = ChunkPacker.createPackedOverview(chunk); - CachedChunk cached = new CachedChunk(chunk.x, chunk.z, packedChunk, packedOverview); + CachedChunk cached = ChunkPacker.pack(chunk); CachedWorld.this.updateCachedChunk(cached); //System.out.println("Processed chunk at " + chunk.x + "," + chunk.z); } catch (InterruptedException e) { diff --git a/src/main/java/baritone/chunk/ChunkPacker.java b/src/main/java/baritone/chunk/ChunkPacker.java index c0688786..7cc569a7 100644 --- a/src/main/java/baritone/chunk/ChunkPacker.java +++ b/src/main/java/baritone/chunk/ChunkPacker.java @@ -28,9 +28,10 @@ import net.minecraft.block.BlockTallGrass; import net.minecraft.block.state.IBlockState; import net.minecraft.init.Blocks; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.chunk.Chunk; -import java.util.BitSet; +import java.util.*; /** * @author Brady @@ -40,30 +41,33 @@ public final class ChunkPacker implements Helper { private ChunkPacker() {} - public static BitSet createPackedChunk(Chunk chunk) { + public static CachedChunk pack(Chunk chunk) { long start = System.currentTimeMillis(); + + Map> specialBlocks = new HashMap<>(); BitSet bitSet = new BitSet(CachedChunk.SIZE); try { for (int y = 0; y < 256; y++) { for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { int index = CachedChunk.getPositionIndex(x, y, z); - boolean[] bits = getPathingBlockType(chunk.getBlockState(x, y, z).getBlock()).getBits(); + Block block = chunk.getBlockState(x, y, z).getBlock(); + boolean[] bits = getPathingBlockType(block).getBits(); bitSet.set(index, bits[0]); bitSet.set(index + 1, bits[1]); + if (CachedChunk.BLOCKS_TO_KEEP_TRACK_OF.contains(block)) { + String name = blockToString(block); + specialBlocks.computeIfAbsent(name, b -> new ArrayList<>()).add(new BlockPos(x, y, z)); + } } } } } catch (Exception e) { e.printStackTrace(); } + //System.out.println("Packed special blocks: " + specialBlocks); long end = System.currentTimeMillis(); //System.out.println("Chunk packing took " + (end - start) + "ms for " + chunk.x + "," + chunk.z); - return bitSet; - } - - public static String[] createPackedOverview(Chunk chunk) { - long start = System.currentTimeMillis(); String[] blockNames = new String[256]; for (int z = 0; z < 16; z++) { for (int x = 0; x < 16; x++) { @@ -75,17 +79,29 @@ public final class ChunkPacker implements Helper { break; } } - ResourceLocation loc = Block.REGISTRY.getNameForObject(blockState.getBlock()); - String name = loc.getPath(); // normally, only write the part after the minecraft: - if (!loc.getNamespace().equals("minecraft")) { - // Baritone is running on top of forge with mods installed, perhaps? - name = loc.toString(); // include the namespace with the colon - } + String name = blockToString(blockState.getBlock()); blockNames[z << 4 | x] = name; } } - long end = System.currentTimeMillis(); - return blockNames; + CachedChunk cached = new CachedChunk(chunk.x, chunk.z, bitSet, blockNames, specialBlocks); + return cached; + } + + public static String blockToString(Block block) { + ResourceLocation loc = Block.REGISTRY.getNameForObject(block); + String name = loc.getPath(); // normally, only write the part after the minecraft: + if (!loc.getNamespace().equals("minecraft")) { + // Baritone is running on top of forge with mods installed, perhaps? + name = loc.toString(); // include the namespace with the colon + } + return name; + } + + public static Block stringToBlock(String name) { + if (!name.contains(":")) { + name = "minecraft:" + name; + } + return Block.getBlockFromName(name); } private static PathingBlockType getPathingBlockType(Block block) { diff --git a/src/main/java/baritone/utils/ExampleBaritoneControl.java b/src/main/java/baritone/utils/ExampleBaritoneControl.java index d1fc14ee..78f418ea 100644 --- a/src/main/java/baritone/utils/ExampleBaritoneControl.java +++ b/src/main/java/baritone/utils/ExampleBaritoneControl.java @@ -21,21 +21,22 @@ import baritone.Baritone; import baritone.Settings; import baritone.behavior.Behavior; import baritone.behavior.impl.PathingBehavior; +import baritone.chunk.ChunkPacker; import baritone.chunk.Waypoint; import baritone.chunk.WorldProvider; import baritone.event.events.ChatEvent; import baritone.pathing.calc.AStarPathFinder; -import baritone.pathing.goals.Goal; -import baritone.pathing.goals.GoalBlock; -import baritone.pathing.goals.GoalXZ; -import baritone.pathing.goals.GoalYLevel; +import baritone.pathing.goals.*; import baritone.pathing.movement.ActionCosts; import baritone.pathing.movement.CalculationContext; import baritone.pathing.movement.Movement; import baritone.utils.pathing.BetterBlockPos; +import net.minecraft.block.Block; 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(); @@ -105,6 +106,57 @@ public class ExampleBaritoneControl extends Behavior { displayChatMessageRaw("ok canceled"); return; } + if (msg.toLowerCase().equals("reloadall")) { + WorldProvider.INSTANCE.getCurrentWorld().cache.reloadAllFromDisk(); + displayChatMessageRaw("ok"); + event.cancel(); + return; + } + if (msg.toLowerCase().equals("saveall")) { + WorldProvider.INSTANCE.getCurrentWorld().cache.save(); + displayChatMessageRaw("ok"); + event.cancel(); + return; + } + if (msg.toLowerCase().startsWith("find")) { + String blockType = msg.toLowerCase().substring(4).trim(); + LinkedList locs = WorldProvider.INSTANCE.getCurrentWorld().cache.getLocationsOf(blockType); + displayChatMessageRaw("Have " + locs.size() + " locations"); + for (BlockPos pos : locs) { + Block actually = BlockStateInterface.get(pos).getBlock(); + if (!ChunkPacker.blockToString(actually).equalsIgnoreCase(blockType)) { + System.out.println("Was looking for " + blockType + " but actually found " + actually + " " + ChunkPacker.blockToString(actually)); + } + } + event.cancel(); + return; + } + if (msg.toLowerCase().startsWith("mine")) { + String blockType = msg.toLowerCase().substring(4).trim(); + List locs = new ArrayList<>(WorldProvider.INSTANCE.getCurrentWorld().cache.getLocationsOf(blockType)); + if (locs.isEmpty()) { + displayChatMessageRaw("No locations known"); + event.cancel(); + return; + } + BlockPos playerFeet = playerFeet(); + locs.sort(Comparator.comparingDouble(pos -> playerFeet.distanceSq(pos))); + + // 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(blockType)) + .collect(Collectors.toList())); + + if (locs.size() > 10) { + displayChatMessageRaw("Pathing to any of closest 10"); + locs = locs.subList(0, 10); + } + PathingBehavior.INSTANCE.setGoal(new GoalComposite(locs.stream().map(GoalTwoBlocks::new).toArray(Goal[]::new))); + PathingBehavior.INSTANCE.path(); + event.cancel(); + return; + } if (msg.toLowerCase().startsWith("thisway")) { Goal goal = GoalXZ.fromDirection(playerFeetAsVec(), player().rotationYaw, Double.parseDouble(msg.substring(7).trim())); PathingBehavior.INSTANCE.setGoal(goal); diff --git a/src/test/java/baritone/chunk/CachedRegionTest.java b/src/test/java/baritone/chunk/CachedRegionTest.java new file mode 100644 index 00000000..0992cee1 --- /dev/null +++ b/src/test/java/baritone/chunk/CachedRegionTest.java @@ -0,0 +1,50 @@ +/* + * 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 org.junit.Test; + +import static junit.framework.TestCase.assertEquals; + +public class CachedRegionTest { + + @Test + public void blockPosSaving() { + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + for (int y = 0; y < 256; y++) { + byte part1 = (byte) (z << 4 | x); + byte part2 = (byte) (y); + byte xz = part1; + int X = xz & 0x0f; + int Z = (xz >>> 4) & 0x0f; + int Y = (int) part2; + if (Y < 0) { + Y += 256; + } + if (x != X || y != Y || z != Z) { + System.out.println(x + " " + X + " " + y + " " + Y + " " + z + " " + Z); + } + assertEquals(x, X); + assertEquals(y, Y); + assertEquals(z, Z); + } + } + } + } +} \ No newline at end of file