From daabbfdb2ae8ed65daae8c0e236827072506b83b Mon Sep 17 00:00:00 2001 From: OliverSchlueter Date: Mon, 5 Aug 2024 20:42:08 +0200 Subject: [PATCH] Rework how skins are handled --- .../java/de/oliver/fancynpcs/api/NpcData.java | 8 +- .../fancynpcs/api/utils/SkinFetcher.java | 211 +++++++++++------- .../oliver/fancynpcs/v1_19_4/Npc_1_19_4.java | 4 +- .../oliver/fancynpcs/v1_20_1/Npc_1_20_1.java | 4 +- .../oliver/fancynpcs/v1_20_2/Npc_1_20_2.java | 4 +- .../oliver/fancynpcs/v1_20_4/Npc_1_20_4.java | 4 +- .../oliver/fancynpcs/v1_20_6/Npc_1_20_6.java | 4 +- .../de/oliver/fancynpcs/v1_21/Npc_1_21.java | 4 +- .../de/oliver/fancynpcs/NpcManagerImpl.java | 12 +- .../fancynpcs/commands/npc/SkinCMD.java | 88 ++------ 10 files changed, 173 insertions(+), 170 deletions(-) diff --git a/api/src/main/java/de/oliver/fancynpcs/api/NpcData.java b/api/src/main/java/de/oliver/fancynpcs/api/NpcData.java index 2cbadee9..802296bb 100644 --- a/api/src/main/java/de/oliver/fancynpcs/api/NpcData.java +++ b/api/src/main/java/de/oliver/fancynpcs/api/NpcData.java @@ -17,7 +17,7 @@ public class NpcData { private final String name; private final UUID creator; private String displayName; - private SkinFetcher skin; + private SkinFetcher.SkinData skin; private Location location; private boolean showInTab; private boolean spawnEntity; @@ -43,7 +43,7 @@ public NpcData( String name, UUID creator, String displayName, - SkinFetcher skin, + SkinFetcher.SkinData skin, Location location, boolean showInTab, boolean spawnEntity, @@ -141,11 +141,11 @@ public NpcData setDisplayName(String displayName) { return this; } - public SkinFetcher getSkin() { + public SkinFetcher.SkinData getSkin() { return skin; } - public NpcData setSkin(SkinFetcher skin) { + public NpcData setSkin(SkinFetcher.SkinData skin) { this.skin = skin; isDirty = true; return this; diff --git a/api/src/main/java/de/oliver/fancynpcs/api/utils/SkinFetcher.java b/api/src/main/java/de/oliver/fancynpcs/api/utils/SkinFetcher.java index b4474aed..9e183262 100644 --- a/api/src/main/java/de/oliver/fancynpcs/api/utils/SkinFetcher.java +++ b/api/src/main/java/de/oliver/fancynpcs/api/utils/SkinFetcher.java @@ -3,122 +3,165 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; +import de.oliver.fancylib.UUIDFetcher; +import de.oliver.fancynpcs.api.FancyNpcsPlugin; +import me.dave.chatcolorhandler.ChatColorHandler; +import me.dave.chatcolorhandler.parsers.custom.PlaceholderAPIParser; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.DataOutputStream; +import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; -import java.util.HashMap; -import java.util.Map; -import java.util.Scanner; +import java.util.*; -public class SkinFetcher { - public static Map skinCache = new HashMap<>(); +public final class SkinFetcher { + public static Map skinCache = new HashMap<>(); // identifier -> skinData - private final SkinType skinType; - private final String identifier; // uuid or url - private String value; - private String signature; - private boolean loaded; - - public SkinFetcher(String identifier) { - this.skinType = SkinType.getType(identifier); - this.identifier = identifier; + private SkinFetcher() { + } + /** + * Fetches the skin data from the Mojang API. + * + * @param identifier The identifier of the skin. This can be a UUID, username, URL or a placeholder by PAPI. + * @throws IOException If the skin data could not be fetched. + */ + public static SkinData fetchSkin(String identifier) throws IOException { if (skinCache.containsKey(identifier)) { - SkinFetcher cached = skinCache.get(identifier); - this.value = cached.getValue(); - this.signature = cached.getSignature(); - this.loaded = true; - return; + return skinCache.get(identifier); } - this.loaded = false; - load(); - } - - public SkinFetcher(String identifier, String value, String signature) { - this.skinType = SkinType.getType(identifier); - this.identifier = identifier; - this.value = value; - this.signature = signature; - this.loaded = true; - } + if (isPlaceholder(identifier)) { + String parsedIdentifier = ChatColorHandler.translate(identifier, List.of(PlaceholderAPIParser.class)); + return fetchSkin(parsedIdentifier); + } + if (isURL(identifier)) { + return fetchSkinByURL(identifier); + } - public void load() { - this.loaded = false; - try { - URL url = new URL(skinType.getRequestUrl().replace("{uuid}", identifier)); - HttpURLConnection conn = (HttpURLConnection) url.openConnection(); - conn.setRequestMethod(skinType.getRequestMethod()); - if (skinType == SkinType.URL) { - conn.setDoOutput(true); - DataOutputStream outputStream = new DataOutputStream(conn.getOutputStream()); - outputStream.writeBytes("url=" + URLEncoder.encode(identifier, StandardCharsets.UTF_8)); - outputStream.close(); - } + if (isUUID(identifier)) { + return fetchSkinByUUID(identifier); + } - String json = new Scanner(conn.getInputStream(), StandardCharsets.UTF_8).useDelimiter("\\A").next(); - JsonParser parser = new JsonParser(); - JsonObject obj = parser.parse(json).getAsJsonObject(); - if (skinType == SkinType.UUID) { - this.value = obj.getAsJsonArray("properties").get(0).getAsJsonObject().getAsJsonPrimitive("value").getAsString(); - this.signature = obj.getAsJsonArray("properties").get(0).getAsJsonObject().getAsJsonPrimitive("signature").getAsString(); - } else if (skinType == SkinType.URL) { - this.value = obj.getAsJsonObject("data").getAsJsonObject("texture").getAsJsonPrimitive("value").getAsString(); - this.signature = obj.getAsJsonObject("data").getAsJsonObject("texture").getAsJsonPrimitive("signature").getAsString(); - } - this.loaded = true; - skinCache.put(identifier, this); - } catch (Exception e) { - this.loaded = false; + // assume it's a username + UUID uuid = UUIDFetcher.getUUID(identifier); + if (uuid != null) { + return fetchSkinByUUID(uuid.toString()); } - } - public SkinType getSkinType() { - return skinType; + return null; } - public String getIdentifier() { - return identifier; + /** + * Fetches the skin data from the Mojang API. + * + * @throws IOException If the skin data could not be fetched. + */ + public static SkinData fetchSkinByUUID(String uuid) throws IOException { + URL url = new URL("https://sessionserver.mojang.com/session/minecraft/profile/" + uuid + "?unsigned=false"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("GET"); + + String json = new Scanner(conn.getInputStream(), StandardCharsets.UTF_8).useDelimiter("\\A").next(); + JsonParser parser = new JsonParser(); + JsonObject obj = parser.parse(json).getAsJsonObject(); + + String value = obj.getAsJsonArray("properties").get(0).getAsJsonObject().getAsJsonPrimitive("value").getAsString(); + String signature = obj.getAsJsonArray("properties").get(0).getAsJsonObject().getAsJsonPrimitive("signature").getAsString(); + SkinData skinData = new SkinData(uuid, value, signature); + + skinCache.put(uuid, skinData); + return skinData; } - public String getValue() { - return value; + /** + * Fetches the skin data from the Mojang API. + * + * @throws IOException If the skin data could not be fetched. + */ + public static SkinData fetchSkinByURL(String skinURL) throws IOException { + URL url = new URL("https://api.mineskin.org/generate/url"); + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); + conn.setRequestMethod("POST"); + conn.setDoOutput(true); + DataOutputStream outputStream = new DataOutputStream(conn.getOutputStream()); + outputStream.writeBytes("url=" + URLEncoder.encode(skinURL, StandardCharsets.UTF_8)); + outputStream.close(); + + String json = new Scanner(conn.getInputStream(), StandardCharsets.UTF_8).useDelimiter("\\A").next(); + JsonParser parser = new JsonParser(); + JsonObject obj = parser.parse(json).getAsJsonObject(); + + String value = obj.getAsJsonObject("data").getAsJsonObject("texture").getAsJsonPrimitive("value").getAsString(); + String signature = obj.getAsJsonObject("data").getAsJsonObject("texture").getAsJsonPrimitive("signature").getAsString(); + SkinData skinData = new SkinData(skinURL, value, signature); + + skinCache.put(skinURL, skinData); + return skinData; } - public String getSignature() { - return signature; + private static boolean isURL(String identifier) { + return identifier.startsWith("http"); } - public boolean isLoaded() { - return loaded; + private static boolean isPlaceholder(String identifier) { + return identifier.startsWith("%") && identifier.endsWith("%") || identifier.startsWith("{") && identifier.endsWith("}"); } - public enum SkinType { - UUID("https://sessionserver.mojang.com/session/minecraft/profile/{uuid}?unsigned=false", "GET"), - URL("https://api.mineskin.org/generate/url", "POST"); - - private final String requestUrl; - private final String requestMethod; + private static boolean isUUID(String identifier) { + return identifier.length() == 36 && identifier.contains("-"); + } - SkinType(String requestUrl, String requestMethod) { - this.requestUrl = requestUrl; - this.requestMethod = requestMethod; - } + /** + * Represents all required data for a skin. + * + * @param identifier The identifier of the skin. This can be a UUID, username, URL or a placeholder by PAPI. + * @param value The value of the skin. If {@code null}, the skin will be fetched from the Mojang API. + * @param signature The signature of the skin. If {@code null}, the skin will be fetched from the Mojang API. + */ + public record SkinData(@NotNull String identifier, @Nullable String value, @Nullable String signature) { + + /** + * Fetches the skin data from the Mojang API if the value or signature is {@code null}. + * + * @return The value of the skin or {@code null} if the skin data could not be fetched. + */ + @Override + public String value() { + if (value == null || value.isEmpty()) { + try { + SkinData skinData = fetchSkin(identifier); + return skinData == null ? null : skinData.value(); + } catch (IOException e) { + FancyNpcsPlugin.get().getPlugin().getLogger().warning("Failed to fetch skin data for " + identifier); + } + } - public static SkinType getType(String s) { - return s.startsWith("http") ? URL : UUID; + return value; } - public String getRequestUrl() { - return requestUrl; - } + /** + * Fetches the skin data from the Mojang API if the value or signature is {@code null}. + * + * @return The signature of the skin or {@code null} if the skin data could not be fetched. + */ + @Override + public String signature() { + if (signature == null || signature.isEmpty()) { + try { + SkinData skinData = fetchSkin(identifier); + return skinData == null ? null : skinData.signature(); + } catch (IOException e) { + FancyNpcsPlugin.get().getPlugin().getLogger().warning("Failed to fetch skin data for " + identifier); + } + } - public String getRequestMethod() { - return requestMethod; + return signature; } } } \ No newline at end of file diff --git a/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/Npc_1_19_4.java b/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/Npc_1_19_4.java index 73586318..b51b96bd 100644 --- a/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/Npc_1_19_4.java +++ b/implementation_1_19_4/src/main/java/de/oliver/fancynpcs/v1_19_4/Npc_1_19_4.java @@ -67,9 +67,9 @@ public void create() { npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, "")); ((ServerPlayer) npc).gameProfile = gameProfile; - if (data.getSkin() != null && data.getSkin().isLoaded()) { + if (data.getSkin() != null && data.getSkin().value() != null && data.getSkin().signature() != null) { // sessionserver.mojang.com/session/minecraft/profile/?unsigned=false - ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()))); + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().value(), data.getSkin().signature()))); } } else { EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); diff --git a/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/Npc_1_20_1.java b/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/Npc_1_20_1.java index 0a83f2f3..b0a11504 100644 --- a/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/Npc_1_20_1.java +++ b/implementation_1_20_1/src/main/java/de/oliver/fancynpcs/v1_20_1/Npc_1_20_1.java @@ -68,9 +68,9 @@ public void create() { npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, "")); ((ServerPlayer) npc).gameProfile = gameProfile; - if (data.getSkin() != null && data.getSkin().isLoaded()) { + if (data.getSkin() != null && data.getSkin().value() != null && data.getSkin().signature() != null) { // sessionserver.mojang.com/session/minecraft/profile/?unsigned=false - ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()))); + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().value(), data.getSkin().signature()))); } } else { EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); diff --git a/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/Npc_1_20_2.java b/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/Npc_1_20_2.java index a0afc357..922b5cd6 100644 --- a/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/Npc_1_20_2.java +++ b/implementation_1_20_2/src/main/java/de/oliver/fancynpcs/v1_20_2/Npc_1_20_2.java @@ -70,9 +70,9 @@ public void create() { npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); ((ServerPlayer) npc).gameProfile = gameProfile; - if (data.getSkin() != null && data.getSkin().isLoaded()) { + if (data.getSkin() != null && data.getSkin().value() != null && data.getSkin().signature() != null) { // sessionserver.mojang.com/session/minecraft/profile/?unsigned=false - ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()))); + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().value(), data.getSkin().signature()))); } } else { EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); diff --git a/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/Npc_1_20_4.java b/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/Npc_1_20_4.java index f2f25964..f4b57be3 100644 --- a/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/Npc_1_20_4.java +++ b/implementation_1_20_4/src/main/java/de/oliver/fancynpcs/v1_20_4/Npc_1_20_4.java @@ -69,9 +69,9 @@ public void create() { npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); ((ServerPlayer) npc).gameProfile = gameProfile; - if (data.getSkin() != null && data.getSkin().isLoaded()) { + if (data.getSkin() != null && data.getSkin().value() != null && data.getSkin().signature() != null) { // sessionserver.mojang.com/session/minecraft/profile/?unsigned=false - ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()))); + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().value(), data.getSkin().signature()))); } } else { EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); diff --git a/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/Npc_1_20_6.java b/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/Npc_1_20_6.java index ccb102bf..ffcdbb13 100644 --- a/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/Npc_1_20_6.java +++ b/implementation_1_20_6/src/main/java/de/oliver/fancynpcs/v1_20_6/Npc_1_20_6.java @@ -72,9 +72,9 @@ public void create() { npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); ((ServerPlayer) npc).gameProfile = gameProfile; - if (data.getSkin() != null && data.getSkin().isLoaded()) { + if (data.getSkin() != null && data.getSkin().value() != null && data.getSkin().signature() != null) { // sessionserver.mojang.com/session/minecraft/profile/?unsigned=false - ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()))); + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().value(), data.getSkin().signature()))); } } else { EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); diff --git a/implementation_1_21/src/main/java/de/oliver/fancynpcs/v1_21/Npc_1_21.java b/implementation_1_21/src/main/java/de/oliver/fancynpcs/v1_21/Npc_1_21.java index b71a98b4..fb2f0146 100644 --- a/implementation_1_21/src/main/java/de/oliver/fancynpcs/v1_21/Npc_1_21.java +++ b/implementation_1_21/src/main/java/de/oliver/fancynpcs/v1_21/Npc_1_21.java @@ -70,9 +70,9 @@ public void create() { npc = new ServerPlayer(minecraftServer, serverLevel, new GameProfile(uuid, ""), ClientInformation.createDefault()); ((ServerPlayer) npc).gameProfile = gameProfile; - if (data.getSkin() != null && data.getSkin().isLoaded()) { + if (data.getSkin() != null && data.getSkin().value() != null && data.getSkin().signature() != null) { // sessionserver.mojang.com/session/minecraft/profile/?unsigned=false - ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()))); + ((ServerPlayer) npc).getGameProfile().getProperties().replaceValues("textures", ImmutableList.of(new Property("textures", data.getSkin().value(), data.getSkin().signature()))); } } else { EntityType nmsType = BuiltInRegistries.ENTITY_TYPE.get(CraftNamespacedKey.toMinecraft(data.getType().getKey())); diff --git a/src/main/java/de/oliver/fancynpcs/NpcManagerImpl.java b/src/main/java/de/oliver/fancynpcs/NpcManagerImpl.java index 53db9875..abb19798 100644 --- a/src/main/java/de/oliver/fancynpcs/NpcManagerImpl.java +++ b/src/main/java/de/oliver/fancynpcs/NpcManagerImpl.java @@ -164,9 +164,9 @@ public void saveNpcs(boolean force) { npcConfig.set("npcs." + data.getId() + ".mirrorSkin", data.isMirrorSkin()); if (data.getSkin() != null) { - npcConfig.set("npcs." + data.getId() + ".skin.identifier", data.getSkin().getIdentifier()); - npcConfig.set("npcs." + data.getId() + ".skin.value", data.getSkin().getValue()); - npcConfig.set("npcs." + data.getId() + ".skin.signature", data.getSkin().getSignature()); + npcConfig.set("npcs." + data.getId() + ".skin.identifier", data.getSkin().identifier()); + npcConfig.set("npcs." + data.getId() + ".skin.value", data.getSkin().value()); + npcConfig.set("npcs." + data.getId() + ".skin.signature", data.getSkin().signature()); } if (data.getEquipment() != null) { @@ -242,9 +242,9 @@ public void loadNpcs() { String skinIdentifier = npcConfig.getString("npcs." + id + ".skin.identifier", npcConfig.getString("npcs." + id + ".skin.uuid", "")); String skinValue = npcConfig.getString("npcs." + id + ".skin.value"); String skinSignature = npcConfig.getString("npcs." + id + ".skin.signature"); - SkinFetcher skin = null; - if (skinIdentifier.length() > 0) { - skin = new SkinFetcher(skinIdentifier, skinValue, skinSignature); + SkinFetcher.SkinData skin = null; + if (!skinIdentifier.isEmpty()) { + skin = new SkinFetcher.SkinData(skinIdentifier, skinValue, skinSignature); } boolean showInTab = npcConfig.getBoolean("npcs." + id + ".showInTab"); diff --git a/src/main/java/de/oliver/fancynpcs/commands/npc/SkinCMD.java b/src/main/java/de/oliver/fancynpcs/commands/npc/SkinCMD.java index 122e2755..d9974f51 100644 --- a/src/main/java/de/oliver/fancynpcs/commands/npc/SkinCMD.java +++ b/src/main/java/de/oliver/fancynpcs/commands/npc/SkinCMD.java @@ -1,6 +1,5 @@ package de.oliver.fancynpcs.commands.npc; -import de.oliver.fancylib.UUIDFetcher; import de.oliver.fancylib.translations.Translator; import de.oliver.fancynpcs.FancyNpcs; import de.oliver.fancynpcs.api.Npc; @@ -16,23 +15,31 @@ import org.incendo.cloud.annotations.suggestion.Suggestions; import org.incendo.cloud.context.CommandContext; import org.incendo.cloud.context.CommandInput; +import org.jetbrains.annotations.NotNull; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; -import java.util.UUID; -import java.util.regex.Pattern; - -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; public enum SkinCMD { INSTANCE; // SINGLETON private final Translator translator = FancyNpcs.getInstance().getTranslator(); - private static final Pattern USERNAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]{3,16}$"); + /** + * Returns {@code true} if provided string can be parsed to an {@link URL} object. + */ + private static boolean isURL(final @NotNull String url) { + try { + new URL(url); + return true; + } catch (final MalformedURLException e) { + return false; + } + } + + /* PARSERS AND SUGGESTIONS */ @Command("npc skin ") @Permission("fancynpcs.command.npc.skin") @@ -41,17 +48,14 @@ public void onSkin( final @NotNull Npc npc, final @NotNull @Argument(suggestions = "SkinCMD/skin") String skin ) { - // Sending error message if NPC cannot have skin applied. Only players can have skins. if (npc.getData().getType() != EntityType.PLAYER) { translator.translate("command_unsupported_npc_type").send(sender); return; } - // Getting some information about input to handle command accordingly and improve message accuracy. + final boolean isMirror = skin.equalsIgnoreCase("@mirror"); final boolean isNone = skin.equalsIgnoreCase("@none"); - final boolean isURL = isURL(skin); if (isMirror) { - // Calling event and updating the skin if not cancelled, sending error message otherwise. if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.MIRROR_SKIN, true, sender).callEvent()) { npc.getData().setMirrorSkin(true); npc.removeForAll(); @@ -62,7 +66,6 @@ public void onSkin( translator.translate("command_npc_modification_cancelled").send(sender); } } else if (isNone) { - // Calling events and updating the skin if not cancelled, sending error message otherwise. if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, false, sender).callEvent() && new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, null, sender).callEvent()) { npc.getData().setMirrorSkin(false); npc.getData().setSkin(null); @@ -73,18 +76,18 @@ public void onSkin( } else { translator.translate("command_npc_modification_cancelled").send(sender); } - } else if (isURL) { - // Creating SkinFetcher from the specified texture URL. - final SkinFetcher skinFetcher = new SkinFetcher(skin); - // Sending error message if SkinFetcher has failed to load the skin. - if (!skinFetcher.isLoaded()) { + } else { + SkinFetcher.SkinData skinData; + try { + skinData = new SkinFetcher.SkinData(skin, null, null); + } catch (Exception e) { translator.translate("npc_skin_failure_invalid_url").replaceStripped("input", skin).send(sender); return; } - // Calling events and updating the skin if not cancelled, sending error message otherwise. - if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, false, sender).callEvent() && new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, skinFetcher, sender).callEvent()) { + + if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, false, sender).callEvent() && new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, skinData, sender).callEvent()) { npc.getData().setMirrorSkin(false); - npc.getData().setSkin(skinFetcher); + npc.getData().setSkin(skinData); npc.removeForAll(); npc.create(); npc.spawnForAll(); @@ -92,39 +95,10 @@ public void onSkin( } else { translator.translate("command_npc_modification_cancelled").send(sender); } - // Handling as if user input is a player name. Because this may require sending a web request, we should (?) match against valid username pattern to make it somewhat injection-proof. - } else if (USERNAME_PATTERN.matcher(skin).find()) { - // Fetching UUID from the specified player name. - final @Nullable UUID uuid = UUIDFetcher.getUUID(skin); - // Sending error message if message if UUID fetch has (for whatever reason) failed. This can happen eg. when being rate limited. - if (uuid == null) { - translator.translate("npc_skin_failure_invalid_name_or_rate_limit").replaceStripped("input", skin).send(sender); - return; - } - // Creating SkinFetcher from the fetched UUID. - final SkinFetcher skinFetcher = new SkinFetcher(uuid.toString()); - // Sending error message if SkinFetcher has failed to load the skin. - if (!skinFetcher.isLoaded()) { - translator.translate("npc_skin_failure_invalid_name_or_rate_limit").replaceStripped("input", skin).send(sender); - return; - } - // Calling events and updating the skin if not cancelled, sending error message otherwise. - if (new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, false, sender).callEvent() && new NpcModifyEvent(npc, NpcModifyEvent.NpcModification.SKIN, skinFetcher, sender).callEvent()) { - npc.getData().setMirrorSkin(false); - npc.getData().setSkin(skinFetcher); - npc.removeForAll(); - npc.create(); - npc.spawnForAll(); - translator.translate("npc_skin_set_name").replace("npc", npc.getData().getName()).replace("name", skin).send(sender); - } else { - translator.translate("command_npc_modification_cancelled").send(sender); - } - } else { - translator.translate("npc_skin_failure_invalid_name_or_url").replaceStripped("input", skin).send(sender); } } - /* PARSERS AND SUGGESTIONS */ + /* UTILITY METHODS */ @Suggestions("SkinCMD/skin") public List suggestSkin(final CommandContext context, final CommandInput input) { @@ -135,18 +109,4 @@ public List suggestSkin(final CommandContext context, fin }}; } - /* UTILITY METHODS */ - - /** - * Returns {@code true} if provided string can be parsed to an {@link URL} object. - */ - private static boolean isURL(final @NotNull String url) { - try { - new URL(url); - return true; - } catch (final MalformedURLException e) { - return false; - } - } - }