diff --git a/src/main/java/org/spacehq/mc/protocol/MinecraftProtocol.java b/src/main/java/org/spacehq/mc/protocol/MinecraftProtocol.java index 125a2d36..a399fe58 100644 --- a/src/main/java/org/spacehq/mc/protocol/MinecraftProtocol.java +++ b/src/main/java/org/spacehq/mc/protocol/MinecraftProtocol.java @@ -56,12 +56,12 @@ public class MinecraftProtocol extends PacketProtocol { } public MinecraftProtocol(SubProtocol subProtocol) { - if(subProtocol != SubProtocol.LOGIN && subProtocol != SubProtocol.STATUS) { + if (subProtocol != SubProtocol.LOGIN && subProtocol != SubProtocol.STATUS) { throw new IllegalArgumentException("Only login and status modes are permitted."); } this.subProtocol = subProtocol; - if(subProtocol == SubProtocol.LOGIN) { + if (subProtocol == SubProtocol.LOGIN) { this.profile = new GameProfile((UUID) null, "Player"); } } @@ -84,7 +84,7 @@ public class MinecraftProtocol extends PacketProtocol { String clientToken = UUID.randomUUID().toString(); AuthenticationService auth = new AuthenticationService(clientToken, authProxy); auth.setUsername(username); - if(token) { + if (token) { auth.setAccessToken(using); } else { auth.setPassword(using); @@ -126,7 +126,7 @@ public class MinecraftProtocol extends PacketProtocol { @Override public void newClientSession(Client client, Session session) { - if(this.profile != null) { + if (this.profile != null) { session.setFlag(MinecraftConstants.PROFILE_KEY, this.profile); session.setFlag(MinecraftConstants.ACCESS_TOKEN_KEY, this.accessToken); } @@ -144,7 +144,7 @@ public class MinecraftProtocol extends PacketProtocol { protected void enableEncryption(Key key) { try { this.encrypt = new AESEncryption(key); - } catch(GeneralSecurityException e) { + } catch (GeneralSecurityException e) { throw new Error("Failed to enable protocol encryption.", e); } } @@ -155,9 +155,9 @@ public class MinecraftProtocol extends PacketProtocol { protected void setSubProtocol(SubProtocol subProtocol, boolean client, Session session) { this.clearPackets(); - switch(subProtocol) { + switch (subProtocol) { case HANDSHAKE: - if(client) { + if (client) { this.initClientHandshake(session); } else { this.initServerHandshake(session); @@ -165,7 +165,7 @@ public class MinecraftProtocol extends PacketProtocol { break; case LOGIN: - if(client) { + if (client) { this.initClientLogin(session); } else { this.initServerLogin(session); @@ -173,7 +173,7 @@ public class MinecraftProtocol extends PacketProtocol { break; case GAME: - if(client) { + if (client) { this.initClientGame(session); } else { this.initServerGame(session); @@ -181,7 +181,7 @@ public class MinecraftProtocol extends PacketProtocol { break; case STATUS: - if(client) { + if (client) { this.initClientStatus(session); } else { this.initServerStatus(session); diff --git a/src/main/java/org/spacehq/mc/protocol/data/game/chunk/Column.java b/src/main/java/org/spacehq/mc/protocol/data/game/chunk/Column.java index af7d1cb1..5dae6223 100644 --- a/src/main/java/org/spacehq/mc/protocol/data/game/chunk/Column.java +++ b/src/main/java/org/spacehq/mc/protocol/data/game/chunk/Column.java @@ -1,19 +1,21 @@ package org.spacehq.mc.protocol.data.game.chunk; -//TODO: Chunk Data (0x20) now also sends all tile entities in the chunk (at the end of the packet) +import org.spacehq.opennbt.tag.builtin.CompoundTag; + public class Column { private int x; private int z; private Chunk chunks[]; private byte biomeData[]; + private CompoundTag tileEntities[]; private boolean skylight; - public Column(int x, int z, Chunk chunks[]) { - this(x, z, chunks, null); + public Column(int x, int z, Chunk chunks[], CompoundTag tileEntities[]) { + this(x, z, chunks, null, tileEntities); } - public Column(int x, int z, Chunk chunks[], byte biomeData[]) { + public Column(int x, int z, Chunk chunks[], byte biomeData[], CompoundTag tileEntities[]) { if(chunks.length != 16) { throw new IllegalArgumentException("Chunk array length must be 16."); } @@ -24,9 +26,9 @@ public class Column { this.skylight = false; boolean noSkylight = false; - for(int index = 0; index < chunks.length; index++) { - if(chunks[index] != null) { - if(chunks[index].getSkyLight() == null) { + for (Chunk chunk : chunks) { + if (chunk != null) { + if (chunk.getSkyLight() == null) { noSkylight = true; } else { this.skylight = true; @@ -42,6 +44,7 @@ public class Column { this.z = z; this.chunks = chunks; this.biomeData = biomeData; + this.tileEntities = tileEntities; } public int getX() { @@ -64,6 +67,10 @@ public class Column { return this.biomeData; } + public CompoundTag[] getTileEntities() { + return this.tileEntities; + } + public boolean hasSkylight() { return this.skylight; } diff --git a/src/main/java/org/spacehq/mc/protocol/packet/ingame/server/world/ServerChunkDataPacket.java b/src/main/java/org/spacehq/mc/protocol/packet/ingame/server/world/ServerChunkDataPacket.java index a67ad4a6..81a6c8ae 100644 --- a/src/main/java/org/spacehq/mc/protocol/packet/ingame/server/world/ServerChunkDataPacket.java +++ b/src/main/java/org/spacehq/mc/protocol/packet/ingame/server/world/ServerChunkDataPacket.java @@ -3,6 +3,7 @@ package org.spacehq.mc.protocol.packet.ingame.server.world; import org.spacehq.mc.protocol.data.game.chunk.Column; import org.spacehq.mc.protocol.util.NetUtil; import org.spacehq.mc.protocol.util.ReflectionToString; +import org.spacehq.opennbt.tag.builtin.CompoundTag; import org.spacehq.packetlib.io.NetInput; import org.spacehq.packetlib.io.NetOutput; import org.spacehq.packetlib.io.stream.StreamNetOutput; @@ -33,8 +34,9 @@ public class ServerChunkDataPacket implements Packet { boolean fullChunk = in.readBoolean(); int chunkMask = in.readVarInt(); byte data[] = in.readBytes(in.readVarInt()); + byte tileEntityData[] = in.readBytes(in.readVarInt()); - this.column = NetUtil.readColumn(data, x, z, fullChunk, false, chunkMask); + this.column = NetUtil.readColumn(data, x, z, fullChunk, false, chunkMask, tileEntityData); } @Override @@ -49,6 +51,16 @@ public class ServerChunkDataPacket implements Packet { out.writeVarInt(mask); out.writeVarInt(byteOut.size()); out.writeBytes(byteOut.toByteArray(), byteOut.size()); + + ByteArrayOutputStream tileEntitiesByteOut = new ByteArrayOutputStream(); + NetOutput tileEntitiesNetOut = new StreamNetOutput(tileEntitiesByteOut); + + for (CompoundTag compoundTag : column.getTileEntities()) { + NetUtil.writeNBT(tileEntitiesNetOut, compoundTag); + } + out.writeVarInt(tileEntitiesByteOut.size()); + out.writeBytes(tileEntitiesByteOut.toByteArray(), tileEntitiesByteOut.size()); + } @Override diff --git a/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java b/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java index d678ce6f..a771452f 100644 --- a/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java +++ b/src/main/java/org/spacehq/mc/protocol/util/NetUtil.java @@ -236,8 +236,9 @@ public class NetUtil { out.writeByte(255); } - public static Column readColumn(byte data[], int x, int z, boolean fullChunk, boolean hasSkylight, int mask) throws IOException { + public static Column readColumn(byte data[], int x, int z, boolean fullChunk, boolean hasSkylight, int mask, byte tileEntityData[]) throws IOException { NetInput in = new StreamNetInput(new ByteArrayInputStream(data)); + NetInput tileEntityIn = new StreamNetInput(new ByteArrayInputStream(tileEntityData)); Exception ex = null; Column column = null; try { @@ -256,14 +257,20 @@ public class NetUtil { biomeData = in.readBytes(256); } - column = new Column(x, z, chunks, biomeData); + int available = tileEntityIn.available(); + List tileEntities = new ArrayList(); + while (available > 0) { + tileEntities.add(NetUtil.readNBT(tileEntityIn)); + available = tileEntityIn.available(); + } + column = new Column(x, z, chunks, biomeData, tileEntities.toArray(new CompoundTag[tileEntities.size()])); } catch(Exception e) { ex = e; } // Unfortunately, this is needed to detect whether the chunks contain skylight or not. if((in.available() > 0 || ex != null) && !hasSkylight) { - return readColumn(data, x, z, fullChunk, true, mask); + return readColumn(data, x, z, fullChunk, true, mask, tileEntityData); } else if(ex != null) { throw new IOException("Failed to read chunk data.", ex); }