Mojang service

This commit is contained in:
Braydon 2024-04-06 14:45:44 -04:00
parent c4891ccbb5
commit 2c54defef3
4 changed files with 122 additions and 1 deletions

@ -48,6 +48,12 @@
<version>1.18.32</version> <version>1.18.32</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.10.1</version>
<scope>compile</scope>
</dependency>
<!-- Tests --> <!-- Tests -->
<dependency> <dependency>

@ -1,5 +1,7 @@
package me.braydon.mc; package me.braydon.mc;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.NonNull; import lombok.NonNull;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -16,9 +18,14 @@ import java.util.Objects;
*/ */
@SpringBootApplication @SpringBootApplication
@Slf4j(topic = "RESTfulMC") @Slf4j(topic = "RESTfulMC")
public final class RESTfulMC { public class RESTfulMC {
public static final Gson GSON = new GsonBuilder()
.serializeNulls()
.create();
@SneakyThrows @SneakyThrows
public static void main(@NonNull String[] args) { public static void main(@NonNull String[] args) {
// Handle loading of our configuration file
File config = new File("application.yml"); File config = new File("application.yml");
if (!config.exists()) { // Saving the default config if it doesn't exist locally 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); Files.copy(Objects.requireNonNull(RESTfulMC.class.getResourceAsStream("/application.yml")), config.toPath(), StandardCopyOption.REPLACE_EXISTING);
@ -28,6 +35,8 @@ public final class RESTfulMC {
return; return;
} }
log.info("Found configuration at '{}'", config.getAbsolutePath()); // Log the found config log.info("Found configuration at '{}'", config.getAbsolutePath()); // Log the found config
// Start the app
SpringApplication.run(RESTfulMC.class, args); SpringApplication.run(RESTfulMC.class, args);
} }
} }

@ -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";
}

@ -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;
}
}
}