PacketChunkData support, outsource chunk parsing into ChunkUtil.java

This commit is contained in:
bixilon 2020-06-14 21:04:54 +02:00
parent d1b0eb3b2c
commit 699b12e036
No known key found for this signature in database
GPG Key ID: 5CAD791931B09AC4
5 changed files with 175 additions and 72 deletions

View File

@ -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<Byte, ChunkNibble> nibbleMap = new HashMap<>();
for (byte c = 0; c < 16; c++) { // max sections per chunks in chunk column
if (BitByte.isBitSet(sectionBitMask, c)) {
HashMap<ChunkNibbleLocation, Block> 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;
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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);
}
}

View File

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

View File

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

View File

@ -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 <https://www.gnu.org/licenses/>.
*
* 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<Byte, ChunkNibble> nibbleMap = new HashMap<>();
for (byte c = 0; c < 16; c++) { // max sections per chunks in chunk column
if (BitByte.isBitSet(sectionBitMask, c)) {
HashMap<ChunkNibbleLocation, Block> 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!");
}
}