diff --git a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java index 1cd166a3..dd6fa9bb 100644 --- a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java +++ b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java @@ -30,7 +30,6 @@ import java.util.Map; import java.util.Set; import java.util.TreeSet; import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.TimeUnit; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; @@ -104,6 +103,7 @@ public class DynmapCore implements DynmapCommonAPI { public ComponentManager componentManager = new ComponentManager(); public DynmapListenerManager listenerManager = new DynmapListenerManager(this); public PlayerFaces playerfacemgr; + public SkinUrlProvider skinUrlProvider; public Events events = new Events(); public String deftemplatesuffix = ""; private DynmapMapCommands dmapcmds = new DynmapMapCommands(); @@ -155,6 +155,10 @@ public class DynmapCore implements DynmapCommonAPI { /* Constructor for core */ public DynmapCore() { } + + public void setSkinUrlProvider(SkinUrlProvider skinUrlProvider) { + this.skinUrlProvider = skinUrlProvider; + } /* Cleanup method */ public void cleanup() { diff --git a/DynmapCore/src/main/java/org/dynmap/PlayerFaces.java b/DynmapCore/src/main/java/org/dynmap/PlayerFaces.java index de0f591c..83da181a 100644 --- a/DynmapCore/src/main/java/org/dynmap/PlayerFaces.java +++ b/DynmapCore/src/main/java/org/dynmap/PlayerFaces.java @@ -106,12 +106,16 @@ public class PlayerFaces { } private class LoadPlayerImages implements Runnable { + private SkinUrlProvider mSkinUrlProvider; public final String playername; public final String playerskinurl; - public LoadPlayerImages(String playername, String playerskinurl, UUID playeruuid) { + + public LoadPlayerImages(String playername, String playerskinurl, UUID playeruuid, SkinUrlProvider skinUrlProvider) { this.playername = playername; this.playerskinurl = playerskinurl; + mSkinUrlProvider = skinUrlProvider; } + public void run() { boolean has_8x8 = storage.hasPlayerFaceImage(playername, FaceType.FACE_8X8); boolean has_16x16 = storage.hasPlayerFaceImage(playername, FaceType.FACE_16X16); @@ -123,16 +127,20 @@ public class PlayerFaces { BufferedImage img = null; try { if(fetchskins && (refreshskins || missing_any)) { - URL url = null; - if (skinurl.equals("") == false) { - url = new URL(skinurl.replace("%player%", URLEncoder.encode(playername, "UTF-8"))); - } - else if (playerskinurl != null) { - url = new URL(playerskinurl); - } - if (url != null) { - img = ImageIO.read(url); /* Load skin for player */ - } + URL url = null; + + if (mSkinUrlProvider == null) { + if (!skinurl.equals("")) { + url = new URL(skinurl.replace("%player%", URLEncoder.encode(playername, "UTF-8"))); + } else if (playerskinurl != null) { + url = new URL(playerskinurl); + } + } else { + url = mSkinUrlProvider.getSkinUrl(playername); + } + + if (url != null) + img = ImageIO.read(url); /* Load skin for player */ } } catch (IOException iox) { Debug.debug("Error loading skin for '" + playername + "' - " + iox); @@ -292,7 +300,7 @@ public class PlayerFaces { core.listenerManager.addListener(EventType.PLAYER_JOIN, new PlayerEventListener() { @Override public void playerEvent(DynmapPlayer p) { - Runnable job = new LoadPlayerImages(p.getName(), p.getSkinURL(), p.getUUID()); + Runnable job = new LoadPlayerImages(p.getName(), p.getSkinURL(), p.getUUID(), core.skinUrlProvider); if(fetchskins) MapManager.scheduleDelayedJob(job, 0); else diff --git a/DynmapCore/src/main/java/org/dynmap/SkinUrlProvider.java b/DynmapCore/src/main/java/org/dynmap/SkinUrlProvider.java new file mode 100644 index 00000000..bdb4e53d --- /dev/null +++ b/DynmapCore/src/main/java/org/dynmap/SkinUrlProvider.java @@ -0,0 +1,7 @@ +package org.dynmap; + +import java.net.URL; + +public interface SkinUrlProvider { + URL getSkinUrl(String playerName); +} \ No newline at end of file diff --git a/spigot/build.gradle b/spigot/build.gradle index 33c63e82..532c389b 100644 --- a/spigot/build.gradle +++ b/spigot/build.gradle @@ -5,6 +5,9 @@ repositories { maven { url 'https://jitpack.io' } + maven { + url 'https://repo.codemc.org/repository/maven-snapshots' + } } dependencies { @@ -41,6 +44,7 @@ dependencies { implementation(project(':bukkit-helper-115')) { transitive = false } + implementation 'com:skinsrestorer:13.7.5-20191221.213031-7@jar' } processResources { diff --git a/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java b/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java index 03e652c4..579fc4f1 100644 --- a/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java +++ b/spigot/src/main/java/org/dynmap/bukkit/DynmapPlugin.java @@ -105,6 +105,7 @@ import org.dynmap.renderer.DynmapBlockState; import org.dynmap.utils.MapChunkCache; import org.dynmap.utils.Polygon; import org.dynmap.utils.VisibilityLimit; +import skinsrestorer.bukkit.SkinsRestorer; public class DynmapPlugin extends JavaPlugin implements DynmapAPI { private DynmapCore core; @@ -896,6 +897,20 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI { this.setEnabled(false); return; } + + /* Skins support via SkinsRestorer */ + SkinsRestorer skinsRestorer = null; + + if (core.configuration.getBoolean("skinsrestorer-integration", false)) + skinsRestorer = JavaPlugin.getPlugin(SkinsRestorer.class); + + SkinsRestorerSkinUrlProvider skinUrlProvider = null; + + if (skinsRestorer != null) + skinUrlProvider = new SkinsRestorerSkinUrlProvider(skinsRestorer); + + core.setSkinUrlProvider(skinUrlProvider); + /* See if we need to wait before enabling core */ if(!readyToEnable()) { Listener pl = new Listener() { diff --git a/spigot/src/main/java/org/dynmap/bukkit/SkinsRestorerSkinUrlProvider.java b/spigot/src/main/java/org/dynmap/bukkit/SkinsRestorerSkinUrlProvider.java new file mode 100644 index 00000000..54fa98dc --- /dev/null +++ b/spigot/src/main/java/org/dynmap/bukkit/SkinsRestorerSkinUrlProvider.java @@ -0,0 +1,58 @@ +package org.dynmap.bukkit; + +import org.dynmap.SkinUrlProvider; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.json.simple.JSONObject; +import skinsrestorer.bukkit.SkinsRestorer; +import skinsrestorer.bukkit.SkinsRestorerBukkitAPI; +import skinsrestorer.shared.utils.ReflectionUtil; + +import java.net.MalformedURLException; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class SkinsRestorerSkinUrlProvider implements SkinUrlProvider { + private JSONParser mJsonParser; + private SkinsRestorerBukkitAPI mSkinsRestorerApi; + + SkinsRestorerSkinUrlProvider(SkinsRestorer skinsRestorer) { + mJsonParser = new JSONParser(); + mSkinsRestorerApi = skinsRestorer.getSkinsRestorerBukkitAPI(); + } + + @Override + public URL getSkinUrl(String playerName) { + String skinName = mSkinsRestorerApi.getSkinName(playerName); + + Object skinDataProperty = mSkinsRestorerApi.getSkinData(skinName == null ? playerName : skinName); + String skinDataPropertyValue; + + try { + skinDataPropertyValue = (String) ReflectionUtil.invokeMethod(skinDataProperty, "getValue"); + } catch (Exception ex) { + ex.printStackTrace(); + return null; + } + + byte[] skinDataBytes = Base64.getDecoder().decode(skinDataPropertyValue); + + JSONObject skinData; + + try { + skinData = (JSONObject) mJsonParser.parse(new String(skinDataBytes, StandardCharsets.UTF_8)); + } catch (ParseException ex) { + ex.printStackTrace(); + return null; + } + + try { + return new URL((String) ((JSONObject) ((JSONObject) skinData.get("textures")).get("SKIN")).get("url")); + } catch (MalformedURLException ex) { + ex.printStackTrace(); + } + + return null; + } +} \ No newline at end of file diff --git a/spigot/src/main/resources/configuration.txt b/spigot/src/main/resources/configuration.txt index 9de84ea6..68b42d43 100644 --- a/spigot/src/main/resources/configuration.txt +++ b/spigot/src/main/resources/configuration.txt @@ -280,6 +280,9 @@ custom-colors-support: true # Customize URL used for fetching player skins (%player% is macro for name) skin-url: "http://skins.minecraft.net/MinecraftSkins/%player%.png" +# Enable skins via SkinsRestorer plugin instead of internal legacy implementation (disabled by default) +#skinsrestorer-integration: true + render-triggers: #- playermove #- playerjoin diff --git a/spigot/src/main/resources/plugin.yml b/spigot/src/main/resources/plugin.yml index a1e91eff..26d415fb 100644 --- a/spigot/src/main/resources/plugin.yml +++ b/spigot/src/main/resources/plugin.yml @@ -3,7 +3,7 @@ main: org.dynmap.bukkit.DynmapPlugin version: "${version}-${buildnumber}" authors: [mikeprimm] website: "https://www.reddit.com/r/Dynmap/" -softdepend: [ Permissions, PermissionEx, bPermissions, PermissionsBukkit, GroupManager, LuckPerms, Vault ] +softdepend: [ Permissions, PermissionEx, bPermissions, PermissionsBukkit, GroupManager, LuckPerms, Vault, SkinsRestorer ] commands: dynmap: description: Controls Dynmap.