Created
September 17, 2018 03:32
-
-
Save Exerosis/548aa16b977dc7fe2a63b370d8d51a76 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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