From 5ec6b7b72e653c2b1638f52f7c90fa2a80ea159b Mon Sep 17 00:00:00 2001 From: wagyourtail Date: Sat, 9 Oct 2021 14:16:49 -0600 Subject: [PATCH] make chunk cache/scanning work with data driven worldheight --- src/main/java/baritone/cache/CachedChunk.java | 40 ++++++++++++------- .../java/baritone/cache/CachedRegion.java | 17 ++++---- src/main/java/baritone/cache/CachedWorld.java | 5 ++- src/main/java/baritone/cache/ChunkPacker.java | 21 +++++----- src/main/java/baritone/cache/WorldData.java | 5 ++- .../java/baritone/cache/WorldProvider.java | 10 +++-- .../java/baritone/cache/WorldScanner.java | 17 ++++---- .../java/baritone/event/GameEventHandler.java | 2 +- 8 files changed, 67 insertions(+), 50 deletions(-) diff --git a/src/main/java/baritone/cache/CachedChunk.java b/src/main/java/baritone/cache/CachedChunk.java index 56485fd72..bbc713af7 100644 --- a/src/main/java/baritone/cache/CachedChunk.java +++ b/src/main/java/baritone/cache/CachedChunk.java @@ -26,11 +26,10 @@ import java.util.BitSet; import java.util.List; import java.util.Map; import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.dimension.DimensionType; /** * @author Brady @@ -110,17 +109,19 @@ public final class CachedChunk { Blocks.VINE ); + public final int height; + /** * The size of the chunk data in bits. Equal to 16 KiB. *

- * Chunks are 16x16x256, each block requires 2 bits. + * Chunks are 16x16xH, each block requires 2 bits. */ - public static final int SIZE = 2 * 16 * 16 * 256; + public final int size; /** - * The size of the chunk data in bytes. Equal to 16 KiB. + * The size of the chunk data in bytes. Equal to 16 KiB for 256 height. */ - public static final int SIZE_IN_BYTES = SIZE / 8; + public final int sizeInBytes; /** * The chunk x coordinate @@ -152,11 +153,14 @@ public final class CachedChunk { public final long cacheTimestamp; - CachedChunk(int x, int z, BitSet data, BlockState[] overview, Map> specialBlockLocations, long cacheTimestamp) { + CachedChunk(int x, int z, int height, BitSet data, BlockState[] overview, Map> specialBlockLocations, long cacheTimestamp) { + this.size = size(height); + this.sizeInBytes = sizeInBytes(size); validateSize(data); this.x = x; this.z = z; + this.height = height; this.data = data; this.overview = overview; this.heightMap = new int[256]; @@ -171,6 +175,14 @@ public final class CachedChunk { calculateHeightMap(); } + public static int size(int dimension_height) { + return 2 * 16 * 16 * dimension_height; + } + + public static int sizeInBytes(int size) { + return size / 8; + } + private final void setSpecial() { for (Map.Entry> entry : specialBlockLocations.entrySet()) { for (BlockPos pos : entry.getValue()) { @@ -179,7 +191,7 @@ public final class CachedChunk { } } - public final BlockState getBlock(int x, int y, int z, ResourceKey dimension) { + public final BlockState getBlock(int x, int y, int z, DimensionType dimension) { int index = getPositionIndex(x, y, z); PathingBlockType type = getType(index); int internalPos = z << 4 | x; @@ -201,11 +213,11 @@ public final class CachedChunk { } if (type == PathingBlockType.SOLID) { - if (y == 127 && dimension == Level.NETHER) { + if (y == dimension.logicalHeight() - 1 && dimension.hasCeiling()) { // nether roof is always unbreakable return Blocks.BEDROCK.defaultBlockState(); } - if (y < 5 && dimension == Level.OVERWORLD) { + if (y < 5 && dimension.natural()) { // solid blocks below 5 are commonly bedrock // however, returning bedrock always would be a little yikes // discourage paths that include breaking blocks below 5 a little more heavily just so that it takes paths breaking what's known to be stone (at 5 or above) instead of what could maybe be bedrock (below 5) @@ -224,7 +236,7 @@ public final class CachedChunk { for (int x = 0; x < 16; x++) { int index = z << 4 | x; heightMap[index] = 0; - for (int y = 256; y >= 0; y--) { + for (int y = height; y >= 0; y--) { int i = getPositionIndex(x, y, z); if (data.get(i) || data.get(i + 1)) { heightMap[index] = y; @@ -275,14 +287,14 @@ public final class CachedChunk { /** * Validates the size of an input {@link BitSet} containing the raw - * packed chunk data. Sizes that exceed {@link CachedChunk#SIZE} are + * packed chunk data. Sizes that exceed {@link CachedChunk#size} are * considered invalid, and thus, an exception will be thrown. * * @param data The raw data * @throws IllegalArgumentException if the bitset size exceeds the maximum size */ - private static void validateSize(BitSet data) { - if (data.size() > SIZE) { + private void validateSize(BitSet data) { + if (data.size() > size) { throw new IllegalArgumentException("BitSet of invalid length provided"); } } diff --git a/src/main/java/baritone/cache/CachedRegion.java b/src/main/java/baritone/cache/CachedRegion.java index 7581e2bdc..e501a6bb4 100644 --- a/src/main/java/baritone/cache/CachedRegion.java +++ b/src/main/java/baritone/cache/CachedRegion.java @@ -28,9 +28,8 @@ import java.util.*; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.dimension.DimensionType; /** * @author Brady @@ -61,14 +60,14 @@ public final class CachedRegion implements ICachedRegion { */ private final int z; - private final ResourceKey dimension; + private final DimensionType dimension; /** * Has this region been modified since its most recent load or save */ private boolean hasUnsavedChanges; - CachedRegion(int x, int z, ResourceKey dimension) { + CachedRegion(int x, int z, DimensionType dimension) { this.x = x; this.z = z; this.hasUnsavedChanges = false; @@ -77,9 +76,10 @@ public final class CachedRegion implements ICachedRegion { @Override public final BlockState getBlock(int x, int y, int z) { + int adjY = y - dimension.minY(); CachedChunk chunk = chunks[x >> 4][z >> 4]; if (chunk != null) { - return chunk.getBlock(x & 15, y, z & 15, dimension); + return chunk.getBlock(x & 15, adjY, z & 15, dimension); } return null; } @@ -143,7 +143,7 @@ public final class CachedRegion implements ICachedRegion { byte[] chunkBytes = chunk.toByteArray(); out.write(chunkBytes); // Messy, but fills the empty 0s that should be trailing to fill up the space. - out.write(new byte[CachedChunk.SIZE_IN_BYTES - chunkBytes.length]); + out.write(new byte[chunk.sizeInBytes - chunkBytes.length]); } } } @@ -224,10 +224,11 @@ public final class CachedRegion implements ICachedRegion { int isChunkPresent = in.read(); switch (isChunkPresent) { case CHUNK_PRESENT: - byte[] bytes = new byte[CachedChunk.SIZE_IN_BYTES]; + byte[] bytes = new byte[CachedChunk.sizeInBytes(CachedChunk.size(dimension.height()))]; in.readFully(bytes); bitSets[x][z] = BitSet.valueOf(bytes); location[x][z] = new HashMap<>(); + //this is top block in columns overview[x][z] = new BlockState[256]; present[x][z] = true; break; @@ -291,7 +292,7 @@ public final class CachedRegion implements ICachedRegion { int regionZ = this.z; int chunkX = x + 32 * regionX; int chunkZ = z + 32 * regionZ; - this.chunks[x][z] = new CachedChunk(chunkX, chunkZ, bitSets[x][z], overview[x][z], location[x][z], cacheTimestamp[x][z]); + this.chunks[x][z] = new CachedChunk(chunkX, chunkZ, dimension.height(), bitSets[x][z], overview[x][z], location[x][z], cacheTimestamp[x][z]); } } } diff --git a/src/main/java/baritone/cache/CachedWorld.java b/src/main/java/baritone/cache/CachedWorld.java index 4d9ff16df..83fffd1a7 100644 --- a/src/main/java/baritone/cache/CachedWorld.java +++ b/src/main/java/baritone/cache/CachedWorld.java @@ -38,6 +38,7 @@ import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.Level; import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.dimension.DimensionType; /** * @author Brady @@ -72,9 +73,9 @@ public final class CachedWorld implements ICachedWorld, Helper { */ private final Map toPackMap = new ConcurrentHashMap<>(); - private final ResourceKey dimension; + private final DimensionType dimension; - CachedWorld(Path directory, ResourceKey dimension) { + CachedWorld(Path directory, DimensionType dimension) { if (!Files.exists(directory)) { try { Files.createDirectories(directory); diff --git a/src/main/java/baritone/cache/ChunkPacker.java b/src/main/java/baritone/cache/ChunkPacker.java index f85fc3cce..cd15e4ed7 100644 --- a/src/main/java/baritone/cache/ChunkPacker.java +++ b/src/main/java/baritone/cache/ChunkPacker.java @@ -21,8 +21,6 @@ import baritone.api.utils.BlockUtils; import baritone.pathing.movement.MovementHelper; import baritone.utils.pathing.PathingBlockType; import net.minecraft.core.BlockPos; -import net.minecraft.resources.ResourceKey; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.AirBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; @@ -33,6 +31,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.chunk.LevelChunk; import net.minecraft.world.level.chunk.LevelChunkSection; import net.minecraft.world.level.chunk.PalettedContainer; +import net.minecraft.world.level.dimension.DimensionType; import net.minecraft.world.phys.Vec3; import java.util.*; @@ -50,10 +49,11 @@ public final class ChunkPacker { //long start = System.nanoTime() / 1000000L; Map> specialBlocks = new HashMap<>(); - BitSet bitSet = new BitSet(CachedChunk.SIZE); + final int height = chunk.getLevel().dimensionType().height(); + BitSet bitSet = new BitSet(CachedChunk.size(height)); try { LevelChunkSection[] chunkInternalStorageArray = chunk.getSections(); - for (int y0 = 0; y0 < 16; y0++) { + for (int y0 = 0; y0 < height / 16; y0++) { LevelChunkSection extendedblockstorage = chunkInternalStorageArray[y0]; if (extendedblockstorage == null) { // any 16x16x16 area that's all air will have null storage @@ -95,11 +95,12 @@ public final class ChunkPacker { //System.out.println("Chunk packing took " + (end - start) + "ms for " + chunk.x + "," + chunk.z); BlockState[] blocks = new BlockState[256]; + // get top block in columns // @formatter:off for (int z = 0; z < 16; z++) { https://www.ibm.com/developerworks/library/j-perry-writing-good-java-code/index.html for (int x = 0; x < 16; x++) { - for (int y = 255; y >= 0; y--) { + for (int y = height - 1; y >= 0; y--) { int index = CachedChunk.getPositionIndex(x, y, z); if (bitSet.get(index) || bitSet.get(index + 1)) { blocks[z << 4 | x] = getFromChunk(chunk, x, y, z); @@ -110,7 +111,7 @@ public final class ChunkPacker { } } // @formatter:on - return new CachedChunk(chunk.getPos().x, chunk.getPos().z, bitSet, blocks, specialBlocks, System.currentTimeMillis()); + return new CachedChunk(chunk.getPos().x, chunk.getPos().z, height, bitSet, blocks, specialBlocks, System.currentTimeMillis()); } private static PathingBlockType getPathingBlockType(BlockState state, LevelChunk chunk, int x, int y, int z) { @@ -153,7 +154,7 @@ public final class ChunkPacker { return PathingBlockType.SOLID; } - public static BlockState pathingTypeToBlock(PathingBlockType type, ResourceKey dimension) { + public static BlockState pathingTypeToBlock(PathingBlockType type, DimensionType dimension) { switch (type) { case AIR: return Blocks.AIR.defaultBlockState(); @@ -163,13 +164,13 @@ public final class ChunkPacker { return Blocks.LAVA.defaultBlockState(); case SOLID: // Dimension solid types - if (dimension == Level.OVERWORLD) { + if (dimension.natural()) { return Blocks.STONE.defaultBlockState(); } - if (dimension == Level.NETHER) { + if (dimension.ultraWarm()) { return Blocks.NETHERRACK.defaultBlockState(); } - if (dimension == Level.END) { + if (dimension.createDragonFight()) { return Blocks.END_STONE.defaultBlockState(); } default: diff --git a/src/main/java/baritone/cache/WorldData.java b/src/main/java/baritone/cache/WorldData.java index 31b1964b0..159d2db27 100644 --- a/src/main/java/baritone/cache/WorldData.java +++ b/src/main/java/baritone/cache/WorldData.java @@ -26,6 +26,7 @@ import java.io.IOException; import java.nio.file.Path; import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.Level; +import net.minecraft.world.level.dimension.DimensionType; /** * Data about a world, from baritone's point of view. Includes cached chunks, waypoints, and map data. @@ -39,9 +40,9 @@ public class WorldData implements IWorldData { private final ContainerMemory containerMemory; //public final MapData map; public final Path directory; - public final ResourceKey dimension; + public final DimensionType dimension; - WorldData(Path directory, ResourceKey dimension) { + WorldData(Path directory, DimensionType dimension) { this.directory = directory; this.cache = new CachedWorld(directory.resolve("cache"), dimension); this.waypoints = new WaypointCollection(directory.resolve("waypoints")); diff --git a/src/main/java/baritone/cache/WorldProvider.java b/src/main/java/baritone/cache/WorldProvider.java index 7f992e6dc..3d30603b4 100644 --- a/src/main/java/baritone/cache/WorldProvider.java +++ b/src/main/java/baritone/cache/WorldProvider.java @@ -56,7 +56,7 @@ public class WorldProvider implements IWorldProvider, Helper { * * @param world The world's Registry Data */ - public final void initWorld(ResourceKey world) { + public final void initWorld(ResourceKey worldKey, DimensionType world) { File directory; File readme; @@ -64,7 +64,7 @@ public class WorldProvider implements IWorldProvider, Helper { // If there is an integrated server running (Aka Singleplayer) then do magic to find the world save file if (mc.hasSingleplayerServer()) { - directory = DimensionType.getStorageFolder(world, integratedServer.getWorldPath(LevelResource.ROOT).toFile()); + directory = DimensionType.getStorageFolder(worldKey, integratedServer.getWorldPath(LevelResource.ROOT).toFile()); // Gets the "depth" of this directory relative the the game's run directory, 2 is the location of the world if (directory.toPath().relativize(mc.gameDirectory.toPath()).getNameCount() != 2) { @@ -90,7 +90,7 @@ public class WorldProvider implements IWorldProvider, Helper { } catch (IOException ignored) {} // We will actually store the world data in a subfolder: "DIM" - Path dir = DimensionType.getStorageFolder(world, directory).toPath(); + Path dir = getDimDir(worldKey, world.logicalHeight(), directory); if (!Files.exists(dir)) { try { Files.createDirectories(dir); @@ -103,6 +103,10 @@ public class WorldProvider implements IWorldProvider, Helper { } } + public final Path getDimDir(ResourceKey level, int height, File directory) { + return directory.toPath().resolve(level.location().getNamespace()).resolve(level.location().getPath() + "_" + height); + } + public final void closeWorld() { WorldData world = this.currentWorld; this.currentWorld = null; diff --git a/src/main/java/baritone/cache/WorldScanner.java b/src/main/java/baritone/cache/WorldScanner.java index b4ec4afc8..8a932555a 100644 --- a/src/main/java/baritone/cache/WorldScanner.java +++ b/src/main/java/baritone/cache/WorldScanner.java @@ -37,8 +37,6 @@ public enum WorldScanner implements IWorldScanner { INSTANCE; - private static final int[] DEFAULT_COORDINATE_ITERATION_ORDER = IntStream.range(0, 16).toArray(); - @Override public List scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius) { ArrayList res = new ArrayList<>(); @@ -51,10 +49,10 @@ public enum WorldScanner implements IWorldScanner { int maxSearchRadiusSq = maxSearchRadius * maxSearchRadius; int playerChunkX = ctx.playerFeet().getX() >> 4; int playerChunkZ = ctx.playerFeet().getZ() >> 4; - int playerY = ctx.playerFeet().getY(); + int playerY = ctx.playerFeet().getY() - ctx.world().dimensionType().minY(); int playerYBlockStateContainerIndex = playerY >> 4; - int[] coordinateIterationOrder = IntStream.range(0, 16).boxed().sorted(Comparator.comparingInt(y -> Math.abs(y - playerYBlockStateContainerIndex))).mapToInt(x -> x).toArray(); + int[] coordinateIterationOrder = IntStream.range(0, ctx.world().dimensionType().height() / 16).boxed().sorted(Comparator.comparingInt(y -> Math.abs(y - playerYBlockStateContainerIndex))).mapToInt(x -> x).toArray(); int searchRadiusSq = 0; boolean foundWithinY = false; @@ -75,7 +73,7 @@ public enum WorldScanner implements IWorldScanner { continue; } allUnloaded = false; - if (scanChunkInto(chunkX << 4, chunkZ << 4, chunk, filter, res, max, yLevelThreshold, playerY, coordinateIterationOrder)) { + if (scanChunkInto(chunkX << 4, chunkZ << 4, ctx.world().dimensionType().minY(), chunk, filter, res, max, yLevelThreshold, playerY, coordinateIterationOrder)) { foundWithinY = true; } } @@ -105,7 +103,7 @@ public enum WorldScanner implements IWorldScanner { } ArrayList res = new ArrayList<>(); - scanChunkInto(pos.x << 4, pos.z << 4, chunk, filter, res, max, yLevelThreshold, playerY, DEFAULT_COORDINATE_ITERATION_ORDER); + scanChunkInto(pos.x << 4, pos.z << 4, ctx.world().dimensionType().minY(), chunk, filter, res, max, yLevelThreshold, playerY, IntStream.range(0, ctx.world().dimensionType().height() / 16).toArray()); return res; } @@ -144,11 +142,10 @@ public enum WorldScanner implements IWorldScanner { return queued; } - private boolean scanChunkInto(int chunkX, int chunkZ, LevelChunk chunk, BlockOptionalMetaLookup filter, Collection result, int max, int yLevelThreshold, int playerY, int[] coordinateIterationOrder) { + private boolean scanChunkInto(int chunkX, int chunkZ, int minY, LevelChunk chunk, BlockOptionalMetaLookup filter, Collection result, int max, int yLevelThreshold, int playerY, int[] coordinateIterationOrder) { LevelChunkSection[] chunkInternalStorageArray = chunk.getSections(); boolean foundWithinY = false; - for (int yIndex = 0; yIndex < 16; yIndex++) { - int y0 = coordinateIterationOrder[yIndex]; + for (int y0 : coordinateIterationOrder) { LevelChunkSection section = chunkInternalStorageArray[y0]; if (section == null || LevelChunkSection.isEmpty(section)) { continue; @@ -172,7 +169,7 @@ public enum WorldScanner implements IWorldScanner { } } } - result.add(new BlockPos(chunkX | x, y, chunkZ | z)); + result.add(new BlockPos(chunkX | x, y + minY, chunkZ | z)); } } } diff --git a/src/main/java/baritone/event/GameEventHandler.java b/src/main/java/baritone/event/GameEventHandler.java index 5056b6a0b..b7853e360 100644 --- a/src/main/java/baritone/event/GameEventHandler.java +++ b/src/main/java/baritone/event/GameEventHandler.java @@ -114,7 +114,7 @@ public final class GameEventHandler implements IEventBus, Helper { if (event.getState() == EventState.POST) { cache.closeWorld(); if (event.getWorld() != null) { - cache.initWorld(event.getWorld().dimension()); + cache.initWorld(event.getWorld().dimension(), event.getWorld().dimensionType()); } }