From 0890a151ebca13ba0fdf8d716568d8cde4a1e873 Mon Sep 17 00:00:00 2001 From: Rainnny7 Date: Thu, 11 Apr 2024 05:32:31 -0400 Subject: [PATCH] Cleanup --- Dockerfile | 3 + .../braydon/mc/common/EnvironmentUtils.java | 42 ++++++++++++++ .../mc/controller/PlayerController.java | 2 +- .../me/braydon/mc/service/MojangService.java | 58 ++++++++++++------- 4 files changed, 82 insertions(+), 23 deletions(-) create mode 100644 src/main/java/me/braydon/mc/common/EnvironmentUtils.java diff --git a/Dockerfile b/Dockerfile index 8fe8c31..1f0f412 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,5 +15,8 @@ ENV HOSTNAME "0.0.0.0" EXPOSE 80 ENV PORT 80 +# We're running in production +ENV APP_ENV "production" + # Start the app CMD ["java", "-jar", "target/RESTfulMC.jar"] \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/common/EnvironmentUtils.java b/src/main/java/me/braydon/mc/common/EnvironmentUtils.java new file mode 100644 index 0000000..ef4c2eb --- /dev/null +++ b/src/main/java/me/braydon/mc/common/EnvironmentUtils.java @@ -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")); + } +} \ No newline at end of file diff --git a/src/main/java/me/braydon/mc/controller/PlayerController.java b/src/main/java/me/braydon/mc/controller/PlayerController.java index 0fcf4e2..94f5394 100644 --- a/src/main/java/me/braydon/mc/controller/PlayerController.java +++ b/src/main/java/me/braydon/mc/controller/PlayerController.java @@ -73,7 +73,7 @@ public final class PlayerController { public ResponseEntity getPlayer( @Parameter(description = "The player username or UUID to get", example = "Rainnny") @PathVariable @NonNull String query ) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException { - return ResponseEntity.ofNullable(mojangService.getPlayer(query, false)); + return ResponseEntity.ofNullable(mojangService.getPlayer(query)); } /** diff --git a/src/main/java/me/braydon/mc/service/MojangService.java b/src/main/java/me/braydon/mc/service/MojangService.java index f563710..5bfde88 100644 --- a/src/main/java/me/braydon/mc/service/MojangService.java +++ b/src/main/java/me/braydon/mc/service/MojangService.java @@ -168,10 +168,15 @@ public final class MojangService { @SneakyThrows public byte[] getSkinPartTexture(@NonNull String partName, @NonNull String query, @NonNull String extension, 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 ISkinPart part = ISkinPart.getByName(partName); // The skin part to get if (part == null) { // Default to the face part = ISkinPart.Vanilla.FACE; + log.warn("Invalid skin part {}, defaulting to {}", partName, part.name()); } // Ensure the extension is valid @@ -190,16 +195,26 @@ public final class MojangService { } if (size == null || size <= 0) { // Invalid 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 - Optional cached = skinPartTextureCache.findById(id); // Get the cached texture - if (cached.isPresent()) { // Respond with the cache if present -// return cached.get().getTexture(); + // In production, check the cache for the + // skin part and return it if it's present + if (EnvironmentUtils.isProduction()) { + Optional cached = skinPartTextureCache.findById(id); + if (cached.isPresent()) { // Respond with the cache if present + 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 + long before = System.currentTimeMillis(); try { CachedPlayer player = getPlayer(query, false); // Retrieve the player skin = player.getSkin(); // Use the player's skin @@ -208,8 +223,13 @@ public final class MojangService { } if (skin == null) { // Fallback to the default skin 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 + log.info("Render of skin part took {}ms: {}", System.currentTimeMillis() - before, id); // Convert BufferedImage to byte array try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { @@ -218,6 +238,7 @@ public final class MojangService { byte[] bytes = outputStream.toByteArray(); skinPartTextureCache.save(new CachedSkinPartTexture(id, bytes)); // Cache the texture + log.info("Cached skin part texture: {}", id); return bytes; } } @@ -232,15 +253,13 @@ public final class MojangService { *

* * @param query the query to search for the player by - * @param bypassCache should the cache be bypassed? * @return the player * @throws BadRequestException if the UUID or username is invalid * @throws ResourceNotFoundException if the player is not found * @throws MojangRateLimitException if the Mojang API rate limit is reached */ @NonNull - public CachedPlayer getPlayer(@NonNull String query, boolean bypassCache) - throws BadRequestException, ResourceNotFoundException, MojangRateLimitException { + public CachedPlayer getPlayer(@NonNull String query) throws BadRequestException, ResourceNotFoundException, MojangRateLimitException { log.info("Requesting player with query: {}", query); UUID uuid; // The player UUID to lookup @@ -261,12 +280,11 @@ public final class MojangService { } // Check the cache for the player - if (!bypassCache) { - Optional cached = playerCache.findById(uuid); - if (cached.isPresent()) { // Respond with the cache if present - log.info("Found player in cache: {}", uuid); - return cached.get(); - } + // and return it if it's present + Optional cached = playerCache.findById(uuid); + if (cached.isPresent()) { // Respond with the cache if present + log.info("Found player in cache: {}", uuid); + return cached.get(); } // Send a request to Mojang requesting @@ -280,18 +298,14 @@ public final class MojangService { ProfileAction[] profileActions = token.getProfileActions(); // Build our player model, cache it, and then return it - CachedPlayer player = new CachedPlayer( - uuid, token.getName(), + CachedPlayer player = new CachedPlayer(uuid, token.getName(), skinProperties.getSkin() == null ? Skin.DEFAULT_STEVE : skinProperties.getSkin(), - skinProperties.getCape(), - token.getProperties(), - profileActions.length == 0 ? null : profileActions, + skinProperties.getCape(), token.getProperties(), profileActions.length == 0 ? null : profileActions, System.currentTimeMillis() ); - if (!bypassCache) { // Store in the cache - playerCache.save(player); - log.info("Cached player: {}", uuid); - } + // Store in the cache + playerCache.save(player); + log.info("Cached player: {}", uuid); player.setCached(-1L); // Set to -1 to indicate it's not cached in the response return player;