Support 1.9.2 servers

This code basically reads 1.9 data so it can then be transformed to add
in the tile entities.
it geta a little funky when it already loaded it as a 1.8 chunk tho, so
i had to add some methods to the chunk api.

in short: I hate minecraft
This commit is contained in:
lenis0012 2016-06-23 04:00:59 +02:00
parent 4af98b220c
commit 20bbe7cec6
12 changed files with 283 additions and 32 deletions

View File

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

View File

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

View File

@ -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<CompoundTag> blockEntities;
@Override
public boolean isBiomeData() {
return false;
}
@Override
public byte[] getBiomeData() {
return new byte[0];
}
}

View File

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

View File

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

View File

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

View File

@ -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:
* <ul>
* <li>Block length/palette type</li>
* <li>Palette</li>
* <li>Block hashes/palette reference</li>
* </ul>
*
* @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.
*

View File

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

View File

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

View File

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

View File

@ -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.
*/

View File

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