/* * This file is part of BlueMap, licensed under the MIT License (MIT). * * Copyright (c) Blue (Lukas Rieger) * Copyright (c) contributors * * 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 de.bluecolored.bluemap.common.plugin.skins; import de.bluecolored.bluemap.api.debug.DebugDump; import de.bluecolored.bluemap.api.plugin.PlayerIconFactory; import de.bluecolored.bluemap.api.plugin.SkinProvider; import de.bluecolored.bluemap.common.plugin.Plugin; import de.bluecolored.bluemap.common.serverinterface.ServerEventListener; import de.bluecolored.bluemap.core.BlueMap; import de.bluecolored.bluemap.core.logger.Logger; import de.bluecolored.bluemap.core.map.BmMap; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.IOException; import java.io.OutputStream; import java.util.Map; import java.util.Objects; import java.util.UUID; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @DebugDump public class PlayerSkinUpdater implements ServerEventListener { private final Plugin plugin; private final Map skinUpdates; private SkinProvider skinProvider; private PlayerIconFactory playerMarkerIconFactory; public PlayerSkinUpdater(Plugin plugin) { this.plugin = plugin; this.skinUpdates = new ConcurrentHashMap<>(); skinProvider = new MojangSkinProvider(); playerMarkerIconFactory = new DefaultPlayerIconFactory(); } public CompletableFuture updateSkin(final UUID playerUuid) { // only update if last update was longer then an hour ago long lastUpdate = skinUpdates.getOrDefault(playerUuid, 0L); long now = System.currentTimeMillis(); if (now - lastUpdate < TimeUnit.HOURS.toMillis(1)) return CompletableFuture.completedFuture(null); skinUpdates.put(playerUuid, now); // do the update async return CompletableFuture.supplyAsync(() -> { try { return skinProvider.load(playerUuid); } catch (IOException e) { throw new CompletionException("The skin provider threw an exception while loading the skin for UUID: '" + playerUuid + "'!", e); } }, BlueMap.THREAD_POOL).thenAcceptAsync(skin -> { if (skin.isEmpty()) { Logger.global.logDebug("No player-skin provided for UUID: " + playerUuid); return; } Map maps = plugin.getBlueMap().getMaps(); if (maps == null) { Logger.global.logDebug("Could not update skin, since the plugin seems not to be ready."); return; } BufferedImage playerHead = playerMarkerIconFactory.apply(playerUuid, skin.get()); for (BmMap map : maps.values()) { try (OutputStream out = map.getStorage().writeMeta(map.getId(), "assets/playerheads/" + playerUuid + ".png")) { ImageIO.write(playerHead, "png", out); } catch (IOException ex) { Logger.global.logError("Failed to write player skin to storage: " + playerUuid, ex); } } }, BlueMap.THREAD_POOL); } @Override public void onPlayerJoin(UUID playerUuid) { updateSkin(playerUuid).exceptionally(ex -> { Logger.global.logError("Failed to update player skin: " + playerUuid, ex); return null; }); } public SkinProvider getSkinProvider() { return skinProvider; } public void setSkinProvider(SkinProvider skinProvider) { this.skinProvider = Objects.requireNonNull(skinProvider, "skinProvider can not be null"); } public PlayerIconFactory getPlayerMarkerIconFactory() { return playerMarkerIconFactory; } public void setPlayerMarkerIconFactory(PlayerIconFactory playerMarkerIconFactory) { this.playerMarkerIconFactory = Objects.requireNonNull(playerMarkerIconFactory, "playerMarkerIconFactory can not be null"); } }