package com.playerblocklife; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import org.bukkit.Bukkit; import org.bukkit.OfflinePlayer; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.SkullMeta; import org.bukkit.profile.PlayerProfile; import org.bukkit.profile.PlayerTextures; import org.yaml.snakeyaml.Yaml; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.*; import java.net.URL; import java.nio.file.Files; import java.util.*; import java.util.concurrent.ConcurrentHashMap; public class SkinManager { private final PlayerBlockLife plugin; private final Map playerSkinData = new ConcurrentHashMap<>(); private final Map playerCustomModelData = new ConcurrentHashMap<>(); private final Map skinLoadTime = new ConcurrentHashMap<>(); private final File skinDataFile; private final File skinCacheDir; private int nextCustomModelData = 1000; private static final long SKIN_CACHE_TIME = 7 * 24 * 60 * 60 * 1000L; public SkinManager(PlayerBlockLife plugin) { this.plugin = plugin; this.skinDataFile = new File(plugin.getDataFolder(), "skindata.yml"); this.skinCacheDir = new File(plugin.getDataFolder(), "skincache"); if (!skinCacheDir.exists()) { skinCacheDir.mkdirs(); } } public void loadAllSkins() { loadSkinDataFromFile(); Bukkit.getOnlinePlayers().forEach(this::loadPlayerSkinAsync); } public void loadPlayerSkinAsync(Player player) { UUID playerId = player.getUniqueId(); if (playerSkinData.containsKey(playerId)) { return; } if (loadSkinFromCache(playerId)) { plugin.logInfo("从缓存加载皮肤: " + player.getName()); return; } Bukkit.getScheduler().runTaskAsynchronously(plugin, () -> { try { plugin.logInfo("开始加载皮肤: " + player.getName()); String skinBase64 = null; // 检查是否使用SkinsRestorer插件 if (plugin.getConfigManager().useSkinsRestorer()) { skinBase64 = getSkinFromSkinsRestorer(player); } // 如果SkinsRestorer未启用或获取失败,使用PlayerProfile if (skinBase64 == null) { skinBase64 = getSkinFromPlayerProfile(player); } if (skinBase64 == null) { skinBase64 = getDefaultSteveSkin(); plugin.logWarning("使用默认皮肤: " + player.getName()); } if (skinBase64 != null) { playerSkinData.put(playerId, skinBase64); int modelData = allocateCustomModelData(playerId); saveSkinToCache(playerId, skinBase64); skinLoadTime.put(playerId, System.currentTimeMillis()); plugin.logInfo("皮肤加载完成: " + player.getName() + " (模型数据: " + modelData + ")"); Bukkit.getScheduler().runTask(plugin, () -> { if (player.isOnline()) { player.sendMessage("§a你的皮肤方块已准备就绪!"); } }); } } catch (Exception e) { plugin.logError("加载皮肤失败: " + player.getName(), e); } }); } private String getSkinFromSkinsRestorer(Player player) { try { // 检查SkinsRestorer插件是否存在 if (Bukkit.getPluginManager().getPlugin("SkinsRestorer") == null) { plugin.logInfo("SkinsRestorer插件未安装,跳过从SkinsRestorer获取皮肤"); return null; } plugin.logInfo("尝试从SkinsRestorer获取皮肤: " + player.getName()); // 使用反射调用SkinsRestorer API Class skinsRestorerClass = Class.forName("net.skinsrestorer.api.SkinsRestorerAPI"); Object skinsRestorerAPI = skinsRestorerClass.getMethod("getApi").invoke(null); // 获取玩家皮肤数据 Class skinDataClass = Class.forName("net.skinsrestorer.api.property.SkinProperty"); Object skinProperty = skinsRestorerAPI.getClass().getMethod("getSkinData", String.class) .invoke(skinsRestorerAPI, player.getName()); if (skinProperty != null) { String value = (String) skinProperty.getClass().getMethod("getValue").invoke(skinProperty); String signature = (String) skinProperty.getClass().getMethod("getSignature").invoke(skinProperty); plugin.logInfo("成功从SkinsRestorer获取皮肤: " + player.getName()); return player.getUniqueId().toString(); } } catch (ClassNotFoundException e) { plugin.logWarning("SkinsRestorer API类未找到,插件可能未安装: " + e.getMessage()); } catch (Exception e) { plugin.logWarning("从SkinsRestorer获取皮肤失败: " + e.getMessage()); } return null; } private String getSkinFromPlayerProfile(Player player) { try { PlayerProfile profile = player.getPlayerProfile(); PlayerTextures textures = profile.getTextures(); URL skinUrl = textures.getSkin(); if (skinUrl != null) { BufferedImage skinImage = ImageIO.read(skinUrl); if (skinImage == null) { return null; } int headX = 8; int headY = 8; int headWidth = 8; int headHeight = 8; if (skinImage.getWidth() >= headX + headWidth && skinImage.getHeight() >= headY + headHeight) { BufferedImage headImage = skinImage.getSubimage(headX, headY, headWidth, headHeight); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(headImage, "PNG", baos); byte[] imageBytes = baos.toByteArray(); JsonObject textureJson = new JsonObject(); JsonObject timestampJson = new JsonObject(); return player.getUniqueId().toString(); } } } catch (Exception e) { plugin.logWarning("从PlayerProfile获取皮肤失败: " + e.getMessage()); } return null; } private boolean loadSkinFromCache(UUID playerId) { try { File cacheFile = new File(skinCacheDir, playerId.toString() + ".cache"); if (cacheFile.exists()) { long lastModified = cacheFile.lastModified(); if (System.currentTimeMillis() - lastModified < SKIN_CACHE_TIME) { String skinData = new String(Files.readAllBytes(cacheFile.toPath())); playerSkinData.put(playerId, skinData); allocateCustomModelData(playerId); return true; } } } catch (Exception e) { plugin.logWarning("读取皮肤缓存失败: " + e.getMessage()); } return false; } private void saveSkinToCache(UUID playerId, String skinData) { try { File cacheFile = new File(skinCacheDir, playerId.toString() + ".cache"); Files.write(cacheFile.toPath(), skinData.getBytes()); } catch (Exception e) { plugin.logWarning("保存皮肤缓存失败: " + e.getMessage()); } } private synchronized int allocateCustomModelData(UUID playerId) { if (playerCustomModelData.containsKey(playerId)) { return playerCustomModelData.get(playerId); } int modelData = nextCustomModelData++; playerCustomModelData.put(playerId, modelData); return modelData; } private String getDefaultSteveSkin() { return "8667ba71-b85a-4004-af54-457a9734eed7"; } public ItemStack createPlayerHead(UUID playerId, String playerName) { ItemStack head = new ItemStack(org.bukkit.Material.PLAYER_HEAD); SkullMeta meta = (SkullMeta) head.getItemMeta(); if (meta != null) { OfflinePlayer offlinePlayer = Bukkit.getOfflinePlayer(playerId); meta.setOwningPlayer(offlinePlayer); Integer customModelData = playerCustomModelData.get(playerId); if (customModelData != null) { meta.setCustomModelData(customModelData); } meta.setDisplayName("§e" + playerName + "的生命方块"); List lore = new ArrayList<>(); lore.add("§7所有者: §e" + playerName); lore.add("§c⚠ 警告: 挖掘此方块将减少玩家生命值"); lore.add("§7剩余生命: §a" + plugin.getBlockManager().getRemainingBlocks(playerId) + "/5"); meta.setLore(lore); head.setItemMeta(meta); } return head; } public boolean isSkinLoaded(UUID playerId) { return playerSkinData.containsKey(playerId); } public Integer getCustomModelData(UUID playerId) { return playerCustomModelData.get(playerId); } public Map getAllSkinData() { return new HashMap<>(playerSkinData); } public Map getAllCustomModelData() { return new HashMap<>(playerCustomModelData); } private void loadSkinDataFromFile() { if (!skinDataFile.exists()) { return; } try { Yaml yaml = new Yaml(); Map data = yaml.load(new FileReader(skinDataFile)); if (data != null && data.containsKey("skins")) { Map skins = (Map) data.get("skins"); for (Map.Entry entry : skins.entrySet()) { UUID playerId = UUID.fromString(entry.getKey()); String skinData = (String) entry.getValue(); playerSkinData.put(playerId, skinData); } } if (data != null && data.containsKey("modelData")) { Map modelData = (Map) data.get("modelData"); for (Map.Entry entry : modelData.entrySet()) { UUID playerId = UUID.fromString(entry.getKey()); playerCustomModelData.put(playerId, entry.getValue()); nextCustomModelData = Math.max(nextCustomModelData, entry.getValue() + 1); } } plugin.logInfo("已加载 " + playerSkinData.size() + " 个玩家的皮肤数据"); } catch (Exception e) { plugin.logError("加载皮肤数据文件失败", e); } } public void saveSkinData() { try { Map data = new HashMap<>(); Map skins = new HashMap<>(); for (Map.Entry entry : playerSkinData.entrySet()) { skins.put(entry.getKey().toString(), entry.getValue()); } data.put("skins", skins); Map modelData = new HashMap<>(); for (Map.Entry entry : playerCustomModelData.entrySet()) { modelData.put(entry.getKey().toString(), entry.getValue()); } data.put("modelData", modelData); Yaml yaml = new Yaml(); yaml.dump(data, new FileWriter(skinDataFile)); plugin.logInfo("皮肤数据已保存"); } catch (Exception e) { plugin.logError("保存皮肤数据失败", e); } } public void cleanupOldCache() { File[] cacheFiles = skinCacheDir.listFiles(); if (cacheFiles != null) { long now = System.currentTimeMillis(); int cleaned = 0; for (File file : cacheFiles) { if (now - file.lastModified() > SKIN_CACHE_TIME) { if (file.delete()) { cleaned++; } } } if (cleaned > 0) { plugin.logInfo("清理了 " + cleaned + " 个过期皮肤缓存"); } } } }