Add Bedrock server pinger

This commit is contained in:
Braydon 2024-04-08 23:32:45 -04:00
parent e5342b5445
commit 8cca0c2b51
3 changed files with 56 additions and 11 deletions

@ -911,22 +911,21 @@ public final class MojangService {
}
/**
* Get the favicon of a server on the
* given platform with the given hostname.
* Get the favicon of a Java
* server with the given hostname.
* <p>
* If the favicon of the server cannot be
* retrieved, the default icon will be used.
* </p>
*
* @param platform the platform of the server
* @param hostname the hostname of the server
* @return the server favicon
* @see #DEFAULT_SERVER_ICON for the default server icon
*/
public byte[] getServerFavicon(@NonNull String platform, @NonNull String hostname) {
public byte[] getServerFavicon(@NonNull String hostname) {
String icon = null; // The server base64 icon
try {
MinecraftServer.Favicon favicon = getMinecraftServer(platform, hostname).getValue().getFavicon();
JavaMinecraftServer.Favicon favicon = ((JavaMinecraftServer) getMinecraftServer(MinecraftServer.Platform.JAVA.name(), hostname).getValue()).getFavicon();
if (favicon != null) { // Use the server's favicon
icon = favicon.getBase64();
icon = icon.substring(icon.indexOf(",") + 1); // Remove the data type from the server icon
@ -1014,7 +1013,7 @@ public final class MojangService {
return cached.get();
}
InetSocketAddress address = DNSUtils.resolveSRV(hostname); // Resolve the SRV record
InetSocketAddress address = platform == MinecraftServer.Platform.JAVA ? DNSUtils.resolveSRV(hostname) : null; // Resolve the SRV record
int port = platform.getDefaultPort(); // Port to ping
if (address != null) { // SRV was resolved, use the hostname and port
hostname = address.getHostName();

@ -678,9 +678,17 @@ package me.braydon.mc.service.pinger.impl;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import me.braydon.mc.common.DNSUtils;
import me.braydon.mc.common.packet.impl.bedrock.BedrockPacketUnconnectedPing;
import me.braydon.mc.common.packet.impl.bedrock.BedrockPacketUnconnectedPong;
import me.braydon.mc.exception.impl.BadRequestException;
import me.braydon.mc.exception.impl.ResourceNotFoundException;
import me.braydon.mc.model.server.BedrockMinecraftServer;
import me.braydon.mc.service.pinger.MinecraftServerPinger;
import java.io.IOException;
import java.net.*;
/**
* The {@link MinecraftServerPinger} for
* pinging {@link BedrockMinecraftServer}'s.
@ -689,6 +697,8 @@ import me.braydon.mc.service.pinger.MinecraftServerPinger;
*/
@Log4j2(topic = "Bedrock MC Server Pinger")
public final class BedrockMinecraftServerPinger implements MinecraftServerPinger<BedrockMinecraftServer> {
private static final int TIMEOUT = 3000; // The timeout for the socket
/**
* Ping the server with the given hostname and port.
*
@ -698,6 +708,41 @@ public final class BedrockMinecraftServerPinger implements MinecraftServerPinger
*/
@Override
public BedrockMinecraftServer ping(@NonNull String hostname, int port) {
throw new UnsupportedOperationException("Not yet implemented.");
InetAddress inetAddress = DNSUtils.resolveA(hostname); // Resolve the hostname to an IP address
String ip = inetAddress == null ? null : inetAddress.getHostAddress(); // Get the IP address
if (ip != null) { // Was the IP resolved?
log.info("Resolved hostname: {} -> {}", hostname, ip);
}
log.info("Pinging {}:{}...", hostname, port);
long before = System.currentTimeMillis(); // Timestamp before pinging
// Open a socket connection to the server
try (DatagramSocket socket = new DatagramSocket()) {
socket.setSoTimeout(TIMEOUT);
socket.connect(new InetSocketAddress(hostname, port));
long ping = System.currentTimeMillis() - before; // Calculate the ping
log.info("Pinged {}:{} in {}ms", hostname, port, ping);
// Send the unconnected ping packet
new BedrockPacketUnconnectedPing().process(socket);
// Handle the received unconnected pong packet
BedrockPacketUnconnectedPong unconnectedPong = new BedrockPacketUnconnectedPong();
unconnectedPong.process(socket);
String response = unconnectedPong.getResponse();
if (response == null) { // No pong response
throw new ResourceNotFoundException("Server didn't respond to ping");
}
return BedrockMinecraftServer.create(hostname, ip, port, response); // Return the server
} catch (IOException ex) {
if (ex instanceof UnknownHostException) {
throw new BadRequestException("Unknown hostname: %s".formatted(hostname));
} else if (ex instanceof SocketTimeoutException) {
throw new ResourceNotFoundException(ex);
}
log.error("An error occurred pinging %s:%s:".formatted(hostname, port), ex);
}
return null;
}
}

@ -680,8 +680,8 @@ import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import me.braydon.mc.RESTfulMC;
import me.braydon.mc.common.DNSUtils;
import me.braydon.mc.common.packet.impl.PacketHandshakingInSetProtocol;
import me.braydon.mc.common.packet.impl.PacketStatusInStart;
import me.braydon.mc.common.packet.impl.java.JavaPacketHandshakingInSetProtocol;
import me.braydon.mc.common.packet.impl.java.JavaPacketStatusInStart;
import me.braydon.mc.exception.impl.BadRequestException;
import me.braydon.mc.exception.impl.ResourceNotFoundException;
import me.braydon.mc.model.server.JavaMinecraftServer;
@ -726,6 +726,7 @@ public final class JavaMinecraftServerPinger implements MinecraftServerPinger<Ja
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);
@ -733,10 +734,10 @@ public final class JavaMinecraftServerPinger implements MinecraftServerPinger<Ja
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);
new JavaPacketHandshakingInSetProtocol(hostname, port, 47).process(inputStream, outputStream);
// Send the status request to the server, and await back the response
PacketStatusInStart packetStatusInStart = new PacketStatusInStart();
JavaPacketStatusInStart packetStatusInStart = new JavaPacketStatusInStart();
packetStatusInStart.process(inputStream, outputStream);
JavaServerStatusToken token = RESTfulMC.GSON.fromJson(packetStatusInStart.getResponse(), JavaServerStatusToken.class);
return JavaMinecraftServer.create(hostname, ip, port, token); // Return the server