All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.lenni0451.mcping.MCPing Maven / Gradle / Ivy

The newest version!
package net.lenni0451.mcping;

import net.lenni0451.mcping.pings.APing;
import net.lenni0451.mcping.pings.IStatusListener;
import net.lenni0451.mcping.pings.impl.*;
import net.lenni0451.mcping.pings.sockets.factories.ITCPSocketFactory;
import net.lenni0451.mcping.pings.sockets.factories.IUDPSocketFactory;
import net.lenni0451.mcping.pings.sockets.impl.factories.TCPSocketFactory;
import net.lenni0451.mcping.pings.sockets.impl.factories.UDPSocketFactory;
import net.lenni0451.mcping.responses.*;

import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.function.Consumer;
import java.util.function.Function;

@SuppressWarnings({"unused", "UnusedReturnValue"})
public class MCPing {

    /**
     * Ping a server using the modern ping protocol.
* Defaults to protocol version 47. * * @return The modern ping builder */ public static MCPing pingModern() { return pingModern(47); } /** * Ping a server using the modern ping protocol. * * @param protocolVersion The protocol version to use * @return The modern ping builder */ public static MCPing pingModern(final int protocolVersion) { return new MCPing<>(ping -> new ModernPing(ping.tcpSocketFactory, ping.connectTimeout, ping.readTimeout, protocolVersion)); } /** * Ping a server using the legacy ping protocol.
* Defaults to the protocol version of the given version ({@link LegacyPing.Version#getDefaultId()}).
* Versions between b1.8 and 1.2 are not resolving the address by default. * * @param version The version of the protocol * @return The legacy ping builder */ public static MCPing pingLegacy(final LegacyPing.Version version) { return pingLegacy(version, version.getDefaultId()); } /** * Ping a server using the legacy ping protocol.
* Versions between b1.8 and 1.2 are not resolving the address by default. * * @param version The version of the protocol * @param protocolVersion The protocol version to use * @return The legacy ping builder */ public static MCPing pingLegacy(final LegacyPing.Version version, final int protocolVersion) { MCPing mcPing = new MCPing<>(ping -> new LegacyPing(ping.tcpSocketFactory, ping.connectTimeout, ping.readTimeout, version, protocolVersion)); if (LegacyPing.Version.B1_8.equals(version)) mcPing.noResolve(); return mcPing; } /** * Ping a server using the classic protocol.
* Resolving the address is disabled by default.
* This visibly connects a client to the server causing a disconnect message. * * @param version The version of the protocol * @return The classic ping builder */ public static MCPing pingClassic(final ClassicPing.Version version) { return new MCPing(ping -> new ClassicPing(ping.tcpSocketFactory, ping.connectTimeout, ping.readTimeout, version)).noResolve(); } /** * Ping a server using the query protocol.
* Defaults to full query. * * @return The query ping builder */ public static MCPing pingQuery() { return pingQuery(true); } /** * Ping a server using the query protocol. * * @param full Whether to use full query or not * @return The query ping builder */ public static MCPing pingQuery(final boolean full) { return new MCPing<>(ping -> new QueryPing(ping.udpSocketFactory, ping.readTimeout, full)); } /** * Ping a server using the bedrock protocol.
* Resolving the address is disabled by default. * * @return The bedrock ping builder */ public static MCPing pingBedrock() { return new MCPing(ping -> new BedrockPing(ping.udpSocketFactory, ping.readTimeout)).noResolve(); } /** * Ping a server using a socket.
* This just connects a socket and measures the time it takes to connect. * * @return The socket ping builder */ public static MCPing pingSocket() { return new MCPing<>(ping -> new SocketPing(ping.tcpSocketFactory, ping.readTimeout)); } private final Function, APing> ping; private ITCPSocketFactory tcpSocketFactory = new TCPSocketFactory(); private IUDPSocketFactory udpSocketFactory = new UDPSocketFactory(); private ServerAddress address; private boolean resolve = true; private int connectTimeout = 5_000; private int readTimeout = 10_000; private IStatusListener statusListener; private Consumer exceptionHandler; private Runnable connectedHandler; private Consumer responseHandler; private Consumer finishHandler; private MCPing(final Function, APing> ping) { this.ping = ping; } private void validate() { if (this.ping == null) throw new NullPointerException("Ping cannot be null"); if (this.tcpSocketFactory == null) throw new NullPointerException("TCP Socket Factory cannot be null"); if (this.udpSocketFactory == null) throw new NullPointerException("UDP Socket Factory cannot be null"); if (this.address == null) throw new NullPointerException("Address cannot be null"); } /** * Set the used tcp socket factory.
* Defaults to {@link TCPSocketFactory} which uses {@link java.net.Socket} as the socket implementation. * * @param tcpSocketFactory The tcp socket factory to use * @return This builder */ public MCPing tcpSocketFactory(final ITCPSocketFactory tcpSocketFactory) { this.tcpSocketFactory = tcpSocketFactory; return this; } /** * Set the used udp socket factory.
* Defaults to {@link UDPSocketFactory} which uses {@link java.net.DatagramSocket} as the socket implementation. * * @param udpSocketFactory The udp socket factory to use * @return This builder */ public MCPing udpSocketFactory(final IUDPSocketFactory udpSocketFactory) { this.udpSocketFactory = udpSocketFactory; return this; } /** * Set the address of the server to ping.
* See {@link ServerAddress#parse(String, int)} for more information. * * @param address The string representation of the address and/or port * @return This builder */ public MCPing address(final String address) { this.address = ServerAddress.parse(address, this.ping.apply(this).getDefaultPort()); return this; } /** * Set the address of the server to ping.
* See {@link ServerAddress#of(String, int, int)} for more information. * * @param host The host of the server * @param port The port of the server * @return This builder */ public MCPing address(final String host, final int port) { this.address = ServerAddress.of(host, port, this.ping.apply(this).getDefaultPort()); return this; } /** * Set the address of the server to ping. * * @param serverAddress The server address * @return This builder */ public MCPing address(final ServerAddress serverAddress) { this.address = serverAddress; return this; } /** * Set the address of the server to ping.
* If the address is an {@link InetSocketAddress} the host and port will be used.
* Otherwise, the address will be wrapped and used as is. * * @param address The socket address * @return This builder */ public MCPing address(final SocketAddress address) { this.address = ServerAddress.wrap(address, this.ping.apply(this).getDefaultPort()); return this; } /** * Set the address of the server to ping.
* The host will be resolved using {@link InetAddress#getHostAddress()}. * * @param address The inet address * @return This builder */ public MCPing address(final InetAddress address) { return this.address(address.getHostAddress()); } /** * Set the address to be resolved. * * @return This builder */ public MCPing resolve() { this.resolve = true; return this; } /** * Set the address to not be resolved. * * @return This builder */ public MCPing noResolve() { this.resolve = false; return this; } /** * Set the connect and read timeout.
* Not all ping implementations require both timeouts. * * @param connectTimeout The connect timeout * @param readTimeout The read timeout * @return This builder */ public MCPing timeout(final int connectTimeout, final int readTimeout) { this.connectTimeout = connectTimeout; this.readTimeout = readTimeout; return this; } /** * Set the ping status listener.
* IMPORTANT! See {@link IStatusListener} for more information about the response special cases. * * @param statusListener The status listener * @return This builder */ public MCPing handler(final IStatusListener statusListener) { this.statusListener = statusListener; return this; } /** * Set a dedicated exception handler.
* See {@link IStatusListener#onError(Throwable)} for common exceptions and their cause.
* IMPORTANT! See {@link IStatusListener} for more information about the response special cases. * * @param exceptionHandler The exception handler * @return This builder */ public MCPing exceptionHandler(final Consumer exceptionHandler) { this.exceptionHandler = exceptionHandler; return this; } /** * Set a dedicated connected handler.
* This handler is called when the connection to the server was established.
* IMPORTANT! See {@link IStatusListener} for more information about the response special cases. * * @param connectedHandler The connected handler * @return This builder */ public MCPing connectedHandler(final Runnable connectedHandler) { this.connectedHandler = connectedHandler; return this; } /** * Set a dedicated response handler.
* This handler is called when the server responds with a valid status.
* IMPORTANT! See {@link IStatusListener} for more information about the response special cases. * * @param responseHandler The response handler * @return This builder */ public MCPing responseHandler(final Consumer responseHandler) { this.responseHandler = responseHandler; return this; } /** * Set a dedicated finish handler.
* This handler is called when the ping is finished.
* IMPORTANT! See {@link IStatusListener} for more information about the response special cases. * * @param finishHandler The finish handler * @return This builder */ public MCPing finishHandler(final Consumer finishHandler) { this.finishHandler = finishHandler; return this; } /** * Get the ping response synchronously.
* If no status listener or exception handler is set, any exception will be thrown.
* If a listener is set, null will be returned. * * @return The ping response * @throws RuntimeException If no status listener or exception handler is set and an exception occurs */ public R getSync() { this.validate(); CompletableFuture future = new CompletableFuture<>(); if (this.resolve) this.address.resolve(); this.ping.apply(this).ping(this.address, new StatusListener(future)); try { return future.get(); } catch (InterruptedException ignored) { } catch (ExecutionException e) { if (this.statusListener == null && this.exceptionHandler == null) throw new RuntimeException("Unhandled exception during ping", e.getCause()); } return null; } /** * Get a completable future for the ping response.
* The ping will be executed in a separate thread and started immediately. * * @return The completable future */ public CompletableFuture getAsync() { this.validate(); return new MCPingFuture(); } /** * Get a future for the ping response.
* The ping will be executed in the given executor and started immediately. * * @param executor The executor * @return The future */ public Future getAsync(final ExecutorService executor) { this.validate(); return new MCPingFuture(executor); } private class MCPingFuture extends CompletableFuture { private final Runnable task = () -> { if (MCPing.this.resolve) MCPing.this.address.resolve(); this.ping = MCPing.this.ping.apply(MCPing.this); this.ping.ping(MCPing.this.address, new StatusListener(this)); }; private final Thread thread; private final Future future; private APing ping; private MCPingFuture() { this.thread = ThreadLauncher.startThread(this.task, "MCPing Thread"); this.future = null; } private MCPingFuture(final ExecutorService executor) { this.thread = null; this.future = (Future) executor.submit(this.task); } @Override public boolean cancel(boolean mayInterruptIfRunning) { if (this.future != null) this.future.cancel(mayInterruptIfRunning); if (this.thread != null) this.thread.interrupt(); try { this.ping.close(); } catch (Throwable ignored) { } return super.cancel(mayInterruptIfRunning); } } private class StatusListener implements IStatusListener { private final CompletableFuture future; private StatusListener(final CompletableFuture future) { this.future = future; } @Override public void onError(Throwable throwable) { if (MCPing.this.statusListener != null) MCPing.this.statusListener.onError(throwable); if (MCPing.this.exceptionHandler != null) MCPing.this.exceptionHandler.accept(throwable); if (this.future != null) this.future.completeExceptionally(throwable); } @Override public void onConnected() { if (MCPing.this.statusListener != null) MCPing.this.statusListener.onConnected(); if (MCPing.this.connectedHandler != null) MCPing.this.connectedHandler.run(); } @Override public void onResponse(IPingResponse pingResponse) { if (MCPing.this.statusListener != null) MCPing.this.statusListener.onResponse(pingResponse); if (MCPing.this.responseHandler != null) MCPing.this.responseHandler.accept((R) pingResponse); } @Override public void onPing(IPingResponse pingResponse, long ping) { if (MCPing.this.statusListener != null) MCPing.this.statusListener.onPing(pingResponse, ping); if (MCPing.this.finishHandler != null) MCPing.this.finishHandler.accept((R) pingResponse); if (this.future != null) this.future.complete((R) pingResponse); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy