add skin part caching
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m24s
All checks were successful
Deploy App / docker (ubuntu-latest, 2.44.0, 17, 3.8.5) (push) Successful in 1m24s
This commit is contained in:
parent
3790d4a312
commit
28cd7f192d
@ -1,7 +1,6 @@
|
|||||||
package cc.fascinated.controller;
|
package cc.fascinated.controller;
|
||||||
|
|
||||||
import cc.fascinated.common.PlayerUtils;
|
import cc.fascinated.model.cache.CachedPlayer;
|
||||||
import cc.fascinated.model.player.Player;
|
|
||||||
import cc.fascinated.model.player.Skin;
|
import cc.fascinated.model.player.Skin;
|
||||||
import cc.fascinated.service.PlayerService;
|
import cc.fascinated.service.PlayerService;
|
||||||
import io.swagger.v3.oas.annotations.Parameter;
|
import io.swagger.v3.oas.annotations.Parameter;
|
||||||
@ -21,11 +20,11 @@ import java.util.concurrent.TimeUnit;
|
|||||||
public class PlayerController {
|
public class PlayerController {
|
||||||
|
|
||||||
private final CacheControl cacheControl = CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic();
|
private final CacheControl cacheControl = CacheControl.maxAge(1, TimeUnit.HOURS).cachePublic();
|
||||||
private final PlayerService playerManagerService;
|
private final PlayerService playerService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public PlayerController(PlayerService playerManagerService) {
|
public PlayerController(PlayerService playerManagerService) {
|
||||||
this.playerManagerService = playerManagerService;
|
this.playerService = playerManagerService;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ResponseBody
|
@ResponseBody
|
||||||
@ -34,7 +33,7 @@ public class PlayerController {
|
|||||||
@Parameter(description = "The UUID or Username of the player", example = "ImFascinated") @PathVariable String id) {
|
@Parameter(description = "The UUID or Username of the player", example = "ImFascinated") @PathVariable String id) {
|
||||||
return ResponseEntity.ok()
|
return ResponseEntity.ok()
|
||||||
.cacheControl(cacheControl)
|
.cacheControl(cacheControl)
|
||||||
.body(playerManagerService.getPlayer(id));
|
.body(playerService.getPlayer(id));
|
||||||
}
|
}
|
||||||
|
|
||||||
@GetMapping(value = "/{part}/{id}")
|
@GetMapping(value = "/{part}/{id}")
|
||||||
@ -43,7 +42,7 @@ public class PlayerController {
|
|||||||
@Parameter(description = "The UUID or Username of the player", example = "ImFascinated") @PathVariable String id,
|
@Parameter(description = "The UUID or Username of the player", example = "ImFascinated") @PathVariable String id,
|
||||||
@Parameter(description = "The size of the image", example = "256") @RequestParam(required = false, defaultValue = "256") int size,
|
@Parameter(description = "The size of the image", example = "256") @RequestParam(required = false, defaultValue = "256") int size,
|
||||||
@Parameter(description = "Whether to download the image") @RequestParam(required = false, defaultValue = "false") boolean download) {
|
@Parameter(description = "Whether to download the image") @RequestParam(required = false, defaultValue = "false") boolean download) {
|
||||||
Player player = playerManagerService.getPlayer(id);
|
CachedPlayer player = playerService.getPlayer(id);
|
||||||
Skin.Parts skinPart = Skin.Parts.fromName(part);
|
Skin.Parts skinPart = Skin.Parts.fromName(part);
|
||||||
String dispositionHeader = download ? "attachment; filename=%s.png" : "inline; filename=%s.png";
|
String dispositionHeader = download ? "attachment; filename=%s.png" : "inline; filename=%s.png";
|
||||||
|
|
||||||
@ -52,6 +51,6 @@ public class PlayerController {
|
|||||||
.cacheControl(cacheControl)
|
.cacheControl(cacheControl)
|
||||||
.contentType(MediaType.IMAGE_PNG)
|
.contentType(MediaType.IMAGE_PNG)
|
||||||
.header(HttpHeaders.CONTENT_DISPOSITION, dispositionHeader.formatted(player.getUsername()))
|
.header(HttpHeaders.CONTENT_DISPOSITION, dispositionHeader.formatted(player.getUsername()))
|
||||||
.body(PlayerUtils.getSkinPartBytes(player.getSkin(), skinPart, size));
|
.body(playerService.getSkinPart(player, skinPart, size).getBytes());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
22
src/main/java/cc.fascinated/model/cache/CachedPlayerSkinPart.java
vendored
Normal file
22
src/main/java/cc.fascinated/model/cache/CachedPlayerSkinPart.java
vendored
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package cc.fascinated.model.cache;
|
||||||
|
|
||||||
|
import lombok.*;
|
||||||
|
import org.springframework.data.annotation.Id;
|
||||||
|
import org.springframework.data.redis.core.RedisHash;
|
||||||
|
|
||||||
|
@Setter
|
||||||
|
@Getter
|
||||||
|
@AllArgsConstructor
|
||||||
|
@RedisHash(value = "player", timeToLive = 60L * 60L) // 1 hour (in seconds)
|
||||||
|
public class CachedPlayerSkinPart {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ID of the skin part
|
||||||
|
*/
|
||||||
|
@Id @NonNull private String id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The skin part bytes
|
||||||
|
*/
|
||||||
|
private byte[] bytes;
|
||||||
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
package cc.fascinated.repository;
|
||||||
|
|
||||||
|
import cc.fascinated.model.cache.CachedPlayerName;
|
||||||
|
import cc.fascinated.model.cache.CachedPlayerSkinPart;
|
||||||
|
import org.springframework.data.repository.CrudRepository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache repository for player skin parts.
|
||||||
|
* <p>
|
||||||
|
* This will allow us to easily lookup a
|
||||||
|
* player skin part by it's id.
|
||||||
|
* </p>
|
||||||
|
*/
|
||||||
|
public interface PlayerSkinPartCacheRepository extends CrudRepository<CachedPlayerSkinPart, String> { }
|
@ -8,12 +8,15 @@ import cc.fascinated.exception.impl.RateLimitException;
|
|||||||
import cc.fascinated.exception.impl.ResourceNotFoundException;
|
import cc.fascinated.exception.impl.ResourceNotFoundException;
|
||||||
import cc.fascinated.model.cache.CachedPlayer;
|
import cc.fascinated.model.cache.CachedPlayer;
|
||||||
import cc.fascinated.model.cache.CachedPlayerName;
|
import cc.fascinated.model.cache.CachedPlayerName;
|
||||||
|
import cc.fascinated.model.cache.CachedPlayerSkinPart;
|
||||||
import cc.fascinated.model.mojang.MojangProfile;
|
import cc.fascinated.model.mojang.MojangProfile;
|
||||||
import cc.fascinated.model.mojang.MojangUsernameToUuid;
|
import cc.fascinated.model.mojang.MojangUsernameToUuid;
|
||||||
import cc.fascinated.model.player.Cape;
|
import cc.fascinated.model.player.Cape;
|
||||||
|
import cc.fascinated.model.player.Player;
|
||||||
import cc.fascinated.model.player.Skin;
|
import cc.fascinated.model.player.Skin;
|
||||||
import cc.fascinated.repository.PlayerCacheRepository;
|
import cc.fascinated.repository.PlayerCacheRepository;
|
||||||
import cc.fascinated.repository.PlayerNameCacheRepository;
|
import cc.fascinated.repository.PlayerNameCacheRepository;
|
||||||
|
import cc.fascinated.repository.PlayerSkinPartCacheRepository;
|
||||||
import lombok.extern.log4j.Log4j2;
|
import lombok.extern.log4j.Log4j2;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@ -27,12 +30,15 @@ public class PlayerService {
|
|||||||
private final MojangService mojangAPIService;
|
private final MojangService mojangAPIService;
|
||||||
private final PlayerCacheRepository playerCacheRepository;
|
private final PlayerCacheRepository playerCacheRepository;
|
||||||
private final PlayerNameCacheRepository playerNameCacheRepository;
|
private final PlayerNameCacheRepository playerNameCacheRepository;
|
||||||
|
private final PlayerSkinPartCacheRepository playerSkinPartCacheRepository;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
public PlayerService(MojangService mojangAPIService, PlayerCacheRepository playerCacheRepository, PlayerNameCacheRepository playerNameCacheRepository) {
|
public PlayerService(MojangService mojangAPIService, PlayerCacheRepository playerCacheRepository,
|
||||||
|
PlayerNameCacheRepository playerNameCacheRepository, PlayerSkinPartCacheRepository playerSkinPartCacheRepository) {
|
||||||
this.mojangAPIService = mojangAPIService;
|
this.mojangAPIService = mojangAPIService;
|
||||||
this.playerCacheRepository = playerCacheRepository;
|
this.playerCacheRepository = playerCacheRepository;
|
||||||
this.playerNameCacheRepository = playerNameCacheRepository;
|
this.playerNameCacheRepository = playerNameCacheRepository;
|
||||||
|
this.playerSkinPartCacheRepository = playerSkinPartCacheRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -104,4 +110,33 @@ public class PlayerService {
|
|||||||
throw new MojangAPIRateLimitException();
|
throw new MojangAPIRateLimitException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a skin part from the player's skin.
|
||||||
|
*
|
||||||
|
* @param player the player
|
||||||
|
* @param part the part of the skin
|
||||||
|
* @return the skin part
|
||||||
|
*/
|
||||||
|
public CachedPlayerSkinPart getSkinPart(Player player, Skin.Parts part, int size) {
|
||||||
|
log.info("Getting skin part: {} for player: {}", part.getName(), player.getUuid());
|
||||||
|
String key = "%s-%s-%s".formatted(player.getUuid(), part.getName(), size);
|
||||||
|
Optional<CachedPlayerSkinPart> cache = playerSkinPartCacheRepository.findById(key);
|
||||||
|
|
||||||
|
// The skin part is cached
|
||||||
|
if (cache.isPresent()) {
|
||||||
|
log.info("Skin part {} for player {} is cached", part.getName(), player.getUuid());
|
||||||
|
return cache.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] skinPartBytes = PlayerUtils.getSkinPartBytes(player.getSkin(), part, size);
|
||||||
|
CachedPlayerSkinPart skinPart = new CachedPlayerSkinPart(
|
||||||
|
key,
|
||||||
|
skinPartBytes
|
||||||
|
);
|
||||||
|
log.info("Fetched skin part: {} for player: {}", part.getName(), player.getUuid());
|
||||||
|
|
||||||
|
playerSkinPartCacheRepository.save(skinPart);
|
||||||
|
return skinPart;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user