mirror of
https://github.com/ViaVersion/ViaVersion.git
synced 2024-11-25 11:35:18 +01:00
Add Via channel handlers instead of wrapping Vanilla handlers on Bukkit (#3132)
This commit is contained in:
parent
43ad855499
commit
c5738a2203
@ -34,29 +34,21 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class PipelineUtil {
|
public final class PipelineUtil {
|
||||||
private static Method DECODE_METHOD;
|
private static final Method DECODE_METHOD;
|
||||||
private static Method ENCODE_METHOD;
|
private static final Method ENCODE_METHOD;
|
||||||
private static Method MTM_DECODE;
|
private static final Method MTM_DECODE;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
DECODE_METHOD = ByteToMessageDecoder.class.getDeclaredMethod("decode", ChannelHandlerContext.class, ByteBuf.class, List.class);
|
DECODE_METHOD = ByteToMessageDecoder.class.getDeclaredMethod("decode", ChannelHandlerContext.class, ByteBuf.class, List.class);
|
||||||
DECODE_METHOD.setAccessible(true);
|
DECODE_METHOD.setAccessible(true);
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
ENCODE_METHOD = MessageToByteEncoder.class.getDeclaredMethod("encode", ChannelHandlerContext.class, Object.class, ByteBuf.class);
|
ENCODE_METHOD = MessageToByteEncoder.class.getDeclaredMethod("encode", ChannelHandlerContext.class, Object.class, ByteBuf.class);
|
||||||
ENCODE_METHOD.setAccessible(true);
|
ENCODE_METHOD.setAccessible(true);
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
MTM_DECODE = MessageToMessageDecoder.class.getDeclaredMethod("decode", ChannelHandlerContext.class, Object.class, List.class);
|
MTM_DECODE = MessageToMessageDecoder.class.getDeclaredMethod("decode", ChannelHandlerContext.class, Object.class, List.class);
|
||||||
MTM_DECODE.setAccessible(true);
|
MTM_DECODE.setAccessible(true);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (NoSuchMethodException e) {
|
||||||
e.printStackTrace();
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ import com.viaversion.viaversion.api.platform.ViaPlatform;
|
|||||||
import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
|
import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
|
||||||
import com.viaversion.viaversion.bukkit.commands.BukkitCommandHandler;
|
import com.viaversion.viaversion.bukkit.commands.BukkitCommandHandler;
|
||||||
import com.viaversion.viaversion.bukkit.commands.BukkitCommandSender;
|
import com.viaversion.viaversion.bukkit.commands.BukkitCommandSender;
|
||||||
import com.viaversion.viaversion.bukkit.listeners.ProtocolLibEnableListener;
|
|
||||||
import com.viaversion.viaversion.bukkit.platform.BukkitViaAPI;
|
import com.viaversion.viaversion.bukkit.platform.BukkitViaAPI;
|
||||||
import com.viaversion.viaversion.bukkit.platform.BukkitViaConfig;
|
import com.viaversion.viaversion.bukkit.platform.BukkitViaConfig;
|
||||||
import com.viaversion.viaversion.bukkit.platform.BukkitViaInjector;
|
import com.viaversion.viaversion.bukkit.platform.BukkitViaInjector;
|
||||||
@ -89,10 +88,6 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onLoad() {
|
public void onLoad() {
|
||||||
// Via should load before PL, so we can't check for it in the constructor
|
|
||||||
Plugin protocolLib = Bukkit.getPluginManager().getPlugin("ProtocolLib");
|
|
||||||
ProtocolLibEnableListener.checkCompat(protocolLib);
|
|
||||||
|
|
||||||
// Spigot detector
|
// Spigot detector
|
||||||
try {
|
try {
|
||||||
Class.forName("org.spigotmc.SpigotConfig");
|
Class.forName("org.spigotmc.SpigotConfig");
|
||||||
@ -134,8 +129,6 @@ public class ViaVersionPlugin extends JavaPlugin implements ViaPlatform<Player>
|
|||||||
getCommand("viaversion").setExecutor(commandHandler);
|
getCommand("viaversion").setExecutor(commandHandler);
|
||||||
getCommand("viaversion").setTabCompleter(commandHandler);
|
getCommand("viaversion").setTabCompleter(commandHandler);
|
||||||
|
|
||||||
getServer().getPluginManager().registerEvents(new ProtocolLibEnableListener(), this);
|
|
||||||
|
|
||||||
// Warn them if they have anti-xray on and they aren't using spigot
|
// Warn them if they have anti-xray on and they aren't using spigot
|
||||||
if (conf.isAntiXRay() && !spigot) {
|
if (conf.isAntiXRay() && !spigot) {
|
||||||
getLogger().info("You have anti-xray on in your config, since you're not using spigot it won't fix xray!");
|
getLogger().info("You have anti-xray on in your config, since you're not using spigot it won't fix xray!");
|
||||||
|
@ -21,8 +21,7 @@ import com.viaversion.viaversion.ViaVersionPlugin;
|
|||||||
import com.viaversion.viaversion.bukkit.handlers.BukkitDecodeHandler;
|
import com.viaversion.viaversion.bukkit.handlers.BukkitDecodeHandler;
|
||||||
import com.viaversion.viaversion.bukkit.handlers.BukkitEncodeHandler;
|
import com.viaversion.viaversion.bukkit.handlers.BukkitEncodeHandler;
|
||||||
import com.viaversion.viaversion.bukkit.util.NMSUtil;
|
import com.viaversion.viaversion.bukkit.util.NMSUtil;
|
||||||
import com.viaversion.viaversion.classgenerator.generated.BasicHandlerConstructor;
|
import com.viaversion.viaversion.classgenerator.generated.HandlerSupplier;
|
||||||
import com.viaversion.viaversion.classgenerator.generated.HandlerConstructor;
|
|
||||||
import javassist.CannotCompileException;
|
import javassist.CannotCompileException;
|
||||||
import javassist.ClassPool;
|
import javassist.ClassPool;
|
||||||
import javassist.CtClass;
|
import javassist.CtClass;
|
||||||
@ -48,11 +47,11 @@ import java.lang.reflect.Method;
|
|||||||
//TODO maybe clean this up a bit 👀
|
//TODO maybe clean this up a bit 👀
|
||||||
public final class ClassGenerator {
|
public final class ClassGenerator {
|
||||||
private static final boolean useModules = hasModuleMethod();
|
private static final boolean useModules = hasModuleMethod();
|
||||||
private static HandlerConstructor constructor = new BasicHandlerConstructor();
|
private static HandlerSupplier constructor = new HandlerSupplier.DefaultHandlerSupplier();
|
||||||
private static String psPackage;
|
private static String psPackage;
|
||||||
private static Class psConnectListener;
|
private static Class psConnectListener;
|
||||||
|
|
||||||
public static HandlerConstructor getConstructor() {
|
public static HandlerSupplier handlerSupplier() {
|
||||||
return constructor;
|
return constructor;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +90,7 @@ public final class ClassGenerator {
|
|||||||
|
|
||||||
// Implement Constructor
|
// Implement Constructor
|
||||||
CtClass generated = pool.makeClass("com.viaversion.viaversion.classgenerator.generated.GeneratedConstructor");
|
CtClass generated = pool.makeClass("com.viaversion.viaversion.classgenerator.generated.GeneratedConstructor");
|
||||||
CtClass handlerInterface = pool.get(HandlerConstructor.class.getName());
|
CtClass handlerInterface = pool.get(HandlerSupplier.class.getName());
|
||||||
|
|
||||||
generated.setInterfaces(new CtClass[]{handlerInterface});
|
generated.setInterfaces(new CtClass[]{handlerInterface});
|
||||||
// Import required classes
|
// Import required classes
|
||||||
@ -107,7 +106,7 @@ public final class ClassGenerator {
|
|||||||
" return new BukkitDecodeHandler(info, minecraftDecoder);\n" +
|
" return new BukkitDecodeHandler(info, minecraftDecoder);\n" +
|
||||||
" }", generated));
|
" }", generated));
|
||||||
|
|
||||||
constructor = (HandlerConstructor) toClass(generated).getConstructor().newInstance();
|
constructor = (HandlerSupplier) toClass(generated).getConstructor().newInstance();
|
||||||
} catch (ReflectiveOperationException | CannotCompileException | NotFoundException e) {
|
} catch (ReflectiveOperationException | CannotCompileException | NotFoundException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -332,7 +331,7 @@ public final class ClassGenerator {
|
|||||||
|
|
||||||
@SuppressWarnings("deprecation")
|
@SuppressWarnings("deprecation")
|
||||||
private static Class<?> toClass(CtClass ctClass) throws CannotCompileException {
|
private static Class<?> toClass(CtClass ctClass) throws CannotCompileException {
|
||||||
return useModules ? ctClass.toClass(HandlerConstructor.class) : ctClass.toClass(HandlerConstructor.class.getClassLoader());
|
return useModules ? ctClass.toClass(HandlerSupplier.class) : ctClass.toClass(HandlerSupplier.class.getClassLoader());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasModuleMethod() {
|
private static boolean hasModuleMethod() {
|
||||||
|
@ -20,31 +20,47 @@ package com.viaversion.viaversion.bukkit.handlers;
|
|||||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||||
import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
|
import com.viaversion.viaversion.bukkit.classgenerator.ClassGenerator;
|
||||||
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
|
import com.viaversion.viaversion.bukkit.platform.PaperViaInjector;
|
||||||
import com.viaversion.viaversion.classgenerator.generated.HandlerConstructor;
|
|
||||||
import com.viaversion.viaversion.connection.UserConnectionImpl;
|
import com.viaversion.viaversion.connection.UserConnectionImpl;
|
||||||
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
|
import com.viaversion.viaversion.platform.WrappedChannelInitializer;
|
||||||
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
|
import com.viaversion.viaversion.protocol.ProtocolPipelineImpl;
|
||||||
import io.netty.channel.Channel;
|
import io.netty.channel.Channel;
|
||||||
import io.netty.channel.ChannelInitializer;
|
import io.netty.channel.ChannelInitializer;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.channel.ChannelPipeline;
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
public class BukkitChannelInitializer extends ChannelInitializer<Channel> implements WrappedChannelInitializer {
|
public final class BukkitChannelInitializer extends ChannelInitializer<Channel> implements WrappedChannelInitializer {
|
||||||
|
|
||||||
|
public static final String VIA_ENCODER = "via-encoder";
|
||||||
|
public static final String VIA_DECODER = "via-decoder";
|
||||||
|
public static final String MINECRAFT_ENCODER = "encoder";
|
||||||
|
public static final String MINECRAFT_DECODER = "decoder";
|
||||||
|
public static final String MINECRAFT_COMPRESSOR = "compress";
|
||||||
|
public static final String MINECRAFT_DECOMPRESSOR = "decompress";
|
||||||
|
public static final Object COMPRESSION_ENABLED_EVENT = paperCompressionEnabledEvent();
|
||||||
private static final Method INIT_CHANNEL_METHOD;
|
private static final Method INIT_CHANNEL_METHOD;
|
||||||
private final ChannelInitializer<Channel> original;
|
private final ChannelInitializer<Channel> original;
|
||||||
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
try {
|
try {
|
||||||
INIT_CHANNEL_METHOD = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
INIT_CHANNEL_METHOD = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
|
||||||
INIT_CHANNEL_METHOD.setAccessible(true);
|
INIT_CHANNEL_METHOD.setAccessible(true);
|
||||||
} catch (NoSuchMethodException e) {
|
} catch (final ReflectiveOperationException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static @Nullable Object paperCompressionEnabledEvent() {
|
||||||
|
try {
|
||||||
|
final Class<?> eventClass = Class.forName("io.papermc.paper.network.ConnectionEvent");
|
||||||
|
return eventClass.getDeclaredField("COMPRESSION_THRESHOLD_SET").get(null);
|
||||||
|
} catch (final ReflectiveOperationException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public BukkitChannelInitializer(ChannelInitializer<Channel> oldInit) {
|
public BukkitChannelInitializer(ChannelInitializer<Channel> oldInit) {
|
||||||
this.original = oldInit;
|
this.original = oldInit;
|
||||||
}
|
}
|
||||||
@ -62,7 +78,7 @@ public class BukkitChannelInitializer extends ChannelInitializer<Channel> implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void afterChannelInitialize(Channel channel) {
|
public static void afterChannelInitialize(Channel channel) {
|
||||||
UserConnection connection = new UserConnectionImpl(channel);
|
final UserConnection connection = new UserConnectionImpl(channel);
|
||||||
new ProtocolPipelineImpl(connection);
|
new ProtocolPipelineImpl(connection);
|
||||||
|
|
||||||
if (PaperViaInjector.PAPER_PACKET_LIMITER) {
|
if (PaperViaInjector.PAPER_PACKET_LIMITER) {
|
||||||
@ -70,12 +86,9 @@ public class BukkitChannelInitializer extends ChannelInitializer<Channel> implem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add our transformers
|
// Add our transformers
|
||||||
HandlerConstructor constructor = ClassGenerator.getConstructor();
|
final ChannelPipeline pipeline = channel.pipeline();
|
||||||
MessageToByteEncoder encoder = constructor.newEncodeHandler(connection, (MessageToByteEncoder) channel.pipeline().get("encoder"));
|
pipeline.addBefore(MINECRAFT_ENCODER, VIA_ENCODER, ClassGenerator.handlerSupplier().newEncodeHandler(connection));
|
||||||
ByteToMessageDecoder decoder = constructor.newDecodeHandler(connection, (ByteToMessageDecoder) channel.pipeline().get("decoder"));
|
pipeline.addBefore(MINECRAFT_DECODER, VIA_DECODER, ClassGenerator.handlerSupplier().newDecodeHandler(connection));
|
||||||
|
|
||||||
channel.pipeline().replace("encoder", "encoder", encoder);
|
|
||||||
channel.pipeline().replace("decoder", "decoder", decoder);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -26,59 +26,64 @@ import com.viaversion.viaversion.exception.CancelDecoderException;
|
|||||||
import com.viaversion.viaversion.exception.InformativeException;
|
import com.viaversion.viaversion.exception.InformativeException;
|
||||||
import com.viaversion.viaversion.util.PipelineUtil;
|
import com.viaversion.viaversion.util.PipelineUtil;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BukkitDecodeHandler extends ByteToMessageDecoder {
|
@ChannelHandler.Sharable
|
||||||
private final ByteToMessageDecoder minecraftDecoder;
|
public final class BukkitDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
|
||||||
private final UserConnection info;
|
private final UserConnection connection;
|
||||||
|
|
||||||
public BukkitDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
|
public BukkitDecodeHandler(final UserConnection connection) {
|
||||||
this.info = info;
|
this.connection = connection;
|
||||||
this.minecraftDecoder = minecraftDecoder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
|
protected void decode(final ChannelHandlerContext ctx, final ByteBuf bytebuf, final List<Object> out) throws Exception {
|
||||||
if (!info.checkServerboundPacket()) {
|
if (!connection.checkServerboundPacket()) {
|
||||||
bytebuf.clear(); // Don't accumulate
|
|
||||||
throw CancelDecoderException.generate(null);
|
throw CancelDecoderException.generate(null);
|
||||||
}
|
}
|
||||||
|
if (!connection.shouldTransformPacket()) {
|
||||||
|
out.add(bytebuf.retain());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
ByteBuf transformedBuf = null;
|
final ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
|
||||||
try {
|
try {
|
||||||
if (info.shouldTransformPacket()) {
|
connection.transformIncoming(transformedBuf, CancelDecoderException::generate);
|
||||||
transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
|
out.add(transformedBuf.retain());
|
||||||
info.transformServerbound(transformedBuf, CancelDecoderException::generate);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, transformedBuf == null ? bytebuf : transformedBuf));
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
if (e.getCause() instanceof Exception) {
|
|
||||||
throw (Exception) e.getCause();
|
|
||||||
} else if (e.getCause() instanceof Error) {
|
|
||||||
throw (Error) e.getCause();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
} finally {
|
||||||
if (transformedBuf != null) {
|
transformedBuf.release();
|
||||||
transformedBuf.release();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
|
||||||
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return; // ProtocolLib compat
|
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
super.exceptionCaught(ctx, cause);
|
super.exceptionCaught(ctx, cause);
|
||||||
if (!NMSUtil.isDebugPropertySet() && PipelineUtil.containsCause(cause, InformativeException.class)
|
if (!NMSUtil.isDebugPropertySet() && PipelineUtil.containsCause(cause, InformativeException.class)
|
||||||
&& (info.getProtocolInfo().getState() != State.HANDSHAKE || Via.getManager().isDebug())) {
|
&& (connection.getProtocolInfo().getState() != State.HANDSHAKE || Via.getManager().isDebug())) {
|
||||||
cause.printStackTrace(); // Print if CB doesn't already do it
|
cause.printStackTrace(); // Print if CB doesn't already do it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void userEventTriggered(final ChannelHandlerContext ctx, final Object event) throws Exception {
|
||||||
|
if (BukkitChannelInitializer.COMPRESSION_ENABLED_EVENT == null || event != BukkitChannelInitializer.COMPRESSION_ENABLED_EVENT) {
|
||||||
|
super.userEventTriggered(ctx, event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When compression handlers are added, the order becomes Minecraft Encoder -> Compressor -> Via Encoder; fix the order again
|
||||||
|
final ChannelPipeline pipeline = ctx.pipeline();
|
||||||
|
pipeline.addAfter(BukkitChannelInitializer.MINECRAFT_COMPRESSOR, BukkitChannelInitializer.VIA_ENCODER, pipeline.remove(BukkitChannelInitializer.VIA_ENCODER));
|
||||||
|
pipeline.addAfter(BukkitChannelInitializer.MINECRAFT_DECOMPRESSOR, BukkitChannelInitializer.VIA_DECODER, pipeline.remove(BukkitChannelInitializer.VIA_DECODER));
|
||||||
|
super.userEventTriggered(ctx, event);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,84 +24,99 @@ import com.viaversion.viaversion.bukkit.util.NMSUtil;
|
|||||||
import com.viaversion.viaversion.exception.CancelCodecException;
|
import com.viaversion.viaversion.exception.CancelCodecException;
|
||||||
import com.viaversion.viaversion.exception.CancelEncoderException;
|
import com.viaversion.viaversion.exception.CancelEncoderException;
|
||||||
import com.viaversion.viaversion.exception.InformativeException;
|
import com.viaversion.viaversion.exception.InformativeException;
|
||||||
import com.viaversion.viaversion.handlers.ChannelHandlerContextWrapper;
|
|
||||||
import com.viaversion.viaversion.handlers.ViaCodecHandler;
|
|
||||||
import com.viaversion.viaversion.util.PipelineUtil;
|
import com.viaversion.viaversion.util.PipelineUtil;
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelHandler;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
|
import io.netty.channel.ChannelPipeline;
|
||||||
|
import io.netty.handler.codec.ByteToMessageDecoder;
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
import io.netty.handler.codec.MessageToByteEncoder;
|
||||||
|
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.util.List;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
|
|
||||||
public class BukkitEncodeHandler extends MessageToByteEncoder implements ViaCodecHandler {
|
@ChannelHandler.Sharable
|
||||||
private static Field versionField;
|
public final class BukkitEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
|
||||||
|
private final UserConnection connection;
|
||||||
|
private boolean handledCompression = BukkitChannelInitializer.COMPRESSION_ENABLED_EVENT != null;
|
||||||
|
|
||||||
static {
|
public BukkitEncodeHandler(final UserConnection connection) {
|
||||||
try {
|
this.connection = connection;
|
||||||
// Attempt to get any version info from the handler
|
|
||||||
versionField = NMSUtil.nms(
|
|
||||||
"PacketEncoder",
|
|
||||||
"net.minecraft.network.PacketEncoder"
|
|
||||||
).getDeclaredField("version");
|
|
||||||
|
|
||||||
versionField.setAccessible(true);
|
|
||||||
} catch (Exception e) {
|
|
||||||
// Not compat version
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final UserConnection info;
|
|
||||||
private final MessageToByteEncoder minecraftEncoder;
|
|
||||||
|
|
||||||
public BukkitEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {
|
|
||||||
this.info = info;
|
|
||||||
this.minecraftEncoder = minecraftEncoder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void encode(final ChannelHandlerContext ctx, Object o, final ByteBuf bytebuf) throws Exception {
|
protected void encode(final ChannelHandlerContext ctx, final ByteBuf bytebuf, final List<Object> out) throws Exception {
|
||||||
if (versionField != null) {
|
if (!connection.checkClientboundPacket()) {
|
||||||
versionField.set(minecraftEncoder, versionField.get(this));
|
throw CancelEncoderException.generate(null);
|
||||||
}
|
}
|
||||||
// handle the packet type
|
if (!connection.shouldTransformPacket()) {
|
||||||
if (!(o instanceof ByteBuf)) {
|
out.add(bytebuf.retain());
|
||||||
// call minecraft encoder
|
return;
|
||||||
try {
|
}
|
||||||
PipelineUtil.callEncode(this.minecraftEncoder, new ChannelHandlerContextWrapper(ctx, this), o, bytebuf);
|
|
||||||
} catch (InvocationTargetException e) {
|
final ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
|
||||||
if (e.getCause() instanceof Exception) {
|
try {
|
||||||
throw (Exception) e.getCause();
|
final boolean needsCompression = !handledCompression && handleCompressionOrder(ctx, transformedBuf);
|
||||||
} else if (e.getCause() instanceof Error) {
|
connection.transformClientbound(transformedBuf, CancelEncoderException::generate);
|
||||||
throw (Error) e.getCause();
|
if (needsCompression) {
|
||||||
}
|
recompress(ctx, transformedBuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
out.add(transformedBuf.retain());
|
||||||
bytebuf.writeBytes((ByteBuf) o);
|
} finally {
|
||||||
|
transformedBuf.release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean handleCompressionOrder(final ChannelHandlerContext ctx, final ByteBuf buf) throws Exception {
|
||||||
|
final ChannelPipeline pipeline = ctx.pipeline();
|
||||||
|
final List<String> names = pipeline.names();
|
||||||
|
final int compressorIndex = names.indexOf(BukkitChannelInitializer.MINECRAFT_COMPRESSOR);
|
||||||
|
if (compressorIndex == -1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
handledCompression = true;
|
||||||
|
if (compressorIndex > names.indexOf(BukkitChannelInitializer.VIA_ENCODER)) {
|
||||||
|
// Need to decompress this packet due to bad order
|
||||||
|
final ByteBuf decompressed = (ByteBuf) PipelineUtil.callDecode((ByteToMessageDecoder) pipeline.get(BukkitChannelInitializer.MINECRAFT_DECOMPRESSOR), ctx, buf).get(0);
|
||||||
|
try {
|
||||||
|
buf.clear().writeBytes(decompressed);
|
||||||
|
} finally {
|
||||||
|
decompressed.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline.addAfter(BukkitChannelInitializer.MINECRAFT_COMPRESSOR, BukkitChannelInitializer.VIA_ENCODER, pipeline.remove(BukkitChannelInitializer.VIA_ENCODER));
|
||||||
|
pipeline.addAfter(BukkitChannelInitializer.MINECRAFT_DECOMPRESSOR, BukkitChannelInitializer.VIA_DECODER, pipeline.remove(BukkitChannelInitializer.VIA_DECODER));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recompress(final ChannelHandlerContext ctx, final ByteBuf buf) throws Exception {
|
||||||
|
final ByteBuf compressed = ctx.alloc().buffer();
|
||||||
|
try {
|
||||||
|
PipelineUtil.callEncode((MessageToByteEncoder<ByteBuf>) ctx.pipeline().get(BukkitChannelInitializer.MINECRAFT_COMPRESSOR), ctx, buf, compressed);
|
||||||
|
buf.clear().writeBytes(compressed);
|
||||||
|
} finally {
|
||||||
|
compressed.release();
|
||||||
}
|
}
|
||||||
transform(bytebuf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void transform(ByteBuf bytebuf) throws Exception {
|
public void exceptionCaught(final ChannelHandlerContext ctx, final Throwable cause) throws Exception {
|
||||||
if (!info.checkClientboundPacket()) throw CancelEncoderException.generate(null);
|
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) {
|
||||||
if (!info.shouldTransformPacket()) return;
|
return;
|
||||||
info.transformClientbound(bytebuf, CancelEncoderException::generate);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
|
|
||||||
if (PipelineUtil.containsCause(cause, CancelCodecException.class)) return; // ProtocolLib compat
|
|
||||||
|
|
||||||
super.exceptionCaught(ctx, cause);
|
super.exceptionCaught(ctx, cause);
|
||||||
if (!NMSUtil.isDebugPropertySet() && PipelineUtil.containsCause(cause, InformativeException.class)
|
if (!NMSUtil.isDebugPropertySet() && PipelineUtil.containsCause(cause, InformativeException.class)
|
||||||
&& (info.getProtocolInfo().getState() != State.HANDSHAKE || Via.getManager().isDebug())) {
|
&& (connection.getProtocolInfo().getState() != State.HANDSHAKE || Via.getManager().isDebug())) {
|
||||||
cause.printStackTrace(); // Print if CB doesn't already do it
|
cause.printStackTrace(); // Print if CB doesn't already do it
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public UserConnection getInfo() {
|
public UserConnection connection() {
|
||||||
return info;
|
return connection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,7 +103,7 @@ public class JoinListener implements Listener {
|
|||||||
|
|
||||||
private @Nullable UserConnection getUserConnection(Channel channel) {
|
private @Nullable UserConnection getUserConnection(Channel channel) {
|
||||||
BukkitEncodeHandler encoder = channel.pipeline().get(BukkitEncodeHandler.class);
|
BukkitEncodeHandler encoder = channel.pipeline().get(BukkitEncodeHandler.class);
|
||||||
return encoder != null ? encoder.getInfo() : null;
|
return encoder != null ? encoder.connection() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Channel getChannel(Player player) throws Exception {
|
private Channel getChannel(Player player) throws Exception {
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
|
||||||
* Copyright (C) 2016-2022 ViaVersion and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.viaversion.viaversion.bukkit.listeners;
|
|
||||||
|
|
||||||
import com.viaversion.viaversion.api.Via;
|
|
||||||
import com.viaversion.viaversion.bukkit.platform.BukkitViaInjector;
|
|
||||||
import org.bukkit.event.EventHandler;
|
|
||||||
import org.bukkit.event.Listener;
|
|
||||||
import org.bukkit.event.server.PluginDisableEvent;
|
|
||||||
import org.bukkit.event.server.PluginEnableEvent;
|
|
||||||
import org.bukkit.plugin.Plugin;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
public class ProtocolLibEnableListener implements Listener {
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginEnable(PluginEnableEvent e) {
|
|
||||||
// Will likely never happen, but try to account for hacky plugin loading systems anyways
|
|
||||||
if (e.getPlugin().getName().equals("ProtocolLib")) {
|
|
||||||
checkCompat(e.getPlugin());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@EventHandler
|
|
||||||
public void onPluginDisable(PluginDisableEvent e) {
|
|
||||||
if (e.getPlugin().getName().equals("ProtocolLib")) {
|
|
||||||
((BukkitViaInjector) Via.getManager().getInjector()).setProtocolLib(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkCompat(@Nullable Plugin protocolLib) {
|
|
||||||
if (protocolLib != null) {
|
|
||||||
String version = protocolLib.getDescription().getVersion();
|
|
||||||
String majorVersion = version.split("\\.", 2)[0];
|
|
||||||
try {
|
|
||||||
// Only need the compat check for version < 5
|
|
||||||
if (Integer.parseInt(majorVersion) < 5) {
|
|
||||||
((BukkitViaInjector) Via.getManager().getInjector()).setProtocolLib(true);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (NumberFormatException ignored) {
|
|
||||||
Via.getPlatform().getLogger().warning("ProtocolLib version check failed for version " + version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
((BukkitViaInjector) Via.getManager().getInjector()).setProtocolLib(false);
|
|
||||||
}
|
|
||||||
}
|
|
@ -35,7 +35,6 @@ import java.lang.reflect.Method;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class BukkitViaInjector extends LegacyViaInjector {
|
public class BukkitViaInjector extends LegacyViaInjector {
|
||||||
private boolean protocolLib;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void inject() throws ReflectiveOperationException {
|
public void inject() throws ReflectiveOperationException {
|
||||||
@ -112,11 +111,6 @@ public class BukkitViaInjector extends LegacyViaInjector {
|
|||||||
throw new RuntimeException("Failed to get server");
|
throw new RuntimeException("Failed to get server");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDecoderName() {
|
|
||||||
return protocolLib ? "protocol_lib_decoder" : "decoder";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected @Nullable Object getServerConnection() throws ReflectiveOperationException {
|
protected @Nullable Object getServerConnection() throws ReflectiveOperationException {
|
||||||
Class<?> serverClass = NMSUtil.nms(
|
Class<?> serverClass = NMSUtil.nms(
|
||||||
@ -189,8 +183,4 @@ public class BukkitViaInjector extends LegacyViaInjector {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setProtocolLib(boolean protocolLib) {
|
|
||||||
this.protocolLib = protocolLib;
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of ViaVersion - https://github.com/ViaVersion/ViaVersion
|
|
||||||
* Copyright (C) 2016-2022 ViaVersion and contributors
|
|
||||||
*
|
|
||||||
* This program is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
package com.viaversion.viaversion.classgenerator.generated;
|
|
||||||
|
|
||||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
|
||||||
|
|
||||||
public interface HandlerConstructor {
|
|
||||||
public MessageToByteEncoder newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder);
|
|
||||||
|
|
||||||
public ByteToMessageDecoder newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder);
|
|
||||||
}
|
|
@ -20,17 +20,25 @@ package com.viaversion.viaversion.classgenerator.generated;
|
|||||||
import com.viaversion.viaversion.api.connection.UserConnection;
|
import com.viaversion.viaversion.api.connection.UserConnection;
|
||||||
import com.viaversion.viaversion.bukkit.handlers.BukkitDecodeHandler;
|
import com.viaversion.viaversion.bukkit.handlers.BukkitDecodeHandler;
|
||||||
import com.viaversion.viaversion.bukkit.handlers.BukkitEncodeHandler;
|
import com.viaversion.viaversion.bukkit.handlers.BukkitEncodeHandler;
|
||||||
import io.netty.handler.codec.ByteToMessageDecoder;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.handler.codec.MessageToByteEncoder;
|
import io.netty.handler.codec.MessageToMessageDecoder;
|
||||||
|
import io.netty.handler.codec.MessageToMessageEncoder;
|
||||||
|
|
||||||
public class BasicHandlerConstructor implements HandlerConstructor {
|
public interface HandlerSupplier {
|
||||||
@Override
|
|
||||||
public BukkitEncodeHandler newEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) {
|
|
||||||
return new BukkitEncodeHandler(info, minecraftEncoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
MessageToMessageEncoder<ByteBuf> newEncodeHandler(UserConnection connection);
|
||||||
public BukkitDecodeHandler newDecodeHandler(UserConnection info, ByteToMessageDecoder minecraftDecoder) {
|
|
||||||
return new BukkitDecodeHandler(info, minecraftDecoder);
|
MessageToMessageDecoder<ByteBuf> newDecodeHandler(UserConnection connection);
|
||||||
|
|
||||||
|
final class DefaultHandlerSupplier implements HandlerSupplier {
|
||||||
|
@Override
|
||||||
|
public MessageToMessageEncoder<ByteBuf> newEncodeHandler(final UserConnection connection) {
|
||||||
|
return new BukkitEncodeHandler(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageToMessageDecoder<ByteBuf> newDecodeHandler(final UserConnection connection) {
|
||||||
|
return new BukkitDecodeHandler(connection);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -235,16 +235,6 @@ public abstract class LegacyViaInjector implements ViaInjector {
|
|||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEncoderName() {
|
|
||||||
return "encoder";
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getDecoderName() {
|
|
||||||
return "decoder";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Vanilla server connection object the channels to be injected should be searched in.
|
* Returns the Vanilla server connection object the channels to be injected should be searched in.
|
||||||
*
|
*
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
|
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
|
||||||
projectVersion=4.4.3-SNAPSHOT
|
projectVersion=4.5.0-SNAPSHOT
|
||||||
|
|
||||||
# Gradle properties
|
# Gradle properties
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
|
@ -50,4 +50,14 @@ public class SpongeViaInjector extends LegacyViaInjector {
|
|||||||
protected void blame(ChannelHandler bootstrapAcceptor) {
|
protected void blame(ChannelHandler bootstrapAcceptor) {
|
||||||
throw new RuntimeException("Unable to find core component 'childHandler', please check your plugins. Issue: " + bootstrapAcceptor.getClass().getName());
|
throw new RuntimeException("Unable to find core component 'childHandler', please check your plugins. Issue: " + bootstrapAcceptor.getClass().getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEncoderName() {
|
||||||
|
return "encoder";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDecoderName() {
|
||||||
|
return "decoder";
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user