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()