From 9315182e3c689c6487638856770e229f9a066728 Mon Sep 17 00:00:00 2001 From: Rainnny7 Date: Sun, 7 Apr 2024 23:17:46 -0400 Subject: [PATCH] Add server icon route --- README.md | 2 +- .../{PlayerUtils.java => ImageUtils.java} | 2 +- .../mc/controller/ServerController.java | 29 +++++++++++++-- .../me/braydon/mc/model/MinecraftServer.java | 37 ++++++++++++++++--- src/main/java/me/braydon/mc/model/Skin.java | 4 +- .../model/server/BedrockMinecraftServer.java | 4 +- .../mc/model/server/JavaMinecraftServer.java | 10 ++--- .../me/braydon/mc/service/MojangService.java | 2 +- 8 files changed, 70 insertions(+), 20 deletions(-) rename src/main/java/me/braydon/mc/common/{PlayerUtils.java => ImageUtils.java} (99%) diff --git a/README.md b/README.md index b45fe3b..afee0ad 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ A simple, yet useful RESTful API for Minecraft utilizing Springboot. Hi there! Looking for usage? View the [Wiki](https://git.rainnny.club/Rainnny/RESTfulMC/wiki) for more information! ## TODO -- [ ] Server Icon Route +- [x] Server Icon Route - [ ] Unit Tests - [ ] Blacklist Checking - [ ] HTTP Codes in wiki diff --git a/src/main/java/me/braydon/mc/common/PlayerUtils.java b/src/main/java/me/braydon/mc/common/ImageUtils.java similarity index 99% rename from src/main/java/me/braydon/mc/common/PlayerUtils.java rename to src/main/java/me/braydon/mc/common/ImageUtils.java index ffe2f79..0ae68ef 100644 --- a/src/main/java/me/braydon/mc/common/PlayerUtils.java +++ b/src/main/java/me/braydon/mc/common/ImageUtils.java @@ -692,7 +692,7 @@ import java.net.URL; * @author Braydon */ @UtilityClass -public final class PlayerUtils { +public final class ImageUtils { public static final int SKIN_TEXTURE_SIZE = 64; // The skin of a skin texture /** diff --git a/src/main/java/me/braydon/mc/controller/ServerController.java b/src/main/java/me/braydon/mc/controller/ServerController.java index 9858390..047e851 100644 --- a/src/main/java/me/braydon/mc/controller/ServerController.java +++ b/src/main/java/me/braydon/mc/controller/ServerController.java @@ -686,6 +686,8 @@ import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.util.Base64; + /** * The controller for handling * {@link MinecraftServer} related requests. @@ -696,6 +698,8 @@ import org.springframework.web.bind.annotation.*; @RequestMapping(value = "/server", produces = MediaType.APPLICATION_JSON_VALUE) @Log4j2(topic = "Server Controller") public final class ServerController { + private static final String DEFAULT_SERVER_ICON = "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"; + /** * The Mojang service to use for server information. */ @@ -715,9 +719,28 @@ public final class ServerController { */ @GetMapping("/{platform}/{hostname}") @ResponseBody - public ResponseEntity getServer(@PathVariable @NonNull String platform, - @PathVariable @NonNull String hostname - ) { + public ResponseEntity getServer(@PathVariable @NonNull String platform, @PathVariable @NonNull String hostname) { return ResponseEntity.ofNullable(mojangService.getMinecraftServer(platform, hostname)); } + + /** + * Get the server icon of a Minecraft + * server by its platform and hostname. + * + * @param platform the platform of the server + * @param hostname the hostname of the server + * @return the server icon + */ + @GetMapping("/icon/{platform}/{hostname}") + @ResponseBody + public ResponseEntity getServerIcon(@PathVariable @NonNull String platform, @PathVariable @NonNull String hostname) { + MinecraftServer.Favicon favicon = mojangService.getMinecraftServer(platform, hostname).getValue().getFavicon(); + String icon = favicon == null ? DEFAULT_SERVER_ICON : favicon.getBase64(); // Get the server icon + if (favicon != null) { // Remove the data type from the server icon + icon = icon.substring(icon.indexOf(",") + 1); + } + return ResponseEntity.ok() + .contentType(MediaType.IMAGE_PNG) + .body(Base64.getDecoder().decode(icon)); + } } \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/model/MinecraftServer.java b/src/main/java/me/braydon/mc/model/MinecraftServer.java index 1dd43f7..d862ac8 100644 --- a/src/main/java/me/braydon/mc/model/MinecraftServer.java +++ b/src/main/java/me/braydon/mc/model/MinecraftServer.java @@ -678,6 +678,7 @@ package me.braydon.mc.model; import lombok.*; import me.braydon.mc.common.ColorUtils; +import me.braydon.mc.config.AppConfig; import me.braydon.mc.service.pinger.MinecraftServerPinger; import me.braydon.mc.service.pinger.impl.BedrockMinecraftServerPinger; import me.braydon.mc.service.pinger.impl.JavaMinecraftServerPinger; @@ -712,16 +713,16 @@ public class MinecraftServer { */ @NonNull private final Players players; + /** + * The favicon of this server, null if none. + */ + private final Favicon favicon; + /** * The MOTD of this server. */ @NonNull private final MOTD motd; - /** - * The Base64 encoded icon of this server, null if no icon. - */ - private final String icon; - /** * Player count data for a server. */ @@ -759,6 +760,32 @@ public class MinecraftServer { } } + /** + * The favicon for a server. + */ + @AllArgsConstructor @Getter @ToString + public static class Favicon { + /** + * The raw Base64 encoded favicon. + */ + @NonNull private final String base64; + + /** + * The URL to the favicon. + */ + @NonNull private final String url; + + public static Favicon create(String base64, @NonNull Platform platform, @NonNull String hostname) { + if (base64 == null) { // No favicon to create + return null; + } + return new Favicon( + base64, + AppConfig.INSTANCE.getServerPublicUrl() + "/server/icon/" + platform.name().toLowerCase() + "/" + hostname + ); + } + } + /** * The MOTD for a server. */ diff --git a/src/main/java/me/braydon/mc/model/Skin.java b/src/main/java/me/braydon/mc/model/Skin.java index cc7f80e..a0b5533 100644 --- a/src/main/java/me/braydon/mc/model/Skin.java +++ b/src/main/java/me/braydon/mc/model/Skin.java @@ -679,7 +679,7 @@ package me.braydon.mc.model; import com.google.gson.JsonObject; import com.google.gson.annotations.SerializedName; import lombok.*; -import me.braydon.mc.common.PlayerUtils; +import me.braydon.mc.common.ImageUtils; import me.braydon.mc.config.AppConfig; import java.util.HashMap; @@ -754,7 +754,7 @@ public final class Skin { */ @AllArgsConstructor @Getter @ToString public enum Part { - HEAD(8, 8, PlayerUtils.SKIN_TEXTURE_SIZE / 8, PlayerUtils.SKIN_TEXTURE_SIZE / 8); + HEAD(8, 8, ImageUtils.SKIN_TEXTURE_SIZE / 8, ImageUtils.SKIN_TEXTURE_SIZE / 8); /** * The coordinates of this part. diff --git a/src/main/java/me/braydon/mc/model/server/BedrockMinecraftServer.java b/src/main/java/me/braydon/mc/model/server/BedrockMinecraftServer.java index fc60ed6..44e766a 100644 --- a/src/main/java/me/braydon/mc/model/server/BedrockMinecraftServer.java +++ b/src/main/java/me/braydon/mc/model/server/BedrockMinecraftServer.java @@ -686,7 +686,7 @@ import me.braydon.mc.model.MinecraftServer; */ public final class BedrockMinecraftServer extends MinecraftServer { private BedrockMinecraftServer(@NonNull String hostname, String ip, int port, @NonNull Players players, - @NonNull MOTD motd, String icon) { - super(hostname, ip, port, players, motd, icon); + Favicon favicon, @NonNull MOTD motd) { + super(hostname, ip, port, players, favicon, motd); } } \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/model/server/JavaMinecraftServer.java b/src/main/java/me/braydon/mc/model/server/JavaMinecraftServer.java index f09cbbc..cb61938 100644 --- a/src/main/java/me/braydon/mc/model/server/JavaMinecraftServer.java +++ b/src/main/java/me/braydon/mc/model/server/JavaMinecraftServer.java @@ -724,9 +724,9 @@ public final class JavaMinecraftServer extends MinecraftServer { private final boolean mojangBanned; private JavaMinecraftServer(@NonNull String hostname, String ip, int port, @NonNull Players players, - @NonNull MOTD motd, String icon, @NonNull Version version, ModInfo modInfo, + Favicon favicon, @NonNull MOTD motd, @NonNull Version version, ModInfo modInfo, boolean enforcesSecureChat, boolean preventsChatReports, boolean mojangBanned) { - super(hostname, ip, port, players, motd, icon); + super(hostname, ip, port, players, favicon, motd); this.version = version; this.modInfo = modInfo; this.enforcesSecureChat = enforcesSecureChat; @@ -749,9 +749,9 @@ public final class JavaMinecraftServer extends MinecraftServer { if (motdString == null) { // Not a string motd, convert from Json motdString = new TextComponent(ComponentSerializer.parse(RESTfulMC.GSON.toJson(token.getDescription()))).toLegacyText(); } - return new JavaMinecraftServer(hostname, ip, port, token.getPlayers(), MOTD.create(motdString), - token.getFavicon(), token.getVersion().detailedCopy(), token.getModInfo(), - token.isEnforcesSecureChat(), token.isPreventsChatReports(), false + return new JavaMinecraftServer(hostname, ip, port, token.getPlayers(), Favicon.create(token.getFavicon(), Platform.JAVA, hostname), + MOTD.create(motdString), token.getVersion().detailedCopy(), token.getModInfo(), token.isEnforcesSecureChat(), + token.isPreventsChatReports(), false ); } diff --git a/src/main/java/me/braydon/mc/service/MojangService.java b/src/main/java/me/braydon/mc/service/MojangService.java index cf81b4a..1cf0ef2 100644 --- a/src/main/java/me/braydon/mc/service/MojangService.java +++ b/src/main/java/me/braydon/mc/service/MojangService.java @@ -850,7 +850,7 @@ public final class MojangService { if (target == null) { // Fallback to the default skin target = Skin.DEFAULT_STEVE; } - return PlayerUtils.getSkinPart(target, part, size); + return ImageUtils.getSkinPart(target, part, size); } /**