How to make your own values in the server's response (ServerListPingEvent, ServerPing)

Discussion in 'Spigot Plugin Development' started by Jebati, Jun 29, 2018.

Thread Status:
Not open for further replies.
  1. Hello.
    Now, with the usual ping client request to the server, something like this comes up in response:
    {"description":{"text":"Test motd."},"players":{"max":10,"online":1},"version":{"name":"Spigot 1.12.2","protocol":340}}
    How can I implement something like this:
    {"description":{"text":"Test motd."},"players":{"max":10,"online":1},"version":{"name":"Spigot 1.12.2","protocol":340},"tcp":20}
    Well, that is, add some information in response. Tell me which way to look to me.

    Thank you!
     
  2. I am not sure if this is even possible! Well, the server side can handle it fine, but the client may crash. You would probably have to check that first. And yes, that is certainly possible... if you overwrite the server software. A plugin cannot do this, as we can only use the API to get the netty pipeline after authentication. The packet that regulates that stuff is unreachable for a spigot plugin. If you really want to change it, you have to rewrite the default server info packet class.
     
  3. And if you work with query? I'm so tight that I did not even find in the API any information about it.
     
    #3 Jebati, Jun 30, 2018
    Last edited: Jun 30, 2018
  4. I don't know if this will work with query either. Have you considered a mod perhaps? A mod on the client side that could catch information like that if it is sent from the server?
     
  5. I am not sure what you mean with a query? But for who is this extra information useful, as it is not for the client. Because if you know the target, there are probably better ways to do it! So if you tell who it is supposed for, we might be able to suggest an alternative!
     
  6. In server.properties, there is enable-query, and there is query.port, query is the GameSpy4 protocol. And if enable-query = true, then query.port can get an additional. information, such as a complete list of players, plug-ins, etc. This is the answer, is it possible to modify it?

    I need this for the server control panel.

    >> Here << is an example script, in PHP that queries query.port.
     
    #6 Jebati, Jul 1, 2018
    Last edited: Jul 1, 2018
  7. This might be able to be modified, but I am not sure if there is a way to access it from within a plugin. You should do some testing for it yourself... If you hook into it, I am sure that would be a good solution.
     
  8. just googled, and did not find any mention in the API about it. And I use it, I have a list of players on the query panel.
     
  9. net.minecraft.server.v1_12_R1 -> RemoteStatusListener.class

    It looks like something is very related to this.
    Code (Java):
    package net.minecraft.server.v1_12_R1;

    import java.util.Random;
    import java.nio.charset.StandardCharsets;
    import java.net.SocketException;
    import java.net.PortUnreachableException;
    import java.net.SocketTimeoutException;
    import java.util.Iterator;
    import java.io.IOException;
    import java.util.Date;
    import com.google.common.collect.Maps;
    import java.net.UnknownHostException;
    import java.net.InetAddress;
    import java.net.SocketAddress;
    import java.util.Map;
    import java.net.DatagramPacket;
    import java.net.DatagramSocket;

    public class RemoteStatusListener extends RemoteConnectionThread
    {
        private long h;
        private int i;
        private final int j;
        private final int k;
        private final String l;
        private final String m;
        private DatagramSocket n;
        private final byte[] o;
        private DatagramPacket p;
        private final Map<SocketAddress, String> q;
        private String r;
        private String s;
        private final Map<SocketAddress, RemoteStatusChallenge> t;
        private final long u;
        private final RemoteStatusReply v;
        private long w;
       
        public RemoteStatusListener(final IMinecraftServer minecraftServer) {
            super(minecraftServer, "Query Listener");
            this.o = new byte[1460];
            this.i = minecraftServer.a("query.port", 0);
            this.s = minecraftServer.d_();
            this.j = minecraftServer.e_();
            this.l = minecraftServer.f_();
            this.k = minecraftServer.I();
            this.m = minecraftServer.S();
            this.w = 0L;
            this.r = "0.0.0.0";
            if (this.s.isEmpty() || this.r.equals(this.s)) {
                this.s = "0.0.0.0";
                try {
                    this.r = InetAddress.getLocalHost().getHostAddress();
                }
                catch (UnknownHostException ex) {
                    this.c("Unable to determine local host IP, please set server-ip in '" + minecraftServer.b() + "' : " + ex.getMessage());
                }
            }
            else {
                this.r = this.s;
            }
            if (0 == this.i) {
                this.i = this.j;
                this.b("Setting default query port to " + this.i);
                minecraftServer.a("query.port", (Object)this.i);
                minecraftServer.a("debug", (Object)false);
                minecraftServer.a();
            }
            this.q = (Map<SocketAddress, String>)Maps.<Object, Object>newHashMap();
            this.v = new RemoteStatusReply(1460);
            this.t = (Map<SocketAddress, RemoteStatusChallenge>)Maps.<Object, Object>newHashMap();
            this.u = new Date().getTime();
        }
       
        private void a(final byte[] array, final DatagramPacket datagramPacket) throws IOException {
            this.n.send(new DatagramPacket(array, array.length, datagramPacket.getSocketAddress()));
        }
       
        private boolean a(final DatagramPacket datagramPacket) throws IOException {
            final byte[] data = datagramPacket.getData();
            final int length = datagramPacket.getLength();
            final SocketAddress socketAddress = datagramPacket.getSocketAddress();
            this.a("Packet len " + length + " [" + socketAddress + "]");
            if (3 > length || -2 != data[0] || -3 != data[1]) {
                this.a("Invalid packet [" + socketAddress + "]");
                return false;
            }
            this.a("Packet '" + StatusChallengeUtils.a(data[2]) + "' [" + socketAddress + "]");
            switch (data[2]) {
                case 9:
                    this.d(datagramPacket);
                    this.a("Challenge [" + socketAddress + "]");
                    return true;
                case 0: {
                    if (!(boolean)this.c(datagramPacket)) {
                        this.a("Invalid challenge [" + socketAddress + "]");
                        return false;
                    }
                    if (15 == length) {
                        this.a(this.b(datagramPacket), datagramPacket);
                        this.a("Rules [" + socketAddress + "]");
                        break;
                    }
                    final RemoteStatusReply remoteStatusReply = new RemoteStatusReply(1460);
                    remoteStatusReply.a(0);
                    remoteStatusReply.a(this.a(datagramPacket.getSocketAddress()));
                    remoteStatusReply.a(this.l);
                    remoteStatusReply.a("SMP");
                    remoteStatusReply.a(this.m);
                    remoteStatusReply.a(Integer.toString(this.d()));
                    remoteStatusReply.a(Integer.toString(this.k));
                    remoteStatusReply.a((short)this.j);
                    remoteStatusReply.a(this.r);
                    this.a(remoteStatusReply.a(), datagramPacket);
                    this.a("Status [" + socketAddress + "]");
                    break;
                }
            }
            return true;
        }
       
        private byte[] b(final DatagramPacket datagramPacket) throws IOException {
            final long aw = MinecraftServer.aw();
            if (aw < this.w + 5000L) {
                final byte[] a = this.v.a();
                final byte[] a2 = this.a(datagramPacket.getSocketAddress());
                a[1] = a2[0];
                a[2] = a2[1];
                a[3] = a2[2];
                a[4] = a2[3];
                return a;
            }
            this.w = aw;
            this.v.b();
            this.v.a(0);
            this.v.a(this.a(datagramPacket.getSocketAddress()));
            this.v.a("splitnum");
            this.v.a(128);
            this.v.a(0);
            this.v.a("hostname");
            this.v.a(this.l);
            this.v.a("gametype");
            this.v.a("SMP");
            this.v.a("game_id");
            this.v.a("MINECRAFT");
            this.v.a("version");
            this.v.a(this.b.getVersion());
            this.v.a("plugins");
            this.v.a(this.b.getPlugins());
            this.v.a("map");
            this.v.a(this.m);
            this.v.a("numplayers");
            this.v.a("" + this.d());
            this.v.a("maxplayers");
            this.v.a("" + this.k);
            this.v.a("hostport");
            this.v.a("" + this.j);
            this.v.a("hostip");
            this.v.a(this.r);
            this.v.a(0);
            this.v.a(1);
            this.v.a("player_");
            this.v.a(0);
            final String[] players = this.b.getPlayers();
            for (int length = players.length, i = 0; i < length; ++i) {
                this.v.a(players[i]);
            }
            this.v.a(0);
            return this.v.a();
        }
       
        private byte[] a(final SocketAddress socketAddress) {
            return ((RemoteStatusChallenge)this.t.get(socketAddress)).c();
        }
       
        private Boolean c(final DatagramPacket datagramPacket) {
            final SocketAddress socketAddress = datagramPacket.getSocketAddress();
            if (!this.t.containsKey(socketAddress)) {
                return false;
            }
            if (((RemoteStatusChallenge)this.t.get(socketAddress)).a() != StatusChallengeUtils.c(datagramPacket.getData(), 7, datagramPacket.getLength())) {
                return false;
            }
            return true;
        }
       
        private void d(final DatagramPacket datagramPacket) throws IOException {
            final RemoteStatusChallenge remoteStatusChallenge = new RemoteStatusChallenge(datagramPacket);
            this.t.put(datagramPacket.getSocketAddress(), remoteStatusChallenge);
            this.a(remoteStatusChallenge.b(), datagramPacket);
        }
       
        private void f() {
            if (!this.a) {
                return;
            }
            final long aw = MinecraftServer.aw();
            if (aw < this.h + 30000L) {
                return;
            }
            this.h = aw;
            final Iterator<Map.Entry<SocketAddress, RemoteStatusChallenge>> iterator = this.t.entrySet().iterator();
            while (iterator.hasNext()) {
                if ((boolean)((RemoteStatusChallenge)((Map.Entry<SocketAddress, RemoteStatusChallenge>)iterator.next()).getValue()).a(aw)) {
                    iterator.remove();
                }
            }
        }
       
        @Override
        public void run() {
            this.b("Query running on " + this.s + ":" + this.i);
            this.h = MinecraftServer.aw();
            this.p = new DatagramPacket(this.o, this.o.length);
            try {
                while (this.a) {
                    try {
                        this.n.receive(this.p);
                        this.f();
                        this.a(this.p);
                    }
                    catch (SocketTimeoutException ex2) {
                        this.f();
                    }
                    catch (PortUnreachableException ex3) {}
                    catch (IOException ex) {
                        this.a(ex);
                    }
                }
            }
            finally {
                this.e();
            }
        }
       
        @Override
        public void a() {
            if (this.a) {
                return;
            }
            if (0 >= this.i || 65535 < this.i) {
                this.c("Invalid query port " + this.i + " found in '" + this.b.b() + "' (queries disabled)");
                return;
            }
            if (this.g()) {
                super.a();
            }
        }
       
        private void a(final Exception ex) {
            if (!this.a) {
                return;
            }
            this.c("Unexpected exception, buggy JRE? (" + ex + ")");
            if (!this.g()) {
                this.d("Failed to recover from buggy JRE, shutting down!");
                this.a = false;
            }
        }
       
        private boolean g() {
            try {
                this.a(this.n = new DatagramSocket(this.i, InetAddress.getByName(this.s)));
                this.n.setSoTimeout(500);
                return true;
            }
            catch (SocketException ex) {
                this.c("Unable to initialise query system on " + this.s + ":" + this.i + " (Socket): " + ex.getMessage());
            }
            catch (UnknownHostException ex2) {
                this.c("Unable to initialise query system on " + this.s + ":" + this.i + " (Unknown Host): " + ex2.getMessage());
            }
            catch (Exception ex3) {
                this.c("Unable to initialise query system on " + this.s + ":" + this.i + " (E): " + ex3.getMessage());
            }
            return false;
        }
    }
     
     
  10. Something in this 0 progress. I thought and decided. Maybe then it's easy if enable-query = true, then the query from the kernel will not be included, but occupy the UDP port, plugin. Given that the query is included after the plug-in is loaded, this is more reasonable. I will try to do so, but the ping response will increase so clearly, like the load, well, I need it.
     
Thread Status:
Not open for further replies.