make chunk cache/scanning work with data driven worldheight

This commit is contained in:
wagyourtail 2021-10-09 14:16:49 -06:00
parent de27fb2f68
commit 5ec6b7b72e
8 changed files with 67 additions and 50 deletions

View File

@ -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.
* <p>
* 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<String, List<BlockPos>> specialBlockLocations, long cacheTimestamp) {
CachedChunk(int x, int z, int height, BitSet data, BlockState[] overview, Map<String, List<BlockPos>> 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<String, List<BlockPos>> 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<Level> 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");
}
}

View File

@ -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<Level> 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<Level> 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]);
}
}
}

View File

@ -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<ChunkPos, LevelChunk> toPackMap = new ConcurrentHashMap<>();
private final ResourceKey<Level> dimension;
private final DimensionType dimension;
CachedWorld(Path directory, ResourceKey<Level> dimension) {
CachedWorld(Path directory, DimensionType dimension) {
if (!Files.exists(directory)) {
try {
Files.createDirectories(directory);

View File

@ -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<String, List<BlockPos>> 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<Level> 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:

View File

@ -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<Level> dimension;
public final DimensionType dimension;
WorldData(Path directory, ResourceKey<Level> dimension) {
WorldData(Path directory, DimensionType dimension) {
this.directory = directory;
this.cache = new CachedWorld(directory.resolve("cache"), dimension);
this.waypoints = new WaypointCollection(directory.resolve("waypoints"));

View File

@ -56,7 +56,7 @@ public class WorldProvider implements IWorldProvider, Helper {
*
* @param world The world's Registry Data
*/
public final void initWorld(ResourceKey<Level> world) {
public final void initWorld(ResourceKey<Level> 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<id>"
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> 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;

View File

@ -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<BlockPos> scanChunkRadius(IPlayerContext ctx, BlockOptionalMetaLookup filter, int max, int yLevelThreshold, int maxSearchRadius) {
ArrayList<BlockPos> 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<BlockPos> 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<BlockPos> result, int max, int yLevelThreshold, int playerY, int[] coordinateIterationOrder) {
private boolean scanChunkInto(int chunkX, int chunkZ, int minY, LevelChunk chunk, BlockOptionalMetaLookup filter, Collection<BlockPos> 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));
}
}
}

View File

@ -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());
}
}