From 2c54defef3e26cc2e0ac16cd247e8f285b08bdd6 Mon Sep 17 00:00:00 2001 From: Rainnny7 Date: Sat, 6 Apr 2024 14:45:44 -0400 Subject: [PATCH] Mojang service --- pom.xml | 6 ++ src/main/java/me/braydon/mc/RESTfulMC.java | 11 ++- .../java/me/braydon/mc/service/MojangAPI.java | 13 +++ .../me/braydon/mc/service/MojangService.java | 93 +++++++++++++++++++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 src/main/java/me/braydon/mc/service/MojangAPI.java create mode 100644 src/main/java/me/braydon/mc/service/MojangService.java diff --git a/pom.xml b/pom.xml index a54eec2..b8ef856 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,12 @@ 1.18.32 provided + + com.google.code.gson + gson + 2.10.1 + compile + diff --git a/src/main/java/me/braydon/mc/RESTfulMC.java b/src/main/java/me/braydon/mc/RESTfulMC.java index 2a62f95..9e61dbf 100644 --- a/src/main/java/me/braydon/mc/RESTfulMC.java +++ b/src/main/java/me/braydon/mc/RESTfulMC.java @@ -1,5 +1,7 @@ package me.braydon.mc; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import lombok.NonNull; import lombok.SneakyThrows; import lombok.extern.slf4j.Slf4j; @@ -16,9 +18,14 @@ import java.util.Objects; */ @SpringBootApplication @Slf4j(topic = "RESTfulMC") -public final class RESTfulMC { +public class RESTfulMC { + public static final Gson GSON = new GsonBuilder() + .serializeNulls() + .create(); + @SneakyThrows public static void main(@NonNull String[] args) { + // Handle loading of our configuration file File config = new File("application.yml"); if (!config.exists()) { // Saving the default config if it doesn't exist locally Files.copy(Objects.requireNonNull(RESTfulMC.class.getResourceAsStream("/application.yml")), config.toPath(), StandardCopyOption.REPLACE_EXISTING); @@ -28,6 +35,8 @@ public final class RESTfulMC { return; } log.info("Found configuration at '{}'", config.getAbsolutePath()); // Log the found config + + // Start the app SpringApplication.run(RESTfulMC.class, args); } } \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/service/MojangAPI.java b/src/main/java/me/braydon/mc/service/MojangAPI.java new file mode 100644 index 0000000..7a5120d --- /dev/null +++ b/src/main/java/me/braydon/mc/service/MojangAPI.java @@ -0,0 +1,13 @@ +package me.braydon.mc.service; + +/** + * Endpoints for the Mojang API. + * + * @author Braydon + */ +public class MojangAPI { + private static final String SESSION_SERVER_ENDPOINT = "https://sessionserver.mojang.com"; + private static final String API_ENDPOINT = "https://api.mojang.com"; + protected static final String UUID_TO_PROFILE = SESSION_SERVER_ENDPOINT + "/session/minecraft/profile/%s"; + protected static final String USERNAME_TO_UUID = API_ENDPOINT + "/users/profiles/minecraft/%s"; +} \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/service/MojangService.java b/src/main/java/me/braydon/mc/service/MojangService.java new file mode 100644 index 0000000..fb76975 --- /dev/null +++ b/src/main/java/me/braydon/mc/service/MojangService.java @@ -0,0 +1,93 @@ +package me.braydon.mc.service; + +import lombok.NonNull; +import lombok.extern.log4j.Log4j2; +import me.braydon.mc.common.UUIDUtils; +import me.braydon.mc.common.web.JsonWebException; +import me.braydon.mc.common.web.JsonWebRequest; +import me.braydon.mc.exception.impl.BadRequestException; +import me.braydon.mc.exception.impl.ResourceNotFoundException; +import me.braydon.mc.model.Player; +import me.braydon.mc.model.token.MojangProfileToken; +import me.braydon.mc.model.token.MojangUsernameToUUIDToken; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Service; + +import java.util.UUID; + +/** + * A service for interacting with the Mojang API. + * + * @author Braydon + */ +@Service +@Log4j2(topic = "Mojang Service") +public final class MojangService { + /** + * Get a player by their username or UUID. + * + * @param query the query to search for the player by + * @return the player + * @throws BadRequestException if the UUID is malformed + * @throws ResourceNotFoundException if the player is not found + */ + @NonNull + public Player getPlayer(@NonNull String query) throws BadRequestException, ResourceNotFoundException { + log.info("Requesting player with query: {}", query); + + UUID uuid; // The player UUID to lookup + boolean fullLength = query.length() == 36; // Was a UUID provided? + if (query.length() == 32 || fullLength) { // Parse the query as a UUID + try { + uuid = fullLength ? UUID.fromString(query) : UUIDUtils.addDashes(query); + log.info("Parsed {}UUID: {} -> {}", fullLength ? "" : "trimmed ", query, uuid); + } catch (IllegalArgumentException ex) { + throw new BadRequestException("Malformed UUID provided: %s".formatted(query)); + } + } else { // The query is a username, request from Mojang + uuid = usernameToUUID(query); + log.info("Found UUID for username {}: {}", query, uuid); + } + + // Send a request to Mojang requesting + // the player profile by their UUID + try { + log.info("Retrieving player profile for UUID: {}", uuid); + MojangProfileToken token = JsonWebRequest.makeRequest( + MojangAPI.UUID_TO_PROFILE.formatted(uuid), HttpMethod.GET + ).execute(MojangProfileToken.class); + + // Return our player model representing the requested player + return new Player(uuid, token.getName(), token.getProfileActions()); + } catch (JsonWebException ex) { + // No profile found, return null + if (ex.getStatusCode() == 400) { + throw new ResourceNotFoundException("Player not found with query: %s".formatted(query)); + } + throw ex; + } + } + + /** + * Get the UUID of a player by their username. + * + * @param username the player's username + * @return the player's UUID + * @throws ResourceNotFoundException if the player isn't found + */ + @NonNull + private UUID usernameToUUID(@NonNull String username) throws ResourceNotFoundException { + // Make a request to Mojang requesting the UUID + try { + MojangUsernameToUUIDToken token = JsonWebRequest.makeRequest( + MojangAPI.USERNAME_TO_UUID.formatted(username), HttpMethod.GET + ).execute(MojangUsernameToUUIDToken.class); + return UUIDUtils.addDashes(token.getId()); // Return the UUID with dashes + } catch (JsonWebException ex) { + if (ex.getStatusCode() == 404) { + throw new ResourceNotFoundException("Player not found with username: %s".formatted(username)); + } + throw ex; + } + } +} \ No newline at end of file