diff --git a/src/main/java/com/github/steveice10/mc/protocol/data/game/chunk/Chunk.java b/src/main/java/com/github/steveice10/mc/protocol/data/game/chunk/Chunk.java index 1feaff88..2eef637f 100644 --- a/src/main/java/com/github/steveice10/mc/protocol/data/game/chunk/Chunk.java +++ b/src/main/java/com/github/steveice10/mc/protocol/data/game/chunk/Chunk.java @@ -2,11 +2,7 @@ package com.github.steveice10.mc.protocol.data.game.chunk; import com.github.steveice10.packetlib.io.NetInput; import com.github.steveice10.packetlib.io.NetOutput; -import lombok.AccessLevel; -import lombok.AllArgsConstructor; -import lombok.Data; -import lombok.NonNull; -import lombok.Setter; +import lombok.*; import java.io.IOException; import java.util.ArrayList; @@ -22,25 +18,27 @@ public class Chunk { private int blockCount; private int bitsPerEntry; - private @NonNull List states; + private @NonNull List palette; private @NonNull FlexibleStorage storage; public Chunk() { - this(0, 4, new ArrayList<>(Collections.singletonList(AIR)), new FlexibleStorage(4, 4096)); + this(0, 4, new ArrayList<>(Collections.singletonList(AIR)), new FlexibleStorage(4)); } public static Chunk read(NetInput in) throws IOException { int blockCount = in.readShort(); int bitsPerEntry = in.readUnsignedByte(); - List states = new ArrayList<>(); - int stateCount = bitsPerEntry > 8 || bitsPerEntry == 0 ? 0 : in.readVarInt(); - for(int i = 0; i < stateCount; i++) { - states.add(in.readVarInt()); + List palette = new ArrayList<>(); + if(bitsPerEntry <= 8) { + int paletteLength = in.readVarInt(); + for(int i = 0; i < paletteLength; i++) { + palette.add(in.readVarInt()); + } } FlexibleStorage storage = new FlexibleStorage(bitsPerEntry, in.readLongs(in.readVarInt())); - return new Chunk(blockCount, bitsPerEntry, states, storage); + return new Chunk(blockCount, bitsPerEntry, palette, storage); } public static void write(NetOutput out, Chunk chunk) throws IOException { @@ -48,8 +46,8 @@ public class Chunk { out.writeByte(chunk.getBitsPerEntry()); if(chunk.getBitsPerEntry() <= 8) { - out.writeVarInt(chunk.getStates().size()); - for (int state : chunk.getStates()) { + out.writeVarInt(chunk.getPalette().size()); + for (int state : chunk.getPalette()) { out.writeVarInt(state); } } @@ -65,31 +63,28 @@ public class Chunk { public int get(int x, int y, int z) { int id = this.storage.get(index(x, y, z)); - return this.bitsPerEntry <= 8 ? (id >= 0 && id < this.states.size() ? this.states.get(id) : AIR) : id; + return this.bitsPerEntry <= 8 ? (id >= 0 && id < this.palette.size() ? this.palette.get(id) : AIR) : id; } public void set(int x, int y, int z, @NonNull int state) { - int id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state; + int id = this.bitsPerEntry <= 8 ? this.palette.indexOf(state) : state; if(id == -1) { - this.states.add(state); - if(this.states.size() > 1 << this.bitsPerEntry) { + this.palette.add(state); + if(this.palette.size() > 1 << this.bitsPerEntry) { this.bitsPerEntry++; - List oldStates = this.states; + final List oldStates = this.bitsPerEntry > 8 ? new ArrayList<>(this.palette) : this.palette; if(this.bitsPerEntry > 8) { - oldStates = new ArrayList(this.states); - this.states.clear(); - this.bitsPerEntry = 13; + this.palette.clear(); + this.bitsPerEntry = 14; } FlexibleStorage oldStorage = this.storage; - this.storage = new FlexibleStorage(this.bitsPerEntry, this.storage.getSize()); - for(int index = 0; index < this.storage.getSize(); index++) { - this.storage.set(index, this.bitsPerEntry <= 8 ? oldStorage.get(index) : oldStates.get(index)); - } + this.storage = oldStorage.transferData(this.bitsPerEntry, (index) -> + this.bitsPerEntry <= 8 ? oldStorage.get(index) : oldStates.get(index)); } - id = this.bitsPerEntry <= 8 ? this.states.indexOf(state) : state; + id = this.bitsPerEntry <= 8 ? this.palette.indexOf(state) : state; } int ind = index(x, y, z); diff --git a/src/main/java/com/github/steveice10/mc/protocol/data/game/chunk/FlexibleStorage.java b/src/main/java/com/github/steveice10/mc/protocol/data/game/chunk/FlexibleStorage.java index 27b4c38f..0884c7b1 100644 --- a/src/main/java/com/github/steveice10/mc/protocol/data/game/chunk/FlexibleStorage.java +++ b/src/main/java/com/github/steveice10/mc/protocol/data/game/chunk/FlexibleStorage.java @@ -4,6 +4,7 @@ import lombok.Data; import lombok.NonNull; import java.util.Arrays; +import java.util.function.IntFunction; @Data public class FlexibleStorage { @@ -12,34 +13,32 @@ public class FlexibleStorage { private final int size; private final long maxEntryValue; - public FlexibleStorage(int bitsPerEntry, int size) { - this(bitsPerEntry, new long[roundToNearest(size * bitsPerEntry, 64) / 64]); + private final char valuesPerLong; + private final int magicIndex; + private final long divideMultiply; + private final long divideAdd; + private final long divideShift; + + public FlexibleStorage(int bitsPerEntry) { + this(bitsPerEntry, new long[(4096 + (64 / bitsPerEntry) - 1) / (64 / bitsPerEntry)]); } public FlexibleStorage(int bitsPerEntry, @NonNull long[] data) { this.bitsPerEntry = bitsPerEntry; - if(bitsPerEntry <= 8) { - this.data = padArray(bitsPerEntry, Arrays.copyOf(data, data.length)); - } else { - this.data = padArray(bitsPerEntry, 14, Arrays.copyOf(data, data.length)); - } - this.size = data.length * 64 / this.bitsPerEntry; - this.maxEntryValue = (1L << this.bitsPerEntry) - 1; - } + this.data = Arrays.copyOf(data, data.length); + this.size = data.length * 64 / bitsPerEntry; + this.maxEntryValue = (1L << bitsPerEntry) - 1; - private static int roundToNearest(int value, int roundTo) { - if(roundTo == 0) { - return 0; - } else if(value == 0) { - return roundTo; - } else { - if(value < 0) { - roundTo *= -1; - } - - int remainder = value % roundTo; - return remainder != 0 ? value + roundTo - remainder : value; + this.valuesPerLong = (char) (64 / bitsPerEntry); + int expectedLength = (4096 + valuesPerLong - 1) / valuesPerLong; + if(data.length != expectedLength) { + throw new IllegalArgumentException("Expected " + expectedLength + " longs but got " + data.length + " longs"); } + + this.magicIndex = 3 * (valuesPerLong - 1); + this.divideMultiply = Integer.toUnsignedLong(MAGIC_VALUES[magicIndex]); + this.divideAdd = Integer.toUnsignedLong(MAGIC_VALUES[magicIndex + 1]); + this.divideShift = MAGIC_VALUES[magicIndex + 2]; } public int get(int index) { @@ -47,16 +46,9 @@ public class FlexibleStorage { throw new IndexOutOfBoundsException(); } - int bitIndex = index * this.bitsPerEntry; - int startIndex = bitIndex / 64; - int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64; - int startBitSubIndex = bitIndex % 64; - if(startIndex == endIndex) { - return (int) (this.data[startIndex] >>> startBitSubIndex & this.maxEntryValue); - } else { - int endBitSubIndex = 64 - startBitSubIndex; - return (int) ((this.data[startIndex] >>> startBitSubIndex | this.data[endIndex] << endBitSubIndex) & this.maxEntryValue); - } + int cellIndex = (int) (index * divideMultiply + divideAdd >> 32L >> divideShift); + int bitIndex = (index - cellIndex * valuesPerLong) * bitsPerEntry; + return (int) (data[cellIndex] >> bitIndex & maxEntryValue); } public void set(int index, int value) { @@ -68,18 +60,12 @@ public class FlexibleStorage { throw new IllegalArgumentException("Value cannot be outside of accepted range."); } - int bitIndex = index * this.bitsPerEntry; - int startIndex = bitIndex / 64; - int endIndex = ((index + 1) * this.bitsPerEntry - 1) / 64; - int startBitSubIndex = bitIndex % 64; - this.data[startIndex] = this.data[startIndex] & ~(this.maxEntryValue << startBitSubIndex) | ((long) value & this.maxEntryValue) << startBitSubIndex; - if(startIndex != endIndex) { - int endBitSubIndex = 64 - startBitSubIndex; - this.data[endIndex] = this.data[endIndex] >>> endBitSubIndex << endBitSubIndex | ((long) value & this.maxEntryValue) >> endBitSubIndex; - } + int cellIndex = (int) (index * divideMultiply + divideAdd >> 32L >> divideShift); + int bitIndex = (index - cellIndex * valuesPerLong) * bitsPerEntry; + data[cellIndex] &= ~(maxEntryValue << bitIndex) | (value & maxEntryValue) << bitIndex; } - private static final int[] MAGIC_CHUNK_VALUES = { + private static final int[] MAGIC_VALUES = { -1, -1, 0, Integer.MIN_VALUE, 0, 0, 1431655765, 1431655765, 0, Integer.MIN_VALUE, 0, 1, 858993459, 858993459, 0, 715827882, 715827882, 0, 613566756, 613566756, 0, Integer.MIN_VALUE, 0, 2, 477218588, 477218588, 0, 429496729, 429496729, 0, @@ -101,35 +87,11 @@ public class FlexibleStorage { 70409299, 70409299, 0, 69273666, 69273666, 0, 68174084, 68174084, 0, Integer.MIN_VALUE, 0, 5 }; - private static long[] padArray(int bitsPerEntry, long[] oldData) { - return padArray(bitsPerEntry, bitsPerEntry, oldData); - } - - private static long[] padArray(int bitsPerEntry, int newBitsPerEntry, long[] oldData) { - long maxEntryValue = (1L << bitsPerEntry) - 1; - char valuesPerLong = (char) (64 / newBitsPerEntry); - int magicIndex = (valuesPerLong - 1) * 3; - long divideMultiply = Integer.toUnsignedLong(MAGIC_CHUNK_VALUES[magicIndex]); - long divideAdd = Integer.toUnsignedLong(MAGIC_CHUNK_VALUES[magicIndex + 1]); - int divideShift = MAGIC_CHUNK_VALUES[magicIndex + 2]; - long[] data = new long[(4096 + valuesPerLong - 1) / valuesPerLong]; - for(int index = 0; index < 4096; index++) { - int bitIndex = index * bitsPerEntry; - int startIndex = bitIndex / 64; - int endIndex = ((index + 1) * bitsPerEntry - 1) / 64; - int startBitSubIndex = bitIndex % 64; - int value; - if(startIndex != endIndex) { - int endBitSubIndex = 64 - startBitSubIndex; - value = (int) ((oldData[startIndex] >>> startBitSubIndex | oldData[endIndex] << endBitSubIndex) & maxEntryValue); - } else { - value = (int) (oldData[startIndex] >>> startBitSubIndex & maxEntryValue); - } - - int cellIndex = (int) (index * divideMultiply + divideAdd >> 32L >> divideShift); - int newBitIndex = (index - cellIndex * valuesPerLong) * newBitsPerEntry; - data[cellIndex] = data[cellIndex] & ~(maxEntryValue << newBitIndex) | (value & maxEntryValue) << newBitIndex; + public FlexibleStorage transferData(int newBitsPerEntry, IntFunction valueGetter) { + FlexibleStorage newStorage = new FlexibleStorage(newBitsPerEntry); + for(int i = 0; i < 4096; i++) { + newStorage.set(i, valueGetter.apply(i)); } - return data; + return newStorage; } } diff --git a/src/main/java/com/github/steveice10/mc/protocol/packet/ingame/server/world/ServerChunkDataPacket.java b/src/main/java/com/github/steveice10/mc/protocol/packet/ingame/server/world/ServerChunkDataPacket.java index 80ee77e5..b0737cd2 100644 --- a/src/main/java/com/github/steveice10/mc/protocol/packet/ingame/server/world/ServerChunkDataPacket.java +++ b/src/main/java/com/github/steveice10/mc/protocol/packet/ingame/server/world/ServerChunkDataPacket.java @@ -37,10 +37,6 @@ public class ServerChunkDataPacket implements Packet { CompoundTag heightMaps = NBT.read(in); int[] biomeData = fullChunk ? in.readInts(1024) : null; byte[] data = in.readBytes(in.readVarInt()); - CompoundTag[] tileEntities = new CompoundTag[in.readVarInt()]; - for(int i = 0; i < tileEntities.length; i++) { - tileEntities[i] = NBT.read(in); - } NetInput dataIn = new StreamNetInput(new ByteArrayInputStream(data)); Chunk[] chunks = new Chunk[16]; @@ -50,6 +46,11 @@ public class ServerChunkDataPacket implements Packet { } } + CompoundTag[] tileEntities = new CompoundTag[in.readVarInt()]; + for(int i = 0; i < tileEntities.length; i++) { + tileEntities[i] = NBT.read(in); + } + this.column = new Column(x, z, ignoreOldData, chunks, tileEntities, heightMaps, biomeData); }