Skip to content

Commit cb8fb8b

Browse files
committed
Try to uninject from Velocity plugin initializers on termination
Fixes #1489
1 parent a1067f5 commit cb8fb8b

3 files changed

Lines changed: 43 additions & 13 deletions

File tree

api/src/main/java/com/github/retrooper/packetevents/PacketEventsAPI.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public void terminate() {
9494
try {
9595
this.getInjector().uninject();
9696
} catch (Throwable throwable) {
97-
this.getLogManager().warn("Failed to uninject during termination");
97+
this.getLogManager().warn("Failed to uninject during termination, this error can be ignored on shutdown");
9898
throwable.printStackTrace();
9999
}
100100
this.getEventManager().unregisterAllListeners();

velocity/src/main/java/io/github/retrooper/packetevents/injector/VelocityPipelineInjector.java

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package io.github.retrooper.packetevents.injector;
2020

2121
import com.github.retrooper.packetevents.PacketEvents;
22+
import com.github.retrooper.packetevents.PacketEventsAPI;
2223
import com.github.retrooper.packetevents.injector.ChannelInjector;
2324
import com.github.retrooper.packetevents.protocol.player.User;
2425
import com.github.retrooper.packetevents.util.reflection.Reflection;
@@ -29,17 +30,25 @@
2930
import io.github.retrooper.packetevents.handlers.PacketEventsEncoder;
3031
import io.netty.channel.Channel;
3132
import io.netty.channel.ChannelInitializer;
33+
import org.jetbrains.annotations.ApiStatus;
34+
import org.spongepowered.configurate.util.CheckedConsumer;
3235

36+
import java.lang.reflect.Field;
3337
import java.lang.reflect.InvocationTargetException;
3438
import java.lang.reflect.Method;
3539
import java.util.function.Supplier;
3640

41+
@ApiStatus.Internal
3742
public class VelocityPipelineInjector implements ChannelInjector {
43+
3844
private static Class<?> CONNECTION_MANAGER_CLASS, SERVER_INITIALIZER_HOLDER_CLASS, BACKEND_INITIALIZER_HOLDER_CLASS;
3945
private static Method SET_SERVER_INTIIALIZER, SET_BACKEND_INITIALIZER;
46+
47+
private final PacketEventsAPI<?> packetevents;
4048
private final ProxyServer server;
4149

42-
public VelocityPipelineInjector(ProxyServer server) {
50+
public VelocityPipelineInjector(PacketEventsAPI<?> packetevents, ProxyServer server) {
51+
this.packetevents = packetevents;
4352
this.server = server;
4453
}
4554

@@ -76,17 +85,38 @@ public void inject() {
7685

7786
@Override
7887
public void uninject() {
79-
Supplier<ChannelInitializer<Channel>> initializerHolder = getServerChannelInitializerHolder();
80-
ChannelInitializer<Channel> wrapper = initializerHolder.get();
81-
// Check if it's our initializer, could be wrapped by other plugins
82-
if (wrapper instanceof VelocityChannelInitializer) {
83-
try {
84-
SET_SERVER_INTIIALIZER.invoke(initializerHolder, ((VelocityChannelInitializer) wrapper).getWrappedInitializer());
85-
} catch (IllegalAccessException | InvocationTargetException exception) {
86-
throw new RuntimeException("Failed to uninject from frontend pipeline", exception);
88+
Supplier<ChannelInitializer<Channel>> holder = this.getServerChannelInitializerHolder();
89+
ChannelInitializer<?> wrapper = holder.get();
90+
CheckedConsumer<ChannelInitializer<Channel>, ReflectiveOperationException> uninjector = (initializer) -> {
91+
this.packetevents.getLogManager().info("Uninjecting from Velocity channel initializer...");
92+
SET_SERVER_INTIIALIZER.invoke(holder, initializer);
93+
};
94+
95+
try {
96+
while (true) {
97+
// Check if it's our initializer, could be wrapped by other plugins
98+
if (wrapper instanceof VelocityChannelInitializer) {
99+
uninjector.accept(((VelocityChannelInitializer) wrapper).getWrappedInitializer());
100+
break;
101+
} else {
102+
// walk up wrapper tree, if possible to find a single matching field
103+
// this accounts for other plugins (e.g. ViaVersion) also replacing the injector, which may
104+
// wrap our already wrapped injector
105+
Field field = Reflection.getField(wrapper.getClass(), ChannelInitializer.class, 0);
106+
if (field == null) {
107+
throw new IllegalStateException("Can't unwrap foreign channel initializer: " + wrapper);
108+
}
109+
field.setAccessible(true);
110+
ChannelInitializer<?> thisWrapper = wrapper;
111+
wrapper = (ChannelInitializer<?>) field.get(thisWrapper);
112+
uninjector = initializer -> {
113+
field.set(thisWrapper, initializer);
114+
this.packetevents.getLogManager().info("Uninjected from plugin channel initializer " + thisWrapper);
115+
};
116+
}
87117
}
88-
} else {
89-
throw new IllegalStateException("Failed to uninject from frontend pipeline: can't unwrap foreign channel initializer");
118+
} catch (ReflectiveOperationException exception) {
119+
throw new RuntimeException("Failed to uninject from frontend pipeline", exception);
90120
}
91121
}
92122

velocity/src/main/java/io/github/retrooper/packetevents/velocity/factory/VelocityPacketEventsBuilder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ public Object getRegistryCacheKey(User user, ClientVersion version) {
153153

154154
private final PlayerManagerAbstract playerManager = new PlayerManagerImpl();
155155

156-
private final ChannelInjector injector = new VelocityPipelineInjector(server);
156+
private final ChannelInjector injector = new VelocityPipelineInjector(this, server);
157157
private final NettyManager nettyManager = new NettyManagerImpl();
158158
private final LogManager logManager = new LogManager() {
159159
@Override

0 commit comments

Comments
 (0)