Cleanup
This commit is contained in:
parent
0de598b205
commit
0890a151eb
@ -15,5 +15,8 @@ ENV HOSTNAME "0.0.0.0"
|
|||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
ENV PORT 80
|
ENV PORT 80
|
||||||
|
|
||||||
|
# We're running in production
|
||||||
|
ENV APP_ENV "production"
|
||||||
|
|
||||||
# Start the app
|
# Start the app
|
||||||
CMD ["java", "-jar", "target/RESTfulMC.jar"]
|
CMD ["java", "-jar", "target/RESTfulMC.jar"]
|
42
src/main/java/me/braydon/mc/common/EnvironmentUtils.java
Normal file
42
src/main/java/me/braydon/mc/common/EnvironmentUtils.java
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2024 Braydon (Rainnny).
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
package me.braydon.mc.common;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.experimental.UtilityClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Braydon
|
||||||
|
*/
|
||||||
|
@UtilityClass
|
||||||
|
public final class EnvironmentUtils {
|
||||||
|
/**
|
||||||
|
* Is the app running in a production environment?
|
||||||
|
*/
|
||||||
|
@Getter private static final boolean production;
|
||||||
|
static { // Are we running on production?
|
||||||
|
String env = System.getenv("APP_ENV");
|
||||||
|
production = env != null && (env.equals("production"));
|
||||||
|
}
|
||||||
|
}
|
@ -73,7 +73,7 @@ public final class PlayerController {
|
|||||||
public ResponseEntity<CachedPlayer> getPlayer(
|
public ResponseEntity<CachedPlayer> 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
|
||||||
) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException {
|
) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException {
|
||||||
return ResponseEntity.ofNullable(mojangService.getPlayer(query, false));
|
return ResponseEntity.ofNullable(mojangService.getPlayer(query));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -168,10 +168,15 @@ public final class MojangService {
|
|||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
public byte[] getSkinPartTexture(@NonNull String partName, @NonNull String query, @NonNull String extension,
|
public byte[] getSkinPartTexture(@NonNull String partName, @NonNull String query, @NonNull String extension,
|
||||||
boolean overlays, String sizeString) throws BadRequestException, MojangRateLimitException {
|
boolean overlays, String sizeString) throws BadRequestException, MojangRateLimitException {
|
||||||
|
log.info("Requesting skin part {} with query {} (ext: {}, overlays: {}, size: {})",
|
||||||
|
partName, query, extension, overlays, sizeString
|
||||||
|
);
|
||||||
|
|
||||||
// Get the part from the given name
|
// Get the part from the given name
|
||||||
ISkinPart part = ISkinPart.getByName(partName); // The skin part to get
|
ISkinPart part = ISkinPart.getByName(partName); // The skin part to get
|
||||||
if (part == null) { // Default to the face
|
if (part == null) { // Default to the face
|
||||||
part = ISkinPart.Vanilla.FACE;
|
part = ISkinPart.Vanilla.FACE;
|
||||||
|
log.warn("Invalid skin part {}, defaulting to {}", partName, part.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the extension is valid
|
// Ensure the extension is valid
|
||||||
@ -190,16 +195,26 @@ public final class MojangService {
|
|||||||
}
|
}
|
||||||
if (size == null || size <= 0) { // Invalid size
|
if (size == null || size <= 0) { // Invalid size
|
||||||
size = DEFAULT_PART_TEXTURE_SIZE;
|
size = DEFAULT_PART_TEXTURE_SIZE;
|
||||||
|
log.warn("Invalid size {}, defaulting to {}", sizeString, size);
|
||||||
|
}
|
||||||
|
if (size > MAX_PART_TEXTURE_SIZE) { // Limit the size to 512
|
||||||
|
size = MAX_PART_TEXTURE_SIZE;
|
||||||
|
log.warn("Size {} is too large, defaulting to {}", sizeString, MAX_PART_TEXTURE_SIZE);
|
||||||
}
|
}
|
||||||
size = Math.min(size, MAX_PART_TEXTURE_SIZE); // Limit the size to 512
|
|
||||||
String id = "%s-%s-%s-%s-%s".formatted(query.toLowerCase(), part.name(), overlays, size, extension); // The id of the skin part
|
String id = "%s-%s-%s-%s-%s".formatted(query.toLowerCase(), part.name(), overlays, size, extension); // The id of the skin part
|
||||||
|
|
||||||
Optional<CachedSkinPartTexture> cached = skinPartTextureCache.findById(id); // Get the cached texture
|
// In production, check the cache for the
|
||||||
|
// skin part and return it if it's present
|
||||||
|
if (EnvironmentUtils.isProduction()) {
|
||||||
|
Optional<CachedSkinPartTexture> cached = skinPartTextureCache.findById(id);
|
||||||
if (cached.isPresent()) { // Respond with the cache if present
|
if (cached.isPresent()) { // Respond with the cache if present
|
||||||
// return cached.get().getTexture();
|
log.info("Found skin part {} in cache: {}", part.name(), id);
|
||||||
|
return cached.get().getTexture();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Skin skin = null; // The target skin to get the skin part of
|
Skin skin = null; // The target skin to get the skin part of
|
||||||
|
long before = System.currentTimeMillis();
|
||||||
try {
|
try {
|
||||||
CachedPlayer player = getPlayer(query, false); // Retrieve the player
|
CachedPlayer player = getPlayer(query, false); // Retrieve the player
|
||||||
skin = player.getSkin(); // Use the player's skin
|
skin = player.getSkin(); // Use the player's skin
|
||||||
@ -208,8 +223,13 @@ public final class MojangService {
|
|||||||
}
|
}
|
||||||
if (skin == null) { // Fallback to the default skin
|
if (skin == null) { // Fallback to the default skin
|
||||||
skin = Skin.DEFAULT_STEVE;
|
skin = Skin.DEFAULT_STEVE;
|
||||||
|
log.warn("Failed to get skin for player {}, defaulting to Steve", query);
|
||||||
|
} else {
|
||||||
|
log.info("Got skin for player {} in {}ms", query, System.currentTimeMillis() - before);
|
||||||
}
|
}
|
||||||
|
before = System.currentTimeMillis();
|
||||||
BufferedImage texture = part.render(skin, overlays, size); // Render the skin part
|
BufferedImage texture = part.render(skin, overlays, size); // Render the skin part
|
||||||
|
log.info("Render of skin part took {}ms: {}", System.currentTimeMillis() - before, id);
|
||||||
|
|
||||||
// Convert BufferedImage to byte array
|
// Convert BufferedImage to byte array
|
||||||
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
|
||||||
@ -218,6 +238,7 @@ public final class MojangService {
|
|||||||
|
|
||||||
byte[] bytes = outputStream.toByteArray();
|
byte[] bytes = outputStream.toByteArray();
|
||||||
skinPartTextureCache.save(new CachedSkinPartTexture(id, bytes)); // Cache the texture
|
skinPartTextureCache.save(new CachedSkinPartTexture(id, bytes)); // Cache the texture
|
||||||
|
log.info("Cached skin part texture: {}", id);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -232,15 +253,13 @@ public final class MojangService {
|
|||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @param query the query to search for the player by
|
* @param query the query to search for the player by
|
||||||
* @param bypassCache should the cache be bypassed?
|
|
||||||
* @return the player
|
* @return the player
|
||||||
* @throws BadRequestException if the UUID or username is invalid
|
* @throws BadRequestException if the UUID or username is invalid
|
||||||
* @throws ResourceNotFoundException if the player is not found
|
* @throws ResourceNotFoundException if the player is not found
|
||||||
* @throws MojangRateLimitException if the Mojang API rate limit is reached
|
* @throws MojangRateLimitException if the Mojang API rate limit is reached
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public CachedPlayer getPlayer(@NonNull String query, boolean bypassCache)
|
public CachedPlayer getPlayer(@NonNull String query) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException {
|
||||||
throws BadRequestException, ResourceNotFoundException, MojangRateLimitException {
|
|
||||||
log.info("Requesting player with query: {}", query);
|
log.info("Requesting player with query: {}", query);
|
||||||
|
|
||||||
UUID uuid; // The player UUID to lookup
|
UUID uuid; // The player UUID to lookup
|
||||||
@ -261,13 +280,12 @@ public final class MojangService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check the cache for the player
|
// Check the cache for the player
|
||||||
if (!bypassCache) {
|
// and return it if it's present
|
||||||
Optional<CachedPlayer> cached = playerCache.findById(uuid);
|
Optional<CachedPlayer> cached = playerCache.findById(uuid);
|
||||||
if (cached.isPresent()) { // Respond with the cache if present
|
if (cached.isPresent()) { // Respond with the cache if present
|
||||||
log.info("Found player in cache: {}", uuid);
|
log.info("Found player in cache: {}", uuid);
|
||||||
return cached.get();
|
return cached.get();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Send a request to Mojang requesting
|
// Send a request to Mojang requesting
|
||||||
// the player profile by their UUID
|
// the player profile by their UUID
|
||||||
@ -280,18 +298,14 @@ public final class MojangService {
|
|||||||
ProfileAction[] profileActions = token.getProfileActions();
|
ProfileAction[] profileActions = token.getProfileActions();
|
||||||
|
|
||||||
// Build our player model, cache it, and then return it
|
// Build our player model, cache it, and then return it
|
||||||
CachedPlayer player = new CachedPlayer(
|
CachedPlayer player = new CachedPlayer(uuid, token.getName(),
|
||||||
uuid, token.getName(),
|
|
||||||
skinProperties.getSkin() == null ? Skin.DEFAULT_STEVE : skinProperties.getSkin(),
|
skinProperties.getSkin() == null ? Skin.DEFAULT_STEVE : skinProperties.getSkin(),
|
||||||
skinProperties.getCape(),
|
skinProperties.getCape(), token.getProperties(), profileActions.length == 0 ? null : profileActions,
|
||||||
token.getProperties(),
|
|
||||||
profileActions.length == 0 ? null : profileActions,
|
|
||||||
System.currentTimeMillis()
|
System.currentTimeMillis()
|
||||||
);
|
);
|
||||||
if (!bypassCache) { // Store in the cache
|
// Store in the cache
|
||||||
playerCache.save(player);
|
playerCache.save(player);
|
||||||
log.info("Cached player: {}", uuid);
|
log.info("Cached player: {}", uuid);
|
||||||
}
|
|
||||||
|
|
||||||
player.setCached(-1L); // Set to -1 to indicate it's not cached in the response
|
player.setCached(-1L); // Set to -1 to indicate it's not cached in the response
|
||||||
return player;
|
return player;
|
||||||
|
Loading…
Reference in New Issue
Block a user