diff --git a/src/main/java/me/braydon/mc/model/Player.java b/src/main/java/me/braydon/mc/model/Player.java index 5bc7425..c538897 100644 --- a/src/main/java/me/braydon/mc/model/Player.java +++ b/src/main/java/me/braydon/mc/model/Player.java @@ -2,7 +2,6 @@ package me.braydon.mc.model; import lombok.*; import org.springframework.data.annotation.Id; -import org.springframework.data.redis.core.RedisHash; import java.util.UUID; @@ -16,7 +15,6 @@ import java.util.UUID; @Setter @Getter @EqualsAndHashCode(onlyExplicitlyIncluded = true) @ToString -@RedisHash(value = "player", timeToLive = 60L * 60L) // 1 hour (in seconds) public class Player { /** * The unique id of this player. 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 20538e2..c38ad51 100644 --- a/src/main/java/me/braydon/mc/model/cache/CachedPlayer.java +++ b/src/main/java/me/braydon/mc/model/cache/CachedPlayer.java @@ -3,8 +3,10 @@ package me.braydon.mc.model.cache; import lombok.Getter; import lombok.NonNull; import lombok.Setter; +import lombok.ToString; import me.braydon.mc.model.Player; import me.braydon.mc.model.ProfileAction; +import org.springframework.data.redis.core.RedisHash; import java.io.Serializable; import java.util.UUID; @@ -15,6 +17,8 @@ import java.util.UUID; * @author Braydon */ @Setter @Getter +@ToString(callSuper = true) +@RedisHash(value = "player", timeToLive = 60L * 60L) // 1 hour (in seconds) public final class CachedPlayer extends Player implements Serializable { /** * The unix timestamp of when this diff --git a/src/main/java/me/braydon/mc/model/cache/CachedPlayerName.java b/src/main/java/me/braydon/mc/model/cache/CachedPlayerName.java new file mode 100644 index 0000000..307a777 --- /dev/null +++ b/src/main/java/me/braydon/mc/model/cache/CachedPlayerName.java @@ -0,0 +1,27 @@ +package me.braydon.mc.model.cache; + +import lombok.*; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + +import java.util.UUID; + +/** + * @author Braydon + */ +@AllArgsConstructor +@Getter +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@ToString +@RedisHash(value = "playerName", timeToLive = 60L * 60L) // 1 hour (in seconds) +public final class CachedPlayerName { + /** + * The username of the player. + */ + @Id @NonNull private String username; + + /** + * The unique id of the player. + */ + @NonNull private UUID uniqueId; +} \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/repository/PlayerNameCacheRepository.java b/src/main/java/me/braydon/mc/repository/PlayerNameCacheRepository.java new file mode 100644 index 0000000..52c4a2e --- /dev/null +++ b/src/main/java/me/braydon/mc/repository/PlayerNameCacheRepository.java @@ -0,0 +1,15 @@ +package me.braydon.mc.repository; + +import me.braydon.mc.model.cache.CachedPlayerName; +import org.springframework.data.repository.CrudRepository; + +/** + * A cache repository for player usernames. + *

+ * This will allow us to easily lookup a + * player's username and get their uuid. + *

+ * + * @author Braydon + */ +public interface PlayerNameCacheRepository 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 ac5b500..2bb5525 100644 --- a/src/main/java/me/braydon/mc/service/MojangService.java +++ b/src/main/java/me/braydon/mc/service/MojangService.java @@ -10,9 +10,11 @@ import me.braydon.mc.exception.impl.ResourceNotFoundException; import me.braydon.mc.model.Player; import me.braydon.mc.model.ProfileAction; import me.braydon.mc.model.cache.CachedPlayer; +import me.braydon.mc.model.cache.CachedPlayerName; import me.braydon.mc.model.token.MojangProfileToken; import me.braydon.mc.model.token.MojangUsernameToUUIDToken; import me.braydon.mc.repository.PlayerCacheRepository; +import me.braydon.mc.repository.PlayerNameCacheRepository; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpMethod; import org.springframework.stereotype.Service; @@ -33,14 +35,20 @@ public final class MojangService { private static final String UUID_TO_PROFILE = SESSION_SERVER_ENDPOINT + "/session/minecraft/profile/%s"; private static final String USERNAME_TO_UUID = API_ENDPOINT + "/users/profiles/minecraft/%s"; + /** + * The cache repository for {@link Player}'s by their username. + */ + @NonNull private final PlayerNameCacheRepository playerNameCache; + /** * The cache repository for {@link Player}'s. */ - @NonNull private final PlayerCacheRepository playerCacheRepository; + @NonNull private final PlayerCacheRepository playerCache; @Autowired - public MojangService(@NonNull PlayerCacheRepository playerCacheRepository) { - this.playerCacheRepository = playerCacheRepository; + public MojangService(@NonNull PlayerNameCacheRepository playerNameCache, @NonNull PlayerCacheRepository playerCache) { + this.playerNameCache = playerNameCache; + this.playerCache = playerCache; } /** @@ -76,7 +84,7 @@ public final class MojangService { } // Check the cache for the player - Optional cached = playerCacheRepository.findById(uuid); + Optional cached = playerCache.findById(uuid); if (cached.isPresent()) { // Respond with the cache if present log.info("Found player in cache: {}", uuid); return cached.get(); @@ -97,7 +105,7 @@ public final class MojangService { profileActions.length == 0 ? null : profileActions, System.currentTimeMillis() ); - playerCacheRepository.save(player); + playerCache.save(player); log.info("Cached player: {}", uuid); player.setCached(-1L); // Set to -1 to indicate it's not cached in the response @@ -120,12 +128,26 @@ public final class MojangService { */ @NonNull private UUID usernameToUUID(@NonNull String username) throws ResourceNotFoundException { + username = username.toLowerCase(); // Lowercase the username + + // Check the cache for the player's UUID + Optional cached = playerNameCache.findById(username); + if (cached.isPresent()) { // Respond with the cache if present + log.info("Found UUID in cache for username {}: {}", username, cached.get().getUniqueId()); + return cached.get().getUniqueId(); + } + // Make a request to Mojang requesting the UUID try { MojangUsernameToUUIDToken token = JsonWebRequest.makeRequest( USERNAME_TO_UUID.formatted(username), HttpMethod.GET ).execute(MojangUsernameToUUIDToken.class); - return UUIDUtils.addDashes(token.getId()); // Return the UUID with dashes + + // Cache the UUID and return it + UUID uuid = UUIDUtils.addDashes(token.getId()); + playerNameCache.save(new CachedPlayerName(username, uuid)); + log.info("Cached UUID for username {}: {}", username, uuid); + return uuid; } catch (JsonWebException ex) { if (ex.getStatusCode() == 404) { throw new ResourceNotFoundException("Player not found with username: %s".formatted(username));