Player name caching

This commit is contained in:
Braydon 2024-04-06 15:44:00 -04:00
parent aaee6c8ce9
commit fd44a0b25c
5 changed files with 74 additions and 8 deletions

@ -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.

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

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

@ -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.
* <p>
* This will allow us to easily lookup a
* player's username and get their uuid.
* </p>
*
* @author Braydon
*/
public interface PlayerNameCacheRepository extends CrudRepository<CachedPlayerName, String> { }

@ -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<CachedPlayer> cached = playerCacheRepository.findById(uuid);
Optional<CachedPlayer> 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<CachedPlayerName> 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));