Files
Backend/src/main/java/xyz/mcutils/backend/service/PlayerService.java

173 lines
7.4 KiB
Java
Raw Normal View History

2024-04-13 20:44:05 +01:00
package xyz.mcutils.backend.service;
2024-04-10 07:43:38 +01:00
import cc.fascinated.common.ImageUtils;
2024-04-10 07:43:38 +01:00
import cc.fascinated.common.PlayerUtils;
import cc.fascinated.common.Tuple;
import cc.fascinated.common.UUIDUtils;
2024-04-11 03:08:17 +01:00
import cc.fascinated.config.Config;
2024-04-12 18:50:17 +01:00
import cc.fascinated.exception.impl.BadRequestException;
2024-04-10 09:30:35 +01:00
import cc.fascinated.exception.impl.MojangAPIRateLimitException;
import cc.fascinated.exception.impl.RateLimitException;
2024-04-10 07:43:38 +01:00
import cc.fascinated.exception.impl.ResourceNotFoundException;
import cc.fascinated.model.cache.CachedPlayer;
import cc.fascinated.model.cache.CachedPlayerName;
2024-04-10 12:41:35 +01:00
import cc.fascinated.model.cache.CachedPlayerSkinPart;
2024-04-10 07:43:38 +01:00
import cc.fascinated.model.player.Cape;
2024-04-10 12:41:35 +01:00
import cc.fascinated.model.player.Player;
import cc.fascinated.model.skin.ISkinPart;
import cc.fascinated.model.skin.Skin;
2024-04-13 17:40:28 +01:00
import cc.fascinated.model.token.MojangProfileToken;
import cc.fascinated.model.token.MojangUsernameToUuidToken;
2024-04-13 20:44:05 +01:00
import xyz.mcutils.backend.repository.PlayerCacheRepository;
import xyz.mcutils.backend.repository.PlayerNameCacheRepository;
import xyz.mcutils.backend.repository.PlayerSkinPartCacheRepository;
2024-04-10 07:43:38 +01:00
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.awt.image.BufferedImage;
2024-04-10 07:43:38 +01:00
import java.util.Optional;
import java.util.UUID;
@Service @Log4j2
public class PlayerService {
private final MojangService mojangAPIService;
2024-04-10 07:43:38 +01:00
private final PlayerCacheRepository playerCacheRepository;
private final PlayerNameCacheRepository playerNameCacheRepository;
2024-04-10 12:41:35 +01:00
private final PlayerSkinPartCacheRepository playerSkinPartCacheRepository;
2024-04-10 07:43:38 +01:00
@Autowired
2024-04-10 12:41:35 +01:00
public PlayerService(MojangService mojangAPIService, PlayerCacheRepository playerCacheRepository,
PlayerNameCacheRepository playerNameCacheRepository, PlayerSkinPartCacheRepository playerSkinPartCacheRepository) {
2024-04-10 07:43:38 +01:00
this.mojangAPIService = mojangAPIService;
this.playerCacheRepository = playerCacheRepository;
this.playerNameCacheRepository = playerNameCacheRepository;
2024-04-10 12:41:35 +01:00
this.playerSkinPartCacheRepository = playerSkinPartCacheRepository;
2024-04-10 07:43:38 +01:00
}
/**
* Get a player from the cache or
* from the Mojang API.
*
* @param id the id of the player
* @return the player
*/
public CachedPlayer getPlayer(String id) {
// Convert the id to uppercase to prevent case sensitivity
log.info("Getting player: {}", id);
UUID uuid = PlayerUtils.getUuidFromString(id);
2024-04-10 07:43:38 +01:00
if (uuid == null) { // If the id is not a valid uuid, get the uuid from the username
uuid = usernameToUuid(id).getUniqueId();
2024-04-10 07:43:38 +01:00
}
Optional<CachedPlayer> cachedPlayer = playerCacheRepository.findById(uuid);
2024-04-11 03:08:17 +01:00
if (cachedPlayer.isPresent() && Config.INSTANCE.isProduction()) { // Return the cached player if it exists
log.info("Player {} is cached", id);
2024-04-10 07:43:38 +01:00
return cachedPlayer.get();
}
2024-04-10 09:30:35 +01:00
try {
log.info("Getting player profile from Mojang: {}", id);
2024-04-13 17:40:03 +01:00
MojangProfileToken mojangProfile = mojangAPIService.getProfile(uuid.toString()); // Get the player profile from Mojang
log.info("Got player profile from Mojang: {}", id);
2024-04-10 09:30:35 +01:00
Tuple<Skin, Cape> skinAndCape = mojangProfile.getSkinAndCape();
CachedPlayer player = new CachedPlayer(
uuid, // Player UUID
new Player(
uuid, // Player UUID
UUIDUtils.removeDashes(uuid), // Trimmed UUID
mojangProfile.getName(), // Player Name
skinAndCape.getLeft(), // Skin
skinAndCape.getRight(), // Cape
mojangProfile.getProperties() // Raw properties
2024-04-13 17:17:13 +01:00
)
2024-04-10 09:30:35 +01:00
);
2024-04-10 07:43:38 +01:00
2024-04-10 09:30:35 +01:00
playerCacheRepository.save(player);
player.getCache().setCached(false);
2024-04-10 09:30:35 +01:00
return player;
} catch (RateLimitException exception) {
throw new MojangAPIRateLimitException();
}
2024-04-10 07:43:38 +01:00
}
/**
* Gets the player's uuid from their username.
*
* @param username the username of the player
* @return the uuid of the player
*/
2024-04-11 00:49:16 +01:00
public CachedPlayerName usernameToUuid(String username) {
2024-04-10 09:51:31 +01:00
log.info("Getting UUID from username: {}", username);
2024-04-13 17:29:32 +01:00
String id = username.toUpperCase();
Optional<CachedPlayerName> cachedPlayerName = playerNameCacheRepository.findById(id);
2024-04-11 03:08:17 +01:00
if (cachedPlayerName.isPresent() && Config.INSTANCE.isProduction()) {
2024-04-11 00:49:16 +01:00
return cachedPlayerName.get();
2024-04-10 07:43:38 +01:00
}
2024-04-10 09:30:35 +01:00
try {
2024-04-13 17:40:03 +01:00
MojangUsernameToUuidToken mojangUsernameToUuid = mojangAPIService.getUuidFromUsername(username);
2024-04-10 09:30:35 +01:00
if (mojangUsernameToUuid == null) {
2024-04-10 09:51:31 +01:00
log.info("Player with username '{}' not found", username);
2024-04-10 09:30:35 +01:00
throw new ResourceNotFoundException("Player with username '%s' not found".formatted(username));
}
2024-04-11 00:49:16 +01:00
UUID uuid = UUIDUtils.addDashes(mojangUsernameToUuid.getUuid());
2024-04-13 17:29:32 +01:00
CachedPlayerName player = new CachedPlayerName(id, username, uuid);
2024-04-11 00:49:16 +01:00
playerNameCacheRepository.save(player);
2024-04-10 09:51:31 +01:00
log.info("Got UUID from username: {} -> {}", username, uuid);
2024-04-13 17:21:33 +01:00
player.getCache().setCached(false);
2024-04-11 00:49:16 +01:00
return player;
2024-04-10 09:30:35 +01:00
} catch (RateLimitException exception) {
throw new MojangAPIRateLimitException();
2024-04-10 07:43:38 +01:00
}
}
2024-04-10 12:41:35 +01:00
/**
* Gets a skin part from the player's skin.
*
* @param player the player
* @param partName the name of the part
2024-04-11 03:08:17 +01:00
* @param renderOverlay whether to render the overlay
2024-04-10 12:41:35 +01:00
* @return the skin part
*/
public CachedPlayerSkinPart getSkinPart(Player player, String partName, boolean renderOverlay, int size) {
2024-04-11 07:15:12 +01:00
if (size > 512) {
log.info("Size {} is too large, setting to 512", size);
size = 512;
2024-04-11 07:14:13 +01:00
}
2024-04-12 19:52:38 +01:00
if (size < 32) {
log.info("Size {} is too small, setting to 32", size);
size = 32;
}
ISkinPart part = ISkinPart.getByName(partName); // The skin part to get
2024-04-12 18:50:17 +01:00
if (part == null) {
throw new BadRequestException("Invalid skin part: %s".formatted(partName));
}
2024-04-12 18:56:25 +01:00
String name = part.name();
2024-04-12 20:15:17 +01:00
log.info("Getting skin part {} for player: {} (size: {}, overlays: {})", name, player.getUniqueId(), size, renderOverlay);
2024-04-12 18:56:25 +01:00
String key = "%s-%s-%s-%s".formatted(player.getUniqueId(), name, size, renderOverlay);
2024-04-10 12:41:35 +01:00
Optional<CachedPlayerSkinPart> cache = playerSkinPartCacheRepository.findById(key);
// The skin part is cached
2024-04-11 03:08:17 +01:00
if (cache.isPresent() && Config.INSTANCE.isProduction()) {
2024-04-12 18:56:25 +01:00
log.info("Skin part {} for player {} is cached", name, player.getUniqueId());
2024-04-10 12:41:35 +01:00
return cache.get();
}
2024-04-11 03:48:54 +01:00
long before = System.currentTimeMillis();
BufferedImage renderedPart = part.render(player.getSkin(), renderOverlay, size); // Render the skin part
2024-04-12 18:56:25 +01:00
log.info("Took {}ms to render skin part {} for player: {}", System.currentTimeMillis() - before, name, player.getUniqueId());
2024-04-10 12:41:35 +01:00
CachedPlayerSkinPart skinPart = new CachedPlayerSkinPart(
key,
2024-04-12 18:56:25 +01:00
ImageUtils.imageToBytes(renderedPart)
2024-04-10 12:41:35 +01:00
);
2024-04-12 18:56:25 +01:00
log.info("Fetched skin part {} for player: {}", name, player.getUniqueId());
2024-04-10 12:41:35 +01:00
playerSkinPartCacheRepository.save(skinPart);
return skinPart;
}
2024-04-10 07:43:38 +01:00
}