Added the ability to specify a port in server and server favicon requests

This commit is contained in:
Braydon 2024-04-10 04:20:39 -04:00
parent 9e99db3aa3
commit 1d5aa930f0
5 changed files with 52 additions and 90 deletions

@ -39,7 +39,7 @@ jobs:
run: mvn --batch-mode test -q
# Re-checkout to reset the FS before deploying to Dokku
- name: Checkout
- name: Checkout - Reset FS
uses: actions/checkout@v4
with:
fetch-depth: 0

@ -26,17 +26,19 @@ package me.braydon.mc.common;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.ToString;
import lombok.extern.log4j.Log4j2;
/**
* A list of versions for the
* Java edition of Minecraft.
*
* @author Braydon
* @see <a href="https://wiki.vg/Protocol_version_numbers">Protocol Version Numbers</a>
* @see <a href="https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-16">Spigot NMS (1.16+)</a>
* @see <a href="https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-10-1-15">Spigot NMS (1.10 - 1.15)</a>
* @see <a href="https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-legacy">Spigot NMS (1.8 - 1.9)</a>
*/
@RequiredArgsConstructor @Getter @ToString @Log4j2(topic = "Minecraft Version")
public enum MinecraftVersion {
@RequiredArgsConstructor @Getter @ToString
public enum JavaMinecraftVersion {
V1_20_3(765, "v1_20_R3"), // 1.20.3 & 1.20.4
V1_20_2(764, "v1_20_R2"), // 1.20.2
V1_20(763, "v1_20_R1"), // 1.20 & 1.20.1
@ -91,19 +93,19 @@ public enum MinecraftVersion {
UNKNOWN(-1, "Unknown");
// Game Updates
public static final MinecraftVersion TRAILS_AND_TALES = MinecraftVersion.V1_20;
public static final MinecraftVersion THE_WILD_UPDATE = MinecraftVersion.V1_19;
public static final MinecraftVersion CAVES_AND_CLIFFS_PT_2 = MinecraftVersion.V1_18;
public static final MinecraftVersion CAVES_AND_CLIFFS_PT_1 = MinecraftVersion.V1_17;
public static final MinecraftVersion NETHER_UPDATE = MinecraftVersion.V1_16;
public static final MinecraftVersion BUZZY_BEES = MinecraftVersion.V1_15;
public static final MinecraftVersion VILLAGE_AND_PILLAGE = MinecraftVersion.V1_14;
public static final MinecraftVersion UPDATE_AQUATIC = MinecraftVersion.V1_13;
public static final MinecraftVersion WORLD_OF_COLOR_UPDATE = MinecraftVersion.V1_12;
public static final MinecraftVersion EXPLORATION_UPDATE = MinecraftVersion.V1_11;
public static final MinecraftVersion FROSTBURN_UPDATE = MinecraftVersion.V1_10;
public static final MinecraftVersion THE_COMBAT_UPDATE = MinecraftVersion.V1_9;
public static final MinecraftVersion BOUNTIFUL_UPDATE = MinecraftVersion.V1_8;
public static final JavaMinecraftVersion TRAILS_AND_TALES = JavaMinecraftVersion.V1_20;
public static final JavaMinecraftVersion THE_WILD_UPDATE = JavaMinecraftVersion.V1_19;
public static final JavaMinecraftVersion CAVES_AND_CLIFFS_PT_2 = JavaMinecraftVersion.V1_18;
public static final JavaMinecraftVersion CAVES_AND_CLIFFS_PT_1 = JavaMinecraftVersion.V1_17;
public static final JavaMinecraftVersion NETHER_UPDATE = JavaMinecraftVersion.V1_16;
public static final JavaMinecraftVersion BUZZY_BEES = JavaMinecraftVersion.V1_15;
public static final JavaMinecraftVersion VILLAGE_AND_PILLAGE = JavaMinecraftVersion.V1_14;
public static final JavaMinecraftVersion UPDATE_AQUATIC = JavaMinecraftVersion.V1_13;
public static final JavaMinecraftVersion WORLD_OF_COLOR_UPDATE = JavaMinecraftVersion.V1_12;
public static final JavaMinecraftVersion EXPLORATION_UPDATE = JavaMinecraftVersion.V1_11;
public static final JavaMinecraftVersion FROSTBURN_UPDATE = JavaMinecraftVersion.V1_10;
public static final JavaMinecraftVersion THE_COMBAT_UPDATE = JavaMinecraftVersion.V1_9;
public static final JavaMinecraftVersion BOUNTIFUL_UPDATE = JavaMinecraftVersion.V1_8;
/**
* The protocol number of this version.
@ -140,67 +142,14 @@ public enum MinecraftVersion {
return this.name;
}
/**
* Is this version legacy?
*
* @return whether this version is legacy
*/
public boolean isLegacy() {
return this.isBelow(MinecraftVersion.V1_16);
}
/**
* Check if this version is
* above the one given.
*
* @param other the other version
* @return true if above, otherwise false
*/
public boolean isAbove(MinecraftVersion other) {
return this.protocol > other.getProtocol();
}
/**
* Check if this version is
* or above the one given.
*
* @param other the other version
* @return true if is or above, otherwise false
*/
public boolean isOrAbove(MinecraftVersion other) {
return this.protocol >= other.getProtocol();
}
/**
* Check if this version is
* below the one given.
*
* @param other the other version
* @return true if below, otherwise false
*/
public boolean isBelow(MinecraftVersion other) {
return this.protocol < other.getProtocol();
}
/**
* Check if this version is
* or below the one given.
*
* @param other the other version
* @return true if is or below, otherwise false
*/
public boolean isOrBelow(MinecraftVersion other) {
return this.protocol <= other.getProtocol();
}
/**
* Get the version from the given protocol.
*
* @param protocol the protocol to get the version for
* @return the version, null if none
*/
public static MinecraftVersion byProtocol(int protocol) {
for (MinecraftVersion version : values()) {
public static JavaMinecraftVersion byProtocol(int protocol) {
for (JavaMinecraftVersion version : values()) {
if (version.getProtocol() == protocol) {
return version;
}

@ -63,16 +63,17 @@ public final class ServerController {
*
* @param platform the platform of the server
* @param hostname the hostname of the server
* @param port the port of the server, null for default
* @return the server
* @throws BadRequestException if the hostname or platform is invalid
* @throws BadRequestException if the hostname or platform is invalid
* @throws ResourceNotFoundException if the server isn't found
*/
@GetMapping("/{platform}/{hostname}")
@ResponseBody
public ResponseEntity<CachedMinecraftServer> getServer(@PathVariable @NonNull String platform, @PathVariable @NonNull String hostname)
throws BadRequestException, ResourceNotFoundException
{
return ResponseEntity.ofNullable(mojangService.getMinecraftServer(platform, hostname));
public ResponseEntity<CachedMinecraftServer> getServer(@PathVariable @NonNull String platform, @PathVariable @NonNull String hostname,
@RequestParam(required = false) String port
) throws BadRequestException, ResourceNotFoundException {
return ResponseEntity.ofNullable(mojangService.getMinecraftServer(platform, hostname, port));
}
/**
@ -95,14 +96,15 @@ public final class ServerController {
* server by its platform and hostname.
*
* @param hostname the hostname of the server
* @param port the port of the server, null for default
* @return the server icon
*/
@GetMapping(value = "/icon/{hostname}", produces = MediaType.IMAGE_PNG_VALUE)
@ResponseBody
public ResponseEntity<byte[]> getServerFavicon(@PathVariable @NonNull String hostname) {
public ResponseEntity<byte[]> getServerFavicon(@PathVariable @NonNull String hostname, @RequestParam(required = false) String port) {
return ResponseEntity.ok()
.contentType(MediaType.IMAGE_PNG)
.header(HttpHeaders.CONTENT_DISPOSITION, "inline; filename=%s.png".formatted(hostname))
.body(mojangService.getServerFavicon(hostname));
.body(mojangService.getServerFavicon(hostname, port));
}
}

@ -26,7 +26,7 @@ package me.braydon.mc.model.server;
import com.google.gson.annotations.SerializedName;
import lombok.*;
import me.braydon.mc.RESTfulMC;
import me.braydon.mc.common.MinecraftVersion;
import me.braydon.mc.common.JavaMinecraftVersion;
import me.braydon.mc.config.AppConfig;
import me.braydon.mc.model.MinecraftServer;
import me.braydon.mc.model.token.JavaServerStatusToken;
@ -152,7 +152,7 @@ public final class JavaMinecraftServer extends MinecraftServer {
platform = split[0];
}
}
MinecraftVersion minecraftVersion = MinecraftVersion.byProtocol(protocol);
JavaMinecraftVersion minecraftVersion = JavaMinecraftVersion.byProtocol(protocol);
return new Version(name, platform, protocol, minecraftVersion == null ? null : minecraftVersion.getName());
}
}

@ -271,13 +271,14 @@ public final class MojangService {
* </p>
*
* @param hostname the hostname of the server
* @param port the port of the server, null for default
* @return the server favicon
* @see #DEFAULT_SERVER_ICON for the default server icon
*/
public byte[] getServerFavicon(@NonNull String hostname) {
public byte[] getServerFavicon(@NonNull String hostname, String port) {
String icon = null; // The server base64 icon
try {
JavaMinecraftServer.Favicon favicon = ((JavaMinecraftServer) getMinecraftServer(MinecraftServer.Platform.JAVA.name(), hostname).getValue()).getFavicon();
JavaMinecraftServer.Favicon favicon = ((JavaMinecraftServer) getMinecraftServer(MinecraftServer.Platform.JAVA.name(), hostname, port).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
@ -344,35 +345,45 @@ public final class MojangService {
*
* @param platformName the name of the platform
* @param hostname the hostname of the server
* @param portString the port of the server, null for default
* @return the resolved Minecraft server
* @throws BadRequestException if the hostname or platform is invalid
* @throws ResourceNotFoundException if the server isn't found
* @throws BadRequestException if the hostname or platform is invalid
* @throws ResourceNotFoundException if the server isn't found
*/
@NonNull
public CachedMinecraftServer getMinecraftServer(@NonNull String platformName, @NonNull String hostname)
public CachedMinecraftServer getMinecraftServer(@NonNull String platformName, @NonNull String hostname, String portString)
throws BadRequestException, ResourceNotFoundException {
MinecraftServer.Platform platform = EnumUtils.getEnumConstant(MinecraftServer.Platform.class, platformName.toUpperCase());
if (platform == null) { // Invalid platform
throw new BadRequestException("Invalid platform: %s".formatted(platformName));
}
String lookupHostname = hostname; // The hostname used to lookup the server
String lookupPort = portString; // The port used to lookup the server
int port = platform.getDefaultPort(); // Port to ping
if (portString != null) {
try { // Try and parse the port
port = Integer.parseInt(portString);
} catch (NumberFormatException ex) { // Invalid port
throw new BadRequestException("Invalid port defined");
}
}
// Check the cache for the server
Optional<CachedMinecraftServer> cached = minecraftServerCache.findById(platform.name() + "-" + hostname);
Optional<CachedMinecraftServer> cached = minecraftServerCache.findById(platform.name() + "-" + hostname + "-" + port);
if (cached.isPresent()) { // Respond with the cache if present
log.info("Found server in cache: {}", hostname);
return cached.get();
}
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();
port = address.getPort();
}
// Build our server model, cache it, and then return it
CachedMinecraftServer minecraftServer = new CachedMinecraftServer(
platform.name() + "-" + lookupHostname,
platform.name() + "-" + lookupHostname + "-" + (lookupPort == null ? port : lookupPort),
platform.getPinger().ping(hostname, port),
System.currentTimeMillis()
);