Netty handler tidy

This commit is contained in:
creeper123123321 2020-06-07 10:35:27 +02:00 committed by KennyTV
parent d58959fcca
commit a8a1e9448e
No known key found for this signature in database
GPG Key ID: 6BE3B555EBC5982B
15 changed files with 379 additions and 363 deletions

View File

@ -3,19 +3,15 @@ package us.myles.ViaVersion.bukkit.handlers;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.bukkit.util.NMSUtil;
import us.myles.ViaVersion.exception.CancelException; import us.myles.ViaVersion.exception.CancelDecoderException;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil; import us.myles.ViaVersion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.List; import java.util.List;
public class BukkitDecodeHandler extends ByteToMessageDecoder { public class BukkitDecodeHandler extends ByteToMessageDecoder {
private final ByteToMessageDecoder minecraftDecoder; private final ByteToMessageDecoder minecraftDecoder;
private final UserConnection info; private final UserConnection info;
@ -26,64 +22,40 @@ public class BukkitDecodeHandler extends ByteToMessageDecoder {
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
// use transformers if (!info.checkIncomingPacket()) {
if (bytebuf.readableBytes() > 0) { bytebuf.clear(); // Don't accumulate
// Ignore if pending disconnect throw CancelDecoderException.generate(null);
if (info.isPendingDisconnect()) {
return;
}
// Increment received
boolean second = info.incrementReceived();
// Check PPS
if (second) {
if (info.handlePPS())
return;
} }
if (info.isActive()) { ByteBuf draft = null;
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
ByteBuf newPacket = ctx.alloc().buffer();
try { try {
if (id == PacketWrapper.PASSTHROUGH_ID) { if (info.shouldTransformPacket()) {
newPacket.writeBytes(bytebuf); draft = ctx.alloc().buffer().writeBytes(bytebuf);
} else { info.transformIncoming(draft, CancelDecoderException::generate);
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 { try {
list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, bytebuf)); list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, draft == null ? bytebuf : draft));
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) { if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause(); throw (Exception) e.getCause();
} else if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
} }
} finally { } finally {
if (info.isActive()) { if (draft != null) {
bytebuf.release(); draft.release();
}
} }
} }
} }
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelException.class)) return; if (PipelineUtil.containsCause(cause, CancelDecoderException.class)) return; // ProtocolLib compat
super.exceptionCaught(ctx, cause); super.exceptionCaught(ctx, cause);
if (!NMSUtil.isDebugPropertySet()) {
cause.printStackTrace(); // Print if CB doesn't already do it
}
} }
} }

View File

@ -3,22 +3,18 @@ package us.myles.ViaVersion.bukkit.handlers;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.bukkit.util.NMSUtil; import us.myles.ViaVersion.bukkit.util.NMSUtil;
import us.myles.ViaVersion.exception.CancelException; import us.myles.ViaVersion.exception.CancelEncoderException;
import us.myles.ViaVersion.handlers.ChannelHandlerContextWrapper; import us.myles.ViaVersion.handlers.ChannelHandlerContextWrapper;
import us.myles.ViaVersion.handlers.ViaHandler; import us.myles.ViaVersion.handlers.ViaHandler;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil; import us.myles.ViaVersion.util.PipelineUtil;
import java.lang.reflect.Field; import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
public class BukkitEncodeHandler extends MessageToByteEncoder implements ViaHandler { public class BukkitEncodeHandler extends MessageToByteEncoder implements ViaHandler {
private static Field versionField = null; private static Field versionField;
static { static {
try { try {
@ -51,6 +47,8 @@ public class BukkitEncodeHandler extends MessageToByteEncoder implements ViaHand
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) { if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause(); throw (Exception) e.getCause();
} else if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
} }
} }
} }
@ -59,35 +57,17 @@ public class BukkitEncodeHandler extends MessageToByteEncoder implements ViaHand
} }
public void transform(ByteBuf bytebuf) throws Exception { public void transform(ByteBuf bytebuf) throws Exception {
if (bytebuf.readableBytes() == 0) { info.checkOutgoingPacket();
return; // Someone Already Decoded It! if (!info.shouldTransformPacket()) return;
} info.transformOutgoing(bytebuf, CancelEncoderException::generate);
// 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 @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelException.class)) return; if (PipelineUtil.containsCause(cause, CancelEncoderException.class)) return; // ProtocolLib compat
super.exceptionCaught(ctx, cause); super.exceptionCaught(ctx, cause);
if (!NMSUtil.isDebugPropertySet()) {
cause.printStackTrace(); // Print if CB doesn't already do it
}
} }
} }

View File

@ -5,6 +5,17 @@ import org.bukkit.Bukkit;
public class NMSUtil { public class NMSUtil {
private static final String BASE = Bukkit.getServer().getClass().getPackage().getName(); private static final String BASE = Bukkit.getServer().getClass().getPackage().getName();
private static final String NMS = BASE.replace("org.bukkit.craftbukkit", "net.minecraft.server"); private static final String NMS = BASE.replace("org.bukkit.craftbukkit", "net.minecraft.server");
private static final boolean DEBUG_PROPERTY = loadDebugProperty();
private static boolean loadDebugProperty() {
try {
Class<?> serverClass = nms("MinecraftServer");
Object server = serverClass.getDeclaredMethod("getServer").invoke(null);
return (boolean) serverClass.getMethod("isDebugging").invoke(server);
} catch (ReflectiveOperationException e) {
return false;
}
}
public static Class<?> nms(String className) throws ClassNotFoundException { public static Class<?> nms(String className) throws ClassNotFoundException {
return Class.forName(NMS + "." + className); return Class.forName(NMS + "." + className);
@ -17,4 +28,11 @@ public class NMSUtil {
public static String getVersion() { public static String getVersion() {
return BASE.substring(BASE.lastIndexOf('.') + 1); return BASE.substring(BASE.lastIndexOf('.') + 1);
} }
/**
* @return true if debug=true is set in the server.properties (added by CB)
*/
public static boolean isDebugPropertySet() {
return DEBUG_PROPERTY;
}
} }

View File

@ -4,19 +4,13 @@ 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.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.exception.CancelDecoderException;
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.util.List; import java.util.List;
@ChannelHandler.Sharable @ChannelHandler.Sharable
public class BungeeDecodeHandler extends MessageToMessageDecoder<ByteBuf> { public class BungeeDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
private final UserConnection info; private final UserConnection info;
public BungeeDecodeHandler(UserConnection info) { public BungeeDecodeHandler(UserConnection info) {
@ -25,55 +19,24 @@ public class BungeeDecodeHandler extends MessageToMessageDecoder<ByteBuf> {
@Override @Override
protected void decode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception { protected void decode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
// use transformers if (!info.checkIncomingPacket()) throw CancelDecoderException.generate(null);
if (bytebuf.readableBytes() > 0) { if (!info.shouldTransformPacket()) {
// Ignore if pending disconnect out.add(bytebuf.retain());
if (info.isPendingDisconnect()) {
return;
}
// Increment received
boolean second = info.incrementReceived();
// Check PPS
if (second) {
if (info.handlePPS())
return; return;
} }
if (info.isActive()) { ByteBuf draft = ctx.alloc().buffer().writeBytes(bytebuf);
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
ByteBuf newPacket = ctx.alloc().buffer();
try { try {
if (id == PacketWrapper.PASSTHROUGH_ID) { info.transformIncoming(draft, CancelDecoderException::generate);
newPacket.writeBytes(bytebuf); out.add(draft.retain());
} else { } finally {
PacketWrapper wrapper = new PacketWrapper(id, bytebuf, info); draft.release();
ProtocolInfo protInfo = info.get(ProtocolInfo.class);
protInfo.getPipeline().transform(Direction.INCOMING, protInfo.getState(), wrapper);
wrapper.writeToBuffer(newPacket);
}
bytebuf.clear();
bytebuf = newPacket;
} catch (Throwable e) {
// Clear Buffer
bytebuf.clear();
// Release Packet, be free!
newPacket.release();
throw e;
}
} else {
bytebuf.retain();
}
out.add(bytebuf);
} }
} }
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelException.class)) return; if (cause instanceof CancelDecoderException) return;
super.exceptionCaught(ctx, cause); super.exceptionCaught(ctx, cause);
} }
} }

View File

@ -4,84 +4,80 @@ 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.MessageToMessageEncoder; import io.netty.handler.codec.MessageToMessageEncoder;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type;
import us.myles.ViaVersion.bungee.util.BungeePipelineUtil; import us.myles.ViaVersion.bungee.util.BungeePipelineUtil;
import us.myles.ViaVersion.exception.CancelException; import us.myles.ViaVersion.exception.CancelEncoderException;
import us.myles.ViaVersion.packets.Direction;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil;
import java.util.List; import java.util.List;
@ChannelHandler.Sharable @ChannelHandler.Sharable
public class BungeeEncodeHandler extends MessageToMessageEncoder<ByteBuf> { public class BungeeEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
private final UserConnection info; private final UserConnection info;
private boolean handledCompression = false; private boolean handledCompression;
public BungeeEncodeHandler(UserConnection info) { public BungeeEncodeHandler(UserConnection info) {
this.info = info; this.info = info;
} }
@Override @Override
protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception { protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
if (bytebuf.readableBytes() == 0) { info.checkOutgoingPacket();
throw Via.getManager().isDebug() ? new CancelException() : CancelException.CACHED; if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;
} }
ByteBuf draft = ctx.alloc().buffer().writeBytes(bytebuf);
try {
boolean needsCompress = handleCompressionOrder(ctx, draft);
info.transformOutgoing(draft, CancelEncoderException::generate);
if (needsCompress) {
recompress(ctx, draft);
}
out.add(draft.retain());
} finally {
draft.release();
}
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf draft) {
boolean needsCompress = false; boolean needsCompress = false;
if (!handledCompression) { if (!handledCompression) {
if (ctx.pipeline().names().indexOf("compress") > ctx.pipeline().names().indexOf("via-encoder")) { if (ctx.pipeline().names().indexOf("compress") > ctx.pipeline().names().indexOf("via-encoder")) {
// Need to decompress this packet due to bad order // Need to decompress this packet due to bad order
bytebuf = BungeePipelineUtil.decompress(ctx, bytebuf); ByteBuf decompressed = BungeePipelineUtil.decompress(ctx, draft);
ChannelHandler encoder = ctx.pipeline().get("via-decoder"); try {
ChannelHandler decoder = ctx.pipeline().get("via-encoder"); draft.clear().writeBytes(decompressed);
ctx.pipeline().remove(encoder); } finally {
ctx.pipeline().remove(decoder); decompressed.release();
ctx.pipeline().addAfter("decompress", "via-decoder", encoder); }
ctx.pipeline().addAfter("compress", "via-encoder", decoder); ChannelHandler dec = ctx.pipeline().get("via-decoder");
ChannelHandler enc = ctx.pipeline().get("via-encoder");
ctx.pipeline().remove(dec);
ctx.pipeline().remove(enc);
ctx.pipeline().addAfter("decompress", "via-decoder", dec);
ctx.pipeline().addAfter("compress", "via-encoder", enc);
needsCompress = true; needsCompress = true;
handledCompression = true; handledCompression = true;
} }
} }
// Increment sent return needsCompress;
info.incrementSent(); }
if (info.isActive()) {
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
ByteBuf oldPacket = bytebuf.copy();
bytebuf.clear();
private void recompress(ChannelHandlerContext ctx, ByteBuf draft) {
ByteBuf compressed = BungeePipelineUtil.compress(ctx, draft);
try { try {
PacketWrapper wrapper = new PacketWrapper(id, oldPacket, info); draft.clear().writeBytes(compressed);
ProtocolInfo protInfo = info.get(ProtocolInfo.class);
protInfo.getPipeline().transform(Direction.OUTGOING, protInfo.getState(), wrapper);
wrapper.writeToBuffer(bytebuf);
} catch (Throwable e) {
bytebuf.clear();
throw e;
} finally { } finally {
oldPacket.release(); compressed.release();
}
}
if (needsCompress) {
ByteBuf old = bytebuf;
bytebuf = BungeePipelineUtil.compress(ctx, bytebuf);
old.release();
out.add(bytebuf);
} else {
out.add(bytebuf.retain());
} }
} }
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelException.class)) return; if (cause instanceof CancelEncoderException) return;
super.exceptionCaught(ctx, cause); super.exceptionCaught(ctx, cause);
} }
} }

View File

@ -11,6 +11,8 @@ import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Via; import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.ViaVersionConfig; import us.myles.ViaVersion.api.ViaVersionConfig;
import us.myles.ViaVersion.api.type.Type; 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.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil; import us.myles.ViaVersion.util.PipelineUtil;
@ -18,6 +20,7 @@ import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.function.Function;
public class UserConnection { public class UserConnection {
private static final AtomicLong IDS = new AtomicLong(); private static final AtomicLong IDS = new AtomicLong();
@ -190,6 +193,8 @@ public class UserConnection {
channel.close(); // =) channel.close(); // =)
} }
}); });
} else {
getChannel().close(); // Just disconnect, we don't know what the connection is
} }
} }
@ -340,6 +345,81 @@ public class UserConnection {
this.warnings = warnings; this.warnings = warnings;
} }
/**
* Monitors serverbound packets.
*
* @return false if this packet should be cancelled
*/
public boolean checkIncomingPacket() {
// Ignore if pending disconnect
if (pendingDisconnect) return false;
// Increment received + Check PPS
return !incrementReceived() || !handlePPS();
}
/**
* Monitors clientbound packets.
*/
public void checkOutgoingPacket() {
incrementSent();
}
/**
* Checks if packets needs transforming.
*
* @return if packets should be passed through
*/
public boolean shouldTransformPacket() {
return active;
}
/**
* Transforms the clientbound packet contained in draft ByteBuf.
*
* @param draft ByteBuf with packet id and packet contents
* @param cancelSupplier Function called with original CancelException for generating the Exception used when
* packet is cancelled
* @throws Exception when transforming failed or this packet is cancelled
*/
public void transformOutgoing(ByteBuf draft, Function<Throwable, Exception> cancelSupplier) throws Exception {
if (!draft.isReadable()) return;
transform(draft, Direction.OUTGOING, cancelSupplier);
}
/**
* Transforms the serverbound packet contained in draft ByteBuf.
*
* @param draft ByteBuf with packet id and packet contents
* @param cancelSupplier Function called with original CancelException for generating the Exception used when
* packet is cancelled
* @throws Exception when transforming failed or this packet is cancelled
*/
public void transformIncoming(ByteBuf draft, Function<Throwable, Exception> cancelSupplier) throws Exception {
if (!draft.isReadable()) return;
transform(draft, Direction.INCOMING, cancelSupplier);
}
private void transform(ByteBuf draft, Direction direction, Function<Throwable, Exception> cancelSupplier) throws Exception {
int id = Type.VAR_INT.read(draft);
if (id == PacketWrapper.PASSTHROUGH_ID) return;
PacketWrapper wrapper = new PacketWrapper(id, draft, this);
ProtocolInfo protInfo = get(ProtocolInfo.class);
try {
protInfo.getPipeline().transform(direction, protInfo.getState(), wrapper);
} catch (CancelException ex) {
throw cancelSupplier.apply(ex);
}
ByteBuf transformed = draft.alloc().buffer();
try {
wrapper.writeToBuffer(transformed);
draft.clear().writeBytes(transformed);
} finally {
transformed.release();
}
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;

View File

@ -435,7 +435,7 @@ public abstract class Protocol<C1 extends ClientboundPacketType, C2 extends Clie
} }
if (packetWrapper.isCancelled()) { if (packetWrapper.isCancelled()) {
throw Via.getManager().isDebug() ? new CancelException() : CancelException.CACHED; throw CancelException.generate();
} }
} }

View File

@ -0,0 +1,42 @@
package us.myles.ViaVersion.exception;
import io.netty.handler.codec.DecoderException;
import us.myles.ViaVersion.api.Via;
/**
* Used for cancelling packets in decode handlers
*/
public class CancelDecoderException extends DecoderException {
public static final CancelDecoderException CACHED = new CancelDecoderException("CACHED") {
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
};
public CancelDecoderException() {
super();
}
public CancelDecoderException(String message, Throwable cause) {
super(message, cause);
}
public CancelDecoderException(String message) {
super(message);
}
public CancelDecoderException(Throwable cause) {
super(cause);
}
/**
* Returns a cached CancelDecoderException or a new instance when {@link us.myles.ViaVersion.ViaManager}#isDebug() is true
* @param cause cause for being used when a new instance is creeated
* @return a CancelDecoderException instance
*/
public static CancelDecoderException generate(Throwable cause) {
if (Via.getManager().isDebug()) return new CancelDecoderException(cause);
return CACHED;
}
}

View File

@ -0,0 +1,42 @@
package us.myles.ViaVersion.exception;
import io.netty.handler.codec.EncoderException;
import us.myles.ViaVersion.api.Via;
/**
* Used for cancelling packets in encode handlers
*/
public class CancelEncoderException extends EncoderException {
public static final CancelEncoderException CACHED = new CancelEncoderException("CACHED") {
@Override
public synchronized Throwable fillInStackTrace() {
return this;
}
};
public CancelEncoderException() {
super();
}
public CancelEncoderException(String message, Throwable cause) {
super(message, cause);
}
public CancelEncoderException(String message) {
super(message);
}
public CancelEncoderException(Throwable cause) {
super(cause);
}
/**
* Returns a cached CancelEncoderException or a new instance when {@link us.myles.ViaVersion.ViaManager}#isDebug() is true
* @param cause cause for being used when a new instance is creeated
* @return a CancelEncoderException instance
*/
public static CancelEncoderException generate(Throwable cause) {
if (Via.getManager().isDebug()) return new CancelEncoderException(cause);
return CACHED;
}
}

View File

@ -1,5 +1,10 @@
package us.myles.ViaVersion.exception; package us.myles.ViaVersion.exception;
import us.myles.ViaVersion.api.Via;
/**
* Used for cancelling packets
*/
public class CancelException extends Exception { public class CancelException extends Exception {
public static final CancelException CACHED = new CancelException("Cached - Enable /viaver debug to not use cached exception") { public static final CancelException CACHED = new CancelException("Cached - Enable /viaver debug to not use cached exception") {
@Override @Override
@ -26,4 +31,13 @@ public class CancelException extends Exception {
public CancelException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { public CancelException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace); super(message, cause, enableSuppression, writableStackTrace);
} }
/**
* Returns a cached CancelException or a new instance when {@link us.myles.ViaVersion.ViaManager}#isDebug() is true
* @return a CancelException instance
*/
public static CancelException generate() {
if (Via.getManager().isDebug()) return new CancelException();
return CACHED;
}
} }

View File

@ -4,8 +4,8 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
public interface ViaHandler { public interface ViaHandler {
public void transform(ByteBuf bytebuf) throws Exception; void transform(ByteBuf bytebuf) throws Exception;
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception; void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception;
} }

View File

@ -3,12 +3,8 @@ package us.myles.ViaVersion.sponge.handlers;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.exception.CancelDecoderException;
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 us.myles.ViaVersion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
@ -26,64 +22,37 @@ public class SpongeDecodeHandler extends ByteToMessageDecoder {
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> list) throws Exception {
// use transformers if (!info.checkIncomingPacket()) {
if (bytebuf.readableBytes() > 0) { bytebuf.clear(); // Don't accumulate
// Ignore if pending disconnect throw CancelDecoderException.generate(null);
if (info.isPendingDisconnect()) {
return;
}
// Increment received
boolean second = info.incrementReceived();
// Check PPS
if (second) {
if (info.handlePPS())
return;
} }
if (info.isActive()) { ByteBuf draft = null;
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
ByteBuf newPacket = ctx.alloc().buffer();
try { try {
if (id == PacketWrapper.PASSTHROUGH_ID) { if (info.shouldTransformPacket()) {
newPacket.writeBytes(bytebuf); draft = ctx.alloc().buffer().writeBytes(bytebuf);
} else { info.transformIncoming(draft, CancelDecoderException::generate);
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 (Throwable e) {
// Clear Buffer
bytebuf.clear();
// Release Packet, be free!
newPacket.release();
throw e;
}
}
// call minecraft decoder
try { try {
list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, bytebuf)); list.addAll(PipelineUtil.callDecode(this.minecraftDecoder, ctx, draft == null ? bytebuf : draft));
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) { if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause(); throw (Exception) e.getCause();
} else if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
} }
} finally { } finally {
if (info.isActive()) { if (draft != null) {
bytebuf.release(); draft.release();
}
} }
} }
} }
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelException.class)) return; if (cause instanceof CancelDecoderException) return;
super.exceptionCaught(ctx, cause); super.exceptionCaught(ctx, cause);
} }
} }

View File

@ -3,69 +3,52 @@ package us.myles.ViaVersion.sponge.handlers;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.exception.CancelEncoderException;
import us.myles.ViaVersion.exception.CancelException; import us.myles.ViaVersion.handlers.ChannelHandlerContextWrapper;
import us.myles.ViaVersion.packets.Direction; import us.myles.ViaVersion.handlers.ViaHandler;
import us.myles.ViaVersion.protocols.base.ProtocolInfo;
import us.myles.ViaVersion.util.PipelineUtil; import us.myles.ViaVersion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
public class SpongeEncodeHandler extends MessageToByteEncoder { public class SpongeEncodeHandler extends MessageToByteEncoder<Object> implements ViaHandler {
private final UserConnection info; private final UserConnection info;
private final MessageToByteEncoder minecraftEncoder; private final MessageToByteEncoder<?> minecraftEncoder;
public SpongeEncodeHandler(UserConnection info, MessageToByteEncoder minecraftEncoder) { public SpongeEncodeHandler(UserConnection info, MessageToByteEncoder<?> minecraftEncoder) {
this.info = info; this.info = info;
this.minecraftEncoder = minecraftEncoder; this.minecraftEncoder = minecraftEncoder;
} }
@Override @Override
protected void encode(final ChannelHandlerContext ctx, Object o, final ByteBuf bytebuf) throws Exception { protected void encode(final ChannelHandlerContext ctx, Object o, final ByteBuf bytebuf) throws Exception {
// handle the packet type // handle the packet type
if (!(o instanceof ByteBuf)) { if (!(o instanceof ByteBuf)) {
// call minecraft encoder // call minecraft encoder
try { try {
PipelineUtil.callEncode(this.minecraftEncoder, ctx, o, bytebuf); PipelineUtil.callEncode(this.minecraftEncoder, new ChannelHandlerContextWrapper(ctx, this), o, bytebuf);
} catch (InvocationTargetException e) { } catch (InvocationTargetException e) {
if (e.getCause() instanceof Exception) { if (e.getCause() instanceof Exception) {
throw (Exception) e.getCause(); throw (Exception) e.getCause();
} else if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
} }
} }
} }
if (bytebuf.readableBytes() == 0) {
throw Via.getManager().isDebug() ? new CancelException() : CancelException.CACHED;
}
// Increment sent
info.incrementSent();
if (info.isActive()) {
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
ByteBuf oldPacket = bytebuf.copy();
bytebuf.clear();
try { transform(bytebuf);
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 (Throwable e) {
bytebuf.clear();
throw e;
} finally {
oldPacket.release();
}
} }
@Override
public void transform(ByteBuf bytebuf) throws Exception {
info.checkOutgoingPacket();
if (!info.shouldTransformPacket()) return;
info.transformOutgoing(bytebuf, CancelEncoderException::generate);
} }
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelException.class)) return; if (cause instanceof CancelEncoderException) return;
super.exceptionCaught(ctx, cause); super.exceptionCaught(ctx, cause);
} }
} }

View File

@ -4,13 +4,8 @@ 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.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.exception.CancelDecoderException;
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.util.List; import java.util.List;
@ -24,54 +19,24 @@ 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 {
// use transformers if (!info.checkIncomingPacket()) throw CancelDecoderException.generate(null);
if (bytebuf.readableBytes() > 0) { if (!info.shouldTransformPacket()) {
// Ignore if pending disconnect out.add(bytebuf.retain());
if (info.isPendingDisconnect()) {
return; return;
} }
// Increment received
boolean second = info.incrementReceived(); ByteBuf draft = ctx.alloc().buffer().writeBytes(bytebuf);
// 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 { try {
if (id == PacketWrapper.PASSTHROUGH_ID) { info.transformIncoming(draft, CancelDecoderException::generate);
newPacket.writeBytes(bytebuf); out.add(draft.retain());
} else { } finally {
PacketWrapper wrapper = new PacketWrapper(id, bytebuf, info); draft.release();
ProtocolInfo protInfo = info.get(ProtocolInfo.class);
protInfo.getPipeline().transform(Direction.INCOMING, protInfo.getState(), wrapper);
wrapper.writeToBuffer(newPacket);
}
bytebuf.clear();
bytebuf = newPacket;
} catch (Throwable e) {
// Clear Buffer
bytebuf.clear();
// Release Packet, be free!
newPacket.release();
throw e;
}
} else {
bytebuf.retain();
}
out.add(bytebuf);
} }
} }
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelException.class)) return; if (cause instanceof CancelDecoderException) return;
super.exceptionCaught(ctx, cause); super.exceptionCaught(ctx, cause);
} }
} }

View File

@ -6,15 +6,11 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.MessageToMessageEncoder;
import us.myles.ViaVersion.api.PacketWrapper;
import us.myles.ViaVersion.api.Via;
import us.myles.ViaVersion.api.data.UserConnection; import us.myles.ViaVersion.api.data.UserConnection;
import us.myles.ViaVersion.api.type.Type; import us.myles.ViaVersion.exception.CancelEncoderException;
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 us.myles.ViaVersion.util.PipelineUtil;
import java.lang.reflect.InvocationTargetException;
import java.util.List; import java.util.List;
@ChannelHandler.Sharable @ChannelHandler.Sharable
@ -28,14 +24,38 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
@Override @Override
protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception { protected void encode(final ChannelHandlerContext ctx, ByteBuf bytebuf, List<Object> out) throws Exception {
if (bytebuf.readableBytes() == 0) { info.checkOutgoingPacket();
throw Via.getManager().isDebug() ? new CancelException() : CancelException.CACHED; if (!info.shouldTransformPacket()) {
out.add(bytebuf.retain());
return;
} }
ByteBuf draft = ctx.alloc().buffer().writeBytes(bytebuf);
try {
boolean needsCompress = handleCompressionOrder(ctx, draft);
info.transformOutgoing(draft, CancelEncoderException::generate);
if (needsCompress) {
recompress(ctx, draft);
}
out.add(draft.retain());
} finally {
draft.release();
}
}
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf draft) throws InvocationTargetException {
boolean needsCompress = false; boolean needsCompress = false;
if (!handledCompression if (!handledCompression
&& ctx.pipeline().names().indexOf("compression-encoder") > ctx.pipeline().names().indexOf("via-encoder")) { && ctx.pipeline().names().indexOf("compression-encoder") > ctx.pipeline().names().indexOf("via-encoder")) {
// Need to decompress this packet due to bad order // Need to decompress this packet due to bad order
bytebuf = (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder) ctx.pipeline().get("compression-decoder"), ctx, bytebuf).get(0); ByteBuf decompressed = (ByteBuf) PipelineUtil.callDecode((MessageToMessageDecoder<?>) ctx.pipeline().get("compression-decoder"), ctx, draft).get(0);
try {
draft.clear().writeBytes(decompressed);
} finally {
decompressed.release();
}
ChannelHandler encoder = ctx.pipeline().get("via-encoder"); ChannelHandler encoder = ctx.pipeline().get("via-encoder");
ChannelHandler decoder = ctx.pipeline().get("via-decoder"); ChannelHandler decoder = ctx.pipeline().get("via-decoder");
ctx.pipeline().remove(encoder); ctx.pipeline().remove(encoder);
@ -44,51 +64,23 @@ public class VelocityEncodeHandler extends MessageToMessageEncoder<ByteBuf> {
ctx.pipeline().addAfter("compression-decoder", "via-decoder", decoder); ctx.pipeline().addAfter("compression-decoder", "via-decoder", decoder);
needsCompress = true; needsCompress = true;
handledCompression = true; handledCompression = true;
} else {
bytebuf.retain();
} }
// Increment sent return needsCompress;
info.incrementSent(); }
private void recompress(ChannelHandlerContext ctx, ByteBuf draft) throws InvocationTargetException {
if (info.isActive()) { ByteBuf compressed = ctx.alloc().buffer();
// Handle ID
int id = Type.VAR_INT.read(bytebuf);
// Transform
ByteBuf newPacket = bytebuf.alloc().buffer();
try { try {
PacketWrapper wrapper = new PacketWrapper(id, bytebuf, info); PipelineUtil.callEncode((MessageToByteEncoder<?>) ctx.pipeline().get("compression-encoder"), ctx, draft, compressed);
ProtocolInfo protInfo = info.get(ProtocolInfo.class); draft.clear().writeBytes(compressed);
protInfo.getPipeline().transform(Direction.OUTGOING, protInfo.getState(), wrapper);
wrapper.writeToBuffer(newPacket);
bytebuf.clear();
bytebuf.release();
bytebuf = newPacket;
} catch (Throwable e) {
bytebuf.clear();
bytebuf.release();
newPacket.release();
throw e;
}
}
if (needsCompress) {
ByteBuf old = bytebuf;
bytebuf = ctx.alloc().buffer();
try {
PipelineUtil.callEncode((MessageToByteEncoder) ctx.pipeline().get("compression-encoder"), ctx, old, bytebuf);
} finally { } finally {
old.release(); compressed.release();
} }
} }
out.add(bytebuf);
}
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
if (PipelineUtil.containsCause(cause, CancelException.class)) return; if (cause instanceof CancelEncoderException) return;
super.exceptionCaught(ctx, cause); super.exceptionCaught(ctx, cause);
} }
} }