Update src

This commit is contained in:
Braydon 2024-04-06 20:04:40 -04:00
parent 2bd471d607
commit 4cd5369bee
8 changed files with 122 additions and 12 deletions

@ -1,2 +1,2 @@
# RESTfulMC
A simple, yet useful RESTful API for Minecraft!
A simple, yet useful RESTful API for Minecraft utilizing Springboot.

17
pom.xml

@ -33,6 +33,15 @@
</plugins>
</build>
<!-- Repos -->
<repositories>
<!-- Jitpack - Used for dnsjava -->
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<!-- Depends -->
<dependencies>
<!-- Spring -->
@ -71,6 +80,14 @@
<scope>compile</scope>
</dependency>
<!-- DNS Lookup -->
<dependency>
<groupId>com.github.dnsjava</groupId>
<artifactId>dnsjava</artifactId>
<version>v3.5.2</version>
<scope>compile</scope>
</dependency>
<!-- Tests -->
<dependency>
<groupId>org.springframework.boot</groupId>

@ -0,0 +1,53 @@
package me.braydon.mc.common;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;
import org.xbill.DNS.Record;
import org.xbill.DNS.*;
import java.net.InetAddress;
import java.net.InetSocketAddress;
/**
* @author Braydon
*/
@UtilityClass
public final class DNSUtils {
private static final String SRV_QUERY_PREFIX = "_minecraft._tcp.%s";
/**
* Resolve the hostname to an {@link InetSocketAddress}.
*
* @param hostname the hostname to resolve
* @return the resolved {@link InetSocketAddress}
*/
@SneakyThrows
public static InetSocketAddress resolveSRV(@NonNull String hostname) {
Record[] records = new Lookup(SRV_QUERY_PREFIX.formatted(hostname), Type.SRV).run(); // Resolve SRV records
if (records == null) { // No records exist
return null;
}
String host = null;
int port = -1;
for (Record record : records) {
SRVRecord srv = (SRVRecord) record;
host = srv.getTarget().toString().replaceFirst("\\.$", "");
port = srv.getPort();
}
return host == null ? null : new InetSocketAddress(host, port);
}
@SneakyThrows
public static InetAddress resolveA(@NonNull String hostname) {
Record[] records = new Lookup(hostname, Type.A).run(); // Resolve A records
if (records == null) { // No records exist
return null;
}
InetAddress address = null;
for (Record record : records) {
address = ((ARecord) record).getAddress();
}
return address;
}
}

@ -13,9 +13,14 @@ import me.braydon.mc.service.pinger.impl.JavaMinecraftServerPinger;
@AllArgsConstructor @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString
public class MinecraftServer {
/**
* The IP address of this server.
* The hostname of this server.
*/
@EqualsAndHashCode.Include @NonNull private final String ip;
@EqualsAndHashCode.Include @NonNull private final String hostname;
/**
* The IP address of this server, if resolved.
*/
private final String ip;
/**
* The port of this server.

@ -9,7 +9,7 @@ import me.braydon.mc.model.MinecraftServer;
* @author Braydon
*/
public final class BedrockMinecraftServer extends MinecraftServer {
private BedrockMinecraftServer(@NonNull String ip, int port, @NonNull Version version, @NonNull Players players) {
super(ip, port, version, players);
private BedrockMinecraftServer(@NonNull String hostname, String ip, int port, @NonNull Version version, @NonNull Players players) {
super(hostname, ip, port, version, players);
}
}

@ -10,11 +10,21 @@ import me.braydon.mc.model.token.JavaServerStatusToken;
* @author Braydon
*/
public final class JavaMinecraftServer extends MinecraftServer {
private JavaMinecraftServer(@NonNull String ip, int port, @NonNull Version version, @NonNull Players players) {
super(ip, port, version, players);
private JavaMinecraftServer(@NonNull String hostname, String ip, int port, @NonNull Version version, @NonNull Players players) {
super(hostname, ip, port, version, players);
}
public static JavaMinecraftServer create(@NonNull String ip, int port, @NonNull JavaServerStatusToken token) {
return new JavaMinecraftServer(ip, port, token.getVersion(), token.getPlayers());
/**
* Create a new Java Minecraft server.
*
* @param hostname the hostname of the server
* @param ip the IP address of the server
* @param port the port of the server
* @param token the status token
* @return the Java Minecraft server
*/
@NonNull
public static JavaMinecraftServer create(@NonNull String hostname, String ip, int port, @NonNull JavaServerStatusToken token) {
return new JavaMinecraftServer(hostname, ip, port, token.getVersion(), token.getPlayers());
}
}

@ -2,6 +2,7 @@ package me.braydon.mc.service;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import me.braydon.mc.common.DNSUtils;
import me.braydon.mc.common.EnumUtils;
import me.braydon.mc.common.Tuple;
import me.braydon.mc.common.UUIDUtils;
@ -21,6 +22,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import java.net.InetSocketAddress;
import java.util.Optional;
import java.util.UUID;
@ -129,12 +131,28 @@ public final class MojangService {
}
}
public MinecraftServer getMinecraftServer(@NonNull String platformName, @NonNull String hostname) throws InvalidMinecraftServerPlatform {
/**
* Resolve a Minecraft server on the given
* platform with the given hostname.
*
* @param platformName the name of the platform
* @param hostname the hostname of the server
* @return the resolved Minecraft server
* @throws InvalidMinecraftServerPlatform if the platform is invalid
* @throws ResourceNotFoundException if the server isn't found
*/
@NonNull
public MinecraftServer getMinecraftServer(@NonNull String platformName, @NonNull String hostname)
throws InvalidMinecraftServerPlatform, ResourceNotFoundException {
MinecraftServer.Platform platform = EnumUtils.getEnumConstant(MinecraftServer.Platform.class, platformName.toUpperCase());
if (platform == null) { // Invalid platform
throw new InvalidMinecraftServerPlatform();
}
return platform.getPinger().ping(hostname, 25565);
InetSocketAddress address = DNSUtils.resolveSRV(hostname); // Resolve the SRV record
if (address == null) { // No address found
throw new ResourceNotFoundException("No SRV record found for hostname: %s".formatted(hostname));
}
return platform.getPinger().ping(address.getHostName(), address.getPort()); // Ping the server and return with the response
}
/**

@ -3,6 +3,7 @@ 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.DNSUtils;
import me.braydon.mc.common.packet.impl.PacketHandshakingInSetProtocol;
import me.braydon.mc.common.packet.impl.PacketStatusInStart;
import me.braydon.mc.model.server.JavaMinecraftServer;
@ -12,6 +13,7 @@ import me.braydon.mc.service.pinger.MinecraftServerPinger;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
@ -34,6 +36,11 @@ public final class JavaMinecraftServerPinger implements MinecraftServerPinger<Ja
*/
@Override
public JavaMinecraftServer ping(@NonNull String hostname, int port) {
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 {} to {}", hostname, ip);
}
log.info("Pinging {}:{}...", hostname, port);
long before = System.currentTimeMillis(); // Timestamp before pinging
@ -54,7 +61,7 @@ public final class JavaMinecraftServerPinger implements MinecraftServerPinger<Ja
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
return JavaMinecraftServer.create(hostname, ip, port, token); // Return the server
}
} catch (IOException ex) {
log.error("An error occurred pinging %s:%s:".formatted(hostname, port), ex);