Skip to content

Commit ef49276

Browse files
authored
Merge pull request #1155 from booky10/fix/bungee-injection-v2
Support bungee's new combined compressor
2 parents a3116dc + fed7c14 commit ef49276

File tree

4 files changed

+77
-91
lines changed

4 files changed

+77
-91
lines changed

bungeecord/src/main/java/io/github/retrooper/packetevents/handlers/PacketEventsDecoder.java

+35-2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import io.netty.buffer.ByteBuf;
2828
import io.netty.channel.ChannelHandler;
2929
import io.netty.channel.ChannelHandlerContext;
30+
import io.netty.channel.ChannelPipeline;
3031
import io.netty.handler.codec.MessageToMessageDecoder;
3132
import net.md_5.bungee.api.connection.ProxiedPlayer;
3233
import org.jetbrains.annotations.NotNull;
@@ -35,14 +36,20 @@
3536

3637
@ChannelHandler.Sharable
3738
public class PacketEventsDecoder extends MessageToMessageDecoder<ByteBuf> {
39+
3840
public User user;
3941
public ProxiedPlayer player;
42+
public boolean handledCompression;
4043

4144
public PacketEventsDecoder(User user) {
4245
this.user = user;
4346
}
4447

4548
public void read(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> output) throws Exception {
49+
if (this.tryFixCompressorOrder(ctx, byteBuf)) {
50+
return; // skip handling of buffer
51+
}
52+
4653
ByteBuf transformed = ctx.alloc().buffer().writeBytes(byteBuf);
4754
try {
4855
int firstReaderIndex = transformed.readerIndex();
@@ -55,8 +62,7 @@ public void read(ChannelHandlerContext ctx, ByteBuf byteBuf, List<Object> output
5562
ByteBufHelper.clear(packetReceiveEvent.getByteBuf());
5663
packetReceiveEvent.getLastUsedWrapper().writeVarInt(packetReceiveEvent.getPacketId());
5764
packetReceiveEvent.getLastUsedWrapper().write();
58-
}
59-
else {
65+
} else {
6066
transformed.readerIndex(firstReaderIndex);
6167
}
6268
output.add(transformed.retain());
@@ -88,4 +94,31 @@ public void channelInactive(@NotNull ChannelHandlerContext ctx) throws Exception
8894
ServerConnectionInitializer.destroyChannel(ctx.channel());
8995
super.channelInactive(ctx);
9096
}
97+
98+
private boolean tryFixCompressorOrder(ChannelHandlerContext ctx, ByteBuf buffer) {
99+
if (this.handledCompression) {
100+
return false;
101+
}
102+
ChannelPipeline pipe = ctx.pipeline();
103+
List<String> pipeNames = pipe.names();
104+
int decompressorIndex = pipeNames.indexOf("decompress");
105+
if (decompressorIndex == -1) {
106+
return false;
107+
}
108+
this.handledCompression = true;
109+
if (!pipeNames.contains("frame-prepender-compress")) {
110+
// before "modern" version, no need to handle this here
111+
return false;
112+
} else if (decompressorIndex <= pipeNames.indexOf(PacketEvents.DECODER_NAME)) {
113+
return false; // order already seems to be correct
114+
}
115+
// relocate handler - encoder doesn't need relocation
116+
PacketEventsDecoder decoder = (PacketEventsDecoder) pipe.remove(PacketEvents.DECODER_NAME);
117+
pipe.addAfter("decompress", PacketEvents.DECODER_NAME, decoder);
118+
119+
// re-fire packet
120+
ChannelHandlerContext frameDecoderCtx = pipe.context("frame-decoder");
121+
frameDecoderCtx.fireChannelRead(buffer.retain());
122+
return true; // skip further handling
123+
}
91124
}

bungeecord/src/main/java/io/github/retrooper/packetevents/handlers/PacketEventsEncoder.java

+39-81
Original file line numberDiff line numberDiff line change
@@ -30,27 +30,16 @@
3030
import io.netty.channel.ChannelOutboundHandlerAdapter;
3131
import io.netty.channel.ChannelPipeline;
3232
import io.netty.channel.ChannelPromise;
33-
import io.netty.handler.codec.EncoderException;
34-
import io.netty.util.Recycler;
3533
import io.netty.util.ReferenceCountUtil;
36-
import io.netty.util.concurrent.PromiseCombiner;
3734
import net.md_5.bungee.api.connection.ProxiedPlayer;
3835

3936
import java.lang.reflect.InvocationTargetException;
40-
import java.util.ArrayList;
4137
import java.util.List;
4238

4339
// Thanks to ViaVersion for the compression method.
4440
@ChannelHandler.Sharable
4541
public class PacketEventsEncoder extends ChannelOutboundHandlerAdapter {
4642

47-
private static final Recycler<OutList> OUT_LIST_RECYCLER = new Recycler<OutList>() {
48-
@Override
49-
protected OutList newObject(Handle<OutList> handle) {
50-
return new OutList(handle);
51-
}
52-
};
53-
5443
public ProxiedPlayer player;
5544
public User user;
5645
public boolean handledCompression;
@@ -59,8 +48,8 @@ public PacketEventsEncoder(User user) {
5948
this.user = user;
6049
}
6150

62-
public void read(ChannelHandlerContext ctx, ByteBuf buffer, ChannelPromise promise) throws Exception {
63-
boolean doCompression = handleCompressionOrder(ctx, buffer);
51+
public void read(ChannelHandlerContext originalCtx, ByteBuf buffer, ChannelPromise promise) {
52+
ChannelHandlerContext ctx = this.tryFixCompressorOrder(originalCtx, buffer);
6453
int firstReaderIndex = buffer.readerIndex();
6554
PacketSendEvent packetSendEvent = EventCreationUtil.createSendEvent(ctx.channel(), user, player,
6655
buffer, false);
@@ -74,11 +63,7 @@ public void read(ChannelHandlerContext ctx, ByteBuf buffer, ChannelPromise promi
7463
} else {
7564
buffer.readerIndex(firstReaderIndex);
7665
}
77-
if (doCompression) {
78-
this.recompress(ctx, buffer, promise);
79-
} else {
80-
ctx.write(buffer, promise);
81-
}
66+
ctx.write(buffer, promise);
8267
} else {
8368
ReferenceCountUtil.release(packetSendEvent.getByteBuf());
8469
}
@@ -103,79 +88,52 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
10388
}
10489
}
10590

106-
private boolean handleCompressionOrder(ChannelHandlerContext ctx, ByteBuf buffer) {
91+
private ChannelHandlerContext tryFixCompressorOrder(ChannelHandlerContext ctx, ByteBuf buffer) {
92+
if (this.handledCompression) {
93+
return ctx;
94+
}
10795
ChannelPipeline pipe = ctx.pipeline();
108-
if (handledCompression) {
109-
return false;
96+
List<String> pipeNames = pipe.names();
97+
if (pipeNames.contains("frame-prepender-compress")) {
98+
// "modern" version, no need to handle this here
99+
this.handledCompression = true;
100+
return ctx;
110101
}
111-
int encoderIndex = pipe.names().indexOf("compress");
112-
if (encoderIndex == -1) {
113-
return false;
102+
int compressorIndex = pipeNames.indexOf("compress");
103+
if (compressorIndex == -1) {
104+
return ctx;
114105
}
115-
if (encoderIndex > pipe.names().indexOf(PacketEvents.ENCODER_NAME)) {
116-
// Need to decompress this packet due to bad order
117-
ChannelHandler decompressor = pipe.get("decompress");
118-
try {
119-
ByteBuf decompressed = (ByteBuf) CustomPipelineUtil.callPacketDecodeByteBuf(decompressor, ctx, buffer).get(0);
120-
if (buffer != decompressed) {
121-
try {
122-
buffer.clear().writeBytes(decompressed);
123-
} finally {
124-
decompressed.release();
125-
}
126-
}
127-
//Relocate handlers
128-
PacketEventsDecoder decoder = (PacketEventsDecoder) pipe.remove(PacketEvents.DECODER_NAME);
129-
PacketEventsEncoder encoder = (PacketEventsEncoder) pipe.remove(PacketEvents.ENCODER_NAME);
130-
pipe.addAfter("decompress", PacketEvents.DECODER_NAME, decoder);
131-
pipe.addAfter("compress", PacketEvents.ENCODER_NAME, encoder);
132-
//System.out.println("Pipe: " + ChannelHelper.pipelineHandlerNamesAsString(ctx.channel()));
133-
handledCompression = true;
134-
return true;
135-
} catch (InvocationTargetException e) {
136-
e.printStackTrace();
137-
}
106+
this.handledCompression = true;
107+
if (compressorIndex <= pipeNames.indexOf(PacketEvents.ENCODER_NAME)) {
108+
return ctx; // order already seems to be correct
138109
}
139-
return false;
110+
// relocate handlers
111+
PacketEventsDecoder decoder = (PacketEventsDecoder) pipe.remove(PacketEvents.DECODER_NAME);
112+
PacketEventsEncoder encoder = (PacketEventsEncoder) pipe.remove(PacketEvents.ENCODER_NAME);
113+
pipe.addAfter("decompress", PacketEvents.DECODER_NAME, decoder);
114+
pipe.addAfter("compress", PacketEvents.ENCODER_NAME, encoder);
115+
116+
// manually decompress packet and update context,
117+
// so we don't need to additionally manually re-compress the packet
118+
this.decompress(pipe, buffer);
119+
return pipe.context(PacketEvents.ENCODER_NAME);
140120
}
141121

142-
private void recompress(ChannelHandlerContext ctx, ByteBuf buffer, ChannelPromise promise) {
143-
OutList outWrapper = OUT_LIST_RECYCLER.get();
144-
List<Object> out = outWrapper.list;
145-
try {
146-
ChannelHandler compressor = ctx.pipeline().get("compress");
147-
CustomPipelineUtil.callPacketEncodeByteBuf(compressor, ctx, buffer, out);
122+
private void decompress(ChannelPipeline pipe, ByteBuf buffer) {
123+
ChannelHandler decompressor = pipe.get("decompress");
124+
ChannelHandlerContext decompressorCtx = pipe.context("decompress");
148125

149-
int len = out.size();
150-
if (len == 1) {
151-
// should be the only case which
152-
// happens on vanilla bungeecord
153-
ctx.write(out.get(0), promise);
154-
} else {
155-
// copied from MessageToMessageEncoder#writePromiseCombiner
156-
PromiseCombiner combiner = new PromiseCombiner(ctx.executor());
157-
for (int i = 0; i < len; i++) {
158-
combiner.add(ctx.write(out.get(i)));
159-
}
160-
combiner.finish(promise);
126+
ByteBuf decompressed = null;
127+
try {
128+
decompressed = (ByteBuf) CustomPipelineUtil.callPacketDecodeByteBuf(
129+
decompressor, decompressorCtx, buffer).get(0);
130+
if (buffer != decompressed) {
131+
buffer.clear().writeBytes(decompressed);
161132
}
162133
} catch (InvocationTargetException exception) {
163-
throw new EncoderException("Error while recompressing bytebuf " + buffer.readableBytes(), exception);
134+
throw new RuntimeException(exception);
164135
} finally {
165-
out.clear();
166-
outWrapper.handle.recycle(outWrapper);
167-
buffer.release();
168-
}
169-
}
170-
171-
private static final class OutList {
172-
173-
// the default bungee compressor only produces one output bytebuf
174-
private final List<Object> list = new ArrayList<>(1);
175-
private final Recycler.Handle<OutList> handle;
176-
177-
public OutList(Recycler.Handle<OutList> handle) {
178-
this.handle = handle;
136+
ReferenceCountUtil.release(decompressed);
179137
}
180138
}
181139
}

bungeecord/src/main/java/io/github/retrooper/packetevents/injector/BungeePipelineInjector.java

+2-7
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
import com.github.retrooper.packetevents.PacketEvents;
2121
import com.github.retrooper.packetevents.injector.ChannelInjector;
22-
import com.github.retrooper.packetevents.protocol.ConnectionState;
2322
import com.github.retrooper.packetevents.protocol.player.User;
2423
import com.github.retrooper.packetevents.util.reflection.Reflection;
2524
import io.github.retrooper.packetevents.handlers.PacketEventsDecoder;
@@ -29,10 +28,8 @@
2928
import io.netty.channel.ChannelInitializer;
3029
import net.md_5.bungee.api.ProxyServer;
3130
import net.md_5.bungee.api.connection.ProxiedPlayer;
32-
import org.jetbrains.annotations.NotNull;
3331

3432
import java.lang.reflect.Field;
35-
import java.lang.reflect.Method;
3633
import java.util.Set;
3734

3835
//Thanks to ViaVersion for helping us design this injector.
@@ -81,10 +78,8 @@ public void injectChannel(Channel channel) {
8178
}
8279

8380
try {
84-
Field f = bootstrapAcceptor.getClass().getDeclaredField("childHandler");
85-
f.setAccessible(true);
86-
f.set(bootstrapAcceptor, newInitializer);
87-
} catch (IllegalAccessException | NoSuchFieldException e) {
81+
initializerField.set(bootstrapAcceptor, newInitializer);
82+
} catch (IllegalAccessException e) {
8883
throw new RuntimeException(e);
8984
}
9085

bungeecord/src/main/java/io/github/retrooper/packetevents/injector/CustomPipelineUtil.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ public static void callPacketEncodeByteBuf(Object encoder, Object ctx, Object ms
149149
}
150150

151151
public static List<Object> callPacketDecodeByteBuf(Object decoder, Object ctx, Object msg) throws InvocationTargetException {
152-
List<Object> output = new ArrayList<>();
152+
List<Object> output = new ArrayList<>(1);
153153
if (BUNGEE_PACKET_DECODE_BYTEBUF == null) {
154154
try {
155155
BUNGEE_PACKET_DECODE_BYTEBUF = decoder.getClass().getDeclaredMethod("decode", ChannelHandlerContext.class,

0 commit comments

Comments
 (0)