Fix Velocity compatibility (requires Velocity 3.0.0)

This finally removes the hacky reorder fix on Velocity; it added a few more events that could benefit us, but this is the most important right now.
This commit is contained in:
KennyTV 2021-06-14 21:09:47 +02:00
parent 86278e837a
commit 7a82ded94b
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
5 changed files with 48 additions and 113 deletions

View File

@ -39,11 +39,11 @@ import com.viaversion.viaversion.util.GsonUtil;
import com.viaversion.viaversion.util.VersionInfo; import com.viaversion.viaversion.util.VersionInfo;
import com.viaversion.viaversion.velocity.command.VelocityCommandHandler; import com.viaversion.viaversion.velocity.command.VelocityCommandHandler;
import com.viaversion.viaversion.velocity.command.VelocityCommandSender; import com.viaversion.viaversion.velocity.command.VelocityCommandSender;
import com.viaversion.viaversion.velocity.platform.VelocityViaTask;
import com.viaversion.viaversion.velocity.platform.VelocityViaAPI; import com.viaversion.viaversion.velocity.platform.VelocityViaAPI;
import com.viaversion.viaversion.velocity.platform.VelocityViaConfig; import com.viaversion.viaversion.velocity.platform.VelocityViaConfig;
import com.viaversion.viaversion.velocity.platform.VelocityViaInjector; import com.viaversion.viaversion.velocity.platform.VelocityViaInjector;
import com.viaversion.viaversion.velocity.platform.VelocityViaLoader; import com.viaversion.viaversion.velocity.platform.VelocityViaLoader;
import com.viaversion.viaversion.velocity.platform.VelocityViaTask;
import com.viaversion.viaversion.velocity.service.ProtocolDetectorService; import com.viaversion.viaversion.velocity.service.ProtocolDetectorService;
import com.viaversion.viaversion.velocity.util.LoggerWrapper; import com.viaversion.viaversion.velocity.util.LoggerWrapper;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;

View File

@ -26,33 +26,42 @@ import io.netty.channel.ChannelInitializer;
import java.lang.reflect.Method; import java.lang.reflect.Method;
public class VelocityChannelInitializer extends ChannelInitializer<Channel> { public class VelocityChannelInitializer extends ChannelInitializer<Channel> {
public static final String MINECRAFT_ENCODER = "minecraft-encoder";
public static final String MINECRAFT_DECODER = "minecraft-decoder";
public static final String VIA_ENCODER = "via-encoder";
public static final String VIA_DECODER = "via-decoder";
public static final Object COMPRESSION_ENABLED_EVENT;
private static final Method INIT_CHANNEL;
private final ChannelInitializer<?> original; private final ChannelInitializer<?> original;
private final boolean clientSide; private final boolean clientSide;
private static Method initChannel;
static {
try {
INIT_CHANNEL = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
INIT_CHANNEL.setAccessible(true);
Class<?> eventClass = Class.forName("com.velocitypowered.proxy.protocol.VelocityConnectionEvent");
COMPRESSION_ENABLED_EVENT = eventClass.getDeclaredField("COMPRESSION_ENABLED").get(null);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public VelocityChannelInitializer(ChannelInitializer<?> original, boolean clientSide) { public VelocityChannelInitializer(ChannelInitializer<?> original, boolean clientSide) {
this.original = original; this.original = original;
this.clientSide = clientSide; this.clientSide = clientSide;
} }
static {
try {
initChannel = ChannelInitializer.class.getDeclaredMethod("initChannel", Channel.class);
initChannel.setAccessible(true);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
@Override @Override
protected void initChannel(Channel channel) throws Exception { protected void initChannel(Channel channel) throws Exception {
initChannel.invoke(original, channel); INIT_CHANNEL.invoke(original, channel);
UserConnection user = new UserConnectionImpl(channel, clientSide); UserConnection user = new UserConnectionImpl(channel, clientSide);
new ProtocolPipelineImpl(user); new ProtocolPipelineImpl(user);
// We need to add a separated handler because Velocity uses pipeline().get(MINECRAFT_DECODER) // We need to add a separated handler because Velocity uses pipeline().get(MINECRAFT_DECODER)
channel.pipeline().addBefore("minecraft-encoder", "via-encoder", new VelocityEncodeHandler(user)); channel.pipeline().addBefore(MINECRAFT_ENCODER, VIA_ENCODER, new VelocityEncodeHandler(user));
channel.pipeline().addBefore("minecraft-decoder", "via-decoder", new VelocityDecodeHandler(user)); channel.pipeline().addBefore(MINECRAFT_DECODER, VIA_DECODER, new VelocityDecodeHandler(user));
} }
} }

View File

@ -20,21 +20,17 @@ package com.viaversion.viaversion.velocity.handlers;
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
import com.viaversion.viaversion.exception.CancelCodecException; import com.viaversion.viaversion.exception.CancelCodecException;
import com.viaversion.viaversion.exception.CancelDecoderException; import com.viaversion.viaversion.exception.CancelDecoderException;
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.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.channel.ChannelPipeline;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import java.lang.reflect.InvocationTargetException;
import java.util.List; import java.util.List;
@ChannelHandler.Sharable @ChannelHandler.Sharable
public class VelocityDecodeHandler extends MessageToMessageDecoder<ByteBuf> { public class VelocityDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
private final UserConnection info; private final UserConnection info;
private boolean handledCompression;
private boolean skipDoubleTransform;
public VelocityDecodeHandler(UserConnection info) { public VelocityDecodeHandler(UserConnection info) {
this.info = info; this.info = info;
@ -42,12 +38,6 @@ public class VelocityDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
if (skipDoubleTransform) {
skipDoubleTransform = false;
out.add(bytebuf.retain());
return;
}
if (!info.checkIncomingPacket()) throw CancelDecoderException.generate(null); if (!info.checkIncomingPacket()) throw CancelDecoderException.generate(null);
if (!info.shouldTransformPacket()) { if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain()); out.add(bytebuf.retain());
@ -56,58 +46,39 @@ public class VelocityDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf); ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
try { try {
boolean needsCompress = handleCompressionOrder(ctx, transformedBuf);
info.transformIncoming(transformedBuf, CancelDecoderException::generate); info.transformIncoming(transformedBuf, CancelDecoderException::generate);
if (needsCompress) {
recompress(ctx, transformedBuf);
skipDoubleTransform = true;
}
out.add(transformedBuf.retain()); out.add(transformedBuf.retain());
} finally { } finally {
transformedBuf.release(); transformedBuf.release();
} }
} }
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
if (handledCompression) return false;
int decoderIndex = ctx.pipeline().names().indexOf("compression-decoder");
if (decoderIndex == -1) return false;
handledCompression = true;
if (decoderIndex > ctx.pipeline().names().indexOf("via-decoder")) {
// Need to decompress this packet due to bad order
ByteBuf decompressed = (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder<?>) ctx.pipeline().get("compression-decoder"), ctx, buf).get(0);
try {
buf.clear().writeBytes(decompressed);
} finally {
decompressed.release();
}
ChannelHandler encoder = ctx.pipeline().get("via-encoder");
ChannelHandler decoder = ctx.pipeline().get("via-decoder");
ctx.pipeline().remove(encoder);
ctx.pipeline().remove(decoder);
ctx.pipeline().addAfter("compression-encoder", "via-encoder", encoder);
ctx.pipeline().addAfter("compression-decoder", "via-decoder", decoder);
return true;
}
return false;
}
private void recompress(ChannelHandlerContext ctx, ByteBuf buf) throws Exception {
ByteBuf compressed = ctx.alloc().buffer();
try {
PipelineUtil.callEncode((MessageToByteEncoder<?>) ctx.pipeline().get("compression-encoder"), ctx, buf, compressed);
buf.clear().writeBytes(compressed);
} finally {
compressed.release();
}
}
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause instanceof CancelCodecException) return; if (cause instanceof CancelCodecException) return;
super.exceptionCaught(ctx, cause); super.exceptionCaught(ctx, cause);
} }
// Abuse decoder handler to catch events
@Override
public void userEventTriggered(ChannelHandlerContext ctx, Object event) throws Exception {
if (event != VelocityChannelInitializer.COMPRESSION_ENABLED_EVENT) {
super.userEventTriggered(ctx, event);
return;
}
// When Velocity adds the compression handlers, the order becomes Minecraft Encoder -> Compressor -> Via Encoder
// Move Via codec handlers back to right position
ChannelPipeline pipeline = ctx.pipeline();
ChannelHandler encoder = pipeline.get(VelocityChannelInitializer.VIA_ENCODER);
pipeline.remove(encoder);
pipeline.addBefore(VelocityChannelInitializer.MINECRAFT_ENCODER, VelocityChannelInitializer.VIA_ENCODER, encoder);
ChannelHandler decoder = pipeline.get(VelocityChannelInitializer.VIA_DECODER);
pipeline.remove(decoder);
pipeline.addBefore(VelocityChannelInitializer.MINECRAFT_DECODER, VelocityChannelInitializer.VIA_DECODER, decoder);
super.userEventTriggered(ctx, event);
}
} }

View File

@ -20,21 +20,16 @@ package com.viaversion.viaversion.velocity.handlers;
import com.viaversion.viaversion.api.connection.UserConnection; import com.viaversion.viaversion.api.connection.UserConnection;
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.util.PipelineUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.MessageToMessageEncoder;
import java.lang.reflect.InvocationTargetException;
import java.util.List; import java.util.List;
@ChannelHandler.Sharable @ChannelHandler.Sharable
public class VelocityEncodeHandler extends MessageToMessageEncoder<ByteBuf> { public class VelocityEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
private final UserConnection info; private final UserConnection info;
private boolean handledCompression;
public VelocityEncodeHandler(UserConnection info) { public VelocityEncodeHandler(UserConnection info) {
this.info = info; this.info = info;
@ -50,54 +45,13 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf); ByteBuf transformedBuf = ctx.alloc().buffer().writeBytes(bytebuf);
try { try {
boolean needsCompress = handleCompressionOrder(ctx, transformedBuf);
info.transformOutgoing(transformedBuf, CancelEncoderException::generate); info.transformOutgoing(transformedBuf, CancelEncoderException::generate);
if (needsCompress) {
recompress(ctx, transformedBuf);
}
out.add(transformedBuf.retain()); out.add(transformedBuf.retain());
} finally { } finally {
transformedBuf.release(); transformedBuf.release();
} }
} }
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
if (handledCompression) return false;
int encoderIndex = ctx.pipeline().names().indexOf("compression-encoder");
if (encoderIndex == -1) return false;
handledCompression = true;
if (encoderIndex > ctx.pipeline().names().indexOf("via-encoder")) {
// Need to decompress this packet due to bad order
ByteBuf decompressed = (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder<?>) ctx.pipeline().get("compression-decoder"), ctx, buf).get(0);
try {
buf.clear().writeBytes(decompressed);
} finally {
decompressed.release();
}
ChannelHandler encoder = ctx.pipeline().get("via-encoder");
ChannelHandler decoder = ctx.pipeline().get("via-decoder");
ctx.pipeline().remove(encoder);
ctx.pipeline().remove(decoder);
ctx.pipeline().addAfter("compression-encoder", "via-encoder", encoder);
ctx.pipeline().addAfter("compression-decoder", "via-decoder", decoder);
return true;
}
return false;
}
private void recompress(ChannelHandlerContext ctx, ByteBuf buf) throws InvocationTargetException {
ByteBuf compressed = ctx.alloc().buffer();
try {
PipelineUtil.callEncode((MessageToByteEncoder<?>) ctx.pipeline().get("compression-encoder"), ctx, buf, compressed);
buf.clear().writeBytes(compressed);
} finally {
compressed.release();
}
}
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (cause instanceof CancelCodecException) return; if (cause instanceof CancelCodecException) return;

View File

@ -48,6 +48,7 @@ public class VelocityVersionProvider extends BaseVersionProvider {
} }
private int getBackProtocol(UserConnection user) throws Exception { private int getBackProtocol(UserConnection user) throws Exception {
//TODO use newly added Velocity netty event
ChannelHandler mcHandler = user.getChannel().pipeline().get("handler"); ChannelHandler mcHandler = user.getChannel().pipeline().get("handler");
return ProtocolDetectorService.getProtocolId( return ProtocolDetectorService.getProtocolId(
((ServerConnection) getAssociation.invoke(mcHandler)).getServerInfo().getName()); ((ServerConnection) getAssociation.invoke(mcHandler)).getServerInfo().getName());