Server pinger

This commit is contained in:
Braydon 2024-04-06 19:19:33 -04:00
parent 617c19bf3c
commit eadfb191b2
4 changed files with 124 additions and 4 deletions

@ -2,16 +2,15 @@ package me.braydon.mc.service;
import lombok.NonNull; import lombok.NonNull;
import lombok.extern.log4j.Log4j2; import lombok.extern.log4j.Log4j2;
import me.braydon.mc.common.EnumUtils;
import me.braydon.mc.common.Tuple; import me.braydon.mc.common.Tuple;
import me.braydon.mc.common.UUIDUtils; import me.braydon.mc.common.UUIDUtils;
import me.braydon.mc.common.web.JsonWebException; import me.braydon.mc.common.web.JsonWebException;
import me.braydon.mc.common.web.JsonWebRequest; import me.braydon.mc.common.web.JsonWebRequest;
import me.braydon.mc.exception.impl.BadRequestException; import me.braydon.mc.exception.impl.BadRequestException;
import me.braydon.mc.exception.impl.InvalidMinecraftServerPlatform;
import me.braydon.mc.exception.impl.ResourceNotFoundException; import me.braydon.mc.exception.impl.ResourceNotFoundException;
import me.braydon.mc.model.Cape; import me.braydon.mc.model.*;
import me.braydon.mc.model.Player;
import me.braydon.mc.model.ProfileAction;
import me.braydon.mc.model.Skin;
import me.braydon.mc.model.cache.CachedPlayer; import me.braydon.mc.model.cache.CachedPlayer;
import me.braydon.mc.model.cache.CachedPlayerName; import me.braydon.mc.model.cache.CachedPlayerName;
import me.braydon.mc.model.token.MojangProfileToken; import me.braydon.mc.model.token.MojangProfileToken;
@ -130,6 +129,14 @@ public final class MojangService {
} }
} }
public MinecraftServer getMinecraftServer(@NonNull String platformName, @NonNull String hostname) throws InvalidMinecraftServerPlatform {
MinecraftServer.Platform platform = EnumUtils.getEnumConstant(MinecraftServer.Platform.class, platformName.toUpperCase());
if (platform == null) { // Invalid platform
throw new InvalidMinecraftServerPlatform();
}
return platform.getPinger().ping(hostname, 25565);
}
/** /**
* Get the UUID of a player by their username. * Get the UUID of a player by their username.
* *

@ -0,0 +1,22 @@
package me.braydon.mc.service.pinger;
import lombok.NonNull;
import me.braydon.mc.model.MinecraftServer;
/**
* A {@link MinecraftServerPinger} is
* used to ping a {@link MinecraftServer}.
*
* @author Braydon
* @param <T> the type of server to ping
*/
public interface MinecraftServerPinger<T extends MinecraftServer> {
/**
* Ping the server with the given hostname and port.
*
* @param hostname the hostname of the server
* @param port the port of the server
* @return the server that was pinged
*/
T ping(@NonNull String hostname, int port);
}

@ -0,0 +1,27 @@
package me.braydon.mc.service.pinger.impl;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import me.braydon.mc.model.server.BedrockMinecraftServer;
import me.braydon.mc.service.pinger.MinecraftServerPinger;
/**
* The {@link MinecraftServerPinger} for
* pinging {@link BedrockMinecraftServer}'s.
*
* @author Braydon
*/
@Log4j2(topic = "Bedrock MC Server Pinger")
public final class BedrockMinecraftServerPinger implements MinecraftServerPinger<BedrockMinecraftServer> {
/**
* Ping the server with the given hostname and port.
*
* @param hostname the hostname of the server
* @param port the port of the server
* @return the server that was pinged
*/
@Override
public BedrockMinecraftServer ping(@NonNull String hostname, int port) {
throw new UnsupportedOperationException("Not yet implemented.");
}
}

@ -0,0 +1,64 @@
package me.braydon.mc.service.pinger.impl;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import me.braydon.mc.RESTfulMC;
import me.braydon.mc.common.packet.impl.PacketHandshakingInSetProtocol;
import me.braydon.mc.common.packet.impl.PacketStatusInStart;
import me.braydon.mc.model.server.JavaMinecraftServer;
import me.braydon.mc.model.token.JavaServerStatusToken;
import me.braydon.mc.service.pinger.MinecraftServerPinger;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
/**
* The {@link MinecraftServerPinger} for
* pinging {@link JavaMinecraftServer}'s.
*
* @author Braydon
*/
@Log4j2(topic = "Java MC Server Pinger")
public final class JavaMinecraftServerPinger implements MinecraftServerPinger<JavaMinecraftServer> {
private static final int TIMEOUT = 3000; // The timeout for the socket
/**
* Ping the server with the given hostname and port.
*
* @param hostname the hostname of the server
* @param port the port of the server
* @return the server that was pinged
*/
@Override
public JavaMinecraftServer ping(@NonNull String hostname, int port) {
log.info("Pinging {}:{}...", hostname, port);
long before = System.currentTimeMillis(); // Timestamp before pinging
// Open a socket connection to the server
try (Socket socket = new Socket()) {
socket.setTcpNoDelay(true);
socket.connect(new InetSocketAddress(hostname, port), TIMEOUT);
long ping = System.currentTimeMillis() - before; // Calculate the ping
log.info("Pinged {}:{} in {}ms", hostname, port, ping);
// Open data streams to begin packet transaction
try (DataInputStream inputStream = new DataInputStream(socket.getInputStream());
DataOutputStream outputStream = new DataOutputStream(socket.getOutputStream())) {
// Begin handshaking with the server
new PacketHandshakingInSetProtocol(hostname, port, 47).process(inputStream, outputStream);
// Send the status request to the server, and await back the response
PacketStatusInStart packetStatusInStart = new PacketStatusInStart();
packetStatusInStart.process(inputStream, outputStream);
JavaServerStatusToken token = RESTfulMC.GSON.fromJson(packetStatusInStart.getResponse(), JavaServerStatusToken.class);
return JavaMinecraftServer.create(hostname, port, token); // Return the server
}
} catch (IOException ex) {
log.error("An error occurred pinging %s:%s:".formatted(hostname, port), ex);
}
return null;
}
}