Added url skin type

This commit is contained in:
D3v1s0m 2023-07-21 15:51:32 +05:30
parent ea6812b27e
commit 902b902122
No known key found for this signature in database
GPG Key ID: 3B6EC35367B8D82E
7 changed files with 109 additions and 12 deletions

@ -1,8 +1,13 @@
package lol.pyr.znpcsplus.api.skin; package lol.pyr.znpcsplus.api.skin;
import java.net.MalformedURLException;
import java.net.URL;
public interface SkinDescriptorFactory { public interface SkinDescriptorFactory {
SkinDescriptor createMirrorDescriptor(); SkinDescriptor createMirrorDescriptor();
SkinDescriptor createRefreshingDescriptor(String playerName); SkinDescriptor createRefreshingDescriptor(String playerName);
SkinDescriptor createStaticDescriptor(String playerName); SkinDescriptor createStaticDescriptor(String playerName);
SkinDescriptor createStaticDescriptor(String texture, String signature); SkinDescriptor createStaticDescriptor(String texture, String signature);
SkinDescriptor createUrlDescriptor(String url) throws MalformedURLException;
SkinDescriptor createUrlDescriptor(URL url);
} }

@ -17,6 +17,8 @@ import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
import net.kyori.adventure.text.Component; import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.NamedTextColor;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -44,9 +46,7 @@ public class SkinCommand implements CommandHandler {
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new MirrorDescriptor(skinCache)); npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new MirrorDescriptor(skinCache));
npc.respawn(); npc.respawn();
context.halt(Component.text("The NPC's skin will now mirror the player that it's being displayed to", NamedTextColor.GREEN)); context.halt(Component.text("The NPC's skin will now mirror the player that it's being displayed to", NamedTextColor.GREEN));
} } else if (type.equalsIgnoreCase("static")) {
if (type.equalsIgnoreCase("static")) {
context.ensureArgsNotEmpty(); context.ensureArgsNotEmpty();
String name = context.dumpAllArgs(); String name = context.dumpAllArgs();
context.send(Component.text("Fetching skin \"" + name + "\"...", NamedTextColor.GREEN)); 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.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), skin);
npc.respawn(); 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; return;
} } else if (type.equalsIgnoreCase("dynamic")) {
if (type.equalsIgnoreCase("dynamic")) {
context.ensureArgsNotEmpty(); context.ensureArgsNotEmpty();
String name = context.dumpAllArgs(); String name = context.dumpAllArgs();
npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new FetchingDescriptor(skinCache, name)); npc.setProperty(propertyRegistry.getByName("skin", SkinDescriptor.class), new FetchingDescriptor(skinCache, name));
npc.respawn(); npc.respawn();
context.halt(Component.text("The NPC's skin will now be resolved per-player from \"" + name + "\"")); 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 @Override
public List<String> suggest(CommandContext context) throws CommandExecutionException { public List<String> suggest(CommandContext context) throws CommandExecutionException {
if (context.argSize() == 1) return context.suggestCollection(npcRegistry.getModifiableIds()); 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(); if (context.matchSuggestion("*", "static")) return context.suggestPlayers();
return Collections.emptyList(); return Collections.emptyList();
} }

@ -254,6 +254,7 @@ public class EntityPropertyRegistryImpl implements EntityPropertyRegistry {
register(new GlowProperty(packetFactory)); register(new GlowProperty(packetFactory));
register(new EffectsProperty("fire", 0x01)); register(new EffectsProperty("fire", 0x01));
register(new EffectsProperty("invisible", 0x20)); register(new EffectsProperty("invisible", 0x20));
register(new SkinProperty());
} }
private void registerSerializer(PropertySerializer<?> serializer) { private void registerSerializer(PropertySerializer<?> serializer) {

@ -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<SkinDescriptor> {
public SkinProperty() {
super("skin", null, SkinDescriptor.class);
}
@Override
public void apply(SkinDescriptor value, Player player, PacketEntity entity, boolean isSpawned, Map<Integer, EntityData> properties) {
}
}

@ -7,6 +7,9 @@ import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor;
import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor; import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor;
import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor; import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor;
import java.net.MalformedURLException;
import java.net.URL;
public class SkinDescriptorFactoryImpl implements SkinDescriptorFactory { public class SkinDescriptorFactoryImpl implements SkinDescriptorFactory {
private final MojangSkinCache skinCache; private final MojangSkinCache skinCache;
private final MirrorDescriptor mirrorDescriptor; private final MirrorDescriptor mirrorDescriptor;
@ -35,4 +38,14 @@ public class SkinDescriptorFactoryImpl implements SkinDescriptorFactory {
public SkinDescriptor createStaticDescriptor(String texture, String signature) { public SkinDescriptor createStaticDescriptor(String texture, String signature) {
return new PrefetchedDescriptor(new Skin(texture, 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();
}
} }

@ -8,9 +8,7 @@ import lol.pyr.znpcsplus.skin.Skin;
import org.bukkit.Bukkit; import org.bukkit.Bukkit;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.io.IOException; import java.io.*;
import java.io.InputStreamReader;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.MalformedURLException; import java.net.MalformedURLException;
@ -69,6 +67,44 @@ public class MojangSkinCache {
}); });
} }
public CompletableFuture<Skin> 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) { public boolean isNameFullyCached(String s) {
String name = s.toLowerCase(); String name = s.toLowerCase();
if (!idCache.containsKey(name)) return false; if (!idCache.containsKey(name)) return false;

@ -7,6 +7,7 @@ import lol.pyr.znpcsplus.skin.Skin;
import lol.pyr.znpcsplus.skin.cache.MojangSkinCache; import lol.pyr.znpcsplus.skin.cache.MojangSkinCache;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import java.net.URL;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
public class PrefetchedDescriptor implements BaseSkinDescriptor, SkinDescriptor { 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())); return CompletableFuture.supplyAsync(() -> new PrefetchedDescriptor(cache.fetchByName(name).join()));
} }
public static CompletableFuture<PrefetchedDescriptor> fromUrl(MojangSkinCache cache, URL url) {
return CompletableFuture.supplyAsync(() -> new PrefetchedDescriptor(cache.fetchByUrl(url).join()));
}
@Override @Override
public CompletableFuture<Skin> fetch(Player player) { public CompletableFuture<Skin> fetch(Player player) {
return CompletableFuture.completedFuture(skin); return CompletableFuture.completedFuture(skin);