diff --git a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/PaperAPIToolsImpl.java b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/PaperAPIToolsImpl.java index 0b6d741ced..3d2c67364a 100644 --- a/paper/src/main/java/com/denizenscript/denizen/paper/utilities/PaperAPIToolsImpl.java +++ b/paper/src/main/java/com/denizenscript/denizen/paper/utilities/PaperAPIToolsImpl.java @@ -22,6 +22,8 @@ import io.papermc.paper.entity.TeleportFlag; import io.papermc.paper.potion.PotionMix; import io.papermc.paper.world.WeatheringCopperState; +import net.kyori.adventure.resource.ResourcePackInfo; +import net.kyori.adventure.resource.ResourcePackRequest; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.minimessage.MiniMessage; import net.md_5.bungee.api.ChatColor; @@ -115,14 +117,32 @@ public void setSignLine(Sign sign, int line, String text) { sign.line(line, PaperModule.parseFormattedText(text == null ? "" : text, ChatColor.BLACK)); } + public void sendResourcePack(Player player, String url, String hash, boolean forced, String prompt, UUID uuid, boolean replace) { + ResourcePackInfo.Builder builder = ResourcePackInfo.resourcePackInfo(); + builder.hash(hash); + builder.uri(URI.create(url)); + if (uuid != null) { + builder.id(uuid); + } + player.sendResourcePacks( + ResourcePackRequest.resourcePackRequest().prompt(PaperModule.parseFormattedText(prompt, ChatColor.WHITE)).required(forced).replace(replace).packs(builder.build()) + ); + } + @Override - public void sendResourcePack(Player player, String url, String hash, boolean forced, String prompt) { - if (prompt == null && !forced) { - super.sendResourcePack(player, url, hash, false, null); + public void setResourcePack(Player player, String url, String hash, boolean forced, String prompt, UUID uuid) { + if (prompt == null && !forced && uuid == null) { + super.setResourcePack(player, url, hash, false, null, null); } - else { - player.setResourcePack(url, CoreUtilities.toLowerCase(hash), forced, PaperModule.parseFormattedText(prompt, ChatColor.WHITE)); + sendResourcePack(player, url, hash, forced, prompt, uuid, true); + } + + @Override + public void addResourcePack(Player player, String url, String hash, boolean forced, String prompt, UUID uuid) { + if (prompt == null && !forced) { + super.addResourcePack(player, url, hash, false, null, uuid); } + sendResourcePack(player, url, hash, forced, prompt, uuid, false); } @Override @@ -414,7 +434,7 @@ public void setMaterialTags(Material type, Set tags) { public void addLink(ServerLinks links, String display, URI uri) { links.addLink(PaperModule.parseFormattedText(display, ChatColor.WHITE), uri); } - + @Override public double[] getRecentTps() { return Bukkit.getTPS(); diff --git a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/player/ResourcePackCommand.java b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/player/ResourcePackCommand.java index d221734b42..eaea386894 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/player/ResourcePackCommand.java +++ b/plugin/src/main/java/com/denizenscript/denizen/scripts/commands/player/ResourcePackCommand.java @@ -1,33 +1,33 @@ package com.denizenscript.denizen.scripts.commands.player; +import com.denizenscript.denizen.nms.NMSHandler; +import com.denizenscript.denizen.nms.NMSVersion; import com.denizenscript.denizen.objects.PlayerTag; import com.denizenscript.denizen.utilities.PaperAPITools; import com.denizenscript.denizen.utilities.Utilities; import com.denizenscript.denizencore.exceptions.InvalidArgumentsRuntimeException; -import com.denizenscript.denizencore.scripts.commands.generator.ArgDefaultNull; -import com.denizenscript.denizencore.scripts.commands.generator.ArgName; -import com.denizenscript.denizencore.scripts.commands.generator.ArgPrefixed; -import com.denizenscript.denizencore.scripts.commands.generator.ArgSubType; +import com.denizenscript.denizencore.scripts.commands.generator.*; import com.denizenscript.denizencore.utilities.debugging.Debug; import com.denizenscript.denizencore.scripts.ScriptEntry; import com.denizenscript.denizencore.scripts.commands.AbstractCommand; -import java.util.Collections; +import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.UUID; public class ResourcePackCommand extends AbstractCommand { public ResourcePackCommand() { setName("resourcepack"); - setSyntax("resourcepack [url:] [hash:] (forced) (prompt:) (targets:|...)"); - setRequiredArguments(2, 5); + setSyntax("resourcepack ({set}/add/remove) (id:) (url:) (hash:) (forced) (prompt:) (targets:|...)"); + setRequiredArguments(1, 7); isProcedural = false; autoCompile(); } // <--[command] // @Name ResourcePack - // @Syntax resourcepack [url:] [hash:] (forced) (prompt:) (targets:|...) + // @Syntax resourcepack ({set}/add/remove) (id:) (url:) (hash:) (forced) (prompt:) (targets:|...) // @Required 2 // @Maximum 5 // @Short Prompts a player to download a server resource pack. @@ -36,6 +36,9 @@ public ResourcePackCommand() { // @Description // Sets the current resource pack by specifying a valid URL to a resource pack. // + // Optionally, you can send the player additional resource packs by using the "add" argument. + // The "id" argument allows you to overwrite a specific resource pack or remove one with "remove" argument. + // // The player will be prompted to download the pack, with the optional prompt text or a default vanilla message. // Once a player says "yes" once, all future packs will be automatically downloaded. If the player selects "no" once, all future packs will automatically be rejected. // Players can change the automatic setting from their server list in the main menu. @@ -56,33 +59,104 @@ public ResourcePackCommand() { // None // // @Usage - // Use to send a resource pack with a pre-known hash. + // Use to set a resource pack with a pre-known hash. // - resourcepack url:https://example.com/pack.zip hash:0102030405060708090a0b0c0d0e0f1112131415 // + // @Usage + // Use to send multiple resource packs to a player. + // - resourcepack add id:first_pack url:https://example.com/pack1.zip hash:0102030405060708090a0b0c0d0e0f1112131415 + // - resourcepack add id:second_pack url:https://example.com/pack2.zip hash:0102030405060708090a0b0c0d0e0f1112131415 + // + // @Usage + // Use to remove all resource packs from all online players. + // - resourcepack remove targets: // --> + public enum Action { SET, ADD, REMOVE } + public static void autoExecute(ScriptEntry scriptEntry, - @ArgName("url") @ArgPrefixed String url, - @ArgName("hash") @ArgPrefixed String hash, + @ArgName("action") @ArgDefaultText("set") Action action, + @ArgName("id") @ArgPrefixed @ArgDefaultNull String id, + @ArgName("url") @ArgPrefixed @ArgDefaultNull String url, + @ArgName("hash") @ArgPrefixed @ArgDefaultNull String hash, @ArgName("prompt") @ArgPrefixed @ArgDefaultNull String prompt, @ArgName("targets") @ArgPrefixed @ArgDefaultNull @ArgSubType(PlayerTag.class) List targets, @ArgName("forced") boolean forced) { + if (!NMSHandler.getVersion().isAtLeast(NMSVersion.v1_20) && (action == Action.ADD || id != null)) { + throw new UnsupportedOperationException("Adding multiple resource packs is not supported on this server version!"); + } if (targets == null) { if (!Utilities.entryHasPlayer(scriptEntry)) { throw new InvalidArgumentsRuntimeException("Must specify an online player!"); } - targets = Collections.singletonList(Utilities.getEntryPlayer(scriptEntry)); + targets = List.of(Utilities.getEntryPlayer(scriptEntry)); + } + if ((action == Action.ADD || action == Action.SET) && (url == null || hash == null)) { + throw new InvalidArgumentsRuntimeException("Must specify both a resource pack URL and hash!"); } - if (hash.length() != 40) { + if ((action == Action.ADD || action == Action.SET) && hash.length() != 40) { Debug.echoError("Invalid resource_pack hash. Should be 40 characters of hexadecimal data."); return; } - for (PlayerTag player : targets) { - if (!player.isOnline()) { - Debug.echoDebug(scriptEntry, "Player is offline, can't send resource pack to them. Skipping."); - continue; + switch (action) { + case SET -> { + UUID packUUID = id == null ? null : parseUUID(id); + for (PlayerTag player : targets) { + if (checkOnline(player)) { + PaperAPITools.instance.setResourcePack(player.getPlayerEntity(), url, hash, forced, prompt, packUUID); + } + } + } + case ADD -> { + UUID packUUID = id == null ? UUID.nameUUIDFromBytes(url.getBytes(StandardCharsets.UTF_8)) : parseUUID(id); + for (PlayerTag player : targets) { + if (checkOnline(player)) { + PaperAPITools.instance.addResourcePack(player.getPlayerEntity(), url, hash, forced, prompt, packUUID); + } + } } - PaperAPITools.instance.sendResourcePack(player.getPlayerEntity(), url, hash, forced, prompt); + case REMOVE -> { + if (id == null) { + for (PlayerTag player : targets) { + if (checkOnline(player)) { + player.getPlayerEntity().removeResourcePacks(); + } + } + } + else { + UUID packUUID = parseUUID(id); + for (PlayerTag player : targets) { + if (checkOnline(player)) { + player.getPlayerEntity().removeResourcePack(packUUID); + } + } + } + } + } + } + + public static boolean checkOnline(PlayerTag player) { + if (!player.isOnline()) { + Debug.echoError("Invalid player '" + player.getName() + "' specified: must be online."); + return false; + } + return true; + } + + public static UUID parseUUID(String id) { + try { + return UUID.fromString(id); + } + catch (IllegalArgumentException ex) { + return UUID.nameUUIDFromBytes(id.getBytes(StandardCharsets.UTF_8)); + } + } + + public static byte[] parseHash(String hash) { + byte[] hashData = new byte[20]; + for (int i = 0; i < 20; i++) { + hashData[i] = (byte) Integer.parseInt(hash.substring(i * 2, i * 2 + 2), 16); } + return hashData; } } diff --git a/plugin/src/main/java/com/denizenscript/denizen/utilities/PaperAPITools.java b/plugin/src/main/java/com/denizenscript/denizen/utilities/PaperAPITools.java index 6f6877e768..1650c3ac8b 100644 --- a/plugin/src/main/java/com/denizenscript/denizen/utilities/PaperAPITools.java +++ b/plugin/src/main/java/com/denizenscript/denizen/utilities/PaperAPITools.java @@ -3,6 +3,7 @@ import com.denizenscript.denizen.nms.NMSHandler; import com.denizenscript.denizen.nms.NMSVersion; import com.denizenscript.denizen.scripts.commands.entity.TeleportCommand; +import com.denizenscript.denizen.scripts.commands.player.ResourcePackCommand; import com.denizenscript.denizen.scripts.containers.core.ItemScriptContainer; import com.denizenscript.denizen.utilities.packets.NetworkInterceptHelper; import com.denizenscript.denizencore.objects.Mechanism; @@ -27,6 +28,7 @@ import java.net.URI; import java.util.List; import java.util.Set; +import java.util.UUID; import java.util.function.Predicate; public class PaperAPITools { @@ -92,12 +94,12 @@ public void setSignLine(Sign sign, int line, String text) { sign.setLine(line, text == null ? "" : text); } - public void sendResourcePack(Player player, String url, String hash, boolean forced, String prompt) { - byte[] hashData = new byte[20]; - for (int i = 0; i < 20; i++) { - hashData[i] = (byte) Integer.parseInt(hash.substring(i * 2, i * 2 + 2), 16); - } - player.setResourcePack(url, hashData); + public void setResourcePack(Player player, String url, String hash, boolean forced, String prompt, UUID uuid) { + player.setResourcePack(url, ResourcePackCommand.parseHash(hash)); + } + + public void addResourcePack(Player player, String url, String hash, boolean forced, String prompt, UUID uuid) { + player.addResourcePack(uuid, url, ResourcePackCommand.parseHash(hash), prompt, forced); } public void sendSignUpdate(Player player, Location loc, String[] text) {