From 7abf31ea821a0bbddb836adb1a63d0fec2ceee4f Mon Sep 17 00:00:00 2001 From: Leonard Kugis Date: Mon, 25 Apr 2022 18:41:24 +0200 Subject: Initial commit --- .../instancedregions/chunkmap/ChunkMapManager.java | 384 +++++++++++++++++++++ 1 file changed, 384 insertions(+) create mode 100755 src/main/java/com/encrox/instancedregions/chunkmap/ChunkMapManager.java (limited to 'src/main/java/com/encrox/instancedregions/chunkmap/ChunkMapManager.java') diff --git a/src/main/java/com/encrox/instancedregions/chunkmap/ChunkMapManager.java b/src/main/java/com/encrox/instancedregions/chunkmap/ChunkMapManager.java new file mode 100755 index 0000000..87fb3a1 --- /dev/null +++ b/src/main/java/com/encrox/instancedregions/chunkmap/ChunkMapManager.java @@ -0,0 +1,384 @@ +/** + * @author Aleksey Terzi + * + */ + +package com.encrox.instancedregions.chunkmap; + +import java.io.IOException; +import java.util.Arrays; + +import com.encrox.instancedregions.types.BlockState; + +public class ChunkMapManager { + private static final ThreadLocal _buffer = new ThreadLocal() { + @Override + protected ChunkMapBuffer initialValue() { + return new ChunkMapBuffer(); + } + }; + + private ChunkMapBuffer buffer; + private ChunkData chunkData; + private ChunkReader reader; + private int sectionCount; + private int sectionIndex; + private int y; + private int minX; + private int maxX; + private int minZ; + private int maxZ; + private int blockIndex; + + public int getSectionCount() { + return this.sectionCount; + } + + public int getY() { + return this.y; + } + + public ChunkData getChunkData() { + return this.chunkData; + } + + public ChunkMapManager(ChunkData chunkData) { + this.buffer = _buffer.get(); + this.chunkData = chunkData; + } + + public void init() throws IOException { + this.reader = new ChunkReader(this.chunkData.data); + this.sectionCount = 0; + this.sectionIndex = -1; + this.minX = this.chunkData.chunkX << 4; + this.maxX = this.minX + 15; + this.minZ = this.chunkData.chunkZ << 4; + this.maxZ = this.minZ + 15; + + this.buffer.lightArrayLength = 2048; + + if(this.chunkData.isOverworld) { + this.buffer.lightArrayLength <<= 1; + } + + this.buffer.writer.init(); + + int mask = this.chunkData.primaryBitMask; + + while(mask != 0) { + if((mask & 0x1) != 0) { + this.sectionCount++; + } + + mask >>>= 1; + } + + this.buffer.clearLayers(); + + moveToNextLayer(); + } + + public boolean inputHasNonAirBlock() { + return this.buffer.paletteLength > 1 || this.buffer.palette[0] != 0; + } + + public static void blockDataToState(int blockData, BlockState blockState) { + blockState.id = blockData >>> 4; + blockState.meta = blockData & 0xf; + } + + public static int getBlockIdFromData(int blockData) { + return blockData >>> 4; + } + + public static int getBlockMetaFromData(int blockData) { + return blockData & 0xf; + } + + public static int blockStateToData(BlockState blockState) { + return (blockState.id << 4) | blockState.meta; + } + + public static int getBlockDataFromId(int id) { + return id << 4; + } + + public boolean initOutputPalette() { + if(this.buffer.paletteLength == 0 || this.buffer.paletteLength == 255) { + this.buffer.outputPaletteLength = 0; + return false; + } + + Arrays.fill(this.buffer.outputPaletteMap, (byte)-1); + + this.buffer.outputPaletteLength = this.buffer.paletteLength; + + for(int i = 0; i < this.buffer.paletteLength; i++) { + int blockData = this.buffer.palette[i]; + + this.buffer.outputPalette[i] = blockData; + + if(blockData >= 0) { + this.buffer.outputPaletteMap[blockData] = (byte)i; + } + } + + return true; + } + + public boolean addToOutputPalette(int blockData) { + if(this.buffer.outputPaletteMap[blockData] >= 0) return true; + + //255 (-1 for byte) is special code in my algorithm + if(this.buffer.outputPaletteLength == 254) { + this.buffer.outputPaletteLength = 0; + return false; + } + + this.buffer.outputPalette[this.buffer.outputPaletteLength] = blockData; + this.buffer.outputPaletteMap[blockData] = (byte)this.buffer.outputPaletteLength; + + this.buffer.outputPaletteLength++; + + return true; + } + + public void initOutputSection() throws IOException { + calcOutputBitsPerBlock(); + + this.buffer.writer.setBitsPerBlock(this.buffer.outputBitsPerBlock); + + //Bits Per Block + this.buffer.writer.writeByte((byte)this.buffer.outputBitsPerBlock); + + //Palette Length + this.buffer.writer.writeVarInt(this.buffer.outputPaletteLength); + + //Palette + for(int i = 0; i < this.buffer.outputPaletteLength; i++) { + this.buffer.writer.writeVarInt(this.buffer.outputPalette[i]); + } + + int dataArrayLengthInBits = this.buffer.outputBitsPerBlock << 12;// multiply by 4096 + int outputDataArrayLength = dataArrayLengthInBits >>> 6;//divide by 64 + + if((dataArrayLengthInBits & 0x3f) != 0) { + outputDataArrayLength++; + } + + //Data Array Length + this.buffer.writer.writeVarInt(outputDataArrayLength); + + //Copy Block Light and Sky Light arrays + int lightArrayStartIndex = this.buffer.dataArrayStartIndex + (this.buffer.dataArrayLength << 3); + int outputLightArrayStartIndex = this.buffer.writer.getByteIndex() + (outputDataArrayLength << 3); + + System.arraycopy( + this.chunkData.data, + lightArrayStartIndex, + this.buffer.output, + outputLightArrayStartIndex, + this.buffer.lightArrayLength + ); + } + + public void writeOutputBlock(int blockData) throws IOException { + if(this.buffer.outputPaletteLength > 0) { + long paletteIndex = this.buffer.outputPaletteMap[blockData] & 0xffL; + + if(paletteIndex == 255) { + BlockState blockState = new BlockState(); + blockDataToState(blockData, blockState); + throw new IllegalArgumentException("Block " + blockState.id + ":" + blockState.meta + " is absent in output palette."); + } + + this.buffer.writer.writeBlockBits(paletteIndex); + } else { + this.buffer.writer.writeBlockBits(blockData); + } + } + + public void finalizeOutput() throws IOException { + if(this.buffer.writer.getByteIndex() == 0) return; + + this.buffer.writer.save(); + this.buffer.writer.skip(this.buffer.lightArrayLength); + } + + public byte[] createOutput() { + int readerByteIndex = this.reader.getByteIndex(); + int writerByteIndex = this.buffer.writer.getByteIndex(); + int biomesSize = this.chunkData.data.length - readerByteIndex; + byte[] output = new byte[writerByteIndex + biomesSize]; + + System.arraycopy(this.buffer.output, 0, output, 0, writerByteIndex); + + if(biomesSize > 0) { + System.arraycopy( + this.chunkData.data, + readerByteIndex, + output, + writerByteIndex, + biomesSize + ); + } + + return output; + } + + private void calcOutputBitsPerBlock() { + if(this.buffer.outputPaletteLength == 0) { + this.buffer.outputBitsPerBlock = 13; + } else { + byte mask = (byte)this.buffer.outputPaletteLength; + int index = 0; + + while((mask & 0x80) == 0) { + index++; + mask <<= 1; + } + + this.buffer.outputBitsPerBlock = 8 - index; + + if(this.buffer.outputBitsPerBlock < 4) { + this.buffer.outputBitsPerBlock = 4; + } + } + } + + public int readNextBlock() throws IOException { + if(this.blockIndex == 16 * 16) { + if(!moveToNextLayer()) return -1; + } + + return this.buffer.curLayer.map[this.blockIndex++]; + } + + public int get(int x, int y, int z) throws IOException { + if(x < minX || x > maxX + || z < minZ || z > maxZ + || y > 255 || y < this.y - 1 || y > this.y + 1 + ) { + return -1; + } + + ChunkLayer layer; + + if(y == this.y) layer = this.buffer.curLayer; + else if(y == this.y - 1) layer = this.buffer.prevLayer; + else layer = this.buffer.nextLayer; + + if(!layer.hasData) return -1; + + int blockIndex = ((z - this.minZ) << 4) | (x - this.minX); + + return layer.map[blockIndex]; + } + + private boolean moveToNextLayer() throws IOException { + if(!increaseY()) return false; + + shiftLayersDown(); + + if(!this.buffer.curLayer.hasData) { + readLayer(this.buffer.curLayer); + } + + if(((this.y + 1) >>> 4) > this.sectionIndex) { + int oldSectionIndex = this.sectionIndex; + + moveToNextSection(); + + if(this.sectionIndex < 16 && oldSectionIndex + 1 == this.sectionIndex) { + readLayer(this.buffer.nextLayer); + } + } else { + readLayer(this.buffer.nextLayer); + } + + this.blockIndex = 0; + + return true; + } + + private boolean increaseY() throws IOException { + if(this.sectionIndex < 0) { + if(!moveToNextSection()) return false; + + this.y = this.sectionIndex << 4; + } + else { + this.y++; + + if((this.y & 0xf) == 0) { + if(this.sectionIndex > 15) return false; + + if((this.y >>> 4) != this.sectionIndex) { + this.buffer.clearLayers(); + this.y = this.sectionIndex << 4; + } + } + } + + return true; + } + + private void shiftLayersDown() { + ChunkLayer temp = this.buffer.prevLayer; + + this.buffer.prevLayer = this.buffer.curLayer; + this.buffer.curLayer = this.buffer.nextLayer; + this.buffer.nextLayer = temp; + this.buffer.nextLayer.hasData = false; + } + + private boolean moveToNextSection() throws IOException { + if(this.sectionIndex >= 0) { + this.reader.skip(this.buffer.lightArrayLength); + } + + do { + this.sectionIndex++; + } while(this.sectionIndex < 16 && (this.chunkData.primaryBitMask & (1 << this.sectionIndex)) == 0); + + if(this.sectionIndex >= 16) return false; + + readSectionHeader(); + + return true; + } + + private void readLayer(ChunkLayer layer) throws IOException { + for(int i = 0; i < 16 * 16; i++) { + int blockData = this.reader.readBlockBits(); + + if(this.buffer.paletteLength > 0) { + blockData = blockData >= 0 && blockData < this.buffer.paletteLength + ? this.buffer.palette[blockData] + : 0; + } + + layer.map[i] = blockData; + } + + layer.hasData = true; + } + + private void readSectionHeader() throws IOException { + this.buffer.bitsPerBlock = this.reader.readByte(); + this.buffer.paletteLength = this.reader.readVarInt(); + + for(int i = 0; i < this.buffer.paletteLength; i++) { + int paletteData = this.reader.readVarInt(); + + this.buffer.palette[i] = paletteData; + } + + this.buffer.dataArrayLength = this.reader.readVarInt(); + + this.buffer.dataArrayStartIndex = this.reader.getByteIndex(); + + this.reader.setBitsPerBlock(this.buffer.bitsPerBlock); + } +} \ No newline at end of file -- cgit v1.2.1