From ea6812b27e26b4921b5292e1cff66fcf530dcc43 Mon Sep 17 00:00:00 2001 From: D3v1s0m <49519439+D3v1s0m@users.noreply.github.com> Date: Thu, 20 Jul 2023 14:49:39 +0530 Subject: [PATCH 1/2] removed skin_cape from common properties --- plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java index 03104ea..4d406c0 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/npc/NpcTypeImpl.java @@ -87,7 +87,7 @@ public class NpcTypeImpl implements NpcType { public NpcTypeImpl build() { ServerVersion version = PacketEvents.getAPI().getServerManager().getVersion(); - addProperties("fire", "invisible", "silent", "look", "skin_cape", + addProperties("fire", "invisible", "silent", "look", "using_item", "potion_color", "potion_ambient", "dinnerbone"); if (version.isNewerThanOrEquals(ServerVersion.V_1_9)) addProperties("glow"); if (version.isNewerThanOrEquals(ServerVersion.V_1_14)) addProperties("pose"); From 902b90212204ef511a05b08a678fb4b5cdcae7b5 Mon Sep 17 00:00:00 2001 From: D3v1s0m <49519439+D3v1s0m@users.noreply.github.com> Date: Fri, 21 Jul 2023 15:51:32 +0530 Subject: [PATCH 2/2] Added url skin type --- .../api/skin/SkinDescriptorFactory.java | 5 +++ .../pyr/znpcsplus/commands/SkinCommand.java | 35 ++++++++++++---- .../entity/EntityPropertyRegistryImpl.java | 1 + .../entity/properties/SkinProperty.java | 20 +++++++++ .../skin/SkinDescriptorFactoryImpl.java | 13 ++++++ .../znpcsplus/skin/cache/MojangSkinCache.java | 42 +++++++++++++++++-- .../skin/descriptor/PrefetchedDescriptor.java | 5 +++ 7 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/SkinProperty.java diff --git a/api/src/main/java/lol/pyr/znpcsplus/api/skin/SkinDescriptorFactory.java b/api/src/main/java/lol/pyr/znpcsplus/api/skin/SkinDescriptorFactory.java index ba67e88..bc6954a 100644 --- a/api/src/main/java/lol/pyr/znpcsplus/api/skin/SkinDescriptorFactory.java +++ b/api/src/main/java/lol/pyr/znpcsplus/api/skin/SkinDescriptorFactory.java @@ -1,8 +1,13 @@ package lol.pyr.znpcsplus.api.skin; +import java.net.MalformedURLException; +import java.net.URL; + public interface SkinDescriptorFactory { SkinDescriptor createMirrorDescriptor(); SkinDescriptor createRefreshingDescriptor(String playerName); SkinDescriptor createStaticDescriptor(String playerName); SkinDescriptor createStaticDescriptor(String texture, String signature); + SkinDescriptor createUrlDescriptor(String url) throws MalformedURLException; + SkinDescriptor createUrlDescriptor(URL url); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/SkinCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/SkinCommand.java index abe40fd..69a81e7 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/commands/SkinCommand.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/SkinCommand.java @@ -17,6 +17,8 @@ import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; +import java.net.MalformedURLException; +import java.net.URL; import java.util.Collections; import java.util.List; @@ -44,9 +46,7 @@ public class SkinCommand implements CommandHandler { npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new MirrorDescriptor(skinCache)); npc.respawn(); context.halt(Component.text("The NPC's skin will now mirror the player that it's being displayed to", NamedTextColor.GREEN)); - } - - if (type.equalsIgnoreCase("static")) { + } else if (type.equalsIgnoreCase("static")) { context.ensureArgsNotEmpty(); String name = context.dumpAllArgs(); context.send(Component.text("Fetching skin \"" + name + "\"...", NamedTextColor.GREEN)); @@ -57,25 +57,42 @@ public class SkinCommand implements CommandHandler { } npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), skin); npc.respawn(); - context.send(Component.text("The NPC's skin has been set to \"" + name + "\"")); + context.send(Component.text("The NPC's skin has been set to \"" + name + "\"", NamedTextColor.GREEN)); }); return; - } - - if (type.equalsIgnoreCase("dynamic")) { + } else if (type.equalsIgnoreCase("dynamic")) { context.ensureArgsNotEmpty(); String name = context.dumpAllArgs(); npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new FetchingDescriptor(skinCache, name)); npc.respawn(); context.halt(Component.text("The NPC's skin will now be resolved per-player from \"" + name + "\"")); + } else if (type.equalsIgnoreCase("url")) { + context.ensureArgsNotEmpty(); + String urlString = context.dumpAllArgs(); + try { + URL url = new URL(urlString); + context.send(Component.text("Fetching skin from url \"" + urlString + "\"...", NamedTextColor.GREEN)); + PrefetchedDescriptor.fromUrl(skinCache, url).thenAccept(skin -> { + if (skin.getSkin() == null) { + context.send(Component.text("Failed to fetch skin, are you sure the url is valid?", NamedTextColor.RED)); + return; + } + npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), skin); + npc.respawn(); + context.send(Component.text("The NPC's skin has been set.", NamedTextColor.GREEN)); + }); + } catch (MalformedURLException e) { + context.send(Component.text("Invalid url!", NamedTextColor.RED)); + } + return; } - context.send(Component.text("Unknown skin type! Please use one of the following: mirror, static, dynamic")); + context.send(Component.text("Unknown skin type! Please use one of the following: mirror, static, dynamic, url")); } @Override public List suggest(CommandContext context) throws CommandExecutionException { if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds()); - if (context.argSize() == 2) return context.suggestLiteral("mirror", "static", "dynamic"); + if (context.argSize() == 2) return context.suggestLiteral("mirror", "static", "dynamic", "url"); if (context.matchSuggestion("*", "static")) return context.suggestPlayers(); return Collections.emptyList(); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java index f0b6baa..d6b5ada 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/EntityPropertyRegistryImpl.java @@ -254,6 +254,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry { register(new GlowProperty(packetFactory)); register(new EffectsProperty("fire", 0x01)); register(new EffectsProperty("invisible", 0x20)); + register(new SkinProperty()); } private void registerSerializer(PropertySerializer serializer) { diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/SkinProperty.java b/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/SkinProperty.java new file mode 100644 index 0000000..b0d7579 --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/entity/properties/SkinProperty.java @@ -0,0 +1,20 @@ +package lol.pyr.znpcsplus.entity.properties; + +import com.github.retrooper.packetevents.protocol.entity.data.EntityData; +import lol.pyr.znpcsplus.api.skin.SkinDescriptor; +import lol.pyr.znpcsplus.entity.EntityPropertyImpl; +import lol.pyr.znpcsplus.entity.PacketEntity; +import org.bukkit.entity.Player; + +import java.util.Map; + +public class SkinProperty extends EntityPropertyImpl { + public SkinProperty() { + super("skin", null, SkinDescriptor.class); + } + + @Override + public void apply(SkinDescriptor value, Player player, PacketEntity entity, boolean isSpawned, Map properties) { + + } +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/skin/SkinDescriptorFactoryImpl.java b/plugin/src/main/java/lol/pyr/znpcsplus/skin/SkinDescriptorFactoryImpl.java index ca52520..8fc2b73 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/SkinDescriptorFactoryImpl.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/SkinDescriptorFactoryImpl.java @@ -7,6 +7,9 @@ import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor; import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor; import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor; +import java.net.MalformedURLException; +import java.net.URL; + public class SkinDescriptorFactoryImpl implements SkinDescriptorFactory { private final MojangSkinCache skinCache; private final MirrorDescriptor mirrorDescriptor; @@ -35,4 +38,14 @@ public class SkinDescriptorFactoryImpl implements SkinDescriptorFactory { public SkinDescriptor createStaticDescriptor(String texture, String signature) { return new PrefetchedDescriptor(new Skin(texture, signature)); } + + @Override + public SkinDescriptor createUrlDescriptor(String url) throws MalformedURLException { + return createUrlDescriptor(new URL(url)); + } + + @Override + public SkinDescriptor createUrlDescriptor(URL url) { + return PrefetchedDescriptor.fromUrl(skinCache, url).join(); + } } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/skin/cache/MojangSkinCache.java b/plugin/src/main/java/lol/pyr/znpcsplus/skin/cache/MojangSkinCache.java index 8df8bcc..31a3b6a 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/cache/MojangSkinCache.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/cache/MojangSkinCache.java @@ -8,9 +8,7 @@ import lol.pyr.znpcsplus.skin.Skin; import org.bukkit.Bukkit; import org.bukkit.entity.Player; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; +import java.io.*; import java.lang.reflect.InvocationTargetException; import java.net.HttpURLConnection; import java.net.MalformedURLException; @@ -69,6 +67,44 @@ public class MojangSkinCache { }); } + public CompletableFuture fetchByUrl(URL url) { + + return CompletableFuture.supplyAsync(() -> { + URL apiUrl = parseUrl("https://api.mineskin.org/generate/url"); + HttpURLConnection connection = null; + try { + connection = (HttpURLConnection) apiUrl.openConnection(); + connection.setRequestMethod("POST"); + connection.setRequestProperty("accept", "application/json"); + connection.setRequestProperty("Content-Type", "application/json"); + connection.setDoOutput(true); + OutputStream outStream = connection.getOutputStream(); + DataOutputStream out = new DataOutputStream(outStream); + out.writeBytes("{\"variant\":\"classic\",\"url\":\"" + url.toString() + "\"}"); // TODO: configurable variant (slim, classic) default: classic + out.flush(); + out.close(); + outStream.close(); + + try (Reader reader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)) { + JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject(); + if (obj.has("error")) return null; + if (!obj.has("data")) return null; + JsonObject texture = obj.get("data").getAsJsonObject().get("texture").getAsJsonObject(); + return new Skin(texture.get("value").getAsString(), texture.get("signature").getAsString()); + } + + } catch (IOException exception) { + if (!configManager.getConfig().disableSkinFetcherWarnings()) { + logger.warning("Failed to get skin from url:"); + exception.printStackTrace(); + } + } finally { + if (connection != null) connection.disconnect(); + } + return null; + }); + } + public boolean isNameFullyCached(String s) { String name = s.toLowerCase(); if (!idCache.containsKey(name)) return false; diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/PrefetchedDescriptor.java b/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/PrefetchedDescriptor.java index 767757d..f6cc66a 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/PrefetchedDescriptor.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/descriptor/PrefetchedDescriptor.java @@ -7,6 +7,7 @@ import lol.pyr.znpcsplus.skin.Skin; import lol.pyr.znpcsplus.skin.cache.MojangSkinCache; import org.bukkit.entity.Player; +import java.net.URL; import java.util.concurrent.CompletableFuture; public class PrefetchedDescriptor implements BaseSkinDescriptor, SkinDescriptor { @@ -20,6 +21,10 @@ public class PrefetchedDescriptor implements BaseSkinDescriptor, SkinDescriptor return CompletableFuture.supplyAsync(() -> new PrefetchedDescriptor(cache.fetchByName(name).join())); } + public static CompletableFuture fromUrl(MojangSkinCache cache, URL url) { + return CompletableFuture.supplyAsync(() -> new PrefetchedDescriptor(cache.fetchByUrl(url).join())); + } + @Override public CompletableFuture fetch(Player player) { return CompletableFuture.completedFuture(skin);