From 0297fd3aea293bac6ec6c40d2735dc7c603cb004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20Ellil=C3=A4?= Date: Wed, 21 Aug 2024 01:15:56 +0300 Subject: [PATCH] fix(bluemap/auth): force fetch playerhead --- .../auth/bluemap/integration/BMSkin.java | 92 +++++++++++++++++++ .../BlueMapAuthIntegrationPlugin.java | 24 +++++ .../src/main/resources/bluemap-integration.js | 4 +- 3 files changed, 118 insertions(+), 2 deletions(-) create mode 100644 BlueMap/Integration/src/main/java/codes/antti/auth/bluemap/integration/BMSkin.java diff --git a/BlueMap/Integration/src/main/java/codes/antti/auth/bluemap/integration/BMSkin.java b/BlueMap/Integration/src/main/java/codes/antti/auth/bluemap/integration/BMSkin.java new file mode 100644 index 0000000..47b4b09 --- /dev/null +++ b/BlueMap/Integration/src/main/java/codes/antti/auth/bluemap/integration/BMSkin.java @@ -0,0 +1,92 @@ +/* + * This file is part of BMUtils, licensed under the MPL2 License (MPL). + * Please keep tabs on https://github.com/TechnicJelle/BMUtils for updates. + * + * Copyright (c) TechnicJelle + * Copyright (c) contributors + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +package codes.antti.auth.bluemap.integration; + +import de.bluecolored.bluemap.api.BlueMapAPI; +import de.bluecolored.bluemap.api.BlueMapMap; +import de.bluecolored.bluemap.api.plugin.SkinProvider; +import org.jetbrains.annotations.NotNull; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Optional; +import java.util.UUID; + +/** + * Utility functions for BlueMap skins and playerheads + */ +public class BMSkin { + private BMSkin() { + throw new IllegalStateException("Utility class"); + } + + private static final String FALLBACK_ICON = "assets/steve.png"; + + /** + * Gets the URL to a player head icon for a specific map.
+ * If the icon doesn't exist yet, it will be created. + * + * @param blueMapAPI The BlueMapAPI instance + * @param playerUUID The player to get the head of + * @param blueMapMap The map to get the head for (each map has its own playerheads folder) + * @return The URL to the player head, relative to BlueMap's web root,
+ * or a Steve head if the head couldn't be found + */ + public static String getPlayerHeadIconAddress( + final @NotNull BlueMapAPI blueMapAPI, + final @NotNull UUID playerUUID, + final @NotNull BlueMapMap blueMapMap + ) { + final String assetName = "playerheads/" + playerUUID + ".png"; + + try { + if (!blueMapMap.getAssetStorage().assetExists(assetName)) { + createPlayerHead(blueMapAPI, playerUUID, assetName, blueMapMap); + } + } catch (IOException e) { + return FALLBACK_ICON; + } + + return blueMapMap.getAssetStorage().getAssetUrl(assetName); + } + + /** + * For when BlueMap doesn't have an icon for this player yet, so we need to make it create one. + */ + private static void createPlayerHead( + final @NotNull BlueMapAPI blueMapAPI, + final @NotNull UUID playerUUID, + final @NotNull String assetName, + final @NotNull BlueMapMap map + ) throws IOException { + final SkinProvider skinProvider = blueMapAPI.getPlugin().getSkinProvider(); + try { + final Optional oImgSkin = skinProvider.load(playerUUID); + if (oImgSkin.isEmpty()) { + throw new IOException(playerUUID + " doesn't have a skin"); + } + + try (OutputStream out = map.getAssetStorage().writeAsset(assetName)) { + final BufferedImage head = blueMapAPI.getPlugin().getPlayerMarkerIconFactory() + .apply(playerUUID, oImgSkin.get()); + ImageIO.write(head, "png", out); + } catch (IOException e) { + throw new IOException("Failed to write " + playerUUID + "'s head to asset-storage", e); + } + } catch (IOException e) { + throw new IOException("Failed to load skin for player " + playerUUID, e); + } + } +} diff --git a/BlueMap/Integration/src/main/java/codes/antti/auth/bluemap/integration/BlueMapAuthIntegrationPlugin.java b/BlueMap/Integration/src/main/java/codes/antti/auth/bluemap/integration/BlueMapAuthIntegrationPlugin.java index 62eded7..0c49194 100644 --- a/BlueMap/Integration/src/main/java/codes/antti/auth/bluemap/integration/BlueMapAuthIntegrationPlugin.java +++ b/BlueMap/Integration/src/main/java/codes/antti/auth/bluemap/integration/BlueMapAuthIntegrationPlugin.java @@ -3,6 +3,7 @@ import codes.antti.auth.common.http.WebServer; import com.google.gson.JsonObject; import de.bluecolored.bluemap.api.BlueMapAPI; +import de.bluecolored.bluemap.api.BlueMapMap; import org.bukkit.configuration.Configuration; import org.bukkit.plugin.java.JavaPlugin; @@ -13,6 +14,7 @@ import java.nio.file.Path; import java.nio.file.StandardOpenOption; import java.util.Objects; +import java.util.Optional; import java.util.UUID; public final class BlueMapAuthIntegrationPlugin extends JavaPlugin { @@ -54,6 +56,28 @@ public void onEnable() { request.respond(200); }); + this.http.get("/playerheads/*", request -> { + String[] parts = request.getPath().split("/"); + if (parts.length != 4) { + request.respond(400); + return; + } + String mapId = parts[2]; + UUID uuid; + try { + uuid = UUID.fromString(parts[3]); + } catch (IllegalArgumentException ex) { + request.respond(400); + return; + } + Optional map = api.getMap(mapId); + if (map.isEmpty()) { + request.respond(404); + return; + } + request.redirect(301, "../../../../" + BMSkin.getPlayerHeadIconAddress(api, uuid, map.get())); + }); + this.http.start(); getLogger().info("Webserver bound to " + this.http.getAddress()); diff --git a/BlueMap/Integration/src/main/resources/bluemap-integration.js b/BlueMap/Integration/src/main/resources/bluemap-integration.js index 5cb0315..62e94dc 100644 --- a/BlueMap/Integration/src/main/resources/bluemap-integration.js +++ b/BlueMap/Integration/src/main/resources/bluemap-integration.js @@ -42,7 +42,7 @@ void async function() { userTemplate.innerHTML = `
Your playerhead ${username} @@ -60,7 +60,7 @@ void async function() { if (uuid) { buttonList.appendChild(logOutButton); buttonList.appendChild(logOutAllButton); - userElement.children.item(0).src = `./maps/${window.bluemap.mapViewer.data.map.id}/assets/playerheads/${uuid}.png` + userElement.children.item(0).src = `./addons/integration/playerheads/${window.bluemap.mapViewer.data.map.id}/${uuid}` buttonList.insertBefore(userElement, buttonList.childNodes[0]); } else { buttonList.appendChild(logInButton);