diff --git a/src/main/java/de/bixilon/minosoft/game/datatypes/blocks/Blocks.java b/src/main/java/de/bixilon/minosoft/game/datatypes/blocks/Blocks.java index 7d7d5284a..1e6adf5cc 100644 --- a/src/main/java/de/bixilon/minosoft/game/datatypes/blocks/Blocks.java +++ b/src/main/java/de/bixilon/minosoft/game/datatypes/blocks/Blocks.java @@ -497,6 +497,11 @@ public class Blocks { blockList.add(block); } + // set nullBlock + if (block.getIdentifier().equals("air")) { + nullBlock = block; + } + int blockId = getBlockId(statesJSON, version); checkAndCrashIfBlockIsIn(blockId, identifierName, versionMapping, version); versionMapping.put(blockId, block); diff --git a/src/main/java/de/bixilon/minosoft/game/datatypes/world/palette/DirectPalette.java b/src/main/java/de/bixilon/minosoft/game/datatypes/world/palette/DirectPalette.java new file mode 100644 index 000000000..a71237cbe --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/game/datatypes/world/palette/DirectPalette.java @@ -0,0 +1,49 @@ +/* + * Codename Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.game.datatypes.world.palette; + +import de.bixilon.minosoft.game.datatypes.blocks.Block; +import de.bixilon.minosoft.game.datatypes.blocks.Blocks; +import de.bixilon.minosoft.protocol.protocol.InByteBuffer; +import de.bixilon.minosoft.protocol.protocol.ProtocolVersion; + +public class DirectPalette implements Palette { + ProtocolVersion version; + + @Override + public Block byId(int id) { + return Blocks.getBlock(id, version); + } + + @Override + public byte getBitsPerBlock() { + switch (version) { + case VERSION_1_9_4: + case VERSION_1_10: + case VERSION_1_11_2: + case VERSION_1_12_2: + return 13; + case VERSION_1_13_2: + case VERSION_1_14_4: + return 14; + } + return -1; + } + + @Override + public void read(InByteBuffer buffer) { + this.version = buffer.getVersion(); + buffer.readVarInt(); + } +} diff --git a/src/main/java/de/bixilon/minosoft/game/datatypes/world/palette/IndirectPalette.java b/src/main/java/de/bixilon/minosoft/game/datatypes/world/palette/IndirectPalette.java new file mode 100644 index 000000000..373ab9033 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/game/datatypes/world/palette/IndirectPalette.java @@ -0,0 +1,65 @@ +/* + * Codename Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.game.datatypes.world.palette; + +import com.google.common.collect.HashBiMap; +import de.bixilon.minosoft.game.datatypes.blocks.Block; +import de.bixilon.minosoft.game.datatypes.blocks.Blocks; +import de.bixilon.minosoft.protocol.protocol.InByteBuffer; +import de.bixilon.minosoft.protocol.protocol.ProtocolVersion; + +public class IndirectPalette implements Palette { + ProtocolVersion version; + HashBiMap map = HashBiMap.create(); + byte bitsPerBlock; + + public IndirectPalette(byte bitsPerBlock) { + this.bitsPerBlock = bitsPerBlock; + } + + @Override + public Block byId(int id) { + Block block; + if (map.containsKey(id)) { + block = Blocks.getBlock(map.get(id), version); + } else { + block = Blocks.getBlock(id, version); + } + return block; + } + + @Override + public byte getBitsPerBlock() { + return bitsPerBlock; + } + + @Override + public void read(InByteBuffer buffer) { + this.version = buffer.getVersion(); + switch (version) { + case VERSION_1_9_4: + case VERSION_1_10: + case VERSION_1_11_2: + case VERSION_1_12_2: + case VERSION_1_13_2: + case VERSION_1_14_4: { + int paletteLength = buffer.readVarInt(); + for (int i = 0; i < paletteLength; i++) { + map.put(i, buffer.readVarInt()); + } + break; + } + } + } +} diff --git a/src/main/java/de/bixilon/minosoft/game/datatypes/world/palette/Palette.java b/src/main/java/de/bixilon/minosoft/game/datatypes/world/palette/Palette.java new file mode 100644 index 000000000..090be6a77 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/game/datatypes/world/palette/Palette.java @@ -0,0 +1,34 @@ +/* + * Codename Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program 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. + * + * This program 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 this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.game.datatypes.world.palette; + +import de.bixilon.minosoft.game.datatypes.blocks.Block; +import de.bixilon.minosoft.protocol.protocol.InByteBuffer; + +public interface Palette { + static Palette choosePalette(byte bitsPerBlock) { + if (bitsPerBlock <= 4) { + return new IndirectPalette((byte) 4); + } else if (bitsPerBlock <= 8) { + return new IndirectPalette(bitsPerBlock); + } + return new DirectPalette(); + } + + Block byId(int id); + + byte getBitsPerBlock(); + + void read(InByteBuffer buffer); +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java index 4ea902946..4c04fb9c2 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java @@ -23,6 +23,7 @@ import de.bixilon.minosoft.protocol.packets.ClientboundPacket; import de.bixilon.minosoft.protocol.protocol.InByteBuffer; import de.bixilon.minosoft.protocol.protocol.InPacketBuffer; import de.bixilon.minosoft.protocol.protocol.PacketHandler; +import de.bixilon.minosoft.protocol.protocol.ProtocolVersion; import de.bixilon.minosoft.util.ChunkUtil; import de.bixilon.minosoft.util.Util; @@ -31,6 +32,7 @@ import java.util.HashMap; public class PacketChunkData implements ClientboundPacket { ChunkLocation location; Chunk chunk; + CompoundTag heightMap; HashMap blockEntities = new HashMap<>(); @@ -70,10 +72,14 @@ public class PacketChunkData implements ClientboundPacket { case VERSION_1_10: case VERSION_1_11_2: case VERSION_1_12_2: - case VERSION_1_13_2: { + case VERSION_1_13_2: + case VERSION_1_14_4: { this.location = new ChunkLocation(buffer.readInt(), buffer.readInt()); boolean groundUpContinuous = buffer.readBoolean(); short sectionBitMask = (short) buffer.readVarInt(); + if (buffer.getVersion().getVersionNumber() >= ProtocolVersion.VERSION_1_14_4.getVersionNumber()) { + heightMap = buffer.readNBT(); + } int size = buffer.readVarInt(); int lastPos = buffer.getPosition(); @@ -109,6 +115,10 @@ public class PacketChunkData implements ClientboundPacket { return blockEntities; } + public CompoundTag getHeightMap() { + return heightMap; + } + @Override public void handle(PacketHandler h) { h.handle(this); diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketTags.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketTags.java index 061d1f7c4..163296521 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketTags.java +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketTags.java @@ -48,6 +48,7 @@ public class PacketTags implements ClientboundPacket { Tag[] ret = new Tag[buffer.readVarInt()]; switch (buffer.getVersion()) { case VERSION_1_13_2: + case VERSION_1_14_4: for (int i = 0; i < ret.length; i++) { ret[i] = new Tag(buffer.readString(), buffer.readVarIntArray(buffer.readVarInt())); } diff --git a/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java index d7965dd74..c01df3760 100644 --- a/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java +++ b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java @@ -18,6 +18,7 @@ import de.bixilon.minosoft.game.datatypes.blocks.Blocks; import de.bixilon.minosoft.game.datatypes.world.Chunk; import de.bixilon.minosoft.game.datatypes.world.ChunkNibble; import de.bixilon.minosoft.game.datatypes.world.ChunkNibbleLocation; +import de.bixilon.minosoft.game.datatypes.world.palette.Palette; import de.bixilon.minosoft.protocol.protocol.InByteBuffer; import de.bixilon.minosoft.protocol.protocol.ProtocolVersion; @@ -148,32 +149,20 @@ public class ChunkUtil { case VERSION_1_10: case VERSION_1_11_2: case VERSION_1_12_2: - case VERSION_1_13_2: { + case VERSION_1_13_2: + case VERSION_1_14_4: { // really big thanks to: https://wiki.vg/index.php?title=Chunk_Format&oldid=13712 HashMap nibbleMap = new HashMap<>(); for (byte c = 0; c < 16; c++) { // max sections per chunks in chunk column if (!BitByte.isBitSet(sectionBitMask, c)) { continue; } - - byte bitsPerBlock = buffer.readByte(); - if (bitsPerBlock < 4) { - bitsPerBlock = 4; - } else if (bitsPerBlock > 8) { - bitsPerBlock = (byte) ((buffer.getVersion().getVersionNumber() >= ProtocolVersion.VERSION_1_13_2.getVersionNumber()) ? 14 : 13); + if (buffer.getVersion().getVersionNumber() >= ProtocolVersion.VERSION_1_14_4.getVersionNumber()) { + buffer.readShort(); // block count } - boolean usePalette = (bitsPerBlock <= 8); - - int[] palette = null; - if (usePalette) { - palette = new int[buffer.readVarInt()]; - for (int i = 0; i < palette.length; i++) { - palette[i] = buffer.readVarInt(); - } - } else { - buffer.readVarInt(); - } - int individualValueMask = ((1 << bitsPerBlock) - 1); + Palette palette = Palette.choosePalette(buffer.readByte()); + palette.read(buffer); + int individualValueMask = ((1 << palette.getBitsPerBlock()) - 1); long[] data = buffer.readLongs(buffer.readVarInt()); @@ -184,9 +173,9 @@ public class ChunkUtil { int blockNumber = (((nibbleY * 16) + nibbleZ) * 16) + nibbleX; - int startLong = (blockNumber * bitsPerBlock) / 64; - int startOffset = (blockNumber * bitsPerBlock) % 64; - int endLong = ((blockNumber + 1) * bitsPerBlock - 1) / 64; + int startLong = (blockNumber * palette.getBitsPerBlock()) / 64; + int startOffset = (blockNumber * palette.getBitsPerBlock()) % 64; + int endLong = ((blockNumber + 1) * palette.getBitsPerBlock() - 1) / 64; int blockId; @@ -198,19 +187,7 @@ public class ChunkUtil { } blockId &= individualValueMask; - if (usePalette) { - // data should always be within the palette length - // If you're reading a power of 2 minus one (15, 31, 63, 127, etc...) that's out of bounds, - // you're probably reading light data instead - blockId = palette[blockId]; - } - Block block; - if (buffer.getVersion().getVersionNumber() >= ProtocolVersion.VERSION_1_13_2.getVersionNumber()) { - // no meta data anymore - block = Blocks.getBlock(blockId, buffer.getVersion()); - } else { - block = Blocks.getBlockByLegacy(blockId >>> 4, blockId & 0xF); - } + Block block = palette.byId(blockId); if (block == Blocks.nullBlock) { continue; } @@ -219,9 +196,11 @@ public class ChunkUtil { } } - byte[] light = buffer.readBytes(2048); - if (containsSkyLight) { - byte[] skyLight = buffer.readBytes(2048); + if (buffer.getVersion().getVersionNumber() < ProtocolVersion.VERSION_1_14_4.getVersionNumber()) { + byte[] light = buffer.readBytes(2048); + if (containsSkyLight) { + byte[] skyLight = buffer.readBytes(2048); + } } nibbleMap.put(c, new ChunkNibble(blockMap));