Skip to content

Instantly share code, notes, and snippets.

@Exerosis
Created September 17, 2018 03:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Exerosis/548aa16b977dc7fe2a63b370d8d51a76 to your computer and use it in GitHub Desktop.
Save Exerosis/548aa16b977dc7fe2a63b370d8d51a76 to your computer and use it in GitHub Desktop.
public class MessageFilter implements Listener {
private static final String
HANDLER = "packet_handler",
CUSTOM_HANDLER = "custom_packet_handler";
private static final Field
FIELD_COMPONENT,
FIELD_CONNECTION,
FIELD_NETWORK,
FIELD_CHANNEL;
private static final Method
METHOD_TEXT,
METHOD_HANDLE;
private static final Class<?>
CLASS_PACKET_CHAT;
static {
try {
final String cb = Bukkit.getServer().getClass().getPackage().getName();
final String nms = "net.minecraft.server." + cb.substring(23);
final Class<?> chatComponent = Class.forName(nms + ".IChatBaseComponent");
CLASS_PACKET_CHAT = Class.forName(nms + ".PacketPlayOutChat");
FIELD_COMPONENT = Arrays.stream(CLASS_PACKET_CHAT.getDeclaredFields())
.filter(field -> field.getType() == chatComponent)
.findFirst().orElseThrow(IllegalStateException::new);
METHOD_TEXT = chatComponent.getDeclaredMethod("toPlainText");
METHOD_HANDLE = Class.forName(cb + ".CraftPlayer").getDeclaredMethod("getHandle");
FIELD_CONNECTION = METHOD_HANDLE.getReturnType().getDeclaredField("playerConnection");
FIELD_NETWORK = FIELD_CONNECTION.getType().getDeclaredField("networkManager");
FIELD_CHANNEL = FIELD_NETWORK.getType().getDeclaredField("channel");
} catch (Throwable reason) {
throw new RuntimeException(reason);
}
}
private final Predicate<String> filter;
private final Map<Player, Closeable> injections = new IdentityHashMap<>();
public MessageFilter(Predicate<String> filter) {
this.filter = filter;
}
@EventHandler
void onJoin(PlayerJoinEvent event) {
try {
final ChannelPipeline pipeline = ((Channel) FIELD_CHANNEL.get(
FIELD_NETWORK.get(
FIELD_CONNECTION.get(
METHOD_HANDLE.invoke(event.getPlayer())
)
)
)).pipeline();
final ChannelHandler handler = new ChannelDuplexHandler() {
@Override
public void write(ChannelHandlerContext context, Object packet, ChannelPromise promise) throws Exception {
if (packet.getClass() == CLASS_PACKET_CHAT)
if (!filter.test(METHOD_TEXT.invoke(FIELD_COMPONENT.get(packet)).toString()))
return;
super.write(context, packet, promise);
}
};
injections.put(event.getPlayer(), () -> pipeline.remove(handler));
} catch (Throwable reason) {
throw new RuntimeException(reason);
}
}
@EventHandler
void onQuit(PlayerQuitEvent event) {
try {
final Closeable injection = injections.remove(event.getPlayer());
if (injection != null)
injection.close();
} catch (Throwable reason) {
throw new RuntimeException(reason);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment