diff --git a/src/main/java/us/myles/ViaVersion/api/minecraft/chunks/Chunk.java b/src/main/java/us/myles/ViaVersion/api/minecraft/chunks/Chunk.java index ff414c7df..634b9c343 100644 --- a/src/main/java/us/myles/ViaVersion/api/minecraft/chunks/Chunk.java +++ b/src/main/java/us/myles/ViaVersion/api/minecraft/chunks/Chunk.java @@ -1,4 +1,17 @@ package us.myles.ViaVersion.api.minecraft.chunks; public interface Chunk { + int getX(); + + int getZ(); + + ChunkSection[] getSections(); + + boolean isGroundUp(); + + boolean isBiomeData(); + + byte[] getBiomeData(); + + int getBitmask(); } diff --git a/src/main/java/us/myles/ViaVersion/api/minecraft/chunks/ChunkSection.java b/src/main/java/us/myles/ViaVersion/api/minecraft/chunks/ChunkSection.java new file mode 100644 index 000000000..bd6d59fa0 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/api/minecraft/chunks/ChunkSection.java @@ -0,0 +1,18 @@ +package us.myles.ViaVersion.api.minecraft.chunks; + +import io.netty.buffer.ByteBuf; + +/** + * Created by Lennart on 6/23/2016. + */ +public interface ChunkSection { + int getBlockId(int x, int y, int z); + + void writeBlocks(ByteBuf output) throws Exception; + + void writeBlockLight(ByteBuf output) throws Exception; + + boolean hasSkyLight(); + + void writeSkyLight(ByteBuf output) throws Exception; +} diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/Chunk1_9_3_4.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/Chunk1_9_3_4.java index 309429473..3592a9241 100644 --- a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/Chunk1_9_3_4.java +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/Chunk1_9_3_4.java @@ -14,7 +14,16 @@ public class Chunk1_9_3_4 implements Chunk { private int z; private boolean groundUp; private int bitmask; - private byte[] sections; + private ChunkSection1_9_3_4[] sections; List blockEntities; + @Override + public boolean isBiomeData() { + return false; + } + + @Override + public byte[] getBiomeData() { + return new byte[0]; + } } diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/Chunk1_9_3_4Type.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/Chunk1_9_3_4Type.java index 668011548..78d3d88f6 100644 --- a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/Chunk1_9_3_4Type.java +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/Chunk1_9_3_4Type.java @@ -31,7 +31,7 @@ public class Chunk1_9_3_4Type extends BaseChunkType { for (int i = 0; i < blockEntities; i++) { nbtData.add(Type.NBT.read(input)); } - return new Chunk1_9_3_4(chunkX, chunkZ, groundUp, primaryBitmask, sections, nbtData); + return new Chunk1_9_3_4(chunkX, chunkZ, groundUp, primaryBitmask, new ChunkSection1_9_3_4[] {new ChunkSection1_9_3_4(sections)}, nbtData); } @Override @@ -46,8 +46,8 @@ public class Chunk1_9_3_4Type extends BaseChunkType { buffer.writeBoolean(chunk.isGroundUp()); Type.VAR_INT.write(buffer, chunk.getBitmask()); - Type.VAR_INT.write(buffer, chunk.getSections().length); - buffer.writeBytes(chunk.getSections()); + Type.VAR_INT.write(buffer, chunk.getSections()[0].getData().length); + buffer.writeBytes(chunk.getSections()[0].getData()); // no block entities as it's earlier } diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/ChunkSection1_9_3_4.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/ChunkSection1_9_3_4.java new file mode 100644 index 000000000..85cc4d352 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3_4/ChunkSection1_9_3_4.java @@ -0,0 +1,44 @@ +package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3_4; + +import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection; + +/** + * Created by Lennart on 6/23/2016. + */ +public class ChunkSection1_9_3_4 implements ChunkSection { + private final byte[] data; + + public ChunkSection1_9_3_4(byte[] data) { + this.data = data; + } + + public byte[] getData() { + return data; + } + + @Override + public int getBlockId(int x, int y, int z) { + throw new UnsupportedOperationException("Invalid chunk type!"); + } + + @Override + public void writeBlocks(ByteBuf output) throws Exception { + throw new UnsupportedOperationException("Invalid chunk type!"); + } + + @Override + public void writeBlockLight(ByteBuf output) throws Exception { + throw new UnsupportedOperationException("Invalid chunk type!"); + } + + @Override + public boolean hasSkyLight() { + throw new UnsupportedOperationException("Invalid chunk type!"); + } + + @Override + public void writeSkyLight(ByteBuf output) throws Exception { + throw new UnsupportedOperationException("Invalid chunk type!"); + } +} diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/Chunk1_9_1_2Type.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/Chunk1_9_1_2Type.java index b40721ae8..fa881c54f 100644 --- a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/Chunk1_9_1_2Type.java +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/Chunk1_9_1_2Type.java @@ -2,18 +2,24 @@ package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import org.bukkit.World; import org.spacehq.opennbt.tag.builtin.CompoundTag; import us.myles.ViaVersion.api.minecraft.chunks.Chunk; +import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection; import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.api.type.types.minecraft.BaseChunkType; -import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.ChunkSection1_9to1_8; +import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.sotrage.ClientWorld; +import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks; import java.util.ArrayList; import java.util.BitSet; public class Chunk1_9_1_2Type extends BaseChunkType { - public Chunk1_9_1_2Type() { + private final ClientWorld clientWorld; + + public Chunk1_9_1_2Type(ClientWorld clientWorld) { super("1.9.1/2 Chunk"); + this.clientWorld = clientWorld; } @Override @@ -39,29 +45,27 @@ public class Chunk1_9_1_2Type extends BaseChunkType { if (!usedSections.get(i)) continue; // Section not set ChunkSection1_9_1_2 section = new ChunkSection1_9_1_2(); sections[i] = section; - // WIP. -// section.readBlocks(input); -// section.readBlockLight(input); -// section.readSkyLight(input); - /* - BlockStorage blocks = new BlockStorage(in); - NibbleArray3d blocklight = new NibbleArray3d(in, 2048); (1024 bytes) - NibbleArray3d skylight = hasSkylight ? new NibbleArray3d(in, 2048) : null; (1024 bytes) - chunks[index] = new Chunk(blocks, blocklight, skylight); - */ + section.readBlocks(input); + section.readBlockLight(input); + if(clientWorld.getEnvironment() == World.Environment.NORMAL) { + section.readSkyLight(input); + } } + byte[] biomeData = groundUp ? new byte[256] : null; - if (groundUp) + if (groundUp) { input.readBytes(biomeData); + } return new Chunk1_9_1_2(chunkX, chunkZ, groundUp, primaryBitmask, sections, biomeData, new ArrayList()); } @Override public void write(ByteBuf output, Chunk input) throws Exception { - if (!(input instanceof Chunk1_9_1_2)) - throw new Exception("Tried to send the wrong chunk type from 1.9.3-4 chunk: " + input.getClass()); - Chunk1_9_1_2 chunk = (Chunk1_9_1_2) input; +// if (!(input instanceof Chunk1_9_1_2)) +// throw new Exception("Tried to send the wrong chunk type from 1.9.3-4 chunk: " + input.getClass()); +// Chunk1_9_1_2 chunk = (Chunk1_9_1_2) input; + Chunk chunk = input; output.writeInt(chunk.getX()); output.writeInt(chunk.getZ()); @@ -71,7 +75,7 @@ public class Chunk1_9_1_2Type extends BaseChunkType { ByteBuf buf = Unpooled.buffer(); for (int i = 0; i < 16; i++) { - ChunkSection1_9_1_2 section = chunk.getSections()[i]; + ChunkSection section = chunk.getSections()[i]; if (section == null) continue; // Section not set section.writeBlocks(buf); section.writeBlockLight(buf); diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/ChunkSection1_9_1_2.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/ChunkSection1_9_1_2.java index 4cff93fd6..70109671e 100644 --- a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/ChunkSection1_9_1_2.java +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/ChunkSection1_9_1_2.java @@ -3,12 +3,14 @@ package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2; import com.google.common.collect.Lists; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection; import us.myles.ViaVersion.api.minecraft.chunks.NibbleArray; import us.myles.ViaVersion.api.type.Type; +import java.util.Arrays; import java.util.List; -public class ChunkSection1_9_1_2 { +public class ChunkSection1_9_1_2 implements ChunkSection { /** * Size (dimensions) of blocks in a chunks section. */ @@ -91,6 +93,82 @@ public class ChunkSection1_9_1_2 { return z << 8 | y << 4 | x; } + /** + * Read blocks from input stream. + * This reads all the block related data: + * + * + * @param input The buffer to read from. + * @throws Exception + */ + public void readBlocks(ByteBuf input) throws Exception { + // Clear current data +// Arrays.fill(blocks, 0); + palette.clear(); + + // Reaad bits per block + int bitsPerBlock = input.readByte(); + long maxEntryValue = (1L << bitsPerBlock) - 1; + + if(bitsPerBlock == 0) { + throw new RuntimeException("Aye me m8 its the global palette!"); + } + + // Read palette + int paletteLength = Type.VAR_INT.read(input); + for(int i = 0; i < paletteLength; i++) { + palette.add(Type.VAR_INT.read(input)); + } + + // Read blocks + int blockLength = Type.VAR_INT.read(input); + long[] blockData = new long[blockLength]; + for(int i = 0; i < blocks.length; i++) { + int bitIndex = i * bitsPerBlock; + int startIndex = bitIndex / 64; + int endIndex = ((i + 1) * bitsPerBlock - 1) / 64; + int startBitSubIndex = bitIndex % 64; + if(startIndex == endIndex) { + blocks[i] = (int) (blockData[startIndex] >>> startBitSubIndex & maxEntryValue); + } else { + int endBitSubIndex = 64 - startBitSubIndex; + blocks[i] = (int) ((blockData[startIndex] >>> startBitSubIndex | blockData[endIndex] << endBitSubIndex) & maxEntryValue); + } + } + } + + /** + * Read block light from buffer. + * + * @param input The buffer to read from + */ + public void readBlockLight(ByteBuf input) { + byte[] handle = new byte[LIGHT_LENGTH]; + input.readBytes(handle); + blockLight.setHandle(handle); + } + + /** + * Read sky light from buffer. + * Note: Only sent in overworld! + * + * @param input The buffer to read from + */ + public void readSkyLight(ByteBuf input) { + byte[] handle = new byte[LIGHT_LENGTH]; + input.readBytes(handle); + if(skyLight != null) { + skyLight.setHandle(handle); + return; + } + + this.skyLight = new NibbleArray(handle); + } + /** * Write the blocks to a buffer. * diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/Protocol1_9_3TO1_9_1_2.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/Protocol1_9_3TO1_9_1_2.java index 1a91bb68a..2437523a6 100644 --- a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/Protocol1_9_3TO1_9_1_2.java +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/Protocol1_9_3TO1_9_1_2.java @@ -6,11 +6,14 @@ import org.spacehq.opennbt.tag.builtin.StringTag; import us.myles.ViaVersion.api.PacketWrapper; import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.minecraft.Position; +import us.myles.ViaVersion.api.minecraft.chunks.Chunk; +import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection; import us.myles.ViaVersion.api.protocol.Protocol; import us.myles.ViaVersion.api.remapper.PacketHandler; import us.myles.ViaVersion.api.remapper.PacketRemapper; import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.packets.State; +import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.sotrage.ClientWorld; import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.Chunk1_9to1_8; import us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks.ChunkSection1_9to1_8; import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks; @@ -18,6 +21,7 @@ import us.myles.ViaVersion.protocols.protocol1_9to1_8.types.ChunkType; import java.util.ArrayList; import java.util.List; +import java.util.Objects; public class Protocol1_9_3TO1_9_1_2 extends Protocol { @Override @@ -74,15 +78,19 @@ public class Protocol1_9_3TO1_9_1_2 extends Protocol { handler(new PacketHandler() { @Override public void handle(PacketWrapper wrapper) throws Exception { - ClientChunks clientChunks = wrapper.user().get(ClientChunks.class); + ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); - ChunkType type = new ChunkType(clientChunks); + Chunk1_9_1_2Type type = new Chunk1_9_1_2Type(clientWorld); if (wrapper.isReadable(type, 0)) { - Chunk1_9to1_8 chunk = (Chunk1_9to1_8) wrapper.read(type); + Chunk chunk = wrapper.read(type); +// if(rawChunk instanceof Chunk1_9to1_8) { +// throw new RuntimeException("Sweet berry candies"); +// } +// Chunk1_9_1_2 chunk = (Chunk1_9_1_2) rawChunk; List tags = new ArrayList<>(); for (int i = 0; i < chunk.getSections().length; i++) { - ChunkSection1_9to1_8 section = chunk.getSections()[i]; + ChunkSection section = chunk.getSections()[i]; if (section == null) continue; @@ -99,18 +107,49 @@ public class Protocol1_9_3TO1_9_1_2 extends Protocol { wrapper.write(type, chunk); wrapper.write(Type.NBT_ARRAY, tags.toArray(new CompoundTag[0])); - } else { - wrapper.passthroughAll(); - wrapper.write(Type.VAR_INT, 0); } } }); } }); + + // Join (save dimension id) + registerOutgoing(State.PLAY, 0x23, 0x23, new PacketRemapper() { + @Override + public void registerMap() { + handler(new PacketHandler() { + @Override + public void handle(PacketWrapper wrapper) throws Exception { + ClientWorld clientChunks = wrapper.user().get(ClientWorld.class); + wrapper.passthrough(Type.INT); + wrapper.passthrough(Type.UNSIGNED_BYTE); + int dimensionId = wrapper.passthrough(Type.INT); + clientChunks.setEnvironment(dimensionId); + wrapper.passthroughAll(); + } + }); + } + }); + + // Respawn (save dimension id) + registerOutgoing(State.PLAY, 0x33, 0x33, new PacketRemapper() { + @Override + public void registerMap() { + handler(new PacketHandler() { + @Override + public void handle(PacketWrapper wrapper) throws Exception { + ClientWorld clientWorld = wrapper.user().get(ClientWorld.class); + int dimensionId = wrapper.passthrough(Type.INT); + clientWorld.setEnvironment(dimensionId); + wrapper.passthroughAll(); + } + }); + } + }); } @Override - public void init(UserConnection userConnection) { - + public void init(UserConnection user) { + user.put(new ClientWorld(user)); } } diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/sotrage/ClientWorld.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/sotrage/ClientWorld.java new file mode 100644 index 000000000..3acfd44fe --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_3to1_9_1_2/sotrage/ClientWorld.java @@ -0,0 +1,32 @@ +package us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.sotrage; + +import lombok.Getter; +import org.bukkit.World; +import us.myles.ViaVersion.api.data.StoredObject; +import us.myles.ViaVersion.api.data.UserConnection; + +@Getter +public class ClientWorld extends StoredObject { + private World.Environment environment; + + public ClientWorld(UserConnection user) { + super(user); + } + + public void setEnvironment(int environmentId) { + this.environment = getEnvFromId(environmentId); + } + + private World.Environment getEnvFromId(int id) { + switch(id) { + case -1: + return World.Environment.NETHER; + case 0: + return World.Environment.NORMAL; + case 1: + return World.Environment.THE_END; + default: + throw new IllegalArgumentException("Invalid environment id:" + id); + } + } +} diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/chunks/Chunk1_9to1_8.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/chunks/Chunk1_9to1_8.java index f994cf18a..5a254119c 100644 --- a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/chunks/Chunk1_9to1_8.java +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/chunks/Chunk1_9to1_8.java @@ -36,4 +36,14 @@ public class Chunk1_9to1_8 implements Chunk { public boolean hasBiomeData() { return biomeData != null && groundUp; } + + @Override + public boolean isBiomeData() { + return biomeData != null; + } + + @Override + public int getBitmask() { + return primaryBitmask; + } } diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/chunks/ChunkSection1_9to1_8.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/chunks/ChunkSection1_9to1_8.java index 7825bb2fb..142867562 100644 --- a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/chunks/ChunkSection1_9to1_8.java +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/chunks/ChunkSection1_9to1_8.java @@ -3,12 +3,13 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.chunks; import com.google.common.collect.Lists; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import us.myles.ViaVersion.api.minecraft.chunks.ChunkSection; import us.myles.ViaVersion.api.minecraft.chunks.NibbleArray; import us.myles.ViaVersion.api.type.Type; import java.util.List; -public class ChunkSection1_9to1_8 { +public class ChunkSection1_9to1_8 implements ChunkSection { /** * Size (dimensions) of blocks in a chunks section. */ diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/storage/ClientChunks.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/storage/ClientChunks.java index bc2147b05..c4f68b179 100644 --- a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/storage/ClientChunks.java +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_8/storage/ClientChunks.java @@ -3,7 +3,10 @@ package us.myles.ViaVersion.protocols.protocol1_9to1_8.storage; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import lombok.Getter; +import lombok.Setter; import org.bukkit.Bukkit; +import org.bukkit.World; +import org.bukkit.WorldType; import us.myles.ViaVersion.api.ViaVersion; import us.myles.ViaVersion.api.data.StoredObject; import us.myles.ViaVersion.api.data.UserConnection;