From 699b12e03639c06e7da3f45a97e10dcf8c414530 Mon Sep 17 00:00:00 2001 From: bixilon Date: Sun, 14 Jun 2020 21:04:54 +0200 Subject: [PATCH] PacketChunkData support, outsource chunk parsing into ChunkUtil.java --- .../clientbound/play/PacketChunkBulk.java | 76 +------------- .../clientbound/play/PacketChunkData.java | 67 +++++++++++++ .../protocol/protocol/PacketHandler.java | 4 + .../minosoft/protocol/protocol/Protocol.java | 1 + .../de/bixilon/minosoft/util/ChunkUtil.java | 99 +++++++++++++++++++ 5 files changed, 175 insertions(+), 72 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java create mode 100644 src/main/java/de/bixilon/minosoft/util/ChunkUtil.java diff --git a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkBulk.java b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkBulk.java index 3eb40c63a..b1e0890d2 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkBulk.java +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkBulk.java @@ -13,18 +13,15 @@ package de.bixilon.minosoft.protocol.packets.clientbound.play; -import de.bixilon.minosoft.game.datatypes.blocks.Block; import de.bixilon.minosoft.game.datatypes.world.Chunk; import de.bixilon.minosoft.game.datatypes.world.ChunkLocation; -import de.bixilon.minosoft.game.datatypes.world.ChunkNibble; -import de.bixilon.minosoft.game.datatypes.world.ChunkNibbleLocation; import de.bixilon.minosoft.logging.Log; 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.BitByte; +import de.bixilon.minosoft.util.ChunkUtil; import de.bixilon.minosoft.util.Util; import java.util.HashMap; @@ -37,8 +34,7 @@ public class PacketChunkBulk implements ClientboundPacket { public void read(InPacketBuffer buffer, ProtocolVersion v) { switch (v) { case VERSION_1_7_10: - // ToDo only implement once, not twice (chunk data and chunk bulk) - short chunkColumnCount = buffer.readShort(); + short chunkCount = buffer.readShort(); int dataLen = buffer.readInteger(); boolean containsSkyLight = buffer.readBoolean(); @@ -46,77 +42,13 @@ public class PacketChunkBulk implements ClientboundPacket { InByteBuffer decompressed = Util.decompress(buffer.readBytes(dataLen)); // chunk meta data - int read = 0; - for (int i = 0; i < chunkColumnCount; i++) { + for (int i = 0; i < chunkCount; i++) { int x = buffer.readInteger(); int z = buffer.readInteger(); short sectionBitMask = buffer.readShort(); short addBitMask = buffer.readShort(); - - //chunk - byte sections = BitByte.getBitCount(sectionBitMask); - int totalBytes = 4096 * sections; // 16 * 16 * 16 * sections; Section Width * Section Height * Section Width * sections - int halfBytes = totalBytes / 2; // half bytes - - byte[] blockTypes = decompressed.readBytes(totalBytes); - byte[] meta = decompressed.readBytes(halfBytes); - byte[] light = decompressed.readBytes(halfBytes); - byte[] skyLight = null; - if (containsSkyLight) { - skyLight = decompressed.readBytes(halfBytes); - } - byte[] addBlockTypes = decompressed.readBytes(Integer.bitCount(addBitMask) * 2048); // 16 * 16 * 16 * addBlocks / 2 - //ToDo test add Block Types - byte[] biomes = decompressed.readBytes(256); - - //parse data - int arrayPos = 0; - HashMap nibbleMap = new HashMap<>(); - for (byte c = 0; c < 16; c++) { // max sections per chunks in chunk column - if (BitByte.isBitSet(sectionBitMask, c)) { - - HashMap blockMap = new HashMap<>(); - - for (int nibbleY = 0; nibbleY < 16; nibbleY++) { - for (int nibbleZ = 0; nibbleZ < 16; nibbleZ++) { - for (int nibbleX = 0; nibbleX < 16; nibbleX++) { - - short singeBlockId = blockTypes[arrayPos]; - byte singleMeta; - // get block meta and shift and add (merge) id if needed - if (arrayPos % 2 == 0) { - // high bits - singleMeta = BitByte.getLow4Bits(meta[arrayPos / 2]); - if (BitByte.isBitSet(addBitMask, c)) { - singeBlockId = (short) ((singeBlockId << 4) | BitByte.getHigh4Bits(addBlockTypes[arrayPos / 2])); - } - } else { - // low 4 bits - singleMeta = BitByte.getHigh4Bits(meta[arrayPos / 2]); - - if (BitByte.isBitSet(addBitMask, c)) { - singeBlockId = (short) ((singeBlockId << 4) | BitByte.getLow4Bits(addBlockTypes[arrayPos / 2])); - } - } - - - // ToDo light, biome - Block block = Block.byLegacy(singeBlockId, singleMeta); - if (block == Block.AIR) { - continue; - } - blockMap.put(new ChunkNibbleLocation(nibbleX, nibbleY, nibbleZ), block); - arrayPos++; - } - } - } - nibbleMap.put(c, new ChunkNibble(blockMap)); - - } - - } - chunkMap.put(new ChunkLocation(x, z), new Chunk(nibbleMap)); + chunkMap.put(new ChunkLocation(x, z), ChunkUtil.readChunkPacket(v, decompressed, sectionBitMask, addBitMask, true, containsSkyLight)); } break; } 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 new file mode 100644 index 000000000..093420069 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/packets/clientbound/play/PacketChunkData.java @@ -0,0 +1,67 @@ +/* + * 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.protocol.packets.clientbound.play; + +import de.bixilon.minosoft.game.datatypes.world.Chunk; +import de.bixilon.minosoft.game.datatypes.world.ChunkLocation; +import de.bixilon.minosoft.logging.Log; +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; + +public class PacketChunkData implements ClientboundPacket { + ChunkLocation location; + Chunk chunk; + + + @Override + public void read(InPacketBuffer buffer, ProtocolVersion v) { + switch (v) { + case VERSION_1_7_10: + this.location = new ChunkLocation(buffer.readInteger(), buffer.readInteger()); + boolean groundUpContinuous = buffer.readBoolean(); + short sectionBitMask = buffer.readShort(); + short addBitMask = buffer.readShort(); + + // decompress chunk data + InByteBuffer decompressed = Util.decompress(buffer.readBytes(buffer.readInteger())); + + chunk = ChunkUtil.readChunkPacket(v, decompressed, sectionBitMask, addBitMask, groundUpContinuous, true); + break; + } + + } + + @Override + public void log() { + Log.protocol(String.format("Chunk packet received (chunk: %s)", location.toString())); + } + + public ChunkLocation getLocation() { + return location; + } + + public Chunk getChunk() { + return chunk; + } + + @Override + public void handle(PacketHandler h) { + h.handle(this); + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java index 1ff071f6d..91af317be 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java @@ -239,4 +239,8 @@ public class PacketHandler { public void handle(PacketSpawnWeatherEntity pkg) { //ToDo } + + public void handle(PacketChunkData pkg) { + connection.getPlayer().getWorld().setChunk(pkg.getLocation(), pkg.getChunk()); + } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/Protocol.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/Protocol.java index b4ffe240c..ad0fe1843 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/Protocol.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/Protocol.java @@ -81,5 +81,6 @@ public interface Protocol { packetClassMapping.put(Packets.Clientbound.PLAY_SPAWN_OBJECT, PacketSpawnObject.class); packetClassMapping.put(Packets.Clientbound.PLAY_SPAWN_EXPERIENCE_ORB, PacketSpawnExperienceOrb.class); packetClassMapping.put(Packets.Clientbound.PLAY_SPAWN_WEATHER_ENTITY, PacketSpawnWeatherEntity.class); + packetClassMapping.put(Packets.Clientbound.PLAY_CHUNK_DATA, PacketChunkData.class); } } \ No newline at end of file diff --git a/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java new file mode 100644 index 000000000..bdec887a4 --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/util/ChunkUtil.java @@ -0,0 +1,99 @@ +/* + * 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.util; + +import de.bixilon.minosoft.game.datatypes.blocks.Block; +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.protocol.protocol.InByteBuffer; +import de.bixilon.minosoft.protocol.protocol.ProtocolVersion; + +import java.util.HashMap; + +public class ChunkUtil { + public static Chunk readChunkPacket(ProtocolVersion v, InByteBuffer buffer, short sectionBitMask, short addBitMask, boolean groundUpContinuous, boolean containsSkyLight) { + + switch (v) { + case VERSION_1_7_10: + //chunk + byte sections = BitByte.getBitCount(sectionBitMask); + int totalBytes = 4096 * sections; // 16 * 16 * 16 * sections; Section Width * Section Height * Section Width * sections + int halfBytes = totalBytes / 2; // half bytes + + byte[] blockTypes = buffer.readBytes(totalBytes); + byte[] meta = buffer.readBytes(halfBytes); + byte[] light = buffer.readBytes(halfBytes); + byte[] skyLight = null; + if (containsSkyLight) { + skyLight = buffer.readBytes(halfBytes); + } + byte[] addBlockTypes = buffer.readBytes(Integer.bitCount(addBitMask) * 2048); // 16 * 16 * 16 * addBlocks / 2 + if (groundUpContinuous) { + byte[] biomes = buffer.readBytes(256); + } + + //parse data + int arrayPos = 0; + HashMap nibbleMap = new HashMap<>(); + for (byte c = 0; c < 16; c++) { // max sections per chunks in chunk column + if (BitByte.isBitSet(sectionBitMask, c)) { + + HashMap blockMap = new HashMap<>(); + + for (int nibbleY = 0; nibbleY < 16; nibbleY++) { + for (int nibbleZ = 0; nibbleZ < 16; nibbleZ++) { + for (int nibbleX = 0; nibbleX < 16; nibbleX++) { + + short singeBlockId = blockTypes[arrayPos]; + byte singleMeta; + // get block meta and shift and add (merge) id if needed + if (arrayPos % 2 == 0) { + // high bits + singleMeta = BitByte.getLow4Bits(meta[arrayPos / 2]); + if (BitByte.isBitSet(addBitMask, c)) { + singeBlockId = (short) ((singeBlockId << 4) | BitByte.getHigh4Bits(addBlockTypes[arrayPos / 2])); + } + } else { + // low 4 bits + singleMeta = BitByte.getHigh4Bits(meta[arrayPos / 2]); + + if (BitByte.isBitSet(addBitMask, c)) { + singeBlockId = (short) ((singeBlockId << 4) | BitByte.getLow4Bits(addBlockTypes[arrayPos / 2])); + } + } + + + // ToDo light, biome + Block block = Block.byLegacy(singeBlockId, singleMeta); + if (block == Block.AIR) { + continue; + } + blockMap.put(new ChunkNibbleLocation(nibbleX, nibbleY, nibbleZ), block); + arrayPos++; + } + } + } + nibbleMap.put(c, new ChunkNibble(blockMap)); + + } + + } + return new Chunk(nibbleMap); + + } + throw new RuntimeException("Could not parse chunk!"); + } + +}