Core/src/main/java/eu/univento/core/api/utils/NettyInjection.java

230 lines
10 KiB
Java

/*
* Copyright (c) 2017 univento.eu - All rights reserved
* You are not allowed to use, distribute or modify this code
*/
package eu.univento.core.api.utils;
import eu.univento.core.api.utils.reflection.IReflection;
import io.netty.channel.*;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerLoginEvent;
import org.bukkit.event.server.PluginDisableEvent;
import org.bukkit.plugin.Plugin;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* -== NettyInjection ==-
*
* This library was created by @Ingrim4 and allows you to inject a handler inside a channel of a PlayerConnection.class to modify or avoid packets
* You need my IReflection class http://bit.ly/IReflect
* You are welcome to use it and redistribute it under the following conditions:
* * Don't claim this class as your own
* * Don't remove this disclaimer
*
* @author Ingrim4
* @version 1.2
*/
public class NettyInjection {
private static final Class<?> CLASS_CRAFTPLAYER = IReflection.getClass(IReflection.ServerPacket.CRAFTBUKKIT_PACKAGE, "entity.CraftPlayer");
private static final Class<?> CLASS_ENTITY = IReflection.getClass(IReflection.ServerPacket.MINECRAFT_PACKAGE, "Entity");
private static final Class<?> CLASS_ENTITYPLAYER = IReflection.getClass(IReflection.ServerPacket.MINECRAFT_PACKAGE, "EntityPlayer");
private static final Class<?> CLASS_PLAYERCONNECTION = IReflection.getClass(IReflection.ServerPacket.MINECRAFT_PACKAGE, "PlayerConnection");
private static final Class<?> CLASS_NETWORKMANAGER = IReflection.getClass(IReflection.ServerPacket.MINECRAFT_PACKAGE, "NetworkManager");
private static final IReflection.FieldAccessor<?> FIELD_CRAFTPLAYER_ENTITY = IReflection.getField(NettyInjection.CLASS_CRAFTPLAYER, NettyInjection.CLASS_ENTITY, 0);
private static final IReflection.FieldAccessor<?> FIELD_ENTITYPLAYER_PLAYERCONNECTION = IReflection.getField(NettyInjection.CLASS_ENTITYPLAYER, NettyInjection.CLASS_PLAYERCONNECTION, 0);
private static final IReflection.FieldAccessor<?> FIELD_PLAYERCONNECTION_NETWORKMANAGER = IReflection.getField(NettyInjection.CLASS_PLAYERCONNECTION, NettyInjection.CLASS_NETWORKMANAGER, 0);
private static final IReflection.FieldAccessor<Channel> FIELD_NETWORKMANAGER_CHANNEL = IReflection.getField(NettyInjection.CLASS_NETWORKMANAGER, Channel.class, 0);
private static final Class<?> CLASS_GAMEPROFILE = IReflection.getClass(IReflection.ServerPacket.MOJANG_AUTHLIB, "GameProfile");
private static final IReflection.FieldAccessor<String> FIELD_GAMEPROFILE_NAME = IReflection.getField(NettyInjection.CLASS_GAMEPROFILE, String.class, 0);
private static final Class<?> CLASS_PACKERLOGININSTART = IReflection.getClass(IReflection.ServerPacket.MINECRAFT_PACKAGE, "PacketLoginInStart");
private static final IReflection.FieldAccessor<?> FIELD_PACKERLOGININSTART_GAMEPROFILE = IReflection.getField(NettyInjection.CLASS_PACKERLOGININSTART, NettyInjection.CLASS_GAMEPROFILE, 0);
private static final Class<?> CLASS_CRAFTSERVER = IReflection.getClass(IReflection.ServerPacket.CRAFTBUKKIT_PACKAGE, "CraftServer");
private static final Class<?> CLASS_MINECRAFTSERVER = IReflection.getClass(IReflection.ServerPacket.MINECRAFT_PACKAGE, "MinecraftServer");
private static final Class<?> CLASS_SERVERCONNECTION = IReflection.getClass(IReflection.ServerPacket.MINECRAFT_PACKAGE, "ServerConnection");
private static final IReflection.FieldAccessor<?> FIELD_CRAFTSERVER_MINECRAFTSERVER = IReflection.getField(NettyInjection.CLASS_CRAFTSERVER, NettyInjection.CLASS_MINECRAFTSERVER, 0);
private static final IReflection.FieldAccessor<?> FIELD_MINECRAFTSERVER_SERVERCONNECTION = IReflection.getField(NettyInjection.CLASS_MINECRAFTSERVER, NettyInjection.CLASS_SERVERCONNECTION, 0);
private static final IReflection.FieldAccessor<?> FIELD_SERVERCONNECTION_CHANNELFUTURE = IReflection.getField(NettyInjection.CLASS_SERVERCONNECTION, List.class, 0);
private String handlerName = "nettyinjection_default";
private Map<String, PacketHandler> handlerList = new HashMap<String, PacketHandler>();
private Listener listener;
private final HashMap<String, Channel> playerChannel = new HashMap<String, Channel>();
private final List<Channel> globalChannel = new ArrayList<Channel>();
private ChannelInboundHandlerAdapter globalHandler;
public interface PacketHandler {
public default Object onPacketIn(Player sender, Channel channel, Object packet) {
return packet;
}
public default Object onPacketOut(Player target, Channel channel, Object packet) {
return packet;
}
public default void exceptionCaught(Player player, Channel channel,Throwable throwable) { }
}
public NettyInjection(Plugin plugin, String handlerName) {
this.handlerName = "inject_" + handlerName;
Bukkit.getPluginManager().registerEvents(this.listener = new Listener() {
@EventHandler
public final void onPlayerLogin(PlayerLoginEvent event) {
NettyInjection.this.inject(event.getPlayer());
}
@EventHandler
public void onDisabled(PluginDisableEvent event) {
if (event.getPlugin().equals(plugin))
NettyInjection.this.disable();
}
}, plugin);
ChannelInitializer<Channel> last = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
NettyInjection.this.injectChannel(channel);
}
};
ChannelInitializer<Channel> first = new ChannelInitializer<Channel>() {
@Override
protected void initChannel(Channel channel) throws Exception {
channel.pipeline().addLast(last);
}
};
this.globalHandler = new ChannelInboundHandlerAdapter() {
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
((Channel) msg).pipeline().addFirst(first);
super.channelRead(ctx, msg);
}
};
this.registerGlobalChannel();
Bukkit.getOnlinePlayers().forEach(this::inject);
}
private void registerGlobalChannel() {
Object server = NettyInjection.FIELD_CRAFTSERVER_MINECRAFTSERVER.get(Bukkit.getServer());
Object connection = NettyInjection.FIELD_MINECRAFTSERVER_SERVERCONNECTION.get(server);
List<Object> channelFuture = (List<Object>) FIELD_SERVERCONNECTION_CHANNELFUTURE.get(connection);
for (Object item : channelFuture) {
if (!ChannelFuture.class.isInstance(item))
break;
Channel channel = ((ChannelFuture) item).channel();
this.globalChannel.add(channel);
channel.pipeline().addFirst("NettyInjectionGlobal", this.globalHandler);
}
}
private void unregisterGlobalChannel() {
for (Channel global : this.globalChannel) {
final ChannelPipeline pipe = global.pipeline();
global.eventLoop().execute(() -> pipe.remove("NettyInjectionGlobal"));
}
}
public final void addHandler(String name, PacketHandler handler) {
this.handlerList.put(name, handler);
}
public final void removeHandler(String name) {
if (this.handlerList.containsKey(name))
this.handlerList.remove(name);
}
private void inject(Player player) {
this.injectChannel(this.getChannel(player)).player = player;
}
private void uninject(Player player) {
this.uninjectChannel(this.getChannel(player));
}
private Channel getChannel(Player player) {
Channel channel = this.playerChannel.get(player.getName());
if (channel == null) {
channel = FIELD_NETWORKMANAGER_CHANNEL.get(FIELD_PLAYERCONNECTION_NETWORKMANAGER.get(FIELD_ENTITYPLAYER_PLAYERCONNECTION.get(FIELD_CRAFTPLAYER_ENTITY.get(player))));
this.playerChannel.put(player.getName(), channel);
}
return channel;
}
private PacketInjection injectChannel(Channel channel) {
try {
PacketInjection handel = (PacketInjection) channel.pipeline().get(this.handlerName);
if (handel == null) {
handel = new PacketInjection();
channel.pipeline().addBefore("packet_handler", this.handlerName, handel);
}
return handel;
} catch (Exception e) {
return (PacketInjection) channel.pipeline().get(this.handlerName);
}
}
private void uninjectChannel(Channel channel) {
Object handel = channel.pipeline().get(this.handlerName);
if (handel != null)
channel.pipeline().remove(this.handlerName);
}
public final void disable() {
Bukkit.getOnlinePlayers().forEach(this::uninject);
HandlerList.unregisterAll(this.listener);
this.unregisterGlobalChannel();
}
private class PacketInjection extends ChannelDuplexHandler {
public Player player;
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
assert CLASS_PACKERLOGININSTART != null;
if (CLASS_PACKERLOGININSTART.isInstance(msg))
NettyInjection.this.playerChannel.put(FIELD_GAMEPROFILE_NAME.get(FIELD_PACKERLOGININSTART_GAMEPROFILE.get(msg)), ctx.channel());
for (PacketHandler handel : NettyInjection.this.handlerList.values()) {
if (msg == null)
break;
msg = handel.onPacketIn(this.player, ctx.channel(), msg);
}
if (msg != null)
super.channelRead(ctx, msg);
}
@Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
for (PacketHandler handel : NettyInjection.this.handlerList.values())
msg = handel.onPacketOut(this.player, ctx.channel(), msg);
super.write(ctx, msg, promise);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) throws Exception {
for(PacketHandler handel : NettyInjection.this.handlerList.values())
handel.exceptionCaught(this.player, ctx.channel(), throwable);
super.exceptionCaught(ctx, throwable);
}
}
}