package net.glowstone.net.query;

import io.netty.channel.ChannelFuture;
import java.net.InetSocketAddress;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import net.glowstone.GlowServer;
import net.glowstone.net.GlowDatagramServer;
import org.bukkit.scheduler.BukkitRunnable;

/**
 * Implementation of a server for the minecraft server query protocol.
 *
 * @see <a href="http://wiki.vg/Query">Protocol Specifications</a>
 */
public class QueryServer extends GlowDatagramServer {

    /**
     * Maps each {@link InetSocketAddress} of a client to its challenge token.
     */
    private Map<InetSocketAddress, Integer> challengeTokens = new ConcurrentHashMap<>();

    /**
     * The task used to invalidate all challenge tokens every 30 seconds.
     */
    private ChallengeTokenFlushTask flushTask;

    public QueryServer(GlowServer server, CountDownLatch latch, boolean showPlugins) {
        super(server, latch);
        bootstrap.handler(new QueryHandler(this, showPlugins));
    }

    /**
     * Bind the server on the specified address.
     *
     * @param address The address.
     * @return Netty channel future for bind operation.
     */
    public ChannelFuture bind(InetSocketAddress address) {
        GlowServer.logger.info("Binding query to address " + address + "...");
        if (flushTask == null) {
            flushTask = new ChallengeTokenFlushTask();
            flushTask.runTaskTimerAsynchronously(null, 600, 600);
        }
        return super.bind(address);
    }

    /**
     * Shut the query server down.
     */
    public void shutdown() {
        super.shutdown();
        if (flushTask != null) {
            flushTask.cancel();
        }
    }

    /**
     * Generate a new token.
     *
     * @param address The sender address.
     * @return The generated valid token.
     */
    public int generateChallengeToken(InetSocketAddress address) {
        int token = ThreadLocalRandom.current().nextInt();
        challengeTokens.put(address, token);
        return token;
    }

    /**
     * Verify that the request is using the correct challenge token.
     *
     * @param address The sender address.
     * @param token The token.
     * @return {@code true} if the token is valid.
     */
    public boolean verifyChallengeToken(InetSocketAddress address, int token) {
        return Objects.equals(challengeTokens.get(address), token);
    }

    /**
     * Invalidates all challenge tokens.
     */
    public void flushChallengeTokens() {
        challengeTokens.clear();
    }

    @Override
    public void onBindSuccess(InetSocketAddress address) {
        GlowServer.logger.info("Successfully bound query to " + address + '.');
        super.onBindSuccess(address);
    }

    @Override
    public void onBindFailure(InetSocketAddress address, Throwable t) {
        GlowServer.logger.warning("Failed to bind query to" + address + '.');
    }

    /**
     * Inner class for resetting the challenge tokens every 30 seconds.
     */
    private class ChallengeTokenFlushTask extends BukkitRunnable {

        @Override
        public void run() {
            flushChallengeTokens();
        }
    }
}

@@ -937,16 +937,339 @@
 hTask;%0A%0A
+    /**%0A     * Creates an instance for the specified server.%0A     *%0A     * @param server the associated GlowServer%0A     * @param latch The countdown latch used during server startup to wait for network server%0A     *         binding.%0A     * @param showPlugins whether the plugin list should be included in responses%0A     */%0A
     publ
@@ -1343,24 +1343,24 @@
 wPlugins) %7B%0A
-
         supe
@@ -1603,32 +1603,46 @@
 ration.%0A     */%0A
+    @Override%0A
     public Chann
@@ -2013,24 +2013,24 @@
 r down.%0A
-
      */%0A
     publ
@@ -2013,32 +2013,46 @@
 r down.%0A     */%0A
+    @Override%0A
     public void 

Fix CheckStyle issues in net/query/QueryServer.java (#688)