From a03bac8f1d486f40d2094d6299acdce4b12fa184 Mon Sep 17 00:00:00 2001 From: Rainnny7 Date: Thu, 11 Apr 2024 09:09:25 -0400 Subject: [PATCH] Allow for Java user profiles to be signed --- .../braydon/mc/controller/PlayerController.java | 6 ++++-- src/main/java/me/braydon/mc/model/Player.java | 3 +-- .../mc/model/cache/CachedMinecraftServer.java | 2 +- .../me/braydon/mc/model/cache/CachedPlayer.java | 16 ++++++++++++++-- .../mc/repository/PlayerCacheRepository.java | 4 +--- .../me/braydon/mc/service/MojangService.java | 17 +++++++++-------- 6 files changed, 30 insertions(+), 18 deletions(-) diff --git a/src/main/java/me/braydon/mc/controller/PlayerController.java b/src/main/java/me/braydon/mc/controller/PlayerController.java index 94f5394..62974ef 100644 --- a/src/main/java/me/braydon/mc/controller/PlayerController.java +++ b/src/main/java/me/braydon/mc/controller/PlayerController.java @@ -63,6 +63,7 @@ public final class PlayerController { * A GET route to get a player by their username or UUID. * * @param query the query to search for the player by + * @param signed whether the profile is signed * @return the player response * @throws BadRequestException if the UUID or username is invalid * @throws ResourceNotFoundException if the player is not found @@ -71,9 +72,10 @@ public final class PlayerController { @GetMapping("/{query}") @ResponseBody public ResponseEntity getPlayer( - @Parameter(description = "The player username or UUID to get", example = "Rainnny") @PathVariable @NonNull String query + @Parameter(description = "The player username or UUID to get", example = "Rainnny") @PathVariable @NonNull String query, + @Parameter(description = "Whether the profile is signed by Mojang") @RequestParam(required = false) boolean signed ) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException { - return ResponseEntity.ofNullable(mojangService.getPlayer(query)); + return ResponseEntity.ofNullable(mojangService.getPlayer(query, signed)); } /** diff --git a/src/main/java/me/braydon/mc/model/Player.java b/src/main/java/me/braydon/mc/model/Player.java index 84f9cea..f785e46 100644 --- a/src/main/java/me/braydon/mc/model/Player.java +++ b/src/main/java/me/braydon/mc/model/Player.java @@ -26,7 +26,6 @@ package me.braydon.mc.model; import lombok.*; import me.braydon.mc.model.skin.Skin; import me.braydon.mc.model.token.MojangProfileToken; -import org.springframework.data.annotation.Id; import java.util.UUID; @@ -40,7 +39,7 @@ public class Player { /** * The unique id of this player. */ - @Id @EqualsAndHashCode.Include @NonNull private final UUID uniqueId; + @EqualsAndHashCode.Include @NonNull private final UUID uniqueId; /** * The username of this player. diff --git a/src/main/java/me/braydon/mc/model/cache/CachedMinecraftServer.java b/src/main/java/me/braydon/mc/model/cache/CachedMinecraftServer.java index 1148e14..bfab077 100644 --- a/src/main/java/me/braydon/mc/model/cache/CachedMinecraftServer.java +++ b/src/main/java/me/braydon/mc/model/cache/CachedMinecraftServer.java @@ -38,7 +38,7 @@ import java.io.Serializable; @RedisHash(value = "server", timeToLive = 60L) // 1 minute (in seconds) public final class CachedMinecraftServer implements Serializable { /** - * The id of this cached server. + * The id of this cache element. */ @Id @JsonIgnore @NonNull private final String id; diff --git a/src/main/java/me/braydon/mc/model/cache/CachedPlayer.java b/src/main/java/me/braydon/mc/model/cache/CachedPlayer.java index 714e9f9..0a2f091 100644 --- a/src/main/java/me/braydon/mc/model/cache/CachedPlayer.java +++ b/src/main/java/me/braydon/mc/model/cache/CachedPlayer.java @@ -23,6 +23,7 @@ */ package me.braydon.mc.model.cache; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.NonNull; import lombok.Setter; @@ -32,6 +33,7 @@ import me.braydon.mc.model.Player; import me.braydon.mc.model.ProfileAction; import me.braydon.mc.model.skin.Skin; import me.braydon.mc.model.token.MojangProfileToken; +import org.springframework.data.annotation.Id; import org.springframework.data.redis.core.RedisHash; import java.io.Serializable; @@ -46,16 +48,26 @@ import java.util.UUID; @ToString(callSuper = true) @RedisHash(value = "player", timeToLive = 60L * 60L) // 1 hour (in seconds) public final class CachedPlayer extends Player implements Serializable { + /** + * The id of this cache element. + *

+ * This ID is in the given format: + * player:- + *

+ */ + @Id @NonNull @JsonIgnore private final String cacheId; + /** * The unix timestamp of when this * player was cached, -1 if not cached. */ private long cached; - public CachedPlayer(@NonNull UUID uniqueId, @NonNull String username, @NonNull Skin skin, Cape cape, - @NonNull MojangProfileToken.ProfileProperty[] properties, ProfileAction[] profileActions, + public CachedPlayer(@NonNull String cacheId, @NonNull UUID uniqueId, @NonNull String username, @NonNull Skin skin, + Cape cape, @NonNull MojangProfileToken.ProfileProperty[] properties, ProfileAction[] profileActions, long cached) { super(uniqueId, username, skin, cape, properties, profileActions); + this.cacheId = cacheId; this.cached = cached; } } \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/repository/PlayerCacheRepository.java b/src/main/java/me/braydon/mc/repository/PlayerCacheRepository.java index bad024a..4903c2a 100644 --- a/src/main/java/me/braydon/mc/repository/PlayerCacheRepository.java +++ b/src/main/java/me/braydon/mc/repository/PlayerCacheRepository.java @@ -26,11 +26,9 @@ package me.braydon.mc.repository; import me.braydon.mc.model.cache.CachedPlayer; import org.springframework.data.repository.CrudRepository; -import java.util.UUID; - /** * A cache repository for {@link CachedPlayer}'s. * * @author Braydon */ -public interface PlayerCacheRepository extends CrudRepository { } \ No newline at end of file +public interface PlayerCacheRepository extends CrudRepository { } \ 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 index 85ba393..3d905fc 100644 --- a/src/main/java/me/braydon/mc/service/MojangService.java +++ b/src/main/java/me/braydon/mc/service/MojangService.java @@ -214,7 +214,7 @@ public final class MojangService { Skin skin = null; // The target skin to get the skin part of long before = System.currentTimeMillis(); try { - CachedPlayer player = getPlayer(query); // Retrieve the player + CachedPlayer player = getPlayer(query, false); // Retrieve the player skin = player.getSkin(); // Use the player's skin } catch (Exception ignored) { // Simply ignore, and fallback to the default skin @@ -244,14 +244,15 @@ public final class MojangService { * and then return the response. *

* - * @param query the query to search for the player by + * @param query the query to search for the player by + * @param signed whether the profile is signed * @return the player * @throws BadRequestException if the UUID or username is invalid * @throws ResourceNotFoundException if the player is not found * @throws MojangRateLimitException if the Mojang API rate limit is reached */ @NonNull - public CachedPlayer getPlayer(@NonNull String query) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException { + public CachedPlayer getPlayer(@NonNull String query, boolean signed) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException { log.info("Requesting player with query: {}", query); UUID uuid; // The player UUID to lookup @@ -270,10 +271,11 @@ public final class MojangService { uuid = usernameToUUID(query); log.info("Found UUID for username {}: {}", query, uuid); } + String cacheId = "%s-%s".formatted(uuid, signed); // The cache id of the player // Check the cache for the player // and return it if it's present - Optional cached = playerCache.findById(uuid); + Optional cached = playerCache.findById(cacheId); if (cached.isPresent()) { // Respond with the cache if present log.info("Found player in cache: {}", uuid); return cached.get(); @@ -283,14 +285,13 @@ public final class MojangService { // the player profile by their UUID try { log.info("Retrieving player profile for UUID: {}", uuid); - MojangProfileToken token = JsonWebRequest.makeRequest( - UUID_TO_PROFILE.formatted(uuid), HttpMethod.GET - ).execute(MojangProfileToken.class); + String endpoint = UUID_TO_PROFILE.formatted(uuid) + (signed ? "?unsigned=false" : ""); + MojangProfileToken token = JsonWebRequest.makeRequest(endpoint, HttpMethod.GET).execute(MojangProfileToken.class); MojangProfileToken.SkinProperties skinProperties = token.getSkinProperties(); // Get the skin and cape ProfileAction[] profileActions = token.getProfileActions(); // Build our player model, cache it, and then return it - CachedPlayer player = new CachedPlayer(uuid, token.getName(), + CachedPlayer player = new CachedPlayer(cacheId, uuid, token.getName(), skinProperties.getSkin() == null ? Skin.DEFAULT_STEVE : skinProperties.getSkin(), skinProperties.getCape(), token.getProperties(), profileActions.length == 0 ? null : profileActions, System.currentTimeMillis()