diff --git a/pom.xml b/pom.xml index 7ab46f1b7..a8e32f12c 100644 --- a/pom.xml +++ b/pom.xml @@ -1,14 +1,29 @@ - + 4.0.0 us.myles ViaVersion - 1.0-SNAPSHOT + 0.4.6 + + org.apache.maven.plugins + maven-shade-plugin + 1.4 + + false + + + + package + + shade + + + + org.apache.maven.plugins maven-compiler-plugin @@ -18,8 +33,19 @@ + + + src/main/resources + true + + + + org.spacehq + opennbt + 1.0 + org.bukkit bukkit @@ -50,5 +76,9 @@ spigot-repo https://hub.spigotmc.org/nexus/content/repositories/snapshots/ - + + spacehq-repo + https://repo.spacehq.org/content/repositories/releases/ + + diff --git a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java index ae03f1cd1..5c6903e9e 100644 --- a/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java +++ b/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java @@ -14,6 +14,7 @@ import org.bukkit.inventory.ItemStack; import org.bukkit.plugin.java.JavaPlugin; import us.myles.ViaVersion.api.ViaVersion; import us.myles.ViaVersion.api.ViaVersionAPI; +import us.myles.ViaVersion.commands.ViaVersionCommand; import us.myles.ViaVersion.handlers.ViaVersionInitializer; import us.myles.ViaVersion.util.ReflectionUtil; @@ -38,7 +39,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { return; } - getLogger().info("ViaVersion enabled, injecting. (Allows 1.8 to be accessed via 1.9)"); + getLogger().info("ViaVersion " + getDescription().getVersion() + " is now enabled, injecting. (Allows 1.8 to be accessed via 1.9)"); try { injectPacketHandler(); System.setProperty("ViaVersion", getDescription().getVersion()); @@ -52,6 +53,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { setPorted(e.getPlayer().getUniqueId(), false); } }, this); + getCommand("viaversion").setExecutor(new ViaVersionCommand()); } public void injectPacketHandler() throws Exception { @@ -89,6 +91,11 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaVersionAPI { return portedPlayers.contains(player.getUniqueId()); } + @Override + public String getVersion() { + return getDescription().getVersion(); + } + public void setPorted(UUID id, boolean value) { if (value) { portedPlayers.add(id); diff --git a/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java b/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java index cdccc80ce..ba3fe0b0e 100644 --- a/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java +++ b/src/main/java/us/myles/ViaVersion/api/ViaVersionAPI.java @@ -3,6 +3,11 @@ package us.myles.ViaVersion.api; import org.bukkit.entity.Player; public interface ViaVersionAPI { - + /** + * Is player using 1.9? + * @param player + * @return + */ boolean isPorted(Player player); + String getVersion(); } diff --git a/src/main/java/us/myles/ViaVersion/api/slot/ItemSlotRewriter.java b/src/main/java/us/myles/ViaVersion/api/slot/ItemSlotRewriter.java new file mode 100644 index 000000000..fa126972c --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/api/slot/ItemSlotRewriter.java @@ -0,0 +1,191 @@ +package us.myles.ViaVersion.api.slot; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.Material; +import org.spacehq.opennbt.tag.builtin.CompoundTag; +import org.spacehq.opennbt.tag.builtin.StringTag; + +import io.netty.buffer.ByteBuf; + +import us.myles.ViaVersion.CancelException; +import us.myles.ViaVersion.util.PacketUtil; + +public class ItemSlotRewriter { + + public static void rewrite1_9To1_8(ByteBuf input, ByteBuf output) throws CancelException { + try { + ItemStack item = readItemStack(input); + fixIdsFrom1_9To1_8(item); + writeItemStack(item, output); + } catch (Exception e) { + System.out.println("Error while rewriting an item slot."); + e.printStackTrace(); + throw new CancelException(); + } + } + + public static void rewrite1_8To1_9(ByteBuf input, ByteBuf output) throws CancelException { + try { + ItemStack item = readItemStack(input); + fixIdsFrom1_8To1_9(item); + writeItemStack(item, output); + } catch (Exception e) { + System.out.println("Error while rewriting an item slot."); + e.printStackTrace(); + throw new CancelException(); + } + } + + public static void fixIdsFrom1_9To1_8(ItemStack item) { + if (item != null) { + if (item.id == Material.MONSTER_EGG.getId() && item.data == 0) { + CompoundTag tag = item.tag; + int data = 0; + if (tag != null && tag.get("EntityTag") instanceof CompoundTag) { + CompoundTag entityTag = tag.get("EntityTag"); + if (entityTag.get("id") instanceof StringTag) { + StringTag id = entityTag.get("id"); + data = ENTTIY_NAME_TO_ID.get(id.getValue()); + } + } + item.tag = null; + item.data = (short) data; + } + } + } + + public static void fixIdsFrom1_8To1_9(ItemStack item) { + if (item != null) { + if (item.id == Material.MONSTER_EGG.getId() && item.data != 0) { + CompoundTag tag = item.tag; + if (tag == null) { + tag = new CompoundTag("tag"); + } + CompoundTag entityTag = new CompoundTag("EntityTag"); + StringTag id = new StringTag("id", ENTTIY_ID_TO_NAME.get(Integer.valueOf(item.data))); + entityTag.put(id); + tag.put(entityTag); + item.tag = tag; + item.data = 0; + } + } + } + + public static ItemStack readItemStack(ByteBuf input) throws IOException { + short id = input.readShort(); + if (id < 0) { + return null; + } else { + ItemStack item = new ItemStack(); + item.id = id; + item.amount = input.readByte(); + item.data = input.readShort(); + item.tag = PacketUtil.readNBT(input); + return item; + } + } + + public static void writeItemStack(ItemStack item, ByteBuf output) throws IOException { + if (item == null) { + output.writeShort(-1); + } else { + output.writeShort(item.id); + output.writeByte(item.amount); + output.writeShort(item.data); + PacketUtil.writeNBT(output, item.tag); + } + } + + public static class ItemStack { + + private short id; + private byte amount; + private short data; + private CompoundTag tag; + + public static ItemStack fromBukkit(org.bukkit.inventory.ItemStack stack) { + ItemStack item = new ItemStack(); + item.id = (short) stack.getTypeId(); + item.amount = (byte) stack.getAmount(); + item.data = stack.getData().getData(); + // TODO: nbt + return item; + } + } + + private static Map ENTTIY_NAME_TO_ID = new HashMap<>(); + private static Map ENTTIY_ID_TO_NAME = new HashMap<>(); + + static { + register(1, "Item"); + register(2, "XPOrb"); + register(7, "ThrownEgg"); + register(8, "LeashKnot"); + register(9, "Painting"); + register(10, "Arrow"); + register(11, "Snowball"); + register(12, "Fireball"); + register(13, "SmallFireball"); + register(14, "ThrownEnderpearl"); + register(15, "EyeOfEnderSignal"); + register(16, "ThrownPotion"); + register(17, "ThrownExpBottle"); + register(18, "ItemFrame"); + register(19, "WitherSkull"); + register(20, "PrimedTnt"); + register(21, "FallingSand"); + register(22, "FireworksRocketEntity"); + register(30, "ArmorStand"); + register(40, "MinecartCommandBlock"); + register(41, "Boat"); + register(42, "MinecartRideable"); + register(43, "MinecartChest"); + register(44, "MinecartFurnace"); + register(45, "MinecartTNT"); + register(46, "MinecartHopper"); + register(47, "MinecartSpawner"); + register(48, "Mob"); + register(49, "Monster"); + register(50, "Creeper"); + register(51, "Skeleton"); + register(52, "Spider"); + register(53, "Giant"); + register(54, "Zombie"); + register(55, "Slime"); + register(56, "Ghast"); + register(57, "PigZombie"); + register(58, "Enderman"); + register(59, "CaveSpider"); + register(60, "Silverfish"); + register(61, "Blaze"); + register(62, "LavaSlime"); + register(63, "EnderDragon"); + register(64, "WitherBoss"); + register(65, "Bat"); + register(66, "Witch"); + register(67, "Endermite"); + register(68, "Guardian"); + register(90, "Pig"); + register(91, "Sheep"); + register(92, "Cow"); + register(93, "Chicken"); + register(94, "Squid"); + register(95, "Wolf"); + register(96, "MushroomCow"); + register(97, "SnowMan"); + register(98, "Ozelot"); + register(99, "VillagerGolem"); + register(100, "EntityHorse"); + register(101, "Rabbit"); + register(120, "Villager"); + register(200, "EnderCrystal"); + } + + private static void register(Integer id, String name) { + ENTTIY_ID_TO_NAME.put(id, name); + ENTTIY_NAME_TO_ID.put(name, id); + } +} diff --git a/src/main/java/us/myles/ViaVersion/commands/ViaVersionCommand.java b/src/main/java/us/myles/ViaVersion/commands/ViaVersionCommand.java new file mode 100644 index 000000000..11872a831 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/commands/ViaVersionCommand.java @@ -0,0 +1,48 @@ +package us.myles.ViaVersion.commands; + +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import us.myles.ViaVersion.api.ViaVersion; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by fillefilip8 on 2016-03-03. + */ +public class ViaVersionCommand implements CommandExecutor { + + @Override + public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) { + if (sender.hasPermission("viaversion.admin")) { + if (args.length == 0) { + sender.sendMessage(color("&aViaVersion &c" + ViaVersion.getInstance().getVersion())); + sender.sendMessage(color("&6Commands:")); + sender.sendMessage(color("&2/viaversion list &7- &6Shows lists of all 1.9 clients and 1.8 clients.")); + } else if (args.length == 1) { + if (args[0].equalsIgnoreCase("list")) { + List portedPlayers = new ArrayList(); + List normalPlayers = new ArrayList(); + for (Player p : Bukkit.getOnlinePlayers()) { + if (ViaVersion.getInstance().isPorted(p)) { + portedPlayers.add(p.getName()); + } else { + normalPlayers.add(p.getName()); + } + } + + sender.sendMessage(color("&8[&61.9&8]: &b" + portedPlayers.toString())); + sender.sendMessage(color("&8[&61.8&8]: &b" + normalPlayers.toString())); + } + } + + } + return false; + } + public String color(String string){ + return string.replace("&", "ยง"); + } +} diff --git a/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java b/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java index 1dad62f05..a983a41d3 100644 --- a/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java +++ b/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java @@ -11,6 +11,8 @@ import org.bukkit.util.Vector; import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion.api.slot.ItemSlotRewriter; +import us.myles.ViaVersion.api.slot.ItemSlotRewriter.ItemStack; import us.myles.ViaVersion.util.PacketUtil; public class MetadataRewriter { @@ -92,7 +94,9 @@ public class MetadataRewriter { output.writeBoolean(((Byte) value).byteValue() != 0); break; case Slot: - PacketUtil.writeItem(value, output); + ItemStack item = (ItemStack) value; + ItemSlotRewriter.fixIdsFrom1_8To1_9(item); + ItemSlotRewriter.writeItemStack(item, output); break; case Position: Vector vector = (Vector) value; @@ -148,8 +152,13 @@ public class MetadataRewriter { case String: entries.add(new Entry(index, PacketUtil.readString(buf))); break; - case Slot: - entries.add(new Entry(index, PacketUtil.readItem(buf))); + case Slot: { + try { + entries.add(new Entry(index, ItemSlotRewriter.readItemStack(buf))); + } catch (Exception e) { + e.printStackTrace(); + } + } break; case Position: { int x = buf.readInt(); diff --git a/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java b/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java index 93b6ed3fe..bbf0d950a 100644 --- a/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java +++ b/src/main/java/us/myles/ViaVersion/transformers/IncomingTransformer.java @@ -5,6 +5,7 @@ import org.bukkit.inventory.ItemStack; import us.myles.ViaVersion.CancelException; import us.myles.ViaVersion.ConnectionInfo; import us.myles.ViaVersion.ViaVersionPlugin; +import us.myles.ViaVersion.api.slot.ItemSlotRewriter; import us.myles.ViaVersion.packets.PacketType; import us.myles.ViaVersion.packets.State; import us.myles.ViaVersion.util.PacketUtil; @@ -126,7 +127,7 @@ public class IncomingTransformer { output.writeByte(button); output.writeShort(action); output.writeByte(mode); - output.writeBytes(input); + ItemSlotRewriter.rewrite1_9To1_8(input, output); return; } if (packet == PacketType.PLAY_CLIENT_SETTINGS) { @@ -179,22 +180,14 @@ public class IncomingTransformer { int hand = PacketUtil.readVarInt(input); ItemStack inHand = ViaVersionPlugin.getHandItem(info); - Object item = null; try { - Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class); - item = m.invoke(null, inHand); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { + us.myles.ViaVersion.api.slot.ItemSlotRewriter.ItemStack item = us.myles.ViaVersion.api.slot.ItemSlotRewriter.ItemStack.fromBukkit(inHand); + ItemSlotRewriter.fixIdsFrom1_9To1_8(item); + ItemSlotRewriter.writeItemStack(item, output); + } catch (Exception e) { e.printStackTrace(); } - PacketUtil.writeItem(item, output); - short curX = input.readUnsignedByte(); output.writeByte(curX); short curY = input.readUnsignedByte(); @@ -211,26 +204,25 @@ public class IncomingTransformer { output.writeByte(255); // write item in hand ItemStack inHand = ViaVersionPlugin.getHandItem(info); - Object item = null; try { - Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class); - item = m.invoke(null, inHand); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { + us.myles.ViaVersion.api.slot.ItemSlotRewriter.ItemStack item = us.myles.ViaVersion.api.slot.ItemSlotRewriter.ItemStack.fromBukkit(inHand); + ItemSlotRewriter.fixIdsFrom1_9To1_8(item); + ItemSlotRewriter.writeItemStack(item, output); + } catch (Exception e) { e.printStackTrace(); } - PacketUtil.writeItem(item, output); output.writeByte(-1); output.writeByte(-1); output.writeByte(-1); return; } + if (packet == PacketType.PLAY_CREATIVE_INVENTORY_ACTION) { + short slot = input.readShort(); + output.writeShort(slot); + + ItemSlotRewriter.rewrite1_9To1_8(input, output); + } output.writeBytes(input); } } diff --git a/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java b/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java index 8fdfb86ca..2a7db72c0 100644 --- a/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java +++ b/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java @@ -11,6 +11,7 @@ import us.myles.ViaVersion.CancelException; import us.myles.ViaVersion.ConnectionInfo; import us.myles.ViaVersion.ViaVersionPlugin; import us.myles.ViaVersion.api.ViaVersion; +import us.myles.ViaVersion.api.slot.ItemSlotRewriter; import us.myles.ViaVersion.metadata.MetadataRewriter; import us.myles.ViaVersion.packets.PacketType; import us.myles.ViaVersion.packets.State; @@ -225,7 +226,9 @@ public class OutgoingTransformer { slot += 1; // add 1 so it's now 2-5 } PacketUtil.writeVarInt(slot, output); - output.writeBytes(input); + + ItemSlotRewriter.rewrite1_8To1_9(input, output); + return; } if (packet == PacketType.PLAY_ENTITY_METADATA) { int id = PacketUtil.readVarInt(input); @@ -342,6 +345,28 @@ public class OutgoingTransformer { output.writeBytes(input); return; } + if (packet == PacketType.PLAY_SET_SLOT) { + int windowId = input.readUnsignedByte(); + output.writeByte(windowId); + + short slot = input.readShort(); + output.writeShort(slot); + + ItemSlotRewriter.rewrite1_8To1_9(input, output); + return; + } + if (packet == PacketType.PLAY_WINDOW_ITEMS) { + int windowId = input.readUnsignedByte(); + output.writeByte(windowId); + + short count = input.readShort(); + output.writeShort(count); + + for (int i = 0; i < count; i++) { + ItemSlotRewriter.rewrite1_8To1_9(input, output); + } + return; + } if (packet == PacketType.PLAY_SPAWN_MOB) { int id = PacketUtil.readVarInt(input); PacketUtil.writeVarInt(id, output); diff --git a/src/main/java/us/myles/ViaVersion/util/PacketUtil.java b/src/main/java/us/myles/ViaVersion/util/PacketUtil.java index 086b4cdc0..333cab806 100644 --- a/src/main/java/us/myles/ViaVersion/util/PacketUtil.java +++ b/src/main/java/us/myles/ViaVersion/util/PacketUtil.java @@ -3,6 +3,8 @@ package us.myles.ViaVersion.util; import com.google.common.base.Charsets; import com.google.common.base.Preconditions; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import io.netty.buffer.ByteBufOutputStream; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.MessageToByteEncoder; @@ -11,6 +13,9 @@ import us.myles.ViaVersion.chunks.PacketChunk; import us.myles.ViaVersion.chunks.PacketChunkData; import java.io.ByteArrayOutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.nio.ByteBuffer; @@ -21,6 +26,9 @@ import java.util.BitSet; import java.util.List; import java.util.UUID; +import org.spacehq.opennbt.NBTIO; +import org.spacehq.opennbt.tag.builtin.CompoundTag; + public class PacketUtil { private static Method DECODE_METHOD; private static Method ENCODE_METHOD; @@ -42,6 +50,25 @@ public class PacketUtil { } } + public static CompoundTag readNBT(ByteBuf input) throws IOException { + int readerIndex = input.readerIndex(); + byte b = input.readByte(); + if (b == 0) { + return null; + } else { + input.readerIndex(readerIndex); + return (CompoundTag) NBTIO.readTag(new DataInputStream(new ByteBufInputStream(input))); + } + } + + public static void writeNBT(ByteBuf output, CompoundTag tag) throws IOException { + if (tag == null) { + output.writeByte(0); + } else { + NBTIO.writeTag(new DataOutputStream(new ByteBufOutputStream(output)), tag); + } + } + public static List callDecode(ByteToMessageDecoder decoder, ChannelHandlerContext ctx, Object input) { List output = new ArrayList(); try { @@ -354,45 +381,6 @@ public class PacketUtil { return output; } - public static void writeItem(Object value, ByteBuf output) { - try { - Class serializer = ReflectionUtil.nms("PacketDataSerializer"); - Object init = serializer.getDeclaredConstructor(ByteBuf.class).newInstance(output); - Method toCall = init.getClass().getDeclaredMethod("a", ReflectionUtil.nms("ItemStack")); - toCall.invoke(init, value); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - } - - public static Object readItem(ByteBuf output) { - try { - Class serializer = ReflectionUtil.nms("PacketDataSerializer"); - Object init = serializer.getDeclaredConstructor(ByteBuf.class).newInstance(output); - Method toCall = init.getClass().getDeclaredMethod("i"); - return toCall.invoke(init); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - } catch (NoSuchMethodException e) { - e.printStackTrace(); - } catch (InstantiationException e) { - e.printStackTrace(); - } catch (IllegalAccessException e) { - e.printStackTrace(); - } catch (InvocationTargetException e) { - e.printStackTrace(); - } - return null; - } - public static long[] readBlockPosition(ByteBuf buf) { long val = buf.readLong(); long x = (val >> 38); // signed diff --git a/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java b/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java index e5f19187e..9b7829d3e 100644 --- a/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java +++ b/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java @@ -27,6 +27,18 @@ public class ReflectionUtil { return m.invoke(o); } + public static T getStatic(Class clazz, String f, Class t) throws NoSuchFieldException, IllegalAccessException { + Field field = clazz.getDeclaredField(f); + field.setAccessible(true); + return (T) field.get(null); + } + + public static T get(Object instance, Class clazz, String f, Class t) throws NoSuchFieldException, IllegalAccessException { + Field field = clazz.getDeclaredField(f); + field.setAccessible(true); + return (T) field.get(instance); + } + public static T get(Object o, String f, Class t) throws NoSuchFieldException, IllegalAccessException { Field field = o.getClass().getDeclaredField(f); field.setAccessible(true); diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 3f3eaa8a2..18a0b5f85 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -1,6 +1,11 @@ name: ViaVersion main: us.myles.ViaVersion.ViaVersionPlugin author: _MylesC -version: 0.4.6 +version: ${version} load: startup -loadbefore: [ProtocolLib, ProxyPipe] \ No newline at end of file +loadbefore: [ProtocolLib, ProxyPipe] +commands: + viaversion: + description: Shows ViaVersion Version and more. + permission: viaversion.admin + aliases: [viaver] \ No newline at end of file