230 lines
10 KiB
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);
|
|
}
|
|
}
|
|
} |