From ba624a2b4829368e14d68cbd40a286e00ad11f66 Mon Sep 17 00:00:00 2001 From: Myles Date: Tue, 17 May 2016 22:48:56 +0100 Subject: [PATCH] 1.9.4 backport (#399) Allow 1.9.4 spigot to handle 1.9, 1.9.1, 1.9.2 --- .../myles/ViaVersion/api/PacketWrapper.java | 8 ++ .../api/protocol/ProtocolPipeline.java | 22 +++-- .../api/protocol/ProtocolRegistry.java | 16 +++- .../protocols/base/BaseProtocol.java | 11 +++ .../protocol1_9_1_2to1_9_3/BlockEntity.java | 88 +++++++++++++++++++ .../protocol1_9_1_2to1_9_3/Chunk1_9_3.java | 19 ++++ .../Chunk1_9_3Type.java | 48 ++++++++++ .../Protocol1_9_1_2TO1_9_3.java | 71 +++++++++++++++ .../Protocol1_9TO1_9_1.java | 33 +++++++ 9 files changed, 306 insertions(+), 10 deletions(-) create mode 100644 src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/BlockEntity.java create mode 100644 src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Chunk1_9_3.java create mode 100644 src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Chunk1_9_3Type.java create mode 100644 src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Protocol1_9_1_2TO1_9_3.java create mode 100644 src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_9_1/Protocol1_9TO1_9_1.java diff --git a/src/main/java/us/myles/ViaVersion/api/PacketWrapper.java b/src/main/java/us/myles/ViaVersion/api/PacketWrapper.java index db94b0a66..aabf631ac 100644 --- a/src/main/java/us/myles/ViaVersion/api/PacketWrapper.java +++ b/src/main/java/us/myles/ViaVersion/api/PacketWrapper.java @@ -224,6 +224,14 @@ public class PacketWrapper { readableObjects.clear(); // :( } + /** + * Clear the packet, used if you have to change the packet completely + */ + public void clearPacket() { + clearInputBuffer(); + packetValues.clear(); + } + private void writeRemaining(ByteBuf output) { if (inputBuffer != null) { output.writeBytes(inputBuffer); diff --git a/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolPipeline.java b/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolPipeline.java index fd97e4620..04ab4b306 100644 --- a/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolPipeline.java +++ b/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolPipeline.java @@ -85,18 +85,14 @@ public class ProtocolPipeline extends Protocol { ProtocolRegistry.SERVER_PROTOCOL <= ProtocolVersion.v1_9_3.getId()) { PacketType type; - if (ProtocolRegistry.SERVER_PROTOCOL == ProtocolVersion.v1_8.getId()) { + if (ProtocolRegistry.SERVER_PROTOCOL <= ProtocolVersion.v1_8.getId()) { if (direction == Direction.INCOMING) { type = PacketType.findNewPacket(state, direction, originalID); } else { type = PacketType.findOldPacket(state, direction, originalID); } } else { - if (direction == Direction.INCOMING) { - type = PacketType.findOldPacket(state, direction, originalID); - } else { - type = PacketType.findNewPacket(state, direction, originalID); - } + type = PacketType.findNewPacket(state, direction, originalID); } // Filter :) This would be not hard coded too, sorry :( @@ -115,13 +111,19 @@ public class ProtocolPipeline extends Protocol { } String name = packet + "[" + userConnection.get(ProtocolInfo.class).getProtocolVersion() + "]"; ViaVersionPlugin plugin = (ViaVersionPlugin) ViaVersion.getInstance(); - plugin.getLogger().log(Level.INFO, "{0}: {1} {2} -> {3} [{4}]", + + String actualUsername = packetWrapper.user().get(ProtocolInfo.class).getUsername(); + String username = actualUsername != null ? actualUsername + " " : ""; + + plugin.getLogger().log(Level.INFO, "{0}{1}: {2} {3} -> {4} [{5}] Value: {6}", new Object[]{ + username, direction, state, originalID, packetWrapper.getId(), - name + name, + packetWrapper }); } } @@ -157,4 +159,8 @@ public class ProtocolPipeline extends Protocol { return false; } + + public List pipes() { + return protocolList; + } } diff --git a/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolRegistry.java b/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolRegistry.java index 25d3b65d3..0c05ea35f 100644 --- a/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolRegistry.java +++ b/src/main/java/us/myles/ViaVersion/api/protocol/ProtocolRegistry.java @@ -3,9 +3,11 @@ package us.myles.ViaVersion.api.protocol; import org.bukkit.Bukkit; import us.myles.ViaVersion.api.Pair; import us.myles.ViaVersion.protocols.base.BaseProtocol; +import us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3.Protocol1_9_1_2TO1_9_3; import us.myles.ViaVersion.protocols.protocol1_9_1to1_9.Protocol1_9_1TO1_9; import us.myles.ViaVersion.protocols.protocol1_9_3to1_9_1_2.Protocol1_9_3TO1_9_1_2; import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8; +import us.myles.ViaVersion.protocols.protocol1_9to1_9_1.Protocol1_9TO1_9_1; import java.util.*; @@ -24,6 +26,10 @@ public class ProtocolRegistry { registerProtocol(new Protocol1_9TO1_8(), Collections.singletonList(ProtocolVersion.v1_9.getId()), ProtocolVersion.v1_8.getId()); registerProtocol(new Protocol1_9_1TO1_9(), Arrays.asList(ProtocolVersion.v1_9_1.getId(), ProtocolVersion.v1_9_2.getId()), ProtocolVersion.v1_9.getId()); registerProtocol(new Protocol1_9_3TO1_9_1_2(), Arrays.asList(ProtocolVersion.v1_9_3.getId()), ProtocolVersion.v1_9_2.getId()); + // Only supported for 1.9.4 server to 1.9 (nothing else) + registerProtocol(new Protocol1_9TO1_9_1(), Arrays.asList(ProtocolVersion.v1_9.getId()), ProtocolVersion.v1_9_2.getId()); + registerProtocol(new Protocol1_9_1_2TO1_9_3(), Arrays.asList(ProtocolVersion.v1_9_1.getId(), ProtocolVersion.v1_9_2.getId()), ProtocolVersion.v1_9_3.getId()); + } /** @@ -84,6 +90,7 @@ public class ProtocolRegistry { * @return The path which has been generated, null if failed. */ private static List> getProtocolPath(List> current, int clientVersion, int serverVersion) { + if(clientVersion == serverVersion) return null; // We're already there if (current.size() > 50) return null; // Fail safe, protocol too complicated. // First check if there is any protocols for this @@ -97,6 +104,8 @@ public class ProtocolRegistry { return current; // Easy solution } // There might be a more advanced solution... So we'll see if any of the others can get us there + List> shortest = null; + for (Map.Entry entry : inputMap.entrySet()) { // Ensure it wasn't caught by the other loop if (!entry.getKey().equals(serverVersion)) { @@ -109,13 +118,16 @@ public class ProtocolRegistry { // Calculate the rest of the protocol using the current newCurrent = getProtocolPath(newCurrent, entry.getKey(), serverVersion); if (newCurrent != null) { - return newCurrent; + // If it's shorter then choose it + if (shortest == null || shortest.size() > newCurrent.size()) { + shortest = newCurrent; + } } } } } - return null; + return shortest; // null if none found } /** diff --git a/src/main/java/us/myles/ViaVersion/protocols/base/BaseProtocol.java b/src/main/java/us/myles/ViaVersion/protocols/base/BaseProtocol.java index 58daeaa38..d3de8842e 100644 --- a/src/main/java/us/myles/ViaVersion/protocols/base/BaseProtocol.java +++ b/src/main/java/us/myles/ViaVersion/protocols/base/BaseProtocol.java @@ -1,5 +1,6 @@ package us.myles.ViaVersion.protocols.base; +import org.apache.commons.lang.StringUtils; import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; @@ -22,6 +23,7 @@ import us.myles.ViaVersion.packets.State; import java.util.List; import java.util.UUID; +import java.util.logging.Level; public class BaseProtocol extends Protocol { @@ -83,6 +85,15 @@ public class BaseProtocol extends Protocol { info.setUsername(wrapper.get(Type.STRING, 1)); // Add to ported clients ((ViaVersionPlugin) ViaVersion.getInstance()).addPortedClient(wrapper.user()); + if (ViaVersion.getInstance().isDebug()) { + // Print out the route to console + ((ViaVersionPlugin) ViaVersion.getInstance()).getLogger().log(Level.INFO, "{0} logged in with protocol {1}, Route: {2}", + new Object[]{ + wrapper.get(Type.STRING, 1), + info.getProtocolVersion(), + StringUtils.join(info.getPipeline().pipes(), ", ") + }); + } } }); } diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/BlockEntity.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/BlockEntity.java new file mode 100644 index 000000000..52471fb0d --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/BlockEntity.java @@ -0,0 +1,88 @@ +package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3; + +import lombok.Getter; +import org.spacehq.opennbt.tag.builtin.CompoundTag; +import us.myles.ViaVersion.api.PacketWrapper; +import us.myles.ViaVersion.api.ViaVersion; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.minecraft.Position; +import us.myles.ViaVersion.api.type.Type; + +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public class BlockEntity { + @Getter + private static final Map types = new ConcurrentHashMap<>(); + + static { + types.put("MobSpawner", 1); + types.put("Control", 2); + types.put("Beacon", 3); + types.put("Skull", 4); + types.put("FlowerPot", 5); + types.put("Banner", 6); + types.put("UNKNOWN", 7); + types.put("EndGateway", 8); + types.put("Sign", 9); + + //I didn't see anything that didn't work about chests +// types.put("Chest", -1); + } + + public static void handle(List tags, UserConnection connection) { + for (CompoundTag tag : tags) { + try { + if (!tag.contains("id")) + throw new Exception("NBT tag not handled because the id key is missing"); + + String id = (String) tag.get("id").getValue(); + if (!types.containsKey(id)) + throw new Exception("Not handled id: " + id); + + int newId = types.get(id); + if (newId == -1) + continue; + + + int x = (int) tag.get("x").getValue(); + int y = (int) tag.get("y").getValue(); + int z = (int) tag.get("z").getValue(); + + Position pos = new Position((long) x, (long) y, (long) z); + + //Sorry, the PacketWrapper class wil handle this in the future + //TODO let the packetwrapper class handle it + if (newId != 9) { + updateBlockEntity(pos, (short) newId, tag, connection); + } else { + String[] lines = new String[4]; + for (int i = 1; i < 5; i++) + lines[i - 1] = (String) tag.get("Text" + i).getValue(); + updateSign(pos, lines, connection); + } + } catch (Exception e) { + if(ViaVersion.getInstance().isDebug()) { + System.out.println("Block Entity: " + e.getMessage() + ": " + tag); + } + } + } + } + + private static void updateBlockEntity(Position pos, short id, CompoundTag tag, UserConnection connection) throws Exception { + PacketWrapper wrapper = new PacketWrapper(0x09, null, connection); + wrapper.write(Type.POSITION, pos); + wrapper.write(Type.UNSIGNED_BYTE, id); + wrapper.write(Type.NBT, tag); + wrapper.send(); + } + + private static void updateSign(Position pos, String[] lines, UserConnection connection) throws Exception { + PacketWrapper wrapper = new PacketWrapper(0x46, null, connection); + wrapper.write(Type.POSITION, pos); + for (String s : lines) + wrapper.write(Type.STRING, s); + wrapper.send(); + } +} diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Chunk1_9_3.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Chunk1_9_3.java new file mode 100644 index 000000000..a15acd907 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Chunk1_9_3.java @@ -0,0 +1,19 @@ +package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3; + +import lombok.AllArgsConstructor; +import lombok.Data; +import org.spacehq.opennbt.tag.builtin.CompoundTag; + +import java.util.List; + +@Data +@AllArgsConstructor +public class Chunk1_9_3 { + private int x; + private int z; + private boolean groundUp; + private int bitmask; + private byte[] sections; + List blockEntities; + +} diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Chunk1_9_3Type.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Chunk1_9_3Type.java new file mode 100644 index 000000000..05d698e8e --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Chunk1_9_3Type.java @@ -0,0 +1,48 @@ +package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3; + +import io.netty.buffer.ByteBuf; +import org.spacehq.opennbt.tag.builtin.CompoundTag; +import us.myles.ViaVersion.api.type.Type; + +import java.util.ArrayList; +import java.util.List; + +public class Chunk1_9_3Type extends Type { + public Chunk1_9_3Type() { + super("1.9.3 Chunk", Chunk1_9_3.class); + } + + @Override + public Chunk1_9_3 read(ByteBuf input) throws Exception { + int chunkX = input.readInt(); + int chunkZ = input.readInt(); + + boolean groundUp = input.readBoolean(); + int primaryBitmask = Type.VAR_INT.read(input); + int size = Type.VAR_INT.read(input); + + byte[] sections = new byte[size]; + input.readBytes(sections); + + int blockEntities = Type.VAR_INT.read(input); + List nbtData = new ArrayList<>(); + for (int i = 0; i < blockEntities; i++) { + nbtData.add(Type.NBT.read(input)); + } + return new Chunk1_9_3(chunkX, chunkZ, groundUp, primaryBitmask, sections, nbtData); + } + + @Override + public void write(ByteBuf buffer, Chunk1_9_3 chunk) throws Exception { + buffer.writeInt(chunk.getX()); + buffer.writeInt(chunk.getZ()); + + buffer.writeBoolean(chunk.isGroundUp()); + Type.VAR_INT.write(buffer, chunk.getBitmask()); + + Type.VAR_INT.write(buffer, chunk.getSections().length); + buffer.writeBytes(chunk.getSections()); + + // no block entities as it's earlier + } +} diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Protocol1_9_1_2TO1_9_3.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Protocol1_9_1_2TO1_9_3.java new file mode 100644 index 000000000..1660769ef --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9_1_2to1_9_3/Protocol1_9_1_2TO1_9_3.java @@ -0,0 +1,71 @@ +package us.myles.ViaVersion.protocols.protocol1_9_1_2to1_9_3; + +import org.spacehq.opennbt.tag.builtin.CompoundTag; +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.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; + +public class Protocol1_9_1_2TO1_9_3 extends Protocol { + public static Type CHUNK = new Chunk1_9_3Type(); + + @Override + protected void registerPackets() { + + //Unchanged packet structure + registerOutgoing(State.PLAY, 0x46, 0x47); //Sound effect + registerOutgoing(State.PLAY, 0x47, 0x48); //Player list header and footer + registerOutgoing(State.PLAY, 0x48, 0x49); //Collect item + registerOutgoing(State.PLAY, 0x49, 0x4A); //Entity teleport + registerOutgoing(State.PLAY, 0x4A, 0x4B); //Entity properties + registerOutgoing(State.PLAY, 0x4B, 0x4C); //Entity effect + + //Update block entity + registerOutgoing(State.PLAY, 0x09, 0x09, new PacketRemapper() { + @Override + public void registerMap() { + map(Type.POSITION); //Position + map(Type.UNSIGNED_BYTE); //Type + map(Type.NBT); //NBT + handler(new PacketHandler() { + @Override + public void handle(PacketWrapper wrapper) throws Exception { + if (wrapper.get(Type.UNSIGNED_BYTE, 0) == 9) { + Position position = wrapper.get(Type.POSITION, 0); + CompoundTag tag = wrapper.get(Type.NBT, 0); + + wrapper.clearPacket(); //Clear the packet + + wrapper.setId(0x46); //Update sign packet + wrapper.write(Type.POSITION, position); // Position + for (int i = 1; i < 5; i++) + wrapper.write(Type.STRING, (String) tag.get("Text" + i).getValue()); // Sign line + } + } + }); + } + }); + + registerOutgoing(State.PLAY, 0x20, 0x20, new PacketRemapper() { + @Override + public void registerMap() { + handler(new PacketHandler() { + @Override + public void handle(PacketWrapper wrapper) throws Exception { + Chunk1_9_3 chunk = wrapper.passthrough(CHUNK); + BlockEntity.handle(chunk.getBlockEntities(), wrapper.user()); + } + }); + } + }); + } + + @Override + public void init(UserConnection userConnection) { + + } +} diff --git a/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_9_1/Protocol1_9TO1_9_1.java b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_9_1/Protocol1_9TO1_9_1.java new file mode 100644 index 000000000..d0793f33f --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/protocols/protocol1_9to1_9_1/Protocol1_9TO1_9_1.java @@ -0,0 +1,33 @@ +package us.myles.ViaVersion.protocols.protocol1_9to1_9_1; + +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.protocol.Protocol; +import us.myles.ViaVersion.api.remapper.PacketRemapper; +import us.myles.ViaVersion.api.type.Type; +import us.myles.ViaVersion.packets.State; + +public class Protocol1_9TO1_9_1 extends Protocol { + @Override + protected void registerPackets() { + // Currently supports 1.9.1 and 1.9.2 + // Join Game Packet + registerOutgoing(State.PLAY, 0x23, 0x23, new PacketRemapper() { + @Override + public void registerMap() { + map(Type.INT); // 0 - Player ID + map(Type.UNSIGNED_BYTE); // 1 - Player Gamemode + // 1.9.1 PRE 2 Changed this + map(Type.INT, Type.BYTE); // 2 - Player Dimension + map(Type.UNSIGNED_BYTE); // 3 - World Difficulty + map(Type.UNSIGNED_BYTE); // 4 - Max Players (Tab) + map(Type.STRING); // 5 - Level Type + map(Type.BOOLEAN); // 6 - Reduced Debug info + } + }); + } + + @Override + public void init(UserConnection userConnection) { + + } +}