diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java index 3140e82..02c539e 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/ZNpcsPlus.java @@ -190,6 +190,7 @@ public class ZNpcsPlus extends JavaPlugin { manager.registerCommand("npc", new MultiCommand() .addSubcommand("action", new ActionCommand()) .addSubcommand("create", new CreateCommand()) + .addSubcommand("skin", new SkinCommand()) .addSubcommand("delete", new DeleteCommand()) .addSubcommand("move", new MoveCommand()) .addSubcommand("properties", new PropertiesCommand()) diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/SkinCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/SkinCommand.java new file mode 100644 index 0000000..98e84a4 --- /dev/null +++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/SkinCommand.java @@ -0,0 +1,67 @@ +package lol.pyr.znpcsplus.commands; + +import lol.pyr.director.adventure.command.CommandContext; +import lol.pyr.director.adventure.command.CommandHandler; +import lol.pyr.director.common.command.CommandExecutionException; +import lol.pyr.znpcsplus.entity.EntityPropertyImpl; +import lol.pyr.znpcsplus.npc.NpcEntryImpl; +import lol.pyr.znpcsplus.npc.NpcImpl; +import lol.pyr.znpcsplus.npc.NpcRegistryImpl; +import lol.pyr.znpcsplus.npc.NpcTypeImpl; +import lol.pyr.znpcsplus.skin.descriptor.FetchingDescriptor; +import lol.pyr.znpcsplus.skin.descriptor.MirrorDescriptor; +import lol.pyr.znpcsplus.skin.descriptor.PrefetchedDescriptor; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; + +import java.util.Collections; +import java.util.List; + +public class SkinCommand implements CommandHandler { + @Override + public void run(CommandContext context) throws CommandExecutionException { + context.setUsage(context.getLabel() + " skin [value]"); + NpcImpl npc = context.parse(NpcEntryImpl.class).getNpc(); + if (npc.getType() != NpcTypeImpl.byName("player")) context.halt(Component.text("The NPC must be a player to have a skin", NamedTextColor.RED)); + String type = context.popString(); + + if (type.equalsIgnoreCase("mirror")) { + npc.setProperty(EntityPropertyImpl.SKIN, new MirrorDescriptor()); + 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")) { + context.ensureArgsNotEmpty(); + String name = context.dumpAllArgs(); + context.send(Component.text("Fetching skin \"" + name + "\"...", NamedTextColor.GREEN)); + PrefetchedDescriptor.forPlayer(name).thenAccept(skin -> { + if (skin == null) { + context.send(Component.text("Failed to fetch skin, are you sure the player name is valid?", NamedTextColor.RED)); + return; + } + npc.setProperty(EntityPropertyImpl.SKIN, skin); + npc.respawn(); + context.send(Component.text("The NPC's skin has been set to \"" + name + "\"")); + }); + return; + } + + if (type.equalsIgnoreCase("dynamic")) { + context.ensureArgsNotEmpty(); + String name = context.dumpAllArgs(); + npc.setProperty(EntityPropertyImpl.SKIN, new FetchingDescriptor(name)); + npc.respawn(); + context.halt(Component.text("The NPC's skin will now be resolved per-player from \"" + name + "\"")); + } + context.send(Component.text("Unknown skin type! Please use one of the following: mirror, static, dynamic")); + } + + @Override + public List suggest(CommandContext context) throws CommandExecutionException { + if (context.argSize() == 1) return context.suggestCollection(NpcRegistryImpl.get().modifiableIds()); + if (context.argSize() == 2) return context.suggestLiteral("mirror", "static", "dynamic"); + if (context.matchSuggestion("*", "static")) return context.suggestPlayers(); + return Collections.emptyList(); + } +} diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloAddCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloAddCommand.java index dc432cd..d6d3595 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloAddCommand.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloAddCommand.java @@ -18,6 +18,7 @@ public class HoloAddCommand implements CommandHandler { public void run(CommandContext context) throws CommandExecutionException { context.setUsage(context.getLabel() + " holo add "); HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram(); + context.ensureArgsNotEmpty(); hologram.addLine(ZNpcsPlus.LEGACY_AMPERSAND_SERIALIZER.deserialize(context.dumpAllArgs())); context.send(Component.text("NPC line added!", NamedTextColor.GREEN)); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloInsertCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloInsertCommand.java index afc8818..7f113c3 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloInsertCommand.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloInsertCommand.java @@ -21,6 +21,7 @@ public class HoloInsertCommand implements CommandHandler { HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram(); int line = context.parse(Integer.class); if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED)); + context.ensureArgsNotEmpty(); hologram.insertLine(line, ZNpcsPlus.LEGACY_AMPERSAND_SERIALIZER.deserialize(context.dumpAllArgs())); context.send(Component.text("NPC line inserted!", NamedTextColor.GREEN)); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloSetCommand.java b/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloSetCommand.java index d9a522d..97ba33f 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloSetCommand.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/commands/hologram/HoloSetCommand.java @@ -21,6 +21,7 @@ public class HoloSetCommand implements CommandHandler { HologramImpl hologram = context.parse(NpcEntryImpl.class).getNpc().getHologram(); int line = context.parse(Integer.class); if (line < 0 || line >= hologram.getLines().size()) context.halt(Component.text("Invalid line number!", NamedTextColor.RED)); + context.ensureArgsNotEmpty(); hologram.removeLine(line); hologram.insertLine(line, ZNpcsPlus.LEGACY_AMPERSAND_SERIALIZER.deserialize(context.dumpAllArgs())); context.send(Component.text("NPC line set!", NamedTextColor.GREEN)); diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/config/MainConfig.java b/plugin/src/main/java/lol/pyr/znpcsplus/config/MainConfig.java index 3e66cf1..c29f806 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/config/MainConfig.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/config/MainConfig.java @@ -31,4 +31,9 @@ public interface MainConfig { @ConfComments("The storage type to use. Available storage types: YAML") @DefaultString("YAML") NpcStorageType storageType(); + + @ConfKey("disable-skin-fetcher-warnings") + @ConfComments("Set this to true if you don't want to be warned in the console when a skin fails to resolve") + @DefaultBoolean(false) + boolean disableSkinFetcherWarnings(); } diff --git a/plugin/src/main/java/lol/pyr/znpcsplus/skin/cache/SkinCache.java b/plugin/src/main/java/lol/pyr/znpcsplus/skin/cache/SkinCache.java index 1d0f230..6461a37 100644 --- a/plugin/src/main/java/lol/pyr/znpcsplus/skin/cache/SkinCache.java +++ b/plugin/src/main/java/lol/pyr/znpcsplus/skin/cache/SkinCache.java @@ -3,6 +3,8 @@ package lol.pyr.znpcsplus.skin.cache; import com.google.gson.JsonObject; import com.google.gson.JsonParser; import com.mojang.authlib.GameProfile; +import lol.pyr.znpcsplus.ZNpcsPlus; +import lol.pyr.znpcsplus.config.Configs; import lol.pyr.znpcsplus.reflection.Reflections; import lol.pyr.znpcsplus.skin.Skin; import org.bukkit.Bukkit; @@ -36,8 +38,7 @@ public class SkinCache { if (cache.containsKey(name.toLowerCase())) return fetchByUUID(idCache.get(name.toLowerCase()).getId()); - CompletableFuture future = new CompletableFuture<>(); - CompletableFuture.runAsync(() -> { + return CompletableFuture.supplyAsync(() -> { URL url = parseUrl("https://api.mojang.com/users/profiles/minecraft/" + name); HttpURLConnection connection = null; try { @@ -45,18 +46,21 @@ public class SkinCache { connection.setRequestMethod("GET"); try (Reader reader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)) { JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject(); - if (obj.has("errorMessage")) future.complete(null); + if (obj.has("errorMessage")) return null; String id = obj.get("id").getAsString(); idCache.put(name.toLowerCase(), new CachedId(id)); - fetchByUUID(id).thenAccept(future::complete); + return fetchByUUID(id).join(); } } catch (IOException exception) { - exception.printStackTrace(); + if (!Configs.config().disableSkinFetcherWarnings()) { + ZNpcsPlus.LOGGER.warning("Failed to uuid from player name:"); + exception.printStackTrace(); + } } finally { if (connection != null) connection.disconnect(); } + return null; }); - return future; } public static CompletableFuture fetchByUUID(UUID uuid) { @@ -96,7 +100,6 @@ public class SkinCache { HttpURLConnection connection = null; try { connection = (HttpURLConnection) url.openConnection(); - connection.setRequestMethod("GET"); try (Reader reader = new InputStreamReader(connection.getInputStream(), StandardCharsets.UTF_8)) { JsonObject obj = JsonParser.parseReader(reader).getAsJsonObject(); @@ -105,11 +108,13 @@ public class SkinCache { return skin; } } catch (IOException exception) { - exception.printStackTrace(); + if (!Configs.config().disableSkinFetcherWarnings()) { + ZNpcsPlus.LOGGER.warning("Failed to fetch skin:"); + exception.printStackTrace(); + } } finally { if (connection != null) connection.disconnect(); } - Bukkit.broadcastMessage("failed"); return null; }); }