From 64c2ba243f94b63623a31264d8d58c927dbb2cba Mon Sep 17 00:00:00 2001 From: Paulomart Date: Thu, 3 Mar 2016 18:34:17 +0100 Subject: [PATCH] Basic support for rewriting SpawnEggs and Potions, still needs more work --- pom.xml | 7 +- .../ViaVersion/api/slot/ItemSlotRewriter.java | 168 ++++++++++++++++++ .../ViaVersion/metadata/MetadataRewriter.java | 13 +- .../transformers/IncomingTransformer.java | 28 ++- .../transformers/OutgoingTransformer.java | 43 ++++- .../us/myles/ViaVersion/util/PacketUtil.java | 39 ---- .../myles/ViaVersion/util/ReflectionUtil.java | 12 ++ 7 files changed, 254 insertions(+), 56 deletions(-) create mode 100644 src/main/java/us/myles/ViaVersion/api/slot/ItemSlotRewriter.java diff --git a/pom.xml b/pom.xml index 28e6acfee..e85a36124 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,12 @@ - + + org.spigotmc + spigot + 1.8.8-R0.1-SNAPSHOT + + org.bukkit bukkit 1.8.8-R0.1-SNAPSHOT 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..0c94f4ac3 --- /dev/null +++ b/src/main/java/us/myles/ViaVersion/api/slot/ItemSlotRewriter.java @@ -0,0 +1,168 @@ +package us.myles.ViaVersion.api.slot; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.bukkit.Material; + +import net.minecraft.server.v1_8_R3.EntityTypes; +import net.minecraft.server.v1_8_R3.Item; +import net.minecraft.server.v1_8_R3.ItemStack; +import net.minecraft.server.v1_8_R3.MobEffect; +import net.minecraft.server.v1_8_R3.MobEffectList; +import net.minecraft.server.v1_8_R3.NBTTagCompound; +import net.minecraft.server.v1_8_R3.PotionBrewer; + +import io.netty.buffer.ByteBuf; + +import us.myles.ViaVersion.CancelException; +import us.myles.ViaVersion.util.ReflectionUtil; + +public class ItemSlotRewriter { + + public static void rewrite1_9To1_8(ByteBuf input, ByteBuf output) throws CancelException { + try { + Object 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 { + Object 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(Object itemstack) throws NoSuchFieldException, IllegalAccessException { + if (itemstack != null) { + ItemStack stack = (ItemStack) itemstack; + int itemId = Item.getId(stack.getItem()); + if (itemId == Material.MONSTER_EGG.getId() && stack.getData() == 0) { + NBTTagCompound tag = stack.getTag(); + int data = 0; + if (tag != null && tag.hasKeyOfType("EntityTag", 10)) { + NBTTagCompound entityTag = tag.getCompound("EntityTag"); + if (entityTag.hasKeyOfType("id", 8)) { + String id = entityTag.getString("id"); + Map g = (Map) ReflectionUtil.getStatic(EntityTypes.class, "g", Map.class); + data = g.get(id); + } + } + stack.setTag(null); + stack.setData(data); + } else if (itemId == Material.POTION.getId() && stack.getData() == 0) { + NBTTagCompound tag = stack.getTag(); + if (tag != null) { + System.out.println("in: " + tag); + } + } + } + } + + public static void fixIdsFrom1_8To1_9(Object itemstack) { + if (itemstack != null) { + ItemStack stack = (ItemStack) itemstack; + int itemId = Item.getId(stack.getItem()); + if (itemId == Material.MONSTER_EGG.getId() && stack.getData() != 0) { + NBTTagCompound tag = stack.getTag(); + if (tag == null) { + tag = new NBTTagCompound(); + } + NBTTagCompound entityTag = new NBTTagCompound(); + entityTag.setString("id", EntityTypes.b(stack.getData())); + tag.set("EntityTag", entityTag); + stack.setTag(tag); + stack.setData(0); + } else if (itemId == Material.POTION.getId() && stack.getData() != 0) { + NBTTagCompound tag = stack.getTag(); + if (tag == null) { + tag = new NBTTagCompound(); + stack.setTag(tag); + } + try { + List effects = PotionBrewer.getEffects(stack.getData(), true); + if (effects != null && effects.size() >= 1) { + MobEffect effect = effects.get(0); + MobEffectList type = MobEffectList.byId[effect.getEffectId()]; + StringBuilder name = new StringBuilder(); + System.out.println(effect.getDuration() + " ?>? " +type.k()); + if (effect.getAmplifier() > 0) { + name.append("strong_"); + } else if (effect.getDuration() > type.k()) { + name.append("long_"); + } + + name.append(POTION_TYPE_TO_KEY.get(effect.getEffectId())); + System.out.println("Rewriting to: " + name.toString()); + tag.setString("Potion", name.toString()); + } else { + System.out.println("Falling back to water for subId: " + stack.getData()); + tag.setString("Potion", "water"); + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } + } + + public static Object readItemStack(ByteBuf input) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Object serializer = getPacketDataSerializer(input); + return READ_ITEM.invoke(serializer); + } + + public static void writeItemStack(Object itemstack, ByteBuf output) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + Object serializer = getPacketDataSerializer(output); + WRITE_ITEM.invoke(serializer, itemstack); + } + + private static final Map POTION_TYPE_TO_KEY = new HashMap<>(); + private static Constructor SERIALIZER_CONSTRUCTOR; + private static Method WRITE_ITEM; + private static Method READ_ITEM; + + static { + try { + Class list = ReflectionUtil.nms("MobEffectList"); + Map map = ReflectionUtil.getStatic(list, "I", Map.class); + for (Entry e : map.entrySet()) { + System.out.println(e.getValue()); + System.out.println(e.getValue().getClass()); + int id = ReflectionUtil.get(e.getValue(), list, "id", int.class); + String type = ReflectionUtil.get(e.getKey(), "b", String.class); + POTION_TYPE_TO_KEY.put(id, type); + } + } catch (NoSuchFieldException | IllegalAccessException | ClassNotFoundException e1) { + e1.printStackTrace(); + } + try { + Class serializer = ReflectionUtil.nms("PacketDataSerializer"); + Class itemStack = ReflectionUtil.nms("ItemStack"); + SERIALIZER_CONSTRUCTOR = serializer.getDeclaredConstructor(ByteBuf.class); + WRITE_ITEM = serializer.getDeclaredMethod("a", itemStack); + READ_ITEM = serializer.getDeclaredMethod("i"); + } catch (ClassNotFoundException | NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + } + } + + private static Object getPacketDataSerializer(ByteBuf buf) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException { + return SERIALIZER_CONSTRUCTOR.newInstance(buf); + } +} diff --git a/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java b/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java index 1dad62f05..fd2467a33 100644 --- a/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java +++ b/src/main/java/us/myles/ViaVersion/metadata/MetadataRewriter.java @@ -11,6 +11,7 @@ import org.bukkit.util.Vector; import io.netty.buffer.ByteBuf; +import us.myles.ViaVersion.api.slot.ItemSlotRewriter; import us.myles.ViaVersion.util.PacketUtil; public class MetadataRewriter { @@ -92,7 +93,8 @@ public class MetadataRewriter { output.writeBoolean(((Byte) value).byteValue() != 0); break; case Slot: - PacketUtil.writeItem(value, output); + ItemSlotRewriter.fixIdsFrom1_8To1_9(value); + ItemSlotRewriter.writeItemStack(value, output); break; case Position: Vector vector = (Vector) value; @@ -148,8 +150,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 b7c99669d..5d1d0a522 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; @@ -121,7 +122,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) { @@ -178,6 +179,8 @@ public class IncomingTransformer { try { Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class); item = m.invoke(null, inHand); + ItemSlotRewriter.fixIdsFrom1_9To1_8(item); + ItemSlotRewriter.writeItemStack(item, output); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { @@ -186,10 +189,14 @@ public class IncomingTransformer { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (NoSuchFieldException e) { + e.printStackTrace(); } - PacketUtil.writeItem(item, output); - short curX = input.readUnsignedByte(); output.writeByte(curX); short curY = input.readUnsignedByte(); @@ -210,6 +217,8 @@ public class IncomingTransformer { try { Method m = ReflectionUtil.obc("inventory.CraftItemStack").getDeclaredMethod("asNMSCopy", ItemStack.class); item = m.invoke(null, inHand); + ItemSlotRewriter.fixIdsFrom1_9To1_8(item); + ItemSlotRewriter.writeItemStack(item, output); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { @@ -218,14 +227,25 @@ public class IncomingTransformer { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (InstantiationException e) { + e.printStackTrace(); + } catch (NoSuchFieldException 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 9fcc91970..7c5bdc958 100644 --- a/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java +++ b/src/main/java/us/myles/ViaVersion/transformers/OutgoingTransformer.java @@ -14,6 +14,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; @@ -64,11 +65,11 @@ public class OutgoingTransformer { int catid = 0; String newname = name; if (effect != null) { - if(effect.isBreakPlaceSound()) { - input.readBytes(input.readableBytes()); - output.clear(); - return; - } + if(effect.isBreakPlaceSound()) { + input.readBytes(input.readableBytes()); + output.clear(); + return; + } catid = effect.getCategory().getId(); newname = effect.getNewName(); } @@ -221,7 +222,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); @@ -338,6 +341,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); @@ -521,11 +546,11 @@ public class OutgoingTransformer { line = "{\"text\":" + line + "}"; } try { - new JSONParser().parse(line); + new JSONParser().parse(line); } catch (org.json.simple.parser.ParseException e) { - System.out.println("Invalid JSON String: \"" + line + "\" Please report this issue to the ViaVersion Github!"); - return "{\"text\":\"\"}"; + System.out.println("Invalid JSON String: \"" + line + "\" Please report this issue to the ViaVersion Github!"); + return "{\"text\":\"\"}"; } return line; } diff --git a/src/main/java/us/myles/ViaVersion/util/PacketUtil.java b/src/main/java/us/myles/ViaVersion/util/PacketUtil.java index 1c2dde784..5d703b4fe 100644 --- a/src/main/java/us/myles/ViaVersion/util/PacketUtil.java +++ b/src/main/java/us/myles/ViaVersion/util/PacketUtil.java @@ -354,45 +354,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);