diff --git a/bukkit/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java b/bukkit/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java index 844905033..75a5aea71 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/ViaVersionPlugin.java @@ -17,6 +17,7 @@ import us.myles.ViaVersion.api.platform.ViaPlatform; import us.myles.ViaVersion.bukkit.*; import us.myles.ViaVersion.classgenerator.ClassGenerator; import us.myles.ViaVersion.dump.PluginInfo; +import us.myles.ViaVersion.util.NMSUtil; import us.myles.ViaVersion.util.ReflectionUtil; import java.util.ArrayList; @@ -76,7 +77,7 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform { // Check if it's a spigot build with a protocol mod try { - compatSpigotBuild = ReflectionUtil.nms("PacketEncoder").getDeclaredField("version") != null; + compatSpigotBuild = NMSUtil.nms("PacketEncoder").getDeclaredField("version") != null; } catch (Exception e) { compatSpigotBuild = false; } diff --git a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaBulkChunkTranslator.java b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaBulkChunkTranslator.java index 82244c31d..dc57fa445 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaBulkChunkTranslator.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaBulkChunkTranslator.java @@ -5,6 +5,7 @@ import us.myles.ViaVersion.ViaVersionPlugin; import us.myles.ViaVersion.api.Via; import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider; import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks; +import us.myles.ViaVersion.util.NMSUtil; import us.myles.ViaVersion.util.ReflectionUtil; import java.lang.reflect.Method; @@ -19,10 +20,10 @@ public class BukkitViaBulkChunkTranslator extends BulkChunkTranslatorProvider { static { try { - mapChunkBulkRef = new ReflectionUtil.ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunkBulk")); - mapChunkRef = new ReflectionUtil.ClassReflection(ReflectionUtil.nms("PacketPlayOutMapChunk")); + mapChunkBulkRef = new ReflectionUtil.ClassReflection(NMSUtil.nms("PacketPlayOutMapChunkBulk")); + mapChunkRef = new ReflectionUtil.ClassReflection(NMSUtil.nms("PacketPlayOutMapChunk")); if (((ViaVersionPlugin) Via.getPlatform()).isSpigot()) { - obfuscateRef = Class.forName("org.spigotmc.AntiXray").getMethod("obfuscate", int.class, int.class, int.class, byte[].class, ReflectionUtil.nms("World")); + obfuscateRef = Class.forName("org.spigotmc.AntiXray").getMethod("obfuscate", int.class, int.class, int.class, byte[].class, NMSUtil.nms("World")); } } catch (ClassNotFoundException e) { // Ignore as server is probably 1.9+ diff --git a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaInjector.java b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaInjector.java index f6aab8bed..dd06a2e45 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaInjector.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaInjector.java @@ -11,6 +11,7 @@ import us.myles.ViaVersion.api.platform.ViaInjector; import us.myles.ViaVersion.handlers.ViaVersionInitializer; import us.myles.ViaVersion.util.ConcurrentList; import us.myles.ViaVersion.util.ListWrapper; +import us.myles.ViaVersion.util.NMSUtil; import us.myles.ViaVersion.util.ReflectionUtil; import java.lang.reflect.Field; @@ -128,9 +129,9 @@ public class BukkitViaInjector implements ViaInjector { @Override public int getServerProtocolVersion() throws Exception { try { - Class serverClazz = ReflectionUtil.nms("MinecraftServer"); + Class serverClazz = NMSUtil.nms("MinecraftServer"); Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer"); - Class pingClazz = ReflectionUtil.nms("ServerPing"); + Class pingClazz = NMSUtil.nms("ServerPing"); Object ping = null; // Search for ping method for (Field f : serverClazz.getDeclaredFields()) { @@ -173,7 +174,7 @@ public class BukkitViaInjector implements ViaInjector { } public static Object getServerConnection() throws Exception { - Class serverClazz = ReflectionUtil.nms("MinecraftServer"); + Class serverClazz = NMSUtil.nms("MinecraftServer"); Object server = ReflectionUtil.invokeStatic(serverClazz, "getServer"); Object connection = null; for (Method m : serverClazz.getDeclaredMethods()) { diff --git a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaMovementTransmitter.java b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaMovementTransmitter.java index 81806eedb..316783853 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaMovementTransmitter.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/bukkit/BukkitViaMovementTransmitter.java @@ -7,6 +7,7 @@ import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.protocols.base.ProtocolInfo; import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.MovementTransmitterProvider; import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.MovementTracker; +import us.myles.ViaVersion.util.NMSUtil; import us.myles.ViaVersion.util.ReflectionUtil; import java.lang.reflect.Field; @@ -28,7 +29,7 @@ public class BukkitViaMovementTransmitter extends MovementTransmitterProvider { Class idlePacketClass; try { - idlePacketClass = ReflectionUtil.nms("PacketPlayInFlying"); + idlePacketClass = NMSUtil.nms("PacketPlayInFlying"); } catch (ClassNotFoundException e) { return; // We'll hope this is 1.9.4+ } @@ -45,19 +46,19 @@ public class BukkitViaMovementTransmitter extends MovementTransmitterProvider { } if (USE_NMS) { try { - getHandle = ReflectionUtil.obc("entity.CraftPlayer").getDeclaredMethod("getHandle"); + getHandle = NMSUtil.obc("entity.CraftPlayer").getDeclaredMethod("getHandle"); } catch (NoSuchMethodException | ClassNotFoundException e) { throw new RuntimeException("Couldn't find CraftPlayer", e); } try { - connection = ReflectionUtil.nms("EntityPlayer").getDeclaredField("playerConnection"); + connection = NMSUtil.nms("EntityPlayer").getDeclaredField("playerConnection"); } catch (NoSuchFieldException | ClassNotFoundException e) { throw new RuntimeException("Couldn't find Player Connection", e); } try { - handleFlying = ReflectionUtil.nms("PlayerConnection").getDeclaredMethod("a", idlePacketClass); + handleFlying = NMSUtil.nms("PlayerConnection").getDeclaredMethod("a", idlePacketClass); } catch (NoSuchMethodException | ClassNotFoundException e) { throw new RuntimeException("Couldn't find CraftPlayer", e); } diff --git a/bukkit/src/main/java/us/myles/ViaVersion/classgenerator/ClassGenerator.java b/bukkit/src/main/java/us/myles/ViaVersion/classgenerator/ClassGenerator.java index d29089d02..f9daaa86f 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/classgenerator/ClassGenerator.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/classgenerator/ClassGenerator.java @@ -8,6 +8,7 @@ import org.bukkit.plugin.Plugin; import us.myles.ViaVersion.api.ViaVersion; import us.myles.ViaVersion.handlers.ViaDecodeHandler; import us.myles.ViaVersion.handlers.ViaEncodeHandler; +import us.myles.ViaVersion.util.NMSUtil; import us.myles.ViaVersion.util.ReflectionUtil; public class ClassGenerator { @@ -27,8 +28,8 @@ public class ClassGenerator { } if (ViaVersion.getInstance().isCompatSpigotBuild()) { - Class decodeSuper = ReflectionUtil.nms("PacketDecoder"); - Class encodeSuper = ReflectionUtil.nms("PacketEncoder"); + Class decodeSuper = NMSUtil.nms("PacketDecoder"); + Class encodeSuper = NMSUtil.nms("PacketEncoder"); // Generate the classes addSpigotCompatibility(pool, ViaDecodeHandler.class, decodeSuper); addSpigotCompatibility(pool, ViaEncodeHandler.class, encodeSuper); diff --git a/bukkit/src/main/java/us/myles/ViaVersion/listeners/protocol1_9to1_8/CommandBlockListener.java b/bukkit/src/main/java/us/myles/ViaVersion/listeners/protocol1_9to1_8/CommandBlockListener.java index 5f5c713b6..43b49b392 100644 --- a/bukkit/src/main/java/us/myles/ViaVersion/listeners/protocol1_9to1_8/CommandBlockListener.java +++ b/bukkit/src/main/java/us/myles/ViaVersion/listeners/protocol1_9to1_8/CommandBlockListener.java @@ -23,6 +23,7 @@ import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.minecraft.Position; import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.protocols.protocol1_9to1_8.Protocol1_9TO1_8; +import us.myles.ViaVersion.util.NMSUtil; import us.myles.ViaVersion.util.ReflectionUtil; import java.io.DataOutput; @@ -88,7 +89,7 @@ public class CommandBlockListener extends ViaListener { return; CommandBlock cmd = (CommandBlock) b.getState(); - Object tileEntityCommand = ReflectionUtil.get(cmd, "commandBlock", ReflectionUtil.nms("TileEntityCommand")); + Object tileEntityCommand = ReflectionUtil.get(cmd, "commandBlock", NMSUtil.nms("TileEntityCommand")); Object updatePacket = ReflectionUtil.invoke(tileEntityCommand, "getUpdatePacket"); PacketWrapper wrapper = generatePacket(updatePacket, getUserConnection(player)); @@ -98,12 +99,12 @@ public class CommandBlockListener extends ViaListener { private PacketWrapper generatePacket(Object updatePacket, UserConnection usr) throws Exception { PacketWrapper wrapper = new PacketWrapper(0x09, null, usr); // Update block entity - long[] pos = getPosition(ReflectionUtil.get(updatePacket, "a", ReflectionUtil.nms("BlockPosition"))); + long[] pos = getPosition(ReflectionUtil.get(updatePacket, "a", NMSUtil.nms("BlockPosition"))); wrapper.write(Type.POSITION, new Position(pos[0], pos[1], pos[2])); //Block position wrapper.write(Type.BYTE, (byte) 2); // Action id always 2 - CompoundTag nbt = getNBT(ReflectionUtil.get(updatePacket, "c", ReflectionUtil.nms("NBTTagCompound"))); + CompoundTag nbt = getNBT(ReflectionUtil.get(updatePacket, "c", NMSUtil.nms("NBTTagCompound"))); if (nbt == null) { wrapper.write(Type.BYTE, (byte) 0); //If nbt is null. Use 0 as nbt return wrapper; @@ -125,12 +126,12 @@ public class CommandBlockListener extends ViaListener { } private boolean isR1() { - return ReflectionUtil.getVersion().equals("v1_8_R1"); + return NMSUtil.getVersion().equals("v1_8_R1"); } private CompoundTag getNBT(Object obj) throws Exception { ByteBuf buf = Unpooled.buffer(); - Method m = ReflectionUtil.nms("NBTCompressedStreamTools").getMethod("a", ReflectionUtil.nms("NBTTagCompound"), DataOutput.class); + Method m = NMSUtil.nms("NBTCompressedStreamTools").getMethod("a", NMSUtil.nms("NBTTagCompound"), DataOutput.class); m.invoke(null, obj, new DataOutputStream(new ByteBufOutputStream(buf))); try { return Type.NBT.read(buf); diff --git a/bukkit/src/main/java/us/myles/ViaVersion/util/NMSUtil.java b/bukkit/src/main/java/us/myles/ViaVersion/util/NMSUtil.java new file mode 100644 index 000000000..578ef49b5 --- /dev/null +++ b/bukkit/src/main/java/us/myles/ViaVersion/util/NMSUtil.java @@ -0,0 +1,20 @@ +package us.myles.ViaVersion.util; + +import org.bukkit.Bukkit; + +public class NMSUtil { + private static String BASE = Bukkit.getServer().getClass().getPackage().getName(); + private static String NMS = BASE.replace("org.bukkit.craftbukkit", "net.minecraft.server"); + + public static Class nms(String className) throws ClassNotFoundException { + return Class.forName(NMS + "." + className); + } + + public static Class obc(String className) throws ClassNotFoundException { + return Class.forName(BASE + "." + className); + } + + public static String getVersion() { + return BASE.substring(BASE.lastIndexOf('.') + 1); + } +} diff --git a/bukkit/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java b/bukkit/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java deleted file mode 100644 index 307ae2601..000000000 --- a/bukkit/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java +++ /dev/null @@ -1,145 +0,0 @@ -package us.myles.ViaVersion.util; - -import com.google.common.collect.Maps; -import org.bukkit.Bukkit; - -import java.lang.reflect.Field; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Collection; -import java.util.Collections; -import java.util.Map; - -public class ReflectionUtil { - private static String BASE = Bukkit.getServer().getClass().getPackage().getName(); - private static String NMS = BASE.replace("org.bukkit.craftbukkit", "net.minecraft.server"); - - public static Class nms(String className) throws ClassNotFoundException { - return Class.forName(NMS + "." + className); - } - - public static Class obc(String className) throws ClassNotFoundException { - return Class.forName(BASE + "." + className); - } - - public static String getVersion() { - return BASE.substring(BASE.lastIndexOf('.') + 1); - } - - public static Object invokeStatic(Class clazz, String method) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Method m = clazz.getDeclaredMethod(method); - return m.invoke(null); - } - - public static Object invoke(Object o, String method) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { - Method m = o.getClass().getDeclaredMethod(method); - 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 getSuper(Object o, String f, Class t) throws NoSuchFieldException, IllegalAccessException { - Field field = o.getClass().getSuperclass().getDeclaredField(f); - field.setAccessible(true); - return (T) field.get(o); - } - - 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); - return (T) field.get(o); - } - - public static T getPublic(Object o, String f, Class t) throws NoSuchFieldException, IllegalAccessException { - Field field = o.getClass().getField(f); - field.setAccessible(true); - return (T) field.get(o); - } - - - public static void set(Object o, String f, Object value) throws NoSuchFieldException, IllegalAccessException { - Field field = o.getClass().getDeclaredField(f); - field.setAccessible(true); - field.set(o, value); - } - - public static final class ClassReflection { - private final Class handle; - private final Map fields = Maps.newConcurrentMap(); - private final Map methods = Maps.newConcurrentMap(); - - public ClassReflection(Class handle) { - this(handle, true); - } - - public ClassReflection(Class handle, boolean recursive) { - this.handle = handle; - scanFields(handle, recursive); - scanMethods(handle, recursive); - } - - private void scanFields(Class host, boolean recursive) { - if (host.getSuperclass() != null && recursive) { - scanFields(host.getSuperclass(), true); - } - - for (Field field : host.getDeclaredFields()) { - field.setAccessible(true); - fields.put(field.getName(), field); - } - } - - private void scanMethods(Class host, boolean recursive) { - if (host.getSuperclass() != null && recursive) { - scanMethods(host.getSuperclass(), true); - } - - for (Method method : host.getDeclaredMethods()) { - method.setAccessible(true); - methods.put(method.getName(), method); - } - } - - public Object newInstance() throws IllegalAccessException, InstantiationException { - return handle.newInstance(); - } - - public Field getField(String name) { - return fields.get(name); - } - - public void setFieldValue(String fieldName, Object instance, Object value) throws IllegalAccessException { - getField(fieldName).set(instance, value); - } - - public T getFieldValue(String fieldName, Object instance, Class type) throws IllegalAccessException { - return type.cast(getField(fieldName).get(instance)); - } - - public T invokeMethod(Class type, String methodName, Object instance, Object... args) throws InvocationTargetException, IllegalAccessException { - return type.cast(getMethod(methodName).invoke(instance, args)); - } - - public Method getMethod(String name) { - return methods.get(name); - } - - public Collection getFields() { - return Collections.unmodifiableCollection(fields.values()); - } - - public Collection getMethods() { - return Collections.unmodifiableCollection(methods.values()); - } - } -} diff --git a/bungee/pom.xml b/bungee/pom.xml index ce5ba81b2..ccb0e47aa 100644 --- a/bungee/pom.xml +++ b/bungee/pom.xml @@ -25,18 +25,23 @@ - + net.md-5 bungeecord-api 1.10-SNAPSHOT provided + + - us.myles - viaversion-common - 1.0.0-ALPHA-modules + net.md-5 + bungeecord-proxy + 1.4.7-SNAPSHOT + provided + + us.myles viaversion-common diff --git a/bungee/src/main/java/us/myles/ViaVersion/Bungee.java b/bungee/src/main/java/us/myles/ViaVersion/Bungee.java index 2e3284c29..0b825c6d2 100644 --- a/bungee/src/main/java/us/myles/ViaVersion/Bungee.java +++ b/bungee/src/main/java/us/myles/ViaVersion/Bungee.java @@ -28,10 +28,7 @@ public class Bungee extends Plugin implements ViaPlatform { public void onLoad() { api = new BungeeViaAPI(); config = new BungeeConfigProvider(); - } - - @Override - public void onEnable() { + // Init platform Via.init(ViaManager.builder() .platform(this) .injector(new BungeeViaInjector()) @@ -39,6 +36,12 @@ public class Bungee extends Plugin implements ViaPlatform { .build()); } + @Override + public void onEnable() { + // Inject + Via.getManager().init(); + } + @Override public String getPlatformName() { return "BungeeCord"; diff --git a/bungee/src/main/java/us/myles/ViaVersion/bungee/BungeeViaInjector.java b/bungee/src/main/java/us/myles/ViaVersion/bungee/BungeeViaInjector.java index 17214b49f..6482cb397 100644 --- a/bungee/src/main/java/us/myles/ViaVersion/bungee/BungeeViaInjector.java +++ b/bungee/src/main/java/us/myles/ViaVersion/bungee/BungeeViaInjector.java @@ -1,18 +1,38 @@ package us.myles.ViaVersion.bungee; +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import net.md_5.bungee.netty.PipelineUtils; +import us.myles.ViaVersion.api.Via; import us.myles.ViaVersion.api.platform.ViaInjector; +import us.myles.ViaVersion.bungee.handlers.ViaVersionInitializer; +import us.myles.ViaVersion.util.ReflectionUtil; public class BungeeViaInjector implements ViaInjector { @Override public void inject() throws Exception { + try { + try { + ChannelInitializer oldInit = PipelineUtils.SERVER_CHILD; + ChannelInitializer newInit = new ViaVersionInitializer(oldInit); + ReflectionUtil.setStatic(PipelineUtils.class, "SERVER_CHILD", newInit); + } catch (NoSuchFieldException e) { + throw new Exception("Unable to find core component 'childHandler', please check your plugins. issue: "); + + } + } catch (Exception e) { + Via.getPlatform().getLogger().severe("Unable to inject ViaVersion, please post these details on our GitHub and ensure you're using a compatible server version."); + throw e; + } } - @Override - public void uninject() throws Exception { - + public void uninject() { + // TODO: Uninject from players currently online + Via.getPlatform().getLogger().severe("ViaVersion cannot remove itself from Bungee without a reboot!"); } + @Override public int getServerProtocolVersion() throws Exception { return 47; diff --git a/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaDecodeHandler.java b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaDecodeHandler.java new file mode 100644 index 000000000..017c6a7dd --- /dev/null +++ b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaDecodeHandler.java @@ -0,0 +1,89 @@ +package us.myles.ViaVersion.bungee.handlers; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ByteToMessageDecoder; +import us.myles.ViaVersion.api.PacketWrapper; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.type.Type; +import us.myles.ViaVersion.exception.CancelException; +import us.myles.ViaVersion.packets.Direction; +import us.myles.ViaVersion.protocols.base.ProtocolInfo; +import us.myles.ViaVersion.util.PipelineUtil; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +public class ViaDecodeHandler extends ByteToMessageDecoder { + + private final ByteToMessageDecoder minecraftDecoder; + private final UserConnection info; + + public ViaDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) { + this.info = info; + this.minecraftDecoder = minecraftDecoder; + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List list) throws Exception { + // use transformers + if (bytebuf.readableBytes() > 0) { + // Ignore if pending disconnect + if (info.isPendingDisconnect()) { + return; + } + // Increment received + boolean second = info.incrementReceived(); + // Check PPS + if (second) { + if (info.handlePPS()) + return; + } + + if (info.isActive()) { + // Handle ID + int id = Type.VAR_INT.read(bytebuf); + // Transform + ByteBuf newPacket = ctx.alloc().buffer(); + try { + if (id == PacketWrapper.PASSTHROUGH_ID) { + newPacket.writeBytes(bytebuf); + } else { + PacketWrapper wrapper = new PacketWrapper(id, bytebuf, info); + ProtocolInfo protInfo = info.get(ProtocolInfo.class); + protInfo.getPipeline().transform(Direction.INCOMING, protInfo.getState(), wrapper); + wrapper.writeToBuffer(newPacket); + } + + bytebuf.clear(); + bytebuf = newPacket; + } catch (Exception e) { + // Clear Buffer + bytebuf.clear(); + // Release Packet, be free! + newPacket.release(); + throw e; + } + } + + // call minecraft decoder + try { + list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, bytebuf)); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Exception) { + throw (Exception) e.getCause(); + } + } finally { + if (info.isActive()) { + bytebuf.release(); + } + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (PipelineUtil.containsCause(cause, CancelException.class)) return; + super.exceptionCaught(ctx, cause); + } +} diff --git a/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaEncodeHandler.java b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaEncodeHandler.java new file mode 100644 index 000000000..119fb5b13 --- /dev/null +++ b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaEncodeHandler.java @@ -0,0 +1,70 @@ +package us.myles.ViaVersion.bungee.handlers; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import us.myles.ViaVersion.api.PacketWrapper; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.type.Type; +import us.myles.ViaVersion.exception.CancelException; +import us.myles.ViaVersion.packets.Direction; +import us.myles.ViaVersion.protocols.base.ProtocolInfo; +import us.myles.ViaVersion.util.PipelineUtil; + +import java.lang.reflect.InvocationTargetException; + +public class ViaEncodeHandler extends MessageToByteEncoder { + private final UserConnection info; + private final MessageToByteEncoder minecraftEncoder; + + public ViaEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) { + this.info = info; + this.minecraftEncoder = minecraftEncoder; + } + + + @Override + protected void encode(final ChannelHandlerContext ctx, Object o, final ByteBuf bytebuf) throws Exception { + // handle the packet type + if (!(o instanceof ByteBuf)) { + // call minecraft encoder + try { + PipelineUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf); + } catch (InvocationTargetException e) { + if (e.getCause() instanceof Exception) { + throw (Exception) e.getCause(); + } + } + } + if (bytebuf.readableBytes() == 0) { + throw new CancelException(); + } + // Increment sent + info.incrementSent(); + if (info.isActive()) { + // Handle ID + int id = Type.VAR_INT.read(bytebuf); + // Transform + ByteBuf oldPacket = bytebuf.copy(); + bytebuf.clear(); + + try { + PacketWrapper wrapper = new PacketWrapper(id, oldPacket, info); + ProtocolInfo protInfo = info.get(ProtocolInfo.class); + protInfo.getPipeline().transform(Direction.OUTGOING, protInfo.getState(), wrapper); + wrapper.writeToBuffer(bytebuf); + } catch (Exception e) { + bytebuf.clear(); + throw e; + } finally { + oldPacket.release(); + } + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (PipelineUtil.containsCause(cause, CancelException.class)) return; + super.exceptionCaught(ctx, cause); + } +} diff --git a/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaPacketHandler.java b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaPacketHandler.java new file mode 100644 index 000000000..62d1e44fa --- /dev/null +++ b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaPacketHandler.java @@ -0,0 +1,35 @@ +package us.myles.ViaVersion.bungee.handlers; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToMessageEncoder; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.protocols.base.ProtocolInfo; + +import java.util.List; + +public class ViaPacketHandler extends MessageToMessageEncoder { + private final UserConnection info; + + public ViaPacketHandler(UserConnection info) { + this.info = info; + } + + @Override + protected void encode(ChannelHandlerContext ctx, Object o, List list) throws Exception { + // Split chunks bulk packet up in to single chunks packets before it reached the encoder. + // This will prevent issues with several plugins and other protocol handlers due to the chunks being sent twice. + // It also sends the chunks in the right order possible resolving some issues with added chunks/block/entity data. + if (!(o instanceof ByteBuf)) { + info.setLastPacket(o); + /* This transformer is more for fixing issues which we find hard at packet level :) */ + if (info.isActive()) { + if (info.get(ProtocolInfo.class).getPipeline().filter(o, list)) { + return; + } + } + } + + list.add(o); + } +} diff --git a/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaVersionInitializer.java b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaVersionInitializer.java new file mode 100644 index 000000000..ea5808344 --- /dev/null +++ b/bungee/src/main/java/us/myles/ViaVersion/bungee/handlers/ViaVersionInitializer.java @@ -0,0 +1,48 @@ +package us.myles.ViaVersion.bungee.handlers; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.ByteToMessageDecoder; +import io.netty.handler.codec.MessageToByteEncoder; +import us.myles.ViaVersion.api.data.UserConnection; +import us.myles.ViaVersion.api.protocol.ProtocolPipeline; + +import java.lang.reflect.Method; + +public class ViaVersionInitializer extends ChannelInitializer { + + private final ChannelInitializer original; + private Method method; + + public ViaVersionInitializer(ChannelInitializer oldInit) { + this.original = oldInit; + try { + this.method = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class); + this.method.setAccessible(true); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } + } + + public ChannelInitializer getOriginal() { + return original; + } + + @Override + protected void initChannel(SocketChannel socketChannel) throws Exception { + UserConnection info = new UserConnection(socketChannel); + // init protocol + new ProtocolPipeline(info); + // Add originals + this.method.invoke(this.original, socketChannel); + // Add our transformers + MessageToByteEncoder encoder = new ViaEncodeHandler(info, (MessageToByteEncoder) socketChannel.pipeline().get("encoder")); + ByteToMessageDecoder decoder = new ViaDecodeHandler(info, (ByteToMessageDecoder) socketChannel.pipeline().get("decoder")); + ViaPacketHandler chunkHandler = new ViaPacketHandler(info); + + socketChannel.pipeline().replace("encoder", "encoder", encoder); + socketChannel.pipeline().replace("decoder", "decoder", decoder); + socketChannel.pipeline().addAfter("packet_handler", "viaversion_packet_handler", chunkHandler); + } +} diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/util/ReflectionUtil.java b/common/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java similarity index 94% rename from sponge/src/main/java/us/myles/ViaVersion/sponge/util/ReflectionUtil.java rename to common/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java index cb025a014..f44622297 100644 --- a/sponge/src/main/java/us/myles/ViaVersion/sponge/util/ReflectionUtil.java +++ b/common/src/main/java/us/myles/ViaVersion/util/ReflectionUtil.java @@ -1,4 +1,4 @@ -package us.myles.ViaVersion.sponge.util; +package us.myles.ViaVersion.util; import com.google.common.collect.Maps; @@ -27,6 +27,12 @@ public class ReflectionUtil { return (T) field.get(null); } + public static void setStatic(Class clazz, String f, Object value) throws NoSuchFieldException, IllegalAccessException { + Field field = clazz.getDeclaredField(f); + field.setAccessible(true); + field.set(null, value); + } + public static T getSuper(Object o, String f, Class t) throws NoSuchFieldException, IllegalAccessException { Field field = o.getClass().getSuperclass().getDeclaredField(f); field.setAccessible(true); @@ -58,6 +64,7 @@ public class ReflectionUtil { field.set(o, value); } + public static final class ClassReflection { private final Class handle; private final Map fields = Maps.newConcurrentMap(); diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaInjector.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaInjector.java index 8ff70607a..fea7d41ea 100644 --- a/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaInjector.java +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/SpongeViaInjector.java @@ -10,12 +10,11 @@ import us.myles.ViaVersion.api.Pair; import us.myles.ViaVersion.api.Via; import us.myles.ViaVersion.api.platform.ViaInjector; import us.myles.ViaVersion.sponge.handlers.ViaVersionInitializer; -import us.myles.ViaVersion.sponge.util.ReflectionUtil; +import us.myles.ViaVersion.util.ReflectionUtil; import us.myles.ViaVersion.util.ListWrapper; import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.List; diff --git a/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaBulkChunkTranslator.java b/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaBulkChunkTranslator.java index f6fa5ef86..052fa2bd9 100644 --- a/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaBulkChunkTranslator.java +++ b/sponge/src/main/java/us/myles/ViaVersion/sponge/providers/SpongeViaBulkChunkTranslator.java @@ -4,7 +4,7 @@ import com.google.common.collect.Lists; import us.myles.ViaVersion.api.Via; import us.myles.ViaVersion.protocols.protocol1_9to1_8.providers.BulkChunkTranslatorProvider; import us.myles.ViaVersion.protocols.protocol1_9to1_8.storage.ClientChunks; -import us.myles.ViaVersion.sponge.util.ReflectionUtil; +import us.myles.ViaVersion.util.ReflectionUtil; import java.util.List; import java.util.logging.Level;