API PingAPI: Listen for and modify outgoing ping responses

Discussion in 'Resources' started by Skionz, Feb 7, 2015.

Thread Status:
Not open for further replies.
  1. Offline

    MidasAble

    I made this API to v1_8_R3, and it works well.
    but ServerInfoPacket doesn't work ( MOTD, PotocolName doesn't displayed )

    here is my code :

    Code:
    package com.skionz.pingapi.v1_8_R3;
    
    import io.netty.channel.*;
    
    import java.lang.reflect.*;
    import java.util.*;
    
    import net.minecraft.server.v1_8_R3.*;
    import net.minecraft.server.v1_8_R3.IChatBaseComponent.ChatSerializer;
    import net.minecraft.server.v1_8_R3.ServerPing.ServerData;
    import net.minecraft.server.v1_8_R3.ServerPing.ServerPingPlayerSample;
    
    import org.bukkit.*;
    import org.bukkit.craftbukkit.v1_8_R3.util.*;
    
    import com.mojang.authlib.*;
    import com.skionz.pingapi.*;
    import com.skionz.pingapi.reflect.*;
    
    public class DuplexHandler extends ChannelDuplexHandler {
        private static final Field serverPingField = ReflectUtils.getFirstFieldByType(PacketStatusOutServerInfo.class, ServerPing.class);
        private PingEvent event;
       
        @Override
        public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {
            if(msg instanceof PacketStatusOutServerInfo) {
                PacketStatusOutServerInfo packet = (PacketStatusOutServerInfo) msg;
                PingReply reply = this.constructReply(packet, ctx);
                PingEvent event = new PingEvent(reply);
                for(PingListener listener : PingAPI.getListeners()) {
                    listener.onPing(event);
                }
                this.event = event;
                if(!event.isCancelled()) {
                    super.write(ctx, this.constructPacket(reply), promise);
                }
                return;
            }
            if(msg instanceof PacketStatusOutPong) {
                if(this.event != null && this.event.isPongCancelled()) {
                    return;
                }
            }
            super.write(ctx, msg, promise);
        }
       
        @Override
        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
            super.channelRead(ctx, msg);
        }
    
        private PingReply constructReply(PacketStatusOutServerInfo packet, ChannelHandlerContext ctx) {
            try {
                ServerPing ping = (ServerPing) serverPingField.get(packet);
                String motd = ChatSerializer.a(ping.a());
                int max = ping.b().a();
                int online = ping.b().b();
                int protocolVersion = ping.c().b();
                String protocolName = ping.c().a();
                GameProfile[] profiles = ping.b().c();
                List<String> list = new ArrayList<String>();
                for(int i = 0; i < profiles.length; i++) {
                    list.add(profiles[i].getName());
                }
                PingReply reply = new PingReply(ctx, motd, online, max, protocolVersion, protocolName, list);
                return reply;
            } catch(Exception e) {
                e.printStackTrace();
            }
            return null;
        }
       
        private PacketStatusOutServerInfo constructPacket(PingReply reply) {
            GameProfile[] sample = new GameProfile[reply.getPlayerSample().size()];
            List<String> list = reply.getPlayerSample();
            for(int i = 0; i < list.size(); i++) {
                sample[i] = new GameProfile(UUID.randomUUID(), list.get(i));
            }
            ServerPingPlayerSample playerSample = new ServerPingPlayerSample(reply.getMaxPlayers(), reply.getOnlinePlayers());
            playerSample.a(sample);
            ServerPing ping = new ServerPing();
            ping.setMOTD(new ChatComponentText(reply.getMOTD()));
            ping.setPlayerSample(playerSample);
            ping.setServerInfo(new ServerData(reply.getProtocolName(), reply.getProtocolVersion()));
            ping.setFavicon(((CraftIconCache) reply.getIcon()).value);
            Bukkit.getConsoleSender().sendMessage("§bstuff:§c" + ((CraftIconCache) reply.getIcon()).value);
            return new PacketStatusOutServerInfo(ping);
        }
    }

    Code:
    package com.skionz.pingapi.v1_8_R3;
    
    import io.netty.channel.*;
    
    import java.lang.reflect.*;
    import java.util.*;
    
    import net.minecraft.server.v1_8_R3.*;
    
    import org.bukkit.*;
    import org.bukkit.craftbukkit.v1_8_R3.*;
    import org.bukkit.event.*;
    import org.bukkit.event.player.*;
    import org.bukkit.event.server.*;
    
    import com.skionz.pingapi.reflect.*;
    
    public class PingInjector implements Listener {
        private MinecraftServer server;
        private List<?> networkManagers;
       
        public PingInjector() {
            try {
                CraftServer craftserver = (CraftServer) Bukkit.getServer();
                Field console = craftserver.getClass().getDeclaredField("console");
                console.setAccessible(true);
                this.server = (MinecraftServer) console.get(craftserver);
                ServerConnection conn = this.server.aq();
                networkManagers = Collections.synchronizedList((List<?>) this.getNetworkManagerList(conn));
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
       
        public void injectOpenConnections() {
            try {
                Field field = ReflectUtils.getFirstFieldByType(NetworkManager.class, Channel.class);
                field.setAccessible(true);
                for(Object manager : networkManagers) {
                    Channel channel = (Channel) field.get(manager);
                    if(channel.pipeline().context("ping_handler") == null && (channel.pipeline().context("packet_handler") != null)) {
                        channel.pipeline().addBefore("packet_handler", "ping_handler", new DuplexHandler());
                    }
                }
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
       
        public Object getNetworkManagerList(ServerConnection conn) {
            try {
                for(Method method : conn.getClass().getDeclaredMethods()) {
                    method.setAccessible(true);
                    if(method.getReturnType() == List.class) {
                        Object object = method.invoke(null, conn);
                        return object;
                    }
                }
            } catch(Exception e) {
                e.printStackTrace();
            }
            return null;
        }
       
        @EventHandler
        public void serverListPing(ServerListPingEvent event) {
            this.injectOpenConnections();
        }
       
        @EventHandler
        public void onJoin(PlayerJoinEvent event) {
            this.injectOpenConnections();
        }
    }
    

    Code:
    package com.skionz.pingapi.v1_8_R3;
    
    import java.lang.reflect.*;
    import java.util.*;
    
    import net.minecraft.server.v1_8_R3.*;
    import net.minecraft.server.v1_8_R3.ServerPing.ServerData;
    import net.minecraft.server.v1_8_R3.ServerPing.ServerPingPlayerSample;
    
    import org.bukkit.craftbukkit.v1_8_R3.util.*;
    
    import com.mojang.authlib.*;
    import com.skionz.pingapi.*;
    
    public class ServerInfoPacketHandler implements ServerInfoPacket {
        private PingReply reply;
       
        public ServerInfoPacketHandler(PingReply reply) {
            this.reply = reply;
        }
       
        @Override
        public void send() {
            try {
                Field field = this.reply.getClass().getDeclaredField("ctx");
                field.setAccessible(true);
                Object ctx = field.get(this.reply);
                Method writeAndFlush = ctx.getClass().getMethod("writeAndFlush", Object.class);
                writeAndFlush.setAccessible(true);
                writeAndFlush.invoke(ctx, this.constructPacket());
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
       
        @Override
        public PingReply getPingReply() {
            return this.reply;
        }
       
        @Override
        public void setPingReply(PingReply reply) {
            this.reply = reply;
        }
       
        private PacketStatusOutServerInfo constructPacket() {
            GameProfile[] sample = new GameProfile[reply.getPlayerSample().size()];
            List<String> list = reply.getPlayerSample();
            for(int i = 0; i < list.size(); i++) {
                sample[i] = new GameProfile(UUID.randomUUID(), list.get(i));
            }
            ServerPingPlayerSample playerSample = new ServerPingPlayerSample(reply.getMaxPlayers(), reply.getOnlinePlayers());
            playerSample.a(sample);
            ServerPing ping = new ServerPing();
            ping.setMOTD(new ChatComponentText(reply.getMOTD()));
            ping.setPlayerSample(playerSample);
            ping.setServerInfo(new ServerData(reply.getProtocolName(), reply.getProtocolVersion()));
            ping.setFavicon(((CraftIconCache) reply.getIcon()).value);
            return new PacketStatusOutServerInfo(ping);
        }
    }

    how can i use ServerInfoPacket?
     
  2. Offline

    Orange Tabby

    @Skionz
    I've been looking for something like this :D
     
Thread Status:
Not open for further replies.

Share This Page