From 98b3ac157dc726578ec8650515baf0bda4ac66fe Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 24 May 2026 18:54:01 -0500 Subject: [PATCH 01/43] remove unused permission --- .../java/net/donnypz/displayentityutils/command/Permission.java | 1 - 1 file changed, 1 deletion(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/Permission.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/Permission.java index c3838266..c477c10b 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/Permission.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/Permission.java @@ -124,7 +124,6 @@ public enum Permission { ANIM_ADD_PARTICLE("deu.anim.addparticle"), ANIM_REVERSE("deu.anim.reverse"), ANIM_TOGGLE_SCALE("deu.anim.scale"), - ANIM_TOGGLE_TEXTURE_CHANGES("deu.anim.texturechanges"), ANIM_SET_TAG("deu.anim.settag"), ANIM_SET_FRAME_TAG("deu.anim.setframetag"), ANIM_PLAY("deu.anim.play"), From 00130990c284fa1eb6618a6aaa434d7d5f9f527d Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Wed, 27 May 2026 05:56:02 -0500 Subject: [PATCH 02/43] Update version checking logic --- .../player/DEUPlayerConnectionListener.java | 138 +++++++++--------- 1 file changed, 69 insertions(+), 69 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/player/DEUPlayerConnectionListener.java b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/player/DEUPlayerConnectionListener.java index 3b4ae2d9..c101d3b1 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/player/DEUPlayerConnectionListener.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/player/DEUPlayerConnectionListener.java @@ -8,27 +8,30 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; -import org.jetbrains.annotations.ApiStatus; import java.io.IOException; import java.net.URI; -import java.net.URISyntaxException; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.time.Duration; -@ApiStatus.Internal public final class DEUPlayerConnectionListener implements Listener { private static final String GITHUB_API_URL = "https://api.github.com/repos/PZDonny/DisplayEntityUtils/releases/latest"; private static final String LATEST_VERSION_URL = "https://github.com/PZDonny/DisplayEntityUtils/releases/latest"; + private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder() + .connectTimeout(Duration.ofSeconds(15)) + .build(); + + @EventHandler public void onJoin(PlayerJoinEvent e){ Player player = e.getPlayer(); @@ -38,23 +41,14 @@ public void onJoin(PlayerJoinEvent e){ DisplayAPI.getScheduler().runAsync(() -> { String currentVersion = DisplayAPI.getPlugin().getPluginMeta().getVersion(); String latestVersion; + try{ latestVersion = getLatest(); + compareVersions(player, currentVersion, latestVersion); } - catch(Exception ex){ - latestVersion = null; - } - if (latestVersion == null){ + catch(IOException | InterruptedException ex){ player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Could not perform version check.", NamedTextColor.RED))); - player.sendMessage(Component.text("| GitHub has issues or your server has no internet connection", NamedTextColor.GRAY)); - return; - } - - if (!compareVersions(player, currentVersion, latestVersion)){ - player.sendMessage(DisplayAPI.pluginPrefix - .append(Component.text("New version available!", NamedTextColor.GREEN)) - .append(Component.text(" [DOWNLOAD v"+latestVersion+"]", NamedTextColor.GREEN)) - .clickEvent(ClickEvent.openUrl(LATEST_VERSION_URL))); + player.sendMessage(Component.text("| GitHub is experiencing issue or your server has no internet connection", NamedTextColor.GRAY)); } }); @@ -71,75 +65,81 @@ public void onQuit(PlayerQuitEvent e){ RelativePointUtils.removeRelativePoints(player); } - private String getLatest() throws IOException, InterruptedException, URISyntaxException { + private String getLatest() throws IOException, InterruptedException { HttpRequest getRequest = HttpRequest.newBuilder() .timeout(Duration.ofSeconds(15)) - .uri(new URI(GITHUB_API_URL)) + .uri(URI.create(GITHUB_API_URL)) .GET() .build(); - try (HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(15)).build()) { - HttpResponse response = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString()); - if (response.statusCode() == 200){ - JsonObject obj = JsonParser.parseString(response.body()).getAsJsonObject(); - return obj.get("tag_name").getAsString(); - } + HttpResponse response = HTTP_CLIENT.send(getRequest, HttpResponse.BodyHandlers.ofString()); + + if (response.statusCode() != 200) { + throw new IOException("GitHub API error: " + response.statusCode() + " - " + response.body()); + } + + JsonObject obj = JsonParser.parseString(response.body()).getAsJsonObject(); + if (!obj.has("tag_name")) { + throw new IOException("Invalid GitHub response: missing tag_name"); } - return null; + return obj.get("tag_name").getAsString(); } - //-1 = v1 is prev ver - //0 = same ver - //1 = v1 is dev version - static boolean compareVersions(Player player, String v1, String v2) { - int vPart1 = 0; - int vPart2 = 0; - - // loop until both String are processed - for (int i1 = 0, i2 = 0; (i1 < v1.length() || i2 < v2.length());) { - - //Get Version Number 1 Section - while (i1 < v1.length() && v1.charAt(i1) != '.') { - char c = v1.charAt(i1); - if (Character.isDigit(c)){ - vPart1 = vPart1 * 10 - + (c - '0'); - } - else{ - devVersion(player); - return true; - } - i1++; - } + void compareVersions(Player player, String current, String latest){ + String cleanCurrent = current.replaceAll("[^0-9.]", ""); + + boolean isDevVersion = !current.equals(cleanCurrent); + + String[] a = cleanCurrent.split("\\."); + String[] b = latest.split("\\."); - //Get Version Number 2 Section - while (i2 < v2.length() && v2.charAt(i2) != '.') { - char c = v2.charAt(i2); - if (Character.isDigit(c)){ - vPart2 = vPart2 * 10 - + (c - '0'); + int length = Math.max(a.length, b.length); + + for (int i = 0; i < length; i++){ + int num1 = i < a.length ? Integer.parseInt(a[i]) : 0; + int num2 = i < b.length ? Integer.parseInt(b[i]) : 0; + + if (num2 > num1){ //behind + if (isDevVersion){ + sendNewVersionAvailableOnDev(player, latest); } else{ - devVersion(player); - return true; + sendNewVersionAvailable(player, latest); } - i2++; + return; } + } - if (vPart1 > vPart2) //V1 newer than V2 - return true; - if (vPart2 > vPart1) //V2 newer than V1 - return false; - - //Reset - vPart1 = vPart2 = 0; - i1++; - i2++; + String cleanLatest = latest.replaceAll("[^0-9.]", ""); + if (!cleanCurrent.equals(cleanLatest)){ + sendLatestOnDev(player, latest); } - return true; //Same Ver } - private static void devVersion(Player player){ - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("You are using a dev plugin version, a new version may be available.", NamedTextColor.LIGHT_PURPLE))); + private void sendNewVersionAvailable(Player player, String latestVersion){ + player.sendMessage(DisplayAPI.pluginPrefix + .append(Component.text("New version available!", NamedTextColor.GREEN)) + .append(Component.text(" [DOWNLOAD v"+latestVersion+"]", NamedTextColor.GREEN)) + .clickEvent(ClickEvent.openUrl(LATEST_VERSION_URL))); + } + + private void sendNewVersionAvailableOnDev(Player player, String latestVersion){ + player.sendMessage(DisplayAPI.pluginPrefixLong); + player.sendMessage(MiniMessage.miniMessage().deserialize("| You are using a dev/pre-release plugin version. Unexpected issues may occur.")); + player.sendMessage(MiniMessage + .miniMessage() + .deserialize("| New Version available!:") + .append(Component.text(" [DOWNLOAD v"+latestVersion+"]", NamedTextColor.GREEN)) + .clickEvent(ClickEvent.openUrl(LATEST_VERSION_URL))); + } + + private void sendLatestOnDev(Player player, String latestVersion){ + player.sendMessage(DisplayAPI.pluginPrefixLong); + player.sendMessage(MiniMessage.miniMessage().deserialize("| You are using a dev/pre-release plugin version. Unexpected issues may occur.")); + player.sendMessage(MiniMessage + .miniMessage() + .deserialize("| Latest release:") + .append(Component.text(" [DOWNLOAD v"+latestVersion+"]", NamedTextColor.GREEN)) + .clickEvent(ClickEvent.openUrl(LATEST_VERSION_URL))); } } From d3c7372c6c3865dcde5f50d58cc54408daaff262 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Wed, 27 May 2026 05:56:17 -0500 Subject: [PATCH 03/43] Remove Internal annotation --- .../listeners/player/DEUPlayerChatListener.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/player/DEUPlayerChatListener.java b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/player/DEUPlayerChatListener.java index a5fc0b43..45c01132 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/player/DEUPlayerChatListener.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/player/DEUPlayerChatListener.java @@ -17,9 +17,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.ApiStatus; -@ApiStatus.Internal public final class DEUPlayerChatListener implements Listener { @EventHandler(priority = EventPriority.LOWEST) From 7f85c6bc2fb8cc73e3d8217c270cf9b5994e91b9 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Wed, 27 May 2026 16:53:52 -0500 Subject: [PATCH 04/43] Tags can no longer contain spaces, in animations, groups, parts, frames, etc. --- .../net/donnypz/displayentityutils/utils/DisplayUtils.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java index df873b40..7ded3566 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java @@ -840,11 +840,11 @@ static void addManyToPDCList(@NotNull Entity entity, @NotNull List eleme /** * Check if a tag is valid and can be used - * @param tag + * @param tag the tag * @return a boolean */ public static boolean isValidTag(@NotNull String tag){ - return !tag.contains(",") && !tag.startsWith("!") && !tag.isBlank(); + return !tag.isBlank() && !tag.contains(",") && !tag.startsWith("!") && tag.contains(" "); } From 520d969b52eadd5dd0584ab6c49c58c28974e47f Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Wed, 27 May 2026 16:54:57 -0500 Subject: [PATCH 05/43] Tags can no longer contain spaces, in animations, groups, parts, frames, etc. --- .../java/net/donnypz/displayentityutils/utils/DisplayUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java index 7ded3566..a79369eb 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java @@ -841,7 +841,7 @@ static void addManyToPDCList(@NotNull Entity entity, @NotNull List eleme /** * Check if a tag is valid and can be used * @param tag the tag - * @return a boolean + * @return false if the tag is blank, contains commas, starts with an "!", or contains spaces */ public static boolean isValidTag(@NotNull String tag){ return !tag.isBlank() && !tag.contains(",") && !tag.startsWith("!") && tag.contains(" "); From d34f9442e89952bf2c10f0271b1cff5ff053d6c4 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Wed, 27 May 2026 18:41:36 -0500 Subject: [PATCH 06/43] Fix condition not being inverted --- .../java/net/donnypz/displayentityutils/utils/DisplayUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java index a79369eb..35eb62e8 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java @@ -844,7 +844,7 @@ static void addManyToPDCList(@NotNull Entity entity, @NotNull List eleme * @return false if the tag is blank, contains commas, starts with an "!", or contains spaces */ public static boolean isValidTag(@NotNull String tag){ - return !tag.isBlank() && !tag.contains(",") && !tag.startsWith("!") && tag.contains(" "); + return !tag.isBlank() && !tag.contains(",") && !tag.startsWith("!") && !tag.contains(" "); } From 347a1cfd5acbd086d7288af0a481f295dd46a1ca Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Wed, 27 May 2026 18:50:46 -0500 Subject: [PATCH 07/43] Added DEUSound#fromCommand --- .../utils/DisplayEntities/DEUSound.java | 30 +++++++++++++++++++ .../convert/datapack/BDEngineDPConverter.java | 26 ++-------------- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java index 79614e6c..097fd128 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java @@ -57,6 +57,36 @@ public DEUSound(@NotNull DEUSound sound){ this.existsInGameVersion = sound.existsInGameVersion; } + /** + * Create a {@link DEUSound} from a "playsound" or "execute... run playsound" command. + * @param command the command + * @return a {@link DEUSound} or null + */ + public static @Nullable DEUSound fromCommand(@NotNull String command){ + if (command.startsWith("execute") && command.contains("playsound")){ + command = "."+command.split(" playsound ")[1]; //"." added so indexes are consistent + } + else if (!command.startsWith("playsound")){ + return null; + } + + String[] strings = command.split(" "); + String soundStr = strings[1]; + + float volume = getSoundValue(strings, 7); + float pitch = getSoundValue(strings, 8); + return new DEUSound(soundStr, volume, pitch, 0); + } + + private static float getSoundValue(String[] args, int index){ + try{ + return args.length >= index+1 ? Float.parseFloat(args[index]) : 1; + } + catch(IndexOutOfBoundsException e){ + return 1; + } + } + public void playSound(@NotNull Location location, @NotNull ActiveGroup group, @Nullable DisplayAnimator animator){ if (delay == 0){ playSound(location); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java index d0509570..18a4c2f9 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java @@ -288,13 +288,13 @@ else if (line.startsWith("schedule")) { //Animation Sound if (line.startsWith("playsound")){ - DEUSound sound = getSound(line); + DEUSound sound = DEUSound.fromCommand(line); if (sound != null){ bufferedSounds.add(sound); } } else if (line.startsWith("execute") && line.contains("playsound")){ - DEUSound sound = getSound("."+line.split(" playsound ")[1]); + DEUSound sound = DEUSound.fromCommand(line); if (sound != null){ bufferedSounds.add(sound); } @@ -327,26 +327,4 @@ private void setCameraVectorAndDirection(SpawnedDisplayAnimationFrame frame, Str cameraLoc.getWorld().spawnParticle(Particle.DRAGON_BREATH, cameraLoc, 1, 0,0,0,0); } catch(IndexOutOfBoundsException e){} } - - - DEUSound getSound(String line){ - String[] strings = line.split(" "); - String soundStr = strings[1]; - - float volume = -1; - float pitch = -1; - try{ - volume = Float.parseFloat(strings[7]); - pitch = Float.parseFloat(strings[8]); - } - catch(IllegalArgumentException | IndexOutOfBoundsException e){ - if (volume == -1){ - volume = 1; - } - if (pitch == -1){ - pitch = 1; - } - } - return new DEUSound(soundStr, volume, pitch, 0); - } } From 7ff4295af3bd4405ef7868b9b057da58c8a18d9c Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Fri, 29 May 2026 04:39:46 -0500 Subject: [PATCH 08/43] move folia checks into DisplayEntityPlugin --- .../net/donnypz/displayentityutils/DisplayAPI.java | 11 +---------- .../displayentityutils/DisplayEntityPlugin.java | 11 ++++++++++- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java b/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java index b090233f..1fe910eb 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java +++ b/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java @@ -54,7 +54,7 @@ public final class DisplayAPI { static DisplayStorage MONGODB_STORAGE; static AnimationPlayer.AnimationPlayerProvider ANIMATION_PLAYER_SERVICE; static Scheduler SCHEDULER; - private static boolean isFolia; + static boolean isFolia; private DisplayAPI(){} @@ -236,13 +236,4 @@ public static boolean isFolia(){ return isFolia; } - static void checkFolia(){ - try { - Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); - isFolia = true; - } catch (ClassNotFoundException e) { - isFolia = false; - } - } - } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java b/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java index 9951cf31..c2834d81 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java @@ -78,7 +78,7 @@ public void onEnable() { registerListeners(); initializeNamespacedKeys(); initializeBStats(); - DisplayAPI.checkFolia(); + checkFolia(); getServer().getConsoleSender().sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Plugin Enabled!", NamedTextColor.GREEN))); } @@ -88,6 +88,15 @@ public void onDisable() { MongoManager.closeConnection(); } + void checkFolia(){ + try { + Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); + DisplayAPI.isFolia = true; + } catch (ClassNotFoundException e) { + DisplayAPI.isFolia = false; + } + } + private void initializeNamespacedKeys(){ //DO NOT CHANGE DisplayAPI.partUUIDKey = new NamespacedKey(this, "partUUID"); From 8f0e89f7263798a41fed54aab6c20eb4c157e616 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Fri, 29 May 2026 22:07:38 -0500 Subject: [PATCH 09/43] update #equals #hashCode and #clone for SpawnedDisplayAnimationFrame --- .../SpawnedDisplayAnimationFrame.java | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java index 25ffd83f..4b21bfdf 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java @@ -451,6 +451,7 @@ public SpawnedDisplayAnimationFrame clone(){ cloned.displayTransformations = new HashMap<>(this.displayTransformations); cloned.interactionTransformations = new HashMap<>(this.interactionTransformations); cloned.framePoints = new HashMap<>(this.framePoints); + cloned.camera = this.camera == null ? null : new AnimationCamera(this.camera); return cloned; } catch (CloneNotSupportedException e) { @@ -460,19 +461,25 @@ public SpawnedDisplayAnimationFrame clone(){ @Override public boolean equals(Object o) { - if (this == o) return true; - if (!(o instanceof SpawnedDisplayAnimationFrame other)) return false; - - return delay == other.delay && - duration == other.duration && - Objects.equals(tag, other.tag) && - Objects.equals(displayTransformations, other.displayTransformations) && - Objects.equals(interactionTransformations, other.interactionTransformations) && - Objects.equals(framePoints, other.framePoints); + if (!(o instanceof SpawnedDisplayAnimationFrame that)) return false; + return delay == that.delay + && duration == that.duration + && Objects.equals(displayTransformations, that.displayTransformations) + && Objects.equals(interactionTransformations, that.interactionTransformations) + && Objects.equals(camera, that.camera) && Objects.equals(tag, that.tag) + && Objects.equals(framePoints, that.framePoints); } @Override public int hashCode() { - return Objects.hash(delay, duration, tag, displayTransformations, interactionTransformations, framePoints); + return Objects.hash( + displayTransformations, + interactionTransformations, + camera, + delay, + duration, + tag, + framePoints + ); } } From d50ce59c88d99cf36abd484ea0c85b870d452ebb Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Fri, 29 May 2026 22:10:28 -0500 Subject: [PATCH 10/43] added DisplayTransformation#hashCode --- .../utils/DisplayEntities/DisplayTransformation.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DisplayTransformation.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DisplayTransformation.java index e9d90372..c51c63b1 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DisplayTransformation.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DisplayTransformation.java @@ -178,6 +178,12 @@ boolean isSimilar(ActivePart part){ return isSimilar(((Display) ((SpawnedDisplayEntityPart) part).getEntity()).getTransformation()); } + /** + * Check if this is equal to another {@link DisplayTransformation}, per {@link Object#equals(Object)}.
+ * If comparing a {@link DisplayTransformation} with a {@link Transformation}, use {@link #isSimilar(Transformation)}. + * @param obj the reference object with which to compare. + * @return a boolean + */ @Override public boolean equals(Object obj) { if (this == obj) { @@ -203,6 +209,11 @@ public boolean equals(Object obj) { return Objects.equals(data, other.data); } + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), type, data); + } + Transformation toTransformation(){ return new Transformation(getTranslation(), getLeftRotation(), getScale(), getRightRotation()); } From 1cba9dda1cf9c322d0afd6c4b3c09c055921e26b Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Fri, 29 May 2026 22:10:57 -0500 Subject: [PATCH 11/43] Added #equals and #hashCode to AnimationCamera --- .../utils/DisplayEntities/AnimationCamera.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationCamera.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationCamera.java index a56c67e6..1ef01416 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationCamera.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationCamera.java @@ -7,6 +7,7 @@ import java.io.Serial; import java.io.Serializable; +import java.util.Objects; public class AnimationCamera implements Serializable { double x, y, z; @@ -68,4 +69,15 @@ public float getYaw() { public float getPitch() { return pitch; } + + @Override + public boolean equals(Object o) { + if (!(o instanceof AnimationCamera that)) return false; + return Double.compare(x, that.x) == 0 && Double.compare(y, that.y) == 0 && Double.compare(z, that.z) == 0 && Float.compare(yaw, that.yaw) == 0 && Float.compare(pitch, that.pitch) == 0; + } + + @Override + public int hashCode() { + return Objects.hash(x, y, z, yaw, pitch); + } } From a55c8478e642a25902f80b4f7647f2df88189e82 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sat, 30 May 2026 02:26:15 -0500 Subject: [PATCH 12/43] Update command-based bdengine conversion logic --- .../displayentityutils/DisplayAPI.java | 6 + .../convert/common/BDECommandConverter.java | 253 ++++++++++++++ .../convert/common/BDEConversionHandler.java | 7 + .../DisplayEntityPlugin.java | 2 + .../bdengine/BDEngineConvertDatapackCMD.java | 11 +- .../bdengine/DatapackEntitySpawned.java | 89 ++--- .../common/BDEConversionHandlerImpl.java | 13 + .../convert/datapack/BDEngineDPConverter.java | 330 ++++++++---------- .../datapack/BDEngineLegacyDPConverter.java | 5 +- 9 files changed, 465 insertions(+), 251 deletions(-) create mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java create mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandler.java create mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java diff --git a/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java b/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java index 1fe910eb..6814094b 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java +++ b/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java @@ -3,6 +3,7 @@ import net.donnypz.displayentityutils.managers.DisplayStorage; import net.donnypz.displayentityutils.managers.LoadMethod; import net.donnypz.displayentityutils.utils.DisplayEntities.AnimationPlayer; +import net.donnypz.displayentityutils.utils.bdengine.convert.common.BDEConversionHandler; import net.donnypz.displayentityutils.utils.version.folia.Scheduler; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -53,6 +54,7 @@ public final class DisplayAPI { static DisplayStorage MYSQL_STORAGE; static DisplayStorage MONGODB_STORAGE; static AnimationPlayer.AnimationPlayerProvider ANIMATION_PLAYER_SERVICE; + static BDEConversionHandler BDE_CONVERSION_HANDLER; static Scheduler SCHEDULER; static boolean isFolia; @@ -66,6 +68,10 @@ private DisplayAPI(){} return ANIMATION_PLAYER_SERVICE; } + public static @NotNull BDEConversionHandler getBDEConversionHandler(){ + return BDE_CONVERSION_HANDLER; + } + public static @NotNull NamespacedKey getPartUUIDKey() { return partUUIDKey; } diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java new file mode 100644 index 00000000..2c71bee7 --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java @@ -0,0 +1,253 @@ +package net.donnypz.displayentityutils.utils.bdengine.convert.common; + +import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.managers.DisplayAnimationManager; +import net.donnypz.displayentityutils.managers.LoadMethod; +import net.donnypz.displayentityutils.utils.ConversionUtils; +import net.donnypz.displayentityutils.utils.DisplayEntities.*; +import net.donnypz.displayentityutils.utils.version.folia.Scheduler; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Particle; +import org.bukkit.Sound; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.BlockDisplay; +import org.bukkit.entity.Display; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.UUID; + +public abstract class BDECommandConverter { + private static final String FRAME_POINT_SOUND_TAG = "deu_dp_convert"; + protected static final CommandSender SILENT_SENDER = Bukkit.createCommandSender(f -> { + }); + + protected final String conversionId; + protected final Player player; + protected final Location SPAWN_LOCATION; + protected final String SPAWN_COORDINATES; + protected final String groupSaveTag; + protected final String animationSavePrefix; + protected final boolean saveGroup; + protected final boolean saveAnimations; + protected final boolean despawnAfter; + + + protected final HashSet bufferedSounds = new HashSet<>(); + protected final UUID masterEntityUUID; + protected List animations = new ArrayList<>(); + + protected String projectName; + + /** + * @param conversionId the id used to reference this conversion later through events. + * @param spawnLocation where the conversion should take place. This should be in a loaded chunk + * @param groupSaveTag the group's tag + * @param animationSavePrefix the prefix for animation tags + * @param saveGroup whether the created group should be saved + * @param saveAnimations whether created animations should be saved + * @param despawnAfter whether the created group should be despawned after conversion + */ + public BDECommandConverter( + @Nullable String conversionId, + @NotNull Location spawnLocation, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter) { + this(conversionId, null, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + + /** + * @param player the player involved in the conversion. typically supplied when using conversion commands + * @param groupSaveTag the group's tag + * @param animationSavePrefix the prefix for animation tags + * @param saveGroup whether the created group should be saved + * @param saveAnimations whether created animations should be saved + * @param despawnAfter whether the created group should be despawned after conversion + */ + public BDECommandConverter( + @Nullable Player player, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter) { + this(null, player, player.getLocation(), groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + + /** + * @param conversionId the id used to reference this conversion later through events. + * @param player the player involved in the conversion. typically supplied when using conversion commands + * @param spawnLocation where the conversion should take place. This should be in a loaded chunk + * @param groupSaveTag the group's tag + * @param animationSavePrefix the prefix for animation tags + * @param saveGroup whether the created group should be saved + * @param saveAnimations whether created animations should be saved + * @param despawnAfter whether the created group should be despawned after conversion + */ + public BDECommandConverter( + @Nullable String conversionId, + @Nullable Player player, + @NotNull Location spawnLocation, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter) { + this.conversionId = conversionId; + this.player = player; + this.SPAWN_LOCATION = spawnLocation; + this.SPAWN_LOCATION.setPitch(0); + this.SPAWN_LOCATION.setYaw(0); + this.SPAWN_COORDINATES = ConversionUtils.getCoordinateString(SPAWN_LOCATION); + + this.groupSaveTag = groupSaveTag; + this.animationSavePrefix = animationSavePrefix; + this.saveGroup = saveGroup; + this.saveAnimations = saveAnimations; + this.despawnAfter = despawnAfter; + + Display masterEntity = spawnLocation.getWorld() + .spawn(spawnLocation, BlockDisplay.class, bd -> { + bd.setPersistent(false); + }); + this.masterEntityUUID = masterEntity.getUniqueId(); + DisplayAPI.getBDEConversionHandler().createConversionGroup(masterEntity); + } + + protected BDECommandConverter setProjectName(@NotNull String projectName) { + this.projectName = projectName; + return this; + } + + protected void processAnimation(SpawnedDisplayEntityGroup createdGroup, String animationName) { + DisplayAPI.getScheduler().partRunTimer(createdGroup.getMasterPart(), new Scheduler.SchedulerRunnable() { + final SpawnedDisplayAnimation anim = new SpawnedDisplayAnimation(); + final int frameCount = getFrameCount(animationName); + int i = 0; + + @Override + public void run() { + if (frameCount != 0) { + //Apply command to group entities (Transformations, Texture Values, etc.) + SpawnedDisplayAnimationFrame frame = executeFrameCommands(animationName, i); + frame.setDelay(0); + frame.setDuration(2); + + //Set Frame Transformation + frame.setTransformation(createdGroup); + + //Add Sounds + if (!bufferedSounds.isEmpty()) { + FramePoint point = new FramePoint(FRAME_POINT_SOUND_TAG, createdGroup, SPAWN_LOCATION); + for (DEUSound sound : bufferedSounds) { + point.addSound(sound); + } + bufferedSounds.clear(); + frame.addFramePoint(point); + } + + anim.forceAddFrame(frame); + i++; + } + + if (i == frameCount) { + try { + createdGroup.setToFrame(anim, anim.getFrames().getFirst()); + } catch (IndexOutOfBoundsException ignored) { + } + + //Save + DisplayAPI.getScheduler().run(() -> { + if (animationSavePrefix.isBlank()) { + anim.setAnimationTag(projectName.replace(".zip", "_auto_" + animationName)); + } else { + anim.setAnimationTag(animationSavePrefix + "_" + animationName); + } + + if (saveAnimations){ + boolean animationSuccess = DisplayAnimationManager.saveDisplayAnimation(LoadMethod.LOCAL, anim.toDisplayAnimation(), null); + if (animationSuccess) { + sendMessage(MiniMessage.miniMessage().deserialize("| BDEngine Animation converted and saved! (" + anim.getAnimationTag() + ")")); + playSound(Sound.ENTITY_SHEEP_SHEAR, 1, 0.75f); + animations.add(anim); + } else { + sendMessage(MiniMessage.miniMessage().deserialize("| BDEngine Animation conversion failed! Save failure. (" + anim.getAnimationTag() + ")")); + playSound(Sound.ENTITY_SHULKER_AMBIENT, 1, 1.5f); + } + } + else{ + sendMessage(MiniMessage.miniMessage().deserialize("| BDEngine Animation converted! (" + anim.getAnimationTag() + ")")); + playSound(Sound.ENTITY_SHEEP_SHEAR, 1, 0.75f); + animations.add(anim); + } + sendMessage(Component.empty()); + }); + cancel(); + } + } + }, 0, 2); //BDEngine Animation Frame Duration is 2 ticks + } + + protected abstract int getFrameCount(@NotNull String animationName); + + protected String getGroupSaveTag() { + return groupSaveTag.isBlank() ? projectName.replace(".zip", "_auto") : groupSaveTag; + } + + protected abstract SpawnedDisplayAnimationFrame executeFrameCommands(String animationName, int frameId); + + protected void executeFrameCommand(SpawnedDisplayAnimationFrame frame, String command) { + if (command.startsWith("#") || command.startsWith("schedule") || command.isEmpty()) { + return; + } + + if (command.startsWith("tp")) { + setCameraVectorAndDirection(frame, command); + } + + //Animation Sound + if (command.startsWith("playsound") || (command.startsWith("execute") && command.contains("playsound"))) { + DEUSound sound = DEUSound.fromCommand(command); + if (sound != null) { + bufferedSounds.add(sound); + } + } + + String worldName = ConversionUtils.getExecuteCommandWorldName(SPAWN_LOCATION.getWorld()); + Bukkit.dispatchCommand(SILENT_SENDER, "execute at " + masterEntityUUID + " positioned " + SPAWN_COORDINATES + " in " + worldName + " run " + command); + } + + private void setCameraVectorAndDirection(SpawnedDisplayAnimationFrame frame, String line) { + try { + String argsString = line.split("_camera,limit=1,sort=nearest] ")[1]; + String[] args = argsString.split(" "); + double x = Double.parseDouble(args[0].substring(1)); + double y = Double.parseDouble(args[1].substring(1)); + double z = Double.parseDouble(args[2].substring(1)); + double yaw = Double.parseDouble(args[3]); + double pitch = Double.parseDouble(args[4]); + frame.setAnimationCamera(new AnimationCamera(x, y, z, (float) yaw, (float) pitch)); + Location cameraLoc = SPAWN_LOCATION.clone().add(x, y, z); + cameraLoc.getWorld().spawnParticle(Particle.DRAGON_BREATH, cameraLoc, 1, 0, 0, 0, 0); + } catch (IndexOutOfBoundsException e) { + } + } + + protected void sendMessage(Component component) { + if (player != null) player.sendMessage(component); + } + + protected void playSound(Sound sound, float volume, float pitch) { + if (player != null) player.playSound(player, sound, volume, pitch); + } +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandler.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandler.java new file mode 100644 index 00000000..359036c0 --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandler.java @@ -0,0 +1,7 @@ +package net.donnypz.displayentityutils.utils.bdengine.convert.common; + +import org.bukkit.entity.Display; + +public interface BDEConversionHandler { + void createConversionGroup(Display display); +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java b/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java index c2834d81..a613aceb 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java @@ -33,6 +33,7 @@ import net.donnypz.displayentityutils.utils.DisplayEntities.AnimationPlayerProviderImpl; import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; import net.donnypz.displayentityutils.utils.DisplayEntities.machine.MachineState; +import net.donnypz.displayentityutils.utils.bdengine.convert.common.BDEConversionHandlerImpl; import net.donnypz.displayentityutils.utils.controller.DisplayControllerUtils; import net.donnypz.displayentityutils.utils.version.folia.SchedulerImpl; import net.kyori.adventure.text.Component; @@ -69,6 +70,7 @@ public void onEnable() { DisplayAPI.MONGODB_STORAGE = new MongoManager(); DisplayAPI.MYSQL_STORAGE = new MYSQLManager(); DisplayAPI.ANIMATION_PLAYER_SERVICE = new AnimationPlayerProviderImpl(); + DisplayAPI.BDE_CONVERSION_HANDLER = new BDEConversionHandlerImpl(); DisplayAPI.SCHEDULER = new SchedulerImpl(); getConfig().options().copyDefaults(true); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java index c02dddaa..651b96f6 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java @@ -29,9 +29,18 @@ public void execute(Player player, String[] args) { String datapackName = args[2]; String groupTag = args[3]; String animPrefix = args[4]; + boolean saveGroups = !groupTag.equals("-"); player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempting to convert datapack...", NamedTextColor.AQUA))); player.sendMessage(Component.text(" | DO NOT LEAVE THIS AREA UNTIL COMPLETION!", NamedTextColor.YELLOW)); player.sendMessage(Component.text(" | Conversion times may vary.", NamedTextColor.YELLOW)); - new BDEngineDPConverter(player, datapackName, groupTag, animPrefix); + new BDEngineDPConverter( + datapackName, + player, + !saveGroups ? "" : groupTag, + animPrefix, + saveGroups, + true, + false + ); } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/DatapackEntitySpawned.java b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/DatapackEntitySpawned.java index 4e9668d3..a6943024 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/DatapackEntitySpawned.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/DatapackEntitySpawned.java @@ -3,104 +3,73 @@ import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityPart; import net.donnypz.displayentityutils.utils.bdengine.convert.datapack.BDEngineDPConverter; -import org.bukkit.Location; import org.bukkit.entity.Display; import org.bukkit.entity.Entity; import org.bukkit.event.EventHandler; import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.entity.EntitySpawnEvent; -import org.jetbrains.annotations.ApiStatus; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; +import java.util.*; -@ApiStatus.Internal public final class DatapackEntitySpawned implements Listener { - private static final HashMap groups = new HashMap<>(); - private static final HashSet incomingAnimationValue = new HashSet<>(); + private static final HashMap pendingGroups = new HashMap<>(); + private static final HashSet incomingConversions = new HashSet<>(); @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onSpawn(EntitySpawnEvent e){ - if (!(e.getEntity() instanceof Display display)){ - return; - } + if (!(e.getEntity() instanceof Display display)) return; - for (Object projectName : incomingAnimationValue){ - applyToEntity(display, projectName); - } + applyToEntity(display); } - - public static SpawnedDisplayEntityGroup getProjectGroup(String projectName){ - return groups.get(projectName); + public static SpawnedDisplayEntityGroup getProjectGroup(UUID masterEntityUUID){ + return pendingGroups.get(masterEntityUUID); } public static SpawnedDisplayEntityGroup getTimestampGroup(long timestamp){ - return groups.get(timestamp); + return pendingGroups.get(timestamp); } - public static void prepareAnimationMaster(Object projectValue){ - incomingAnimationValue.add(projectValue); + incomingConversions.add(projectValue); } - - private static void applyToEntity(Display display, Object projectValue){ + private static void applyToEntity(Display display){ + UUID rootUUID; for (String tag : display.getScoreboardTags()){ - if (tag.contains(String.valueOf(projectValue))){ - SpawnedDisplayEntityGroup group = groups.get(projectValue); - if (group == null){ - storeGroupAnimation(projectValue, display); - } - - - else { - Location groupLoc = group.getLocation(); - if (groupLoc != null){ - if (groupLoc.distanceSquared(display.getLocation()) > 0.25){ //0.5^2, display can't be part of group - return; - } - } - - //LEGACY ANIMATIONS - //Add parts that aren't grouped/animated later to the group, so the animation can be used - //for other display entities, not created through the animator, (or spawned later, after conversion) - //DisplayEntityGroups created outside the animator spawn ungrouped parts last, - //while the animator spawns them after the MAIN master part - if (tag.contains(projectValue +"_")) { - display.addScoreboardTag(BDEngineDPConverter.UNGROUPED_ADD_LATER_TAG); - } - group.addDisplayEntity(display); + if (tag.startsWith(BDEngineDPConverter.CONVERSION_SCOREBOARD_PREFIX)){ + String uuidStr = tag.substring(BDEngineDPConverter.CONVERSION_SCOREBOARD_PREFIX.length()); + rootUUID = UUID.fromString(uuidStr); + SpawnedDisplayEntityGroup g = pendingGroups.get(rootUUID); + for (Entity e : display.getPassengers()){ + g.addEntity(e); } + display.remove(); return; } } } - private static void storeGroupAnimation(Object projectValue, Display master){ - if (groups.containsKey(projectValue)){ + + public static void createNewGroup(Display master){ + if (pendingGroups.containsKey(master.getUniqueId())){ throw new RuntimeException("Failed to successfully convert animation, conversion may already be in progress?"); } - groups.put(projectValue, new SpawnedDisplayEntityGroup(master)); + pendingGroups.put(master.getUniqueId(), new SpawnedDisplayEntityGroup(master)); } - - @ApiStatus.Internal public static void finalizeAnimationPreparation(Object projectValue){ - SpawnedDisplayEntityGroup group = groups.get(projectValue); - finalize(group); + SpawnedDisplayEntityGroup group = pendingGroups.get(projectValue); + //finalize(group); - groups.remove(projectValue); - incomingAnimationValue.remove(projectValue); + pendingGroups.remove(projectValue); + incomingConversions.remove(projectValue); } private static void finalize(SpawnedDisplayEntityGroup group){ if (group != null){ - List laterParts = new ArrayList<>(); Entity masterPart = group.getMasterPart().getEntity(); for (SpawnedDisplayEntityPart part : group.getDisplayParts()){ if (part.isMaster()){ @@ -111,17 +80,11 @@ private static void finalize(SpawnedDisplayEntityGroup group){ if (display.getScoreboardTags().contains(BDEngineDPConverter.CONVERT_DELETE_SUB_PARENT_TAG)){ part.remove(true); } - else if (display.getScoreboardTags().contains(BDEngineDPConverter.UNGROUPED_ADD_LATER_TAG)){ - laterParts.add(part.remove(false)); - } else{ masterPart.addPassenger(display); } } - for (Entity partEntity : laterParts){ - group.addEntity(partEntity); - } } } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java new file mode 100644 index 00000000..8369799a --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java @@ -0,0 +1,13 @@ +package net.donnypz.displayentityutils.utils.bdengine.convert.common; + +import net.donnypz.displayentityutils.listeners.bdengine.DatapackEntitySpawned; +import org.bukkit.entity.Display; + +public class BDEConversionHandlerImpl implements BDEConversionHandler{ + + @Override + public void createConversionGroup(Display display) { + DatapackEntitySpawned.createNewGroup(display); + } + +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java index 18a4c2f9..b9812818 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java @@ -5,16 +5,17 @@ import net.donnypz.displayentityutils.managers.*; import net.donnypz.displayentityutils.utils.ConversionUtils; import net.donnypz.displayentityutils.utils.DisplayEntities.*; +import net.donnypz.displayentityutils.utils.bdengine.convert.common.BDECommandConverter; import net.donnypz.displayentityutils.utils.version.VersionUtils; -import net.donnypz.displayentityutils.utils.version.folia.Scheduler; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.*; -import org.bukkit.command.CommandSender; +import org.bukkit.command.CommandException; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.BufferedReader; import java.io.IOException; @@ -24,81 +25,89 @@ import java.util.zip.ZipEntry; import java.util.zip.ZipFile; -public class BDEngineDPConverter { +public class BDEngineDPConverter extends BDECommandConverter { - private static final String FRAME_POINT_SOUND_TAG = "deu_dp_convert"; public static final String CONVERT_DELETE_SUB_PARENT_TAG = "deu_delete_sub_parent"; - public static final String UNGROUPED_ADD_LATER_TAG = "deu_add_later"; + public static final String CONVERSION_SCOREBOARD_PREFIX = "deu_dp_conversion_"; - static final CommandSender silentSender = Bukkit.createCommandSender(feedback -> {}); static final String FUNCTION_FOLDER = VersionUtils.IS_1_21 ? "function" : "functions"; private static final String CREATE_MODEL_PATH = "/create.mcfunction"; - - private String projectName = null; - private final String groupSaveTag; - private final String animationSavePrefix; private final LinkedHashMap> animations = new LinkedHashMap<>(); - private final HashSet bufferedSounds = new HashSet<>(); + private final ZipFile zipFile; + + + public BDEngineDPConverter(@NotNull String datapackName, + @Nullable String conversionId, + @NotNull Location spawnLocation, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter) { + this(datapackName, conversionId, null, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + + public BDEngineDPConverter(@NotNull String datapackName, + @Nullable Player player, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter) { + this(datapackName, null, player, player.getLocation(), groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } - Location spawnLoc; + public BDEngineDPConverter(@NotNull String datapackName, + @Nullable String conversionId, + @Nullable Player player, + @NotNull Location spawnLocation, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter) { + super(conversionId, player, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); - public BDEngineDPConverter(@NotNull Player player, @NotNull String datapackName, @NotNull String groupSaveTag, @NotNull String animationSavePrefix){ - this.groupSaveTag = groupSaveTag; - this.animationSavePrefix = animationSavePrefix; - spawnLoc = player.getLocation(); - spawnLoc.setPitch(0); - spawnLoc.setYaw(0); if (!datapackName.endsWith(".zip")){ datapackName = datapackName+".zip"; } + + ZipFile zip; try{ - ZipFile zipFile = new ZipFile(PluginFolders.animDatapackFolder+"/"+datapackName); - searchEntries(player, datapackName, zipFile.entries(), zipFile); + zip = new ZipFile(PluginFolders.animDatapackFolder+"/"+datapackName); } catch (IOException e) { - player.sendMessage(DisplayAPI.pluginPrefix + this.zipFile = null; + super.sendMessage(DisplayAPI.pluginPrefix .append(Component.text("Failed to find datapack with the provided name! Ensure that the datapack is placed in the \"bdenginedatapacks\" folder of this plugin, as a .zip file", NamedTextColor.RED))); + return; } - } - private boolean isAnimationEntry(String entryName){ - return !entryName.endsWith(".mcfunction") && (entryName.contains("/"+ FUNCTION_FOLDER +"/a/") && !entryName.endsWith("/"+ FUNCTION_FOLDER +"/a/")); + this.zipFile = zip; + searchEntries(); } - private boolean isSoundPath(String entryName){ - return entryName.contains("/"+ FUNCTION_FOLDER +"/k_s"); - } - - private String getAnimationName(String entryName, String folder){ - String animName = entryName.split(FUNCTION_FOLDER +"/"+folder+"/")[1]; - return animName.endsWith(".mcfunction") ? animName.split("/")[0] : animName.substring(0, animName.length()-1); - } - - private void getProjectName(String entryName){ + private void spawnModel(ZipEntry createModelEntry){ + //Set projectName from entry if (projectName == null){ - this.projectName = entryName.split("/"+ FUNCTION_FOLDER +"/")[0].substring(5); + // data/{projectName}/functions + String projectName = createModelEntry.getName().split("/"+ FUNCTION_FOLDER +"/")[0].substring(5); + this.setProjectName(projectName); } - } - private void spawnModel(Player player, ZipEntry createModelEntry, ZipFile zipFile){ - getProjectName(createModelEntry.getName()); - executeCommands(createModelEntry, zipFile, player, null); + executeCommands(createModelEntry); } - private void addFrame(ZipEntry entry){ - String animName = getAnimationName(entry.getName(), "k"); - ArrayList frames = animations.computeIfAbsent(animName, k -> new ArrayList<>()); - frames.add(entry); - } + private void searchEntries(){ + Enumeration entries = zipFile.entries(); - private void searchEntries(Player player, String datapackName, Enumeration entries, ZipFile zipFile){ while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String entryName = entry.getName(); - //Summon model + //Spawn model if (entryName.endsWith(CREATE_MODEL_PATH)){ - spawnModel(player, entry, zipFile); + spawnModel(entry); } //Add Animation else if (isAnimationEntry(entryName)){ //Add animation @@ -111,123 +120,63 @@ else if (!isSoundPath(entryName) && entryName.contains("/keyframe_") && entryNam } //Save Model and Animations - DisplayAPI.getScheduler().runLater(() -> playAndSave(player, datapackName, zipFile), 30); + DisplayAPI.getScheduler().runLater(this::saveAndConvert, 1); + } + + private void addFrame(ZipEntry entry){ + String animName = getAnimationName(entry.getName(), "k"); + ArrayList frames = animations.computeIfAbsent(animName, k -> new ArrayList<>()); + frames.add(entry); } - private void playAndSave(Player player, String datapackName, ZipFile zipFile){ - SpawnedDisplayEntityGroup createdGroup = DatapackEntitySpawned.getProjectGroup(projectName); + private void saveAndConvert(){ + SpawnedDisplayEntityGroup createdGroup = DatapackEntitySpawned.getProjectGroup(masterEntityUUID); if (createdGroup == null){ - player.sendMessage(Component.text("Failed to find model/group created from datapack!", NamedTextColor.RED)); - player.sendMessage(Component.text("| The datapack may be a legacy one (Before BDEngine v1.13). Try using /deu bdengine convertdpleg", NamedTextColor.GRAY, TextDecoration.ITALIC)); - player.sendMessage(Component.text("| The datapack may have been generated for a different game version", NamedTextColor.GRAY, TextDecoration.ITALIC)); + super.sendMessage(Component.text("Failed to find model/group created from datapack!", NamedTextColor.RED)); + super.sendMessage(Component.text("| The datapack may be a legacy one (Before BDEngine v1.13). Try using /deu bdengine convertdpleg", NamedTextColor.GRAY, TextDecoration.ITALIC)); + super.sendMessage(Component.text("| The datapack may have been generated for a different game version", NamedTextColor.GRAY, TextDecoration.ITALIC)); return; } - createdGroup.setTag(groupSaveTag.isBlank() ? datapackName.replace(".zip", "_auto") : groupSaveTag); - DatapackEntitySpawned.finalizeAnimationPreparation(projectName); + createdGroup.setTag(super.getGroupSaveTag()); + DatapackEntitySpawned.finalizeAnimationPreparation(masterEntityUUID); createdGroup.seedPartUUIDs(SpawnedDisplayEntityGroup.DEFAULT_PART_UUID_SEED); - player.sendMessage(Component.empty()); - boolean save = !groupSaveTag.equals("-"); + super.sendMessage(Component.empty()); int delay = 0; for (String animName : animations.sequencedKeySet()){ List frames = animations.get(animName); DisplayAPI.getScheduler().runLater(() -> { - player.sendMessage(MiniMessage.miniMessage().deserialize("Converting Animation: "+animName)); - processAnimation(createdGroup, zipFile, frames, datapackName, animName, player); + super.sendMessage(MiniMessage.miniMessage().deserialize("Converting Animation: "+animName)); + processAnimation(createdGroup, animName); }, delay); delay+=(frames.size()*2); } //Despawn group after animation conversions DisplayAPI.getScheduler().runLater(() -> { - player.sendMessage(Component.empty()); - if (save){ + if (saveGroup){ DisplayGroupManager.saveDisplayEntityGroup(LoadMethod.LOCAL, createdGroup.toDisplayEntityGroup(), player); - player.sendMessage(MiniMessage.miniMessage().deserialize("Group Tag: "+createdGroup.getTag())); + super.sendMessage(MiniMessage.miniMessage().deserialize("Group Tag: "+createdGroup.getTag())); } else{ - player.sendMessage(Component.text("The group will not be saved due to setting the group tag to \"-\"", NamedTextColor.GRAY)); + super.sendMessage(Component.text("| The group was not saved.", NamedTextColor.GRAY)); } - DisplayAPI.getScheduler().run(() -> createdGroup.unregister(true, true)); + if (despawnAfter){ + DisplayAPI.getScheduler().run(() -> createdGroup.unregister(true, true)); + } try{ zipFile.close(); } catch(IOException ignored){} - }, delay+5); - } - - - private void processAnimation(SpawnedDisplayEntityGroup createdGroup, ZipFile zipFile, List frames, String datapackName, String animName, Player player){ - DisplayAPI.getScheduler().partRunTimer(createdGroup.getMasterPart(), new Scheduler.SchedulerRunnable() { - final SpawnedDisplayAnimation anim = new SpawnedDisplayAnimation(); - final int frameCount = frames.size(); - int i = 0; - @Override - public void run() { - if (i == frameCount){ - try{ - createdGroup.setToFrame(anim, anim.getFrames().getFirst()); - } - catch(IndexOutOfBoundsException ignored){} - - //Save - DisplayAPI.getScheduler().runLater(() -> { - if (animationSavePrefix.isBlank()){ - anim.setAnimationTag(datapackName.replace(".zip", "_auto_"+animName)); - } - else{ - anim.setAnimationTag(animationSavePrefix+"_"+animName); - } - boolean animationSuccess = DisplayAnimationManager.saveDisplayAnimation(LoadMethod.LOCAL, anim.toDisplayAnimation(), null); - anim.remove(); - - if (animationSuccess){ - player.sendMessage(MiniMessage.miniMessage().deserialize("BDEngine Animation Converted! ("+anim.getAnimationTag()+")")); - player.playSound(player, Sound.ENTITY_SHEEP_SHEAR, 1, 0.75f); - } - else{ - player.sendMessage(MiniMessage.miniMessage().deserialize("BDEngine Animation Conversion Failed! ("+anim.getAnimationTag()+")")); - player.playSound(player, Sound.ENTITY_SHULKER_AMBIENT, 1, 1.5f); - } - }, 1); - cancel(); - return; - } - - SpawnedDisplayAnimationFrame frame = new SpawnedDisplayAnimationFrame(0, 2); - - //Apply Transformations, Texture Values, etc. - ZipEntry entry = frames.get(i); - executeCommands(entry, zipFile, player, frame); - - //Create Frame - frame.setTransformation(createdGroup); - - //Add Sounds - if (!bufferedSounds.isEmpty()){ - FramePoint point = new FramePoint(FRAME_POINT_SOUND_TAG, createdGroup, spawnLoc); - for (DEUSound sound : bufferedSounds){ - point.addSound(sound); - } - bufferedSounds.clear(); - frame.addFramePoint(point); - } - - anim.addFrame(frame); - i++; - } - }, 0, 2); //BDEngine Animation Frame Duration is 2 ticks + }, delay+2); } - private void executeCommands(ZipEntry zipEntry, - ZipFile zipFile, - Player player, - SpawnedDisplayAnimationFrame frame){ + private void executeCommands(ZipEntry zipEntry){ try(InputStream stream = zipFile.getInputStream(zipEntry); BufferedReader br = new BufferedReader(new InputStreamReader(stream))){ String line; @@ -247,84 +196,93 @@ private void executeCommands(ZipEntry zipEntry, continue; } - //Master Part - //if (line.contains("execute as @s")){ - if (line.startsWith("summon block_display")){ - String coordinates = ConversionUtils.getCoordinateString(spawnLoc); - String replacement = "execute at "+player.getName()+" run summon block_display "+coordinates; - line = line.replace("summon block_display ~ ~ ~", replacement); //Before BDEngine 1.15.3 - line = line.replace("summon block_display ~-0.5 ~-0.5 ~-0.5", replacement); //BDEngine 1.15.3 and Later - try{ - DatapackEntitySpawned.prepareAnimationMaster(projectName); - } - catch(NumberFormatException | ArrayIndexOutOfBoundsException e){ - player.sendMessage(Component.text("Animation conversion failed! Read console", NamedTextColor.RED)); - String message = e instanceof NumberFormatException ? "Invalid timestamp value" : "Wrong game version downloaded"; - throw new RuntimeException("Failed to read command from zip entry: "+message, e); - } + //Parent/Master/Root or Camera Spawning + if (line.startsWith("summon block_display") ){ + continue; } //Sub-Master Parts else if (line.contains("@s")){ - line = line.replace("@s", player.getName()); - line = "execute"+line.split("nearest]")[1]; + line = line.split("@s run ", 2)[1]; + line = line.substring(0, line.length()-2)+",\""+CONVERSION_SCOREBOARD_PREFIX+masterEntityUUID+"\"]}"; } if (!line.startsWith("ride")) { line = line.substring(0, line.length()-2)+",\""+CONVERT_DELETE_SUB_PARENT_TAG+"\"]}"; } + else{ + line = line.split("mount")[0]+"mount "+masterEntityUUID; + } } else if (line.startsWith("schedule")) { continue; } + String coordinates = ConversionUtils.getCoordinateString(SPAWN_LOCATION); + World w = SPAWN_LOCATION.getWorld(); + String worldName = ConversionUtils.getExecuteCommandWorldName(w); - if (line.startsWith("tp @e[type=minecraft:block_display,tag="+projectName+"_camera,limit=1,sort=nearest]")){ - if (frame != null){ - setCameraVectorAndDirection(frame, line); - } - continue; + String finalCommand = String.format( + "execute at %s positioned %s in %s run %s", + masterEntityUUID, + coordinates, + worldName, + line + ); + try{ + Bukkit.dispatchCommand(SILENT_SENDER, finalCommand); } - - //Animation Sound - if (line.startsWith("playsound")){ - DEUSound sound = DEUSound.fromCommand(line); - if (sound != null){ - bufferedSounds.add(sound); - } + catch(CommandException e){ + super.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Animation conversion failed! Error in console...", NamedTextColor.RED))); + super.sendMessage(Component.text("| Is your datapack for the correct server version?", NamedTextColor.GRAY, TextDecoration.ITALIC)); + throw new RuntimeException("Failed to read command", e); } - else if (line.startsWith("execute") && line.contains("playsound")){ - DEUSound sound = DEUSound.fromCommand(line); - if (sound != null){ - bufferedSounds.add(sound); - } - } - - String coordinates = ConversionUtils.getCoordinateString(spawnLoc); - World w = spawnLoc.getWorld(); - String worldName = ConversionUtils.getExecuteCommandWorldName(w); - Bukkit.dispatchCommand(silentSender, "execute at "+player.getName()+" positioned "+coordinates+" in "+worldName+" run "+line); } } catch (IOException e){ - player.sendMessage(Component.text("Animation conversion failed! Read console")); + super.sendMessage(Component.text("Animation conversion failed! Read console")); throw new RuntimeException("Failed to execute command from ZipEntry: "+zipEntry.getName(), e); } } - private void setCameraVectorAndDirection(SpawnedDisplayAnimationFrame frame, String line){ - try{ - String argsString = line.split("_camera,limit=1,sort=nearest] ")[1]; - String[] args = argsString.split(" "); - double x = Double.parseDouble(args[0].substring(1)); - double y = Double.parseDouble(args[1].substring(1)); - double z = Double.parseDouble(args[2].substring(1)); - double yaw = Double.parseDouble(args[3]); - double pitch = Double.parseDouble(args[4]); - frame.setAnimationCamera(new AnimationCamera(x, y, z, (float) yaw, (float) pitch)); - Location cameraLoc = spawnLoc.clone().add(x, y, z); - cameraLoc.getWorld().spawnParticle(Particle.DRAGON_BREATH, cameraLoc, 1, 0,0,0,0); - } catch(IndexOutOfBoundsException e){} + private boolean isAnimationEntry(String entryName){ + return !entryName.endsWith(".mcfunction") && (entryName.contains("/"+ FUNCTION_FOLDER +"/a/") && !entryName.endsWith("/"+ FUNCTION_FOLDER +"/a/")); + } + + private boolean isSoundPath(String entryName){ + return entryName.contains("/"+ FUNCTION_FOLDER +"/k_s"); + } + + private String getAnimationName(String entryName, String folder){ + String animName = entryName.split(FUNCTION_FOLDER +"/"+folder+"/")[1]; + return animName.endsWith(".mcfunction") ? animName.split("/")[0] : animName.substring(0, animName.length()-1); + } + + @Override + protected int getFrameCount(@NotNull String animationName) { + List entries = animations.get(animationName); + return entries == null ? 0 : entries.size(); + } + + @Override + protected SpawnedDisplayAnimationFrame executeFrameCommands(String animationName, int frameId) { + List frames = animations.get(animationName); + ZipEntry zipEntry = frames.get(frameId); + SpawnedDisplayAnimationFrame frame = new SpawnedDisplayAnimationFrame(0, 2); + + try(InputStream stream = zipFile.getInputStream(zipEntry); + BufferedReader br = new BufferedReader(new InputStreamReader(stream))){ + + String line; + while ((line = br.readLine()) != null){ + super.executeFrameCommand(frame, line); + } + return frame; + } + catch (IOException e){ + super.sendMessage(Component.text("Animation conversion failed! Read console")); + throw new RuntimeException("Failed to execute command from ZipEntry: "+zipEntry.getName(), e); + } } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineLegacyDPConverter.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineLegacyDPConverter.java index 999cf20a..b29e5bfb 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineLegacyDPConverter.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineLegacyDPConverter.java @@ -13,6 +13,7 @@ import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Sound; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; @@ -30,6 +31,8 @@ @ApiStatus.Internal public class BDEngineLegacyDPConverter { + private static final CommandSender silentSender = Bukkit.createCommandSender(feedback -> {}); + @ApiStatus.Internal public static void saveDatapackAnimation(@NotNull Player player, @NotNull String datapackName, @NotNull String groupSaveTag, @NotNull String animationSaveTag){ if (!datapackName.endsWith(".zip")){ @@ -244,7 +247,7 @@ else if (line.contains("function")) { continue; } - Bukkit.dispatchCommand(BDEngineDPConverter.silentSender, line); + Bukkit.dispatchCommand(silentSender, line); } br.close(); From affd0bf2af973882191bf0897786380daeb54f3d Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sat, 30 May 2026 02:31:29 -0500 Subject: [PATCH 13/43] End support for legacy datapack conversion --- .../command/bdengine/BDEngineCMD.java | 5 +- .../BDEngineConvertLegacyDatapackCMD.java | 38 --- .../convert/datapack/BDEngineDPConverter.java | 3 +- .../datapack/BDEngineLegacyDPConverter.java | 270 ------------------ 4 files changed, 2 insertions(+), 314 deletions(-) delete mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertLegacyDatapackCMD.java delete mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineLegacyDPConverter.java diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineCMD.java index 8a8231ef..c353bca4 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineCMD.java @@ -11,7 +11,6 @@ public final class BDEngineCMD extends ConsoleUsableSubCommand { public BDEngineCMD(){ super(Permission.HELP, new BDEngineHelpCMD()); new BDEngineConvertDatapackCMD(this); - new BDEngineConvertLegacyDatapackCMD(this); new BDEngineImportModelCMD(this); new BDEngineSpawnModelCMD(this); } @@ -40,9 +39,7 @@ static void help(CommandSender sender, int page){ CMDUtils.sendCMD(sender, "/deu bdengine spawnmodel ", "Spawn a model from a BDEngine file located in the plugin's \"bdenginefiles\" folder"); CMDUtils.sendCMD(sender, "/deu bdengine importmodel ", "Import a model directly from BDEngine's Catalog into your game world"); CMDUtils.sendCMD(sender, "/deu bdengine convertdp ", - "Convert a datapack from BDEngine into group and animation files usable for DisplayEntityUtils"); - CMDUtils.sendCMD(sender, "/deu bdengine convertdpleg ", - "Convert an old datapack from BDEngine, before BDEngine v1.13 (Dec. 8th 2024), into group and animation files usable for DisplayEntityUtils"); + "Convert BDEngine datapack into group and animation formats this plugin uses"); sender.sendMessage(MiniMessage.miniMessage().deserialize("--------------------------")); } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertLegacyDatapackCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertLegacyDatapackCMD.java deleted file mode 100644 index 409488c4..00000000 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertLegacyDatapackCMD.java +++ /dev/null @@ -1,38 +0,0 @@ -package net.donnypz.displayentityutils.command.bdengine; - -import net.donnypz.displayentityutils.DisplayAPI; -import net.donnypz.displayentityutils.command.DEUSubCommand; -import net.donnypz.displayentityutils.command.Permission; -import net.donnypz.displayentityutils.command.PlayerSubCommand; -import net.donnypz.displayentityutils.utils.bdengine.convert.datapack.BDEngineLegacyDPConverter; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -class BDEngineConvertLegacyDatapackCMD extends PlayerSubCommand { - BDEngineConvertLegacyDatapackCMD(@NotNull DEUSubCommand parentSubCommand) { - super("convertdpleg", parentSubCommand, Permission.BDENGINE_CONVERT_DATAPACK); - setTabComplete(2, ""); - setTabComplete(3, ""); - setTabComplete(4, ""); - } - - @Override - public void execute(Player player, String[] args) { - if (args.length < 5) { - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Incorrect Usage! /deu bdengine convertdpleg ", NamedTextColor.RED))); - player.sendMessage(Component.text("Use \"-\" for the group tag if you do not want to save the group", NamedTextColor.GRAY)); - return; - } - - String datapackName = args[2]; - String groupTag = args[3]; - String animPrefix = args[4]; - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempting to convert datapack...", NamedTextColor.AQUA))); - player.sendMessage(Component.text(" | DO NOT LEAVE THIS AREA UNTIL COMPLETION!", NamedTextColor.YELLOW)); - player.sendMessage(Component.text(" | Conversion time may vary.", NamedTextColor.YELLOW)); - player.sendMessage(Component.text(" | Entities may not be visible while converting.", NamedTextColor.YELLOW)); - BDEngineLegacyDPConverter.saveDatapackAnimation(player, datapackName, groupTag, animPrefix); - } -} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java index b9812818..fb872b64 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java @@ -133,8 +133,7 @@ private void saveAndConvert(){ SpawnedDisplayEntityGroup createdGroup = DatapackEntitySpawned.getProjectGroup(masterEntityUUID); if (createdGroup == null){ super.sendMessage(Component.text("Failed to find model/group created from datapack!", NamedTextColor.RED)); - super.sendMessage(Component.text("| The datapack may be a legacy one (Before BDEngine v1.13). Try using /deu bdengine convertdpleg", NamedTextColor.GRAY, TextDecoration.ITALIC)); - super.sendMessage(Component.text("| The datapack may have been generated for a different game version", NamedTextColor.GRAY, TextDecoration.ITALIC)); + super.sendMessage(Component.text("| The datapack may have been generated for a different game version or of an older BDEngine format", NamedTextColor.GRAY, TextDecoration.ITALIC)); return; } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineLegacyDPConverter.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineLegacyDPConverter.java deleted file mode 100644 index b29e5bfb..00000000 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineLegacyDPConverter.java +++ /dev/null @@ -1,270 +0,0 @@ -package net.donnypz.displayentityutils.utils.bdengine.convert.datapack; - -import net.donnypz.displayentityutils.DisplayAPI; -import net.donnypz.displayentityutils.listeners.bdengine.DatapackEntitySpawned; -import net.donnypz.displayentityutils.managers.*; -import net.donnypz.displayentityutils.utils.ConversionUtils; -import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; -import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimationFrame; -import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; -import net.donnypz.displayentityutils.utils.version.folia.Scheduler; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.Sound; -import org.bukkit.command.CommandSender; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.ApiStatus; -import org.jetbrains.annotations.NotNull; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; - -@ApiStatus.Internal -public class BDEngineLegacyDPConverter { - - private static final CommandSender silentSender = Bukkit.createCommandSender(feedback -> {}); - - @ApiStatus.Internal - public static void saveDatapackAnimation(@NotNull Player player, @NotNull String datapackName, @NotNull String groupSaveTag, @NotNull String animationSaveTag){ - if (!datapackName.endsWith(".zip")){ - datapackName = datapackName+".zip"; - } - - player.sendMessage(Component.text("Legacy Conversion - If no model/group/result is present, the datapack is likely a modern one", NamedTextColor.LIGHT_PURPLE)); - try{ - ZipFile zipFile = new ZipFile(PluginFolders.animDatapackFolder+"/"+datapackName); - - Enumeration entries = zipFile.entries(); - List frames = new ArrayList<>(); - - long timestamp = 0; - int totalGroups = 0; - Location pLoc = player.getLocation(); - while (entries.hasMoreElements()){ - ZipEntry entry = entries.nextElement(); - if (entry.isDirectory()){ - continue; - } - if (entry.getName().contains("animation_keyframe")){ - frames.add(entry); - continue; - } - if (entry.getName().contains("summon.mcfunction")){ - timestamp = executeCommands(entry, zipFile, player, pLoc, true); - } - if (entry.getName().contains("start_animation.mcfunction")){ - totalGroups = (int) executeCommands(entry, zipFile, player, pLoc, true); - } - } - String finalDatapackName = datapackName; - if (!frames.isEmpty() && timestamp > 0 && totalGroups > 0){ - int finalTotalGroups = totalGroups; - long finalTimestamp = timestamp; - DisplayAPI.getScheduler().runLater(() -> { - readAnimationFiles(finalTimestamp, finalTotalGroups, zipFile, frames, finalDatapackName, player, groupSaveTag, animationSaveTag); - }, 30); - } - } catch (IOException e) { - player.sendMessage(DisplayAPI.pluginPrefix - .append(Component.text("Failed to find datapack with the provided name! Ensure that the datapack is placed in the \"bdenginedatapacks\" folder of this plugin, as a .zip file", NamedTextColor.RED))); - } - } - - private static void readAnimationFiles(long timeStamp, int totalGroups, ZipFile zipFile, List frames, String datapackName, @NotNull Player player, @NotNull String groupSaveTag, @NotNull String animationSaveTag){ - SpawnedDisplayEntityGroup createdGroup = DatapackEntitySpawned.getTimestampGroup(timeStamp); - if (createdGroup == null){ - player.sendMessage(Component.text("Failed to find model/group created from datapack!", NamedTextColor.RED)); - player.sendMessage(Component.text("| The datapack may be a modern one (v1.13+ of BDEngine). Try using /deu bdengine convertdp")); - return; - } - - DatapackEntitySpawned.finalizeAnimationPreparation(timeStamp); - createdGroup.seedPartUUIDs(SpawnedDisplayEntityGroup.DEFAULT_PART_UUID_SEED); - final SpawnedDisplayAnimation anim = new SpawnedDisplayAnimation(); - - - DisplayAPI.getScheduler().partRunTimer(createdGroup.getMasterPart(), new Scheduler.SchedulerRunnable() { - int i = 0; - @Override - public void run() { - if (i == frames.size()){ - try{ - createdGroup.setToFrame(anim, anim.getFrames().getFirst()); - } - catch(IndexOutOfBoundsException ignored){} - - - //Save with first frame applied to group - DisplayAPI.getScheduler().runLater(() -> { - if (animationSaveTag.isBlank()){ - anim.setAnimationTag(datapackName.replace(".zip", "_autoconvert")); - } - else{ - anim.setAnimationTag(animationSaveTag); - } - boolean animationSuccess = DisplayAnimationManager.saveDisplayAnimation(LoadMethod.LOCAL, anim.toDisplayAnimation(), player); - anim.remove(); - - boolean groupSuccess; - player.sendMessage(Component.empty()); - if (!groupSaveTag.equals("-")){ - if (groupSaveTag.isBlank()){ - createdGroup.setTag(datapackName.replace(".zip", "_autoconvert")); - } - else{ - createdGroup.setTag(groupSaveTag); - } - groupSuccess = DisplayGroupManager.saveDisplayEntityGroup(LoadMethod.LOCAL, createdGroup.toDisplayEntityGroup(), player); - } - else{ - groupSuccess = false; - } - - createdGroup.unregister(true, true); - - if (animationSuccess){ - player.sendMessage(DisplayAPI.pluginPrefix - .append(Component.text("Successfully converted BDEngine animation to DisplayAnimation.", NamedTextColor.GREEN))); - player.sendMessage(Component.text(" | Animation Tag: "+anim.getAnimationTag(), NamedTextColor.GRAY)); - player.playSound(player, Sound.ENTITY_SHEEP_SHEAR, 1, 0.75f); - } - - - if (groupSuccess){ - player.sendMessage(Component.text(" | Group Tag: "+createdGroup.getTag(), NamedTextColor.GRAY)); - } - else{ - if (groupSaveTag.equals("-")){ - player.sendMessage(Component.text("The group was not created during this conversion due to setting the group tag to \"-\"", NamedTextColor.GRAY)); - } - else{ - player.sendMessage(Component.text("An error occured when saving the group created from BDEngine conversion, refer to console and report errors if necessary", NamedTextColor.RED)); - } - } - player.sendMessage(Component.empty()); - - try{ - zipFile.close(); - } - catch(IOException e){ - throw new RuntimeException("Failed to close zip file"); - } - },1); - cancel(); - return; - } - - - - //Plays the command for the next frame for every group in the animation - //If 2 groups exist, it will execute animation_keyframe0_1.mcfunction and animation_keyframe0_2.mcfunction - //then animation_keyframe1_1.mcfunction and animation_keyframe1_2.mcfunction on the next iteration, and so on... - Location pLoc = player.getLocation(); - for (int j = 0; j < totalGroups; j++){ - ZipEntry entry = frames.get(i); - executeCommands(entry, zipFile, player, pLoc, false); - i++; - } - - //Create Frame - SpawnedDisplayAnimationFrame frame = new SpawnedDisplayAnimationFrame(0, 2).setTransformation(createdGroup); - if (!frame.isEmptyFrame()){ - anim.addFrame(frame); - } - } - }, 2, 2); - } - - - private static long executeCommands(ZipEntry zipEntry, ZipFile zipFile, Player player, Location location, boolean expectData){ - try{ - InputStream stream = zipFile.getInputStream(zipEntry); - BufferedReader br = new BufferedReader(new InputStreamReader(stream)); - String line; - long value = 0; - while ((line = br.readLine()) != null){ - if (line.startsWith("#") || line.isEmpty()){ - continue; - } - - if (zipEntry.getName().contains("summon")){ - String projectName = zipEntry.getName(); - projectName = projectName.replace(BDEngineDPConverter.FUNCTION_FOLDER +"/summon.mcfunction", ""); - - String[] splitDir = projectName.split("/"); - projectName = splitDir[splitDir.length-1]; - - //Master Part - if (line.contains("execute as @s")){ - - String coordinates = ConversionUtils.getCoordinateString(location); - String replacement = "execute at "+player.getName()+" run summon block_display "+coordinates; - line = line.replace("execute as @s run summon block_display ~ ~ ~", replacement); - String[] timestampSplit = line.split(":\\[\""+projectName.replace("_", "")); - try{ - value = Long.parseLong(timestampSplit[1].replace("\"]}", "")); - DatapackEntitySpawned.prepareAnimationMaster(value); - } - catch(NumberFormatException e){ - br.close(); - zipFile.close(); - player.sendMessage(Component.text("Animation conversion failed! Read console", NamedTextColor.RED)); - throw new RuntimeException("Failed to read command from zip entry: Invalid timestamp value"); - } - catch(ArrayIndexOutOfBoundsException e){ - br.close(); - zipFile.close(); - player.sendMessage(Component.text("Animation conversion failed! Read console", NamedTextColor.RED)); - throw new RuntimeException("Failed to read command from zip entry: Wrong game version downloaded"); - } - } - //Sub-Master Parts - else if (line.contains("@s")){ - //String coordinates = location.x()+" "+location.y()+" "+location.z(); - //String replacement = "positioned "+coordinates; - String replacement = player.getName(); - line = line.replace("@s", replacement); - } - line = line.substring(0, line.length()-2)+",\""+BDEngineDPConverter.CONVERT_DELETE_SUB_PARENT_TAG +"\"]}"; - } - - else if (zipEntry.getName().contains("start_animation.mcfunction")){ - if (line.contains("function")){ - value++; - continue; - } - } - else if (line.contains("function")) { - continue; - } - - Bukkit.dispatchCommand(silentSender, line); - - } - br.close(); - stream.close(); - - //True if command was summon.mcfuntion command, returns group timestamp created by BDEngine - //Also to get total number of groups created by BDEngine commands - if (expectData){ - return value; - } - else{ - return 0; - } - } - catch (IOException e){ - player.sendMessage(Component.text("Animation conversion failed! Read console")); - throw new RuntimeException("Failed to execute command from ZipEntry: "+zipEntry.getName()); - } - } -} From 2bdd11b96e6e40ba0f280f994948f4f47e97a72f Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sat, 30 May 2026 02:36:04 -0500 Subject: [PATCH 14/43] remove unused methods --- .../DisplayEntityPlugin.java | 4 +- ...d.java => BDEngineConversionListener.java} | 46 ++----------------- .../common/BDEConversionHandlerImpl.java | 4 +- .../convert/datapack/BDEngineDPConverter.java | 5 +- 4 files changed, 10 insertions(+), 49 deletions(-) rename plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/{DatapackEntitySpawned.java => BDEngineConversionListener.java} (52%) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java b/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java index a613aceb..ac898320 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java @@ -6,7 +6,7 @@ import com.github.retrooper.packetevents.event.PacketListenerPriority; import net.donnypz.displayentityutils.command.DisplayEntityPluginCommand; import net.donnypz.displayentityutils.listeners.autogroup.DEULoadingListeners; -import net.donnypz.displayentityutils.listeners.bdengine.DatapackEntitySpawned; +import net.donnypz.displayentityutils.listeners.bdengine.BDEngineConversionListener; import net.donnypz.displayentityutils.listeners.entity.DEUEntityListener; import net.donnypz.displayentityutils.listeners.entity.DEUInteractionListener; import net.donnypz.displayentityutils.listeners.entity.DEUMannequinEditorListener; @@ -175,7 +175,7 @@ private void registerSkript(){ private void registerListeners(){ Bukkit.getPluginManager().registerEvents(this, this); Bukkit.getPluginManager().registerEvents(new DEULoadingListeners(), this); - Bukkit.getPluginManager().registerEvents(new DatapackEntitySpawned(), this); + Bukkit.getPluginManager().registerEvents(new BDEngineConversionListener(), this); Bukkit.getPluginManager().registerEvents(new DEUEntityListener(), this); Bukkit.getPluginManager().registerEvents(new DEUPlayerConnectionListener(), this); Bukkit.getPluginManager().registerEvents(new DEUPlayerChatListener(), this); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/DatapackEntitySpawned.java b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/BDEngineConversionListener.java similarity index 52% rename from plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/DatapackEntitySpawned.java rename to plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/BDEngineConversionListener.java index a6943024..7c6c95c6 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/DatapackEntitySpawned.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/BDEngineConversionListener.java @@ -1,7 +1,6 @@ package net.donnypz.displayentityutils.listeners.bdengine; import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; -import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityPart; import net.donnypz.displayentityutils.utils.bdengine.convert.datapack.BDEngineDPConverter; import org.bukkit.entity.Display; import org.bukkit.entity.Entity; @@ -12,9 +11,8 @@ import java.util.*; -public final class DatapackEntitySpawned implements Listener { +public final class BDEngineConversionListener implements Listener { private static final HashMap pendingGroups = new HashMap<>(); - private static final HashSet incomingConversions = new HashSet<>(); @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true) public void onSpawn(EntitySpawnEvent e){ @@ -23,19 +21,7 @@ public void onSpawn(EntitySpawnEvent e){ applyToEntity(display); } - public static SpawnedDisplayEntityGroup getProjectGroup(UUID masterEntityUUID){ - return pendingGroups.get(masterEntityUUID); - } - - public static SpawnedDisplayEntityGroup getTimestampGroup(long timestamp){ - return pendingGroups.get(timestamp); - } - - public static void prepareAnimationMaster(Object projectValue){ - incomingConversions.add(projectValue); - } - - private static void applyToEntity(Display display){ + private void applyToEntity(Display display){ UUID rootUUID; for (String tag : display.getScoreboardTags()){ if (tag.startsWith(BDEngineDPConverter.CONVERSION_SCOREBOARD_PREFIX)){ @@ -59,32 +45,8 @@ public static void createNewGroup(Display master){ pendingGroups.put(master.getUniqueId(), new SpawnedDisplayEntityGroup(master)); } - public static void finalizeAnimationPreparation(Object projectValue){ - SpawnedDisplayEntityGroup group = pendingGroups.get(projectValue); - //finalize(group); - - pendingGroups.remove(projectValue); - incomingConversions.remove(projectValue); + public static SpawnedDisplayEntityGroup removeCreatedGroup(UUID masterEntityUUID){ + return pendingGroups.remove(masterEntityUUID); } - - private static void finalize(SpawnedDisplayEntityGroup group){ - if (group != null){ - Entity masterPart = group.getMasterPart().getEntity(); - for (SpawnedDisplayEntityPart part : group.getDisplayParts()){ - if (part.isMaster()){ - continue; - } - - Display display = (Display) part.getEntity(); - if (display.getScoreboardTags().contains(BDEngineDPConverter.CONVERT_DELETE_SUB_PARENT_TAG)){ - part.remove(true); - } - else{ - masterPart.addPassenger(display); - } - } - - } - } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java index 8369799a..2cc51504 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java @@ -1,13 +1,13 @@ package net.donnypz.displayentityutils.utils.bdengine.convert.common; -import net.donnypz.displayentityutils.listeners.bdengine.DatapackEntitySpawned; +import net.donnypz.displayentityutils.listeners.bdengine.BDEngineConversionListener; import org.bukkit.entity.Display; public class BDEConversionHandlerImpl implements BDEConversionHandler{ @Override public void createConversionGroup(Display display) { - DatapackEntitySpawned.createNewGroup(display); + BDEngineConversionListener.createNewGroup(display); } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java index fb872b64..a5b790b1 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java @@ -1,7 +1,7 @@ package net.donnypz.displayentityutils.utils.bdengine.convert.datapack; import net.donnypz.displayentityutils.DisplayAPI; -import net.donnypz.displayentityutils.listeners.bdengine.DatapackEntitySpawned; +import net.donnypz.displayentityutils.listeners.bdengine.BDEngineConversionListener; import net.donnypz.displayentityutils.managers.*; import net.donnypz.displayentityutils.utils.ConversionUtils; import net.donnypz.displayentityutils.utils.DisplayEntities.*; @@ -130,7 +130,7 @@ private void addFrame(ZipEntry entry){ } private void saveAndConvert(){ - SpawnedDisplayEntityGroup createdGroup = DatapackEntitySpawned.getProjectGroup(masterEntityUUID); + SpawnedDisplayEntityGroup createdGroup = BDEngineConversionListener.removeCreatedGroup(masterEntityUUID); if (createdGroup == null){ super.sendMessage(Component.text("Failed to find model/group created from datapack!", NamedTextColor.RED)); super.sendMessage(Component.text("| The datapack may have been generated for a different game version or of an older BDEngine format", NamedTextColor.GRAY, TextDecoration.ITALIC)); @@ -138,7 +138,6 @@ private void saveAndConvert(){ } createdGroup.setTag(super.getGroupSaveTag()); - DatapackEntitySpawned.finalizeAnimationPreparation(masterEntityUUID); createdGroup.seedPartUUIDs(SpawnedDisplayEntityGroup.DEFAULT_PART_UUID_SEED); super.sendMessage(Component.empty()); From 400de09d9782c089ecd9ea680bc7439f0a85f41a Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sat, 30 May 2026 02:45:26 -0500 Subject: [PATCH 15/43] despawn group after converting datapack --- .../command/bdengine/BDEngineConvertDatapackCMD.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java index 651b96f6..b9c4e9b3 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java @@ -40,7 +40,7 @@ public void execute(Player player, String[] args) { animPrefix, saveGroups, true, - false + true ); } } From 788329514163ebe34ce6b855b668c08dd6533629 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sat, 30 May 2026 18:35:23 -0500 Subject: [PATCH 16/43] Added message for clarity --- .../displayentityutils/command/anim/AnimPreviewPlayCMD.java | 1 + 1 file changed, 1 insertion(+) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPreviewPlayCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPreviewPlayCMD.java index d5c5c587..2e5184b7 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPreviewPlayCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPreviewPlayCMD.java @@ -48,6 +48,7 @@ public void execute(Player player, String[] args) { } player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Previewing animation!", NamedTextColor.AQUA))); + player.sendMessage(Component.text("| Only you can see the animation being played", NamedTextColor.GRAY)); DisplayAnimator.play(player, group, anim, DisplayAnimator.AnimationType.LINEAR); if (args.length >= 3 && args[2].equalsIgnoreCase("-camera")){ From cc2b4457f8bf1bc838a7ce454db67c3551685841 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 01:10:52 -0500 Subject: [PATCH 17/43] Readded BDE project importing (api & cmd changes) and new util methods --- .../utils/bdengine/BDEngineModelResult.java | 85 -------- .../utils/bdengine/BDEngineUtils.java | 123 +++++++++-- .../bdengine/convert/api/BDEAPIConverter.java | 86 ++++++++ .../utils/bdengine/convert/api/BDEResult.java | 125 ++++++++++++ .../convert/api/BDEResultAnimation.java | 21 ++ .../convert/api/BDEResultAnimationFrame.java | 16 ++ .../convert/api/BDEResultDatapack.java | 42 ++++ .../utils/bdengine/convert/api/Util.java | 47 +++++ .../convert/common/BDECommandConverter.java | 192 ++++++++++-------- .../convert/common/BDEConversionHandler.java | 17 ++ .../command/Permission.java | 3 +- .../command/bdengine/BDEngineCMD.java | 4 +- .../bdengine/BDEngineConvertDatapackCMD.java | 11 +- .../command/bdengine/BDEngineImportCMD.java | 73 +++++++ .../bdengine/BDEngineImportModelCMD.java | 75 ------- .../bdengine/BDEngineConversionListener.java | 10 +- .../common/BDEConversionHandlerImpl.java | 17 ++ .../convert/datapack/BDEngineDPConverter.java | 141 ++++--------- 18 files changed, 719 insertions(+), 369 deletions(-) delete mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineModelResult.java create mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEAPIConverter.java create mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResult.java create mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultAnimation.java create mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultAnimationFrame.java create mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultDatapack.java create mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/Util.java create mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportCMD.java delete mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportModelCMD.java diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineModelResult.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineModelResult.java deleted file mode 100644 index 6c2a27a0..00000000 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineModelResult.java +++ /dev/null @@ -1,85 +0,0 @@ -package net.donnypz.displayentityutils.utils.bdengine; - -import org.bukkit.Bukkit; -import org.bukkit.Location; -import org.bukkit.command.CommandSender; -import org.jetbrains.annotations.NotNull; - -import java.util.LinkedHashMap; - -/** - * The result of a BDEngine Request after using {@link BDEngineUtils#requestModel(int)} - * No commands will be provided if the model was uploaded to BDEngine as a BDEngine Project File. - */ -public final class BDEngineModelResult { - private static final CommandSender silentSender = Bukkit.createCommandSender(feedback -> {}); - private String name; - private String author; - private String category; - private String img_url; - private int model_count; - private LinkedHashMap commands; - //ArrayList Passengers; //For Saving a BDEngine model directly to a storage location - - BDEngineModelResult(){} - - - /** - * Spawn the model stored within the result at a location. - * This method should be run synchronously. - * @param location the location to spawn the model - * @return false if the model could not be spawned due to an unloaded chunk, or the result contained 0 commands - */ - public boolean spawn(@NotNull Location location){ - if (!location.isChunkLoaded() || commands.isEmpty()){ - return false; - } - for (String command : commands.sequencedValues()){ - String coordinates = location.x()+" "+location.y()+" "+location.z(); - String replacement = "summon block_display "+coordinates; - String newCommand = command.replace("summon block_display ~-0.5 ~-0.5 ~-0.5", replacement); - Bukkit.dispatchCommand(silentSender, newCommand); - } - return true; - } - - /** - * Get the number of commands required to spawn the entire model - * @return an int - */ - public int getCommandCount() { - return commands.size(); - } - - /** - * Get the name of this model - * @return a string - */ - public String getName(){ - return name; - } - - /** - * Get the name of the author who created this model - * @return a string - */ - public String getAuthor() { - return author; - } - - /** - * Get the name of this model's category - * @return a string - */ - public String getCategory(){ - return category; - } - - /** - * Get the total number of elements in this model - * @return an int - */ - public int getModelCount(){ - return model_count; - } -} diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineUtils.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineUtils.java index 54c04221..dea6f0f0 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineUtils.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineUtils.java @@ -1,11 +1,17 @@ package net.donnypz.displayentityutils.utils.bdengine; import com.google.gson.Gson; +import com.google.gson.JsonSyntaxException; +import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.utils.bdengine.convert.api.BDEResult; import net.donnypz.displayentityutils.utils.bdengine.convert.file.BDEModel; import net.donnypz.displayentityutils.utils.bdengine.convert.file.BDEngineReader; import org.apache.commons.codec.binary.Base64InputStream; +import org.bukkit.Location; +import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.io.*; import java.net.URI; @@ -18,39 +24,122 @@ public final class BDEngineUtils { - private static final Gson gson = new Gson(); - private static final int timeoutTime = 15; + private static final Gson GSON = new Gson(); + private static final int TIMEOUT_TIME = 15; + private static final String BDENGINE_URL = "https://block-display.com/server-api/?id="; + private static final HttpClient HTTP_CLIENT = HttpClient + .newBuilder() + .connectTimeout(Duration.ofSeconds(TIMEOUT_TIME)) + .build(); private BDEngineUtils(){} /** - * Request a model from BDEngine's website. - * This method is blocking and should be run asynchronously. - * @param modelID the model ID from BDEngine - * @return a {@link BDEngineModelResult} + * Import a BDEngine project with an "Export to server" export id supplied from BDEngine. + *
This method is blocking and should be run asynchronously. + * @param exportID the model ID from BDEngine + * @return a {@link BDEResult} * * @throws InterruptedException if the operation is interrupted - * @throws IOException if an I/ O error occurs when sending or receiving, or the client has shut down + * @throws IOException if an I/O error occurs when sending or receiving, or the client has shut down * @throws URISyntaxException if the API url for BDEngine is incorrect */ - public static BDEngineModelResult requestModel(int modelID) throws IOException, InterruptedException, URISyntaxException { - String url = "https://block-display.com/api/?type=getModel&id="+modelID; + public static BDEResult importProject(int exportID) throws IOException, InterruptedException, URISyntaxException { + String url = BDENGINE_URL+exportID; HttpRequest getRequest = HttpRequest.newBuilder() - .timeout(Duration.ofSeconds(timeoutTime)) + .timeout(Duration.ofSeconds(TIMEOUT_TIME)) .uri(new URI(url)) .GET() .build(); - try (HttpClient httpClient = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(timeoutTime)).build()) { - HttpResponse response = httpClient.send(getRequest, HttpResponse.BodyHandlers.ofString()); - if (response.statusCode() == 400){ - BDEngineError error = gson.fromJson(response.body(), BDEngineError.class); - throw new IOException(error.getError()); - } + HttpResponse response = HTTP_CLIENT.send(getRequest, HttpResponse.BodyHandlers.ofString()); - return gson.fromJson(response.body(), BDEngineModelResult.class); + //Check for error this way since an error returns a 200 status code regardless + try{ + BDEngineError error = GSON.fromJson(response.body(), BDEngineError.class); + if (error.getError() != null) throw new IOException(error.getError()); } + catch(JsonSyntaxException e){} + + return BDEResult.create(response.body()); + } + + /** + * Convert a BDEngine datapack project into a group/model & animation format, usable for DisplayEntityUtils. + *
This method should be run synchronously. + * @param datapackName the name of the datapack to be converted + * @param spawnLocation where the conversion should take place. This should be in a loaded chunk + * @param groupSaveTag the group's tag + * @param animationSavePrefix the prefix for animation tags + * @param saveGroup whether the created group should be saved + * @param saveAnimations whether created animations should be saved + * @param despawnAfter whether the created group should be despawned after conversion + */ + public static void convertDatapack(@NotNull String datapackName, + @NotNull Location spawnLocation, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter){ + convertDatapack(datapackName, null, null, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + + /** + * Convert a BDEngine datapack project into a group/model & animation format, usable for DisplayEntityUtils. + *
This method should be run synchronously. + * @param datapackName the name of the datapack to be converted + * @param player the player involved in the conversion. typically supplied when using conversion commands + * @param groupSaveTag the group's tag + * @param animationSavePrefix the prefix for animation tags + * @param saveGroup whether the created group should be saved + * @param saveAnimations whether created animations should be saved + * @param despawnAfter whether the created group should be despawned after conversion + */ + public static void convertDatapack(@NotNull String datapackName, + @NotNull Player player, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter){ + convertDatapack(datapackName, null, player, player.getLocation(), groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + + /** + * Convert a BDEngine datapack project into a group/model & animation format, usable for DisplayEntityUtils. + *
This method should be run synchronously. + * @param datapackName the name of the datapack to be converted + * @param conversionId the id used to reference this conversion later through events. + * @param player the player involved in the conversion. typically supplied when using conversion commands + * @param spawnLocation where the conversion should take place. This should be in a loaded chunk + * @param groupSaveTag the group's tag + * @param animationSavePrefix the prefix for animation tags + * @param saveGroup whether the created group should be saved + * @param saveAnimations whether created animations should be saved + * @param despawnAfter whether the created group should be despawned after conversion + */ + public static void convertDatapack(@NotNull String datapackName, + @Nullable String conversionId, + @Nullable Player player, + @NotNull Location spawnLocation, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter){ + DisplayAPI.getBDEConversionHandler().convertDatapack( + datapackName, + conversionId, + player, + spawnLocation, + groupSaveTag, + animationSavePrefix, + saveGroup, + saveAnimations, + despawnAfter + ); } /** diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEAPIConverter.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEAPIConverter.java new file mode 100644 index 00000000..767eccdb --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEAPIConverter.java @@ -0,0 +1,86 @@ +package net.donnypz.displayentityutils.utils.bdengine.convert.api; + +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimationFrame; +import net.donnypz.displayentityutils.utils.bdengine.convert.common.BDECommandConverter; +import net.kyori.adventure.text.Component; +import org.bukkit.Location; +import org.bukkit.command.CommandException; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; +import java.util.List; + +class BDEAPIConverter extends BDECommandConverter { + + private final BDEResult result; + private final BDEResultDatapack datapack; + + BDEAPIConverter(@NotNull BDEResult result, @Nullable String conversionId, @NotNull Location spawnLocation, @NotNull String groupSaveTag, @NotNull String animationSavePrefix, boolean saveGroup, boolean saveAnimations, boolean despawnAfter) { + this(result, conversionId, null, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + + BDEAPIConverter(@NotNull BDEResult result, @NotNull Player player, @NotNull String groupSaveTag, @NotNull String animationSavePrefix, boolean saveGroup, boolean saveAnimations, boolean despawnAfter) { + this(result, null, player, player.getLocation(), groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + + BDEAPIConverter(@NotNull BDEResult result, @Nullable String conversionId, @Nullable Player player, @NotNull Location spawnLocation, @NotNull String groupSaveTag, @NotNull String animationSavePrefix, boolean saveGroup, boolean saveAnimations, boolean despawnAfter) { + super(conversionId, player, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + this.result = result; + this.datapack = result.datapack; + spawnModel(); + } + + private void spawnModel(){ + String nbt = String.format("{Passengers:[%s],Tags:[\"%s\"]}", result.passengers, getSubMasterScoreboardTag()); + String command = "summon block_display ~ ~ ~ "+nbt; + super.executeCommandAsMasterEntity(command); + } + + @Override + protected void onConversionCompleted() {} + + @Override + protected Collection getAnimationNames() { + return datapack != null ? datapack.animations.keySet() : List.of(); + } + + @Override + protected int getFrameCount(@NotNull String animationName) { + if (datapack == null) return 0; + BDEResultAnimation anim = datapack.animations.get(animationName); + return anim == null ? 0 : anim.frames.size(); + } + + @Override + protected int getLastFrameId(@NotNull String animationName) { + if (datapack == null) return 0; + BDEResultAnimation anim = datapack.animations.get(animationName); + return anim == null ? 0 : anim.getLastFrame(); + } + + @Override + protected SpawnedDisplayAnimationFrame executeFrameCommands(String animationName, int frameId, int lastAddedFrameId) { + BDEResultAnimation anim = datapack.animations.get(animationName); + SpawnedDisplayAnimationFrame frame = new SpawnedDisplayAnimationFrame(Math.max(0, (frameId-lastAddedFrameId-1)*2), 2); + + BDEResultAnimationFrame bdeFrame = anim.frames.get(frameId); + if (bdeFrame == null || bdeFrame.isEmpty()){ + return null; + } + + if (bdeFrame.hasTransforms()){ + for (String cmd : bdeFrame.transformCommands){ + try{ + super.executeFrameCommand(frame, cmd); + } + catch(CommandException e){ + super.sendMessage(Component.text("Animation conversion failed! Read console")); + throw new RuntimeException("Failed to execute command from API Project: "+result.projectId, e); + } + } + } + return frame; + } +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResult.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResult.java new file mode 100644 index 00000000..03ca870f --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResult.java @@ -0,0 +1,125 @@ +package net.donnypz.displayentityutils.utils.bdengine.convert.api; + +import com.google.gson.*; +import net.donnypz.displayentityutils.utils.bdengine.BDEngineUtils; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * The result of a BDEngine Request after using {@link BDEngineUtils#importProject(int)} + * No commands will be provided if the model was uploaded to BDEngine as a BDEngine Project File. + */ +public final class BDEResult { + + private final String version; //game_version + private final String type; + final String projectId; + final String passengers; + final BDEResultDatapack datapack; + + + BDEResult(String version, + String type, + String projectId, + String passengers, + BDEResultDatapack datapack) { + this.version = version; + this.type = type; + this.projectId = projectId; + this.passengers = passengers; + this.datapack = datapack; + } + + public static BDEResult create(@NotNull String json) { + JsonObject root = JsonParser.parseString(json).getAsJsonObject(); + JsonObject content = Util.getObject(root, "content"); + + String version = Util.getString(content, "version"); + String type = Util.getString(content, "type"); + String projectId = Util.getString(content, "project_id"); + + JsonArray passengers = Util.getArray(content, "passengers"); + String passengerData = null; + + if (passengers != null && !passengers.isEmpty()) { + JsonElement first = passengers.get(0); + + if (first != null && !first.isJsonNull()) { + passengerData = first.getAsString(); + } + } + + JsonObject datapack = Util.getObject(content, "datapack"); + + return new BDEResult( + version, + type, + projectId, + passengerData, + datapack == null ? null : BDEResultDatapack.create(datapack) + ); + } + + /** + * Convert the retrieved BDEngine project into a group/model & animation format, usable for DisplayEntityUtils. + *
This method should be run synchronously. + * @param spawnLocation where the conversion should take place. This should be in a loaded chunk + * @param groupSaveTag the group's tag + * @param animationSavePrefix the prefix for animation tags + * @param saveGroup whether the created group should be saved + * @param saveAnimations whether created animations should be saved + * @param despawnAfter whether the created group should be despawned after conversion + */ + public void convert(@NotNull Location spawnLocation, @NotNull String groupSaveTag, @NotNull String animationSavePrefix, boolean saveGroup, boolean saveAnimations, boolean despawnAfter) { + this.convert(null, null, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + + /** + * Convert the retrieved BDEngine project into a group/model & animation format, usable for DisplayEntityUtils. + *
This method should be run synchronously. + * @param player the player involved in the conversion. typically supplied when using conversion commands + * @param groupSaveTag the group's tag + * @param animationSavePrefix the prefix for animation tags + * @param saveGroup whether the created group should be saved + * @param saveAnimations whether created animations should be saved + * @param despawnAfter whether the created group should be despawned after conversion + */ + public void convert(@NotNull Player player, @NotNull String groupSaveTag, @NotNull String animationSavePrefix, boolean saveGroup, boolean saveAnimations, boolean despawnAfter) { + this.convert(null, player, player.getLocation(), groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + + /** + * Convert the retrieved BDEngine project into a group/model & animation format, usable for DisplayEntityUtils. + *
This method should be run synchronously. + * @param conversionId the id used to reference this conversion later through events. + * @param player the player involved in the conversion. typically supplied when using conversion commands + * @param spawnLocation where the conversion should take place. This should be in a loaded chunk + * @param groupSaveTag the group's tag + * @param animationSavePrefix the prefix for animation tags + * @param saveGroup whether the created group should be saved + * @param saveAnimations whether created animations should be saved + * @param despawnAfter whether the created group should be despawned after conversion + */ + public void convert(@Nullable String conversionId, + @Nullable Player player, + @NotNull Location spawnLocation, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter) { + new BDEAPIConverter( + this, + conversionId, + player, + spawnLocation, + groupSaveTag, + animationSavePrefix, + saveGroup, + saveAnimations, + despawnAfter + ); + } +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultAnimation.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultAnimation.java new file mode 100644 index 00000000..83c92200 --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultAnimation.java @@ -0,0 +1,21 @@ +package net.donnypz.displayentityutils.utils.bdengine.convert.api; + +import java.util.List; +import java.util.TreeMap; + +class BDEResultAnimation { + TreeMap frames = new TreeMap<>(); + + void setSounds(int frameId, List sounds){ + frames.computeIfAbsent(frameId, id -> new BDEResultAnimationFrame()).soundCommands = sounds; + } + + void setTransforms(int frameId, List transforms){ + frames.computeIfAbsent(frameId, id -> new BDEResultAnimationFrame()).transformCommands = transforms; + } + + int getLastFrame(){ + return frames.lastKey(); + } + +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultAnimationFrame.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultAnimationFrame.java new file mode 100644 index 00000000..3372cc8e --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultAnimationFrame.java @@ -0,0 +1,16 @@ +package net.donnypz.displayentityutils.utils.bdengine.convert.api; + +import java.util.List; + +class BDEResultAnimationFrame { + List transformCommands; + List soundCommands; + + boolean isEmpty(){ + return (transformCommands == null || transformCommands.isEmpty()) && (soundCommands == null || soundCommands.isEmpty()); + } + + boolean hasTransforms(){ + return transformCommands != null && !transformCommands.isEmpty(); + } +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultDatapack.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultDatapack.java new file mode 100644 index 00000000..16a2e37e --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResultDatapack.java @@ -0,0 +1,42 @@ +package net.donnypz.displayentityutils.utils.bdengine.convert.api; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +class BDEResultDatapack { + + Map animations = new LinkedHashMap<>(); + + static BDEResultDatapack create(JsonObject datapack){ + JsonObject animFrames = Util.getObject(datapack, "anim_keyframes"); + JsonObject soundFrames = Util.getObject(datapack, "sound_keyframes"); + BDEResultDatapack dp = new BDEResultDatapack(); + if (animFrames != null) dp.addCommands(animFrames, false); + if (soundFrames != null) dp.addCommands(soundFrames, true); + return dp; + } + + + private void addCommands(JsonObject object, boolean isSound){ + for (String animationName : object.keySet()){ + JsonObject animJsonObj = object.get(animationName).getAsJsonObject(); + BDEResultAnimation anim = this.animations.computeIfAbsent(animationName, k -> new BDEResultAnimation()); + + for (String frameIdStr : animJsonObj.keySet()){ + int id = Integer.parseInt(frameIdStr); + JsonArray frameArray = animJsonObj.get(frameIdStr).getAsJsonArray(); + List mappedList = frameArray.asList().stream().map(e -> e.getAsString()).toList(); + if (isSound){ + anim.setSounds(id, mappedList); + } + else{ + anim.setTransforms(id, mappedList); + } + } + } + } +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/Util.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/Util.java new file mode 100644 index 00000000..ae62adf9 --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/Util.java @@ -0,0 +1,47 @@ +package net.donnypz.displayentityutils.utils.bdengine.convert.api; + +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +class Util { + + static String getString(JsonObject object, String key) { + if (object == null || !object.has(key)) { + return null; + } + + JsonElement element = object.get(key); + if (element == null || element.isJsonNull()) { + return null; + } + + return element.getAsString(); + } + + static JsonObject getObject(JsonObject object, String key) { + if (object == null || !object.has(key)) { + return null; + } + + JsonElement element = object.get(key); + if (element == null || !element.isJsonObject()) { + return null; + } + + return element.getAsJsonObject(); + } + + static JsonArray getArray(JsonObject object, String key) { + if (object == null || !object.has(key)) { + return null; + } + + JsonElement element = object.get(key); + if (element == null || !element.isJsonArray()) { + return null; + } + + return element.getAsJsonArray(); + } +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java index 2c71bee7..e45df6da 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java @@ -2,11 +2,14 @@ import net.donnypz.displayentityutils.DisplayAPI; import net.donnypz.displayentityutils.managers.DisplayAnimationManager; +import net.donnypz.displayentityutils.managers.DisplayGroupManager; import net.donnypz.displayentityutils.managers.LoadMethod; import net.donnypz.displayentityutils.utils.ConversionUtils; import net.donnypz.displayentityutils.utils.DisplayEntities.*; import net.donnypz.displayentityutils.utils.version.folia.Scheduler; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.Location; @@ -19,13 +22,11 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.UUID; +import java.util.*; public abstract class BDECommandConverter { private static final String FRAME_POINT_SOUND_TAG = "deu_dp_convert"; + public static final String CONVERSION_SCOREBOARD_PREFIX = "deu_dp_conversion_"; protected static final CommandSender SILENT_SENDER = Bukkit.createCommandSender(f -> { }); @@ -42,59 +43,11 @@ public abstract class BDECommandConverter { protected final HashSet bufferedSounds = new HashSet<>(); protected final UUID masterEntityUUID; - protected List animations = new ArrayList<>(); + protected List convertedAnimations = new ArrayList<>(); protected String projectName; - /** - * @param conversionId the id used to reference this conversion later through events. - * @param spawnLocation where the conversion should take place. This should be in a loaded chunk - * @param groupSaveTag the group's tag - * @param animationSavePrefix the prefix for animation tags - * @param saveGroup whether the created group should be saved - * @param saveAnimations whether created animations should be saved - * @param despawnAfter whether the created group should be despawned after conversion - */ - public BDECommandConverter( - @Nullable String conversionId, - @NotNull Location spawnLocation, - @NotNull String groupSaveTag, - @NotNull String animationSavePrefix, - boolean saveGroup, - boolean saveAnimations, - boolean despawnAfter) { - this(conversionId, null, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); - } - - /** - * @param player the player involved in the conversion. typically supplied when using conversion commands - * @param groupSaveTag the group's tag - * @param animationSavePrefix the prefix for animation tags - * @param saveGroup whether the created group should be saved - * @param saveAnimations whether created animations should be saved - * @param despawnAfter whether the created group should be despawned after conversion - */ - public BDECommandConverter( - @Nullable Player player, - @NotNull String groupSaveTag, - @NotNull String animationSavePrefix, - boolean saveGroup, - boolean saveAnimations, - boolean despawnAfter) { - this(null, player, player.getLocation(), groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); - } - - /** - * @param conversionId the id used to reference this conversion later through events. - * @param player the player involved in the conversion. typically supplied when using conversion commands - * @param spawnLocation where the conversion should take place. This should be in a loaded chunk - * @param groupSaveTag the group's tag - * @param animationSavePrefix the prefix for animation tags - * @param saveGroup whether the created group should be saved - * @param saveAnimations whether created animations should be saved - * @param despawnAfter whether the created group should be despawned after conversion - */ - public BDECommandConverter( + protected BDECommandConverter( @Nullable String conversionId, @Nullable Player player, @NotNull Location spawnLocation, @@ -122,45 +75,104 @@ public BDECommandConverter( }); this.masterEntityUUID = masterEntity.getUniqueId(); DisplayAPI.getBDEConversionHandler().createConversionGroup(masterEntity); + + //Save Model and Animations + DisplayAPI.getScheduler().runLater(this::convertAndSave, 1); + } + + public UUID getMasterEntityUUID(){ + return masterEntityUUID; + } + + public String getSubMasterScoreboardTag(){ + return CONVERSION_SCOREBOARD_PREFIX+masterEntityUUID; } - protected BDECommandConverter setProjectName(@NotNull String projectName) { + protected void setProjectName(@NotNull String projectName) { this.projectName = projectName; - return this; } + private void convertAndSave(){ + SpawnedDisplayEntityGroup createdGroup = DisplayAPI.getBDEConversionHandler().removeCreatedGroup(this); + if (createdGroup == null){ + this.sendMessage(Component.text("Failed to find model/group created from datapack!", NamedTextColor.RED)); + this.sendMessage(Component.text("| The datapack may have been generated for a different game version or of an older BDEngine format", NamedTextColor.GRAY, TextDecoration.ITALIC)); + return; + } + + this.sendMessage(Component.empty()); + + int delay = 0; + for (String animName : getAnimationNames()){ + DisplayAPI.getScheduler().runLater(() -> { + this.sendMessage(MiniMessage.miniMessage().deserialize("Converting Animation: "+animName)); + processAnimation(createdGroup, animName); + }, delay); + delay+=getAnimationWaitDelay(animName); + } + + //Despawn group after animation conversions + DisplayAPI.getScheduler().runLater(() -> { + if (saveGroup){ + DisplayGroupManager.saveDisplayEntityGroup(LoadMethod.LOCAL, createdGroup.toDisplayEntityGroup(), player); + this.sendMessage(MiniMessage.miniMessage().deserialize("Group Tag: "+createdGroup.getTag())); + } + else{ + this.sendMessage(Component.text("| The group was not saved.", NamedTextColor.GRAY)); + } + + if (despawnAfter){ + DisplayAPI.getScheduler().run(() -> createdGroup.unregister(true, true)); + } + else{ + if (createdGroup.getParts().size() > 1){ + createdGroup.setPersistent(true); + } + else{ + createdGroup.unregister(true, true); + } + } + onConversionCompleted(); + + }, delay+2); + } + + protected abstract void onConversionCompleted(); + protected void processAnimation(SpawnedDisplayEntityGroup createdGroup, String animationName) { DisplayAPI.getScheduler().partRunTimer(createdGroup.getMasterPart(), new Scheduler.SchedulerRunnable() { final SpawnedDisplayAnimation anim = new SpawnedDisplayAnimation(); final int frameCount = getFrameCount(animationName); - int i = 0; + final int finalFrame = getLastFrameId(animationName); + int frameId = 0; + int lastAddedFrameId = 0; @Override public void run() { if (frameCount != 0) { //Apply command to group entities (Transformations, Texture Values, etc.) - SpawnedDisplayAnimationFrame frame = executeFrameCommands(animationName, i); - frame.setDelay(0); - frame.setDuration(2); - - //Set Frame Transformation - frame.setTransformation(createdGroup); - - //Add Sounds - if (!bufferedSounds.isEmpty()) { - FramePoint point = new FramePoint(FRAME_POINT_SOUND_TAG, createdGroup, SPAWN_LOCATION); - for (DEUSound sound : bufferedSounds) { - point.addSound(sound); + SpawnedDisplayAnimationFrame frame = executeFrameCommands(animationName, frameId, lastAddedFrameId); + if (frame != null){ + //Set Frame Transformation + frame.setTransformation(createdGroup); + + //Add Sounds + if (!bufferedSounds.isEmpty()) { + FramePoint point = new FramePoint(FRAME_POINT_SOUND_TAG, createdGroup, SPAWN_LOCATION); + for (DEUSound sound : bufferedSounds) { + point.addSound(sound); + } + bufferedSounds.clear(); + frame.addFramePoint(point); } - bufferedSounds.clear(); - frame.addFramePoint(point); - } - anim.forceAddFrame(frame); - i++; + anim.forceAddFrame(frame); + lastAddedFrameId = frameId; + } + frameId++; } - if (i == frameCount) { + if (frameId > finalFrame) { try { createdGroup.setToFrame(anim, anim.getFrames().getFirst()); } catch (IndexOutOfBoundsException ignored) { @@ -179,7 +191,7 @@ public void run() { if (animationSuccess) { sendMessage(MiniMessage.miniMessage().deserialize("| BDEngine Animation converted and saved! (" + anim.getAnimationTag() + ")")); playSound(Sound.ENTITY_SHEEP_SHEAR, 1, 0.75f); - animations.add(anim); + convertedAnimations.add(anim); } else { sendMessage(MiniMessage.miniMessage().deserialize("| BDEngine Animation conversion failed! Save failure. (" + anim.getAnimationTag() + ")")); playSound(Sound.ENTITY_SHULKER_AMBIENT, 1, 1.5f); @@ -188,7 +200,7 @@ public void run() { else{ sendMessage(MiniMessage.miniMessage().deserialize("| BDEngine Animation converted! (" + anim.getAnimationTag() + ")")); playSound(Sound.ENTITY_SHEEP_SHEAR, 1, 0.75f); - animations.add(anim); + convertedAnimations.add(anim); } sendMessage(Component.empty()); }); @@ -198,13 +210,21 @@ public void run() { }, 0, 2); //BDEngine Animation Frame Duration is 2 ticks } + protected abstract Collection getAnimationNames(); + protected abstract int getFrameCount(@NotNull String animationName); - protected String getGroupSaveTag() { + protected abstract int getLastFrameId(@NotNull String animationName); + + private int getAnimationWaitDelay(String animationName){ + return (getLastFrameId(animationName)+1)*2; + } + + public String getGroupSaveTag() { return groupSaveTag.isBlank() ? projectName.replace(".zip", "_auto") : groupSaveTag; } - protected abstract SpawnedDisplayAnimationFrame executeFrameCommands(String animationName, int frameId); + protected abstract SpawnedDisplayAnimationFrame executeFrameCommands(String animationName, int frameId, int lastAddedFrameId); protected void executeFrameCommand(SpawnedDisplayAnimationFrame frame, String command) { if (command.startsWith("#") || command.startsWith("schedule") || command.isEmpty()) { @@ -222,9 +242,21 @@ protected void executeFrameCommand(SpawnedDisplayAnimationFrame frame, String co bufferedSounds.add(sound); } } + executeCommandAsMasterEntity(command); + } + + protected void executeCommandAsMasterEntity(String command){ String worldName = ConversionUtils.getExecuteCommandWorldName(SPAWN_LOCATION.getWorld()); - Bukkit.dispatchCommand(SILENT_SENDER, "execute at " + masterEntityUUID + " positioned " + SPAWN_COORDINATES + " in " + worldName + " run " + command); + String finalCommand = String.format( + "execute at %s positioned %s in %s run %s", + masterEntityUUID, + SPAWN_COORDINATES, + worldName, + command + ); + + Bukkit.dispatchCommand(SILENT_SENDER, finalCommand); } private void setCameraVectorAndDirection(SpawnedDisplayAnimationFrame frame, String line) { diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandler.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandler.java index 359036c0..3c7fb325 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandler.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandler.java @@ -1,7 +1,24 @@ package net.donnypz.displayentityutils.utils.bdengine.convert.common; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; +import org.bukkit.Location; import org.bukkit.entity.Display; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public interface BDEConversionHandler { void createConversionGroup(Display display); + + SpawnedDisplayEntityGroup removeCreatedGroup(BDECommandConverter converter); + + void convertDatapack(@NotNull String datapackName, + @Nullable String conversionId, + @Nullable Player player, + @NotNull Location spawnLocation, + @NotNull String groupSaveTag, + @NotNull String animationSavePrefix, + boolean saveGroup, + boolean saveAnimations, + boolean despawnAfter); } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/Permission.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/Permission.java index c477c10b..70bdab6c 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/Permission.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/Permission.java @@ -132,7 +132,8 @@ public enum Permission { ANIM_PREVIEW("deu.anim.preview"), BDENGINE_CONVERT_DATAPACK("deu.bdengine.convertdp"), - BDENGINE_SPAWN_MODEL("deu.bdengine.spawnmodel"); + BDENGINE_SPAWN_MODEL("deu.bdengine.spawnmodel"), + BDENGINE_IMPORT("deu.bdengine.import"); private final String permission; diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineCMD.java index c353bca4..28146047 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineCMD.java @@ -11,7 +11,7 @@ public final class BDEngineCMD extends ConsoleUsableSubCommand { public BDEngineCMD(){ super(Permission.HELP, new BDEngineHelpCMD()); new BDEngineConvertDatapackCMD(this); - new BDEngineImportModelCMD(this); + new BDEngineImportCMD(this); new BDEngineSpawnModelCMD(this); } @@ -37,7 +37,7 @@ static void help(CommandSender sender, int page){ sender.sendMessage(Component.empty()); CMDUtils.sendCMD(sender, "/deu bdengine help", "Get help with BDEngine commands"); CMDUtils.sendCMD(sender, "/deu bdengine spawnmodel ", "Spawn a model from a BDEngine file located in the plugin's \"bdenginefiles\" folder"); - CMDUtils.sendCMD(sender, "/deu bdengine importmodel ", "Import a model directly from BDEngine's Catalog into your game world"); + CMDUtils.sendCMD(sender, "/deu bdengine import ", "Import and convert a BDEngine project's model and animations into your game world"); CMDUtils.sendCMD(sender, "/deu bdengine convertdp ", "Convert BDEngine datapack into group and animation formats this plugin uses"); sender.sendMessage(MiniMessage.miniMessage().deserialize("--------------------------")); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java index b9c4e9b3..09d06547 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java @@ -4,7 +4,7 @@ import net.donnypz.displayentityutils.command.DEUSubCommand; import net.donnypz.displayentityutils.command.Permission; import net.donnypz.displayentityutils.command.PlayerSubCommand; -import net.donnypz.displayentityutils.utils.bdengine.convert.datapack.BDEngineDPConverter; +import net.donnypz.displayentityutils.utils.bdengine.BDEngineUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.entity.Player; @@ -22,7 +22,7 @@ class BDEngineConvertDatapackCMD extends PlayerSubCommand { public void execute(Player player, String[] args) { if (args.length < 5) { player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Incorrect Usage! /deu bdengine convertdp ", NamedTextColor.RED))); - player.sendMessage(Component.text("Use \"-\" for the group tag if you do not want to save the group", NamedTextColor.GRAY)); + player.sendMessage(Component.text("Use \"-\" for a tag if you do not want to save the group/animation(s)", NamedTextColor.GRAY)); return; } @@ -30,16 +30,17 @@ public void execute(Player player, String[] args) { String groupTag = args[3]; String animPrefix = args[4]; boolean saveGroups = !groupTag.equals("-"); + boolean saveAnimations = !animPrefix.equals("-"); player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempting to convert datapack...", NamedTextColor.AQUA))); player.sendMessage(Component.text(" | DO NOT LEAVE THIS AREA UNTIL COMPLETION!", NamedTextColor.YELLOW)); player.sendMessage(Component.text(" | Conversion times may vary.", NamedTextColor.YELLOW)); - new BDEngineDPConverter( + BDEngineUtils.convertDatapack( datapackName, player, !saveGroups ? "" : groupTag, - animPrefix, + !saveAnimations ? "" : animPrefix, saveGroups, - true, + saveAnimations, true ); } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportCMD.java new file mode 100644 index 00000000..c8af1561 --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportCMD.java @@ -0,0 +1,73 @@ +package net.donnypz.displayentityutils.command.bdengine; + +import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.command.DEUSubCommand; +import net.donnypz.displayentityutils.command.Permission; +import net.donnypz.displayentityutils.command.PlayerSubCommand; +import net.donnypz.displayentityutils.utils.bdengine.convert.api.BDEResult; +import net.donnypz.displayentityutils.utils.bdengine.BDEngineUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Location; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.io.IOException; +import java.net.URISyntaxException; + + +class BDEngineImportCMD extends PlayerSubCommand { + + BDEngineImportCMD(@NotNull DEUSubCommand parentSubCommand) { + super("import", parentSubCommand, Permission.BDENGINE_IMPORT); + setTabComplete(2, ""); + setTabComplete(3, ""); + setTabComplete(4, ""); + } + + @Override + public void execute(Player player, String[] args) { + if (args.length < 5) { + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Incorrect Usage! /deu bdengine import ", NamedTextColor.RED))); + player.sendMessage(Component.text("Use \"-\" for a tag if you do not want to save the group/animation(s)", NamedTextColor.GRAY)); + return; + } + Location spawnLoc = player.getLocation(); + DisplayAPI.getScheduler().runAsync(() -> { + try{ + int modelID = Integer.parseInt(args[2]); + String groupTag = args[3]; + String animPrefix = args[4]; + boolean saveGroups = !groupTag.equals("-"); + boolean saveAnimations = !animPrefix.equals("-"); + player.sendMessage(Component.text("Retrieving Model...", NamedTextColor.GRAY)); + BDEResult result = BDEngineUtils.importProject(modelID); + if (result == null){ + throw new InterruptedException("Null Result"); + } + + DisplayAPI.getScheduler().run(() -> { + result.convert(null, + player, + spawnLoc, + !saveGroups ? "" : groupTag, + !saveAnimations ? "" : animPrefix, + saveGroups, + saveAnimations, + false); + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempted to spawn model at your location!", NamedTextColor.GREEN))); + }); + } + catch(NumberFormatException e){ + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Enter a valid project export ID!", NamedTextColor.RED))); + } + catch(IOException e){ + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed retrieve BDEngine project. Import ID was never generated or has expired.", NamedTextColor.RED))); + } + catch(InterruptedException | URISyntaxException e){ + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("An error occurred when attempting to retrieve the BDEngine model! See console.", NamedTextColor.RED))); + e.printStackTrace(); + } + }); + } +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportModelCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportModelCMD.java deleted file mode 100644 index 5239dc07..00000000 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportModelCMD.java +++ /dev/null @@ -1,75 +0,0 @@ -package net.donnypz.displayentityutils.command.bdengine; - -import net.donnypz.displayentityutils.DisplayAPI; -import net.donnypz.displayentityutils.command.DEUSubCommand; -import net.donnypz.displayentityutils.command.Permission; -import net.donnypz.displayentityutils.command.PlayerSubCommand; -import net.donnypz.displayentityutils.utils.bdengine.BDEngineModelResult; -import net.donnypz.displayentityutils.utils.bdengine.BDEngineUtils; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import net.kyori.adventure.text.format.TextDecoration; -import org.bukkit.Location; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import java.io.IOException; -import java.net.URISyntaxException; - - -class BDEngineImportModelCMD extends PlayerSubCommand { - - BDEngineImportModelCMD(@NotNull DEUSubCommand parentSubCommand) { - super("importmodel", parentSubCommand, Permission.BDENGINE_SPAWN_MODEL); - setTabComplete(2, ""); - } - - @Override - public void execute(Player player, String[] args) { - if (args.length < 3) { - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Incorrect Usage! /deu bdengine importmodel ", NamedTextColor.RED))); - return; - } - Location spawnLoc = player.getLocation(); - DisplayAPI.getScheduler().runAsync(() -> { - try{ - int modelID = Integer.parseInt(args[2]); - player.sendMessage(Component.text("Retrieving Model...", NamedTextColor.GRAY)); - BDEngineModelResult result = BDEngineUtils.requestModel(modelID); - if (result == null){ - throw new InterruptedException("Null Result"); - } - - DisplayAPI.getScheduler().run(() -> { - if (!result.spawn(spawnLoc)){ - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed to spawn model! The spawn location's chunk is not loaded or the model was uploaded as a BDEngine file on the website (Spawning Commands not found), and that format is not supported!", NamedTextColor.RED))); - return; - } - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempted to spawn model at your location!", NamedTextColor.GREEN))); - - if (result.getCommandCount() > 1){ - player.sendMessage(Component.text("! The model resulted in creating multiple groups!", NamedTextColor.RED)); - player.sendMessage(Component.text("Select the group then use \"/deu group merge\" to merge the produced group", NamedTextColor.GRAY, TextDecoration.ITALIC)); - player.sendMessage(Component.text("It is not recommended to import groups that use animations this way!", NamedTextColor.RED, TextDecoration.ITALIC)); - } - - player.sendMessage(Component.text("\n- If your model did NOT spawn, the model was uploaded as a BDEngine file on the website, and that format is not supported.\n", NamedTextColor.GRAY)); - }); - - } - catch(NumberFormatException e){ - player.sendMessage(Component.text("Enter a valid Model ID! This can be found on the page of a model on BDEngine. " + - "Look for \"ID for API\" and enter the number provided", NamedTextColor.RED)); - } - catch(InterruptedException | IOException | URISyntaxException e){ - player.sendMessage(Component.text("An error occurred when attempting to retrieve the BDEngine model!", NamedTextColor.RED)); - e.printStackTrace(); - } - catch(RuntimeException e){ - player.sendMessage(Component.text("An error occurred when attempting to retrieve the BDEngine model!", NamedTextColor.RED)); - player.sendMessage(Component.text("Error: "+e.getMessage(), NamedTextColor.GRAY, TextDecoration.ITALIC)); - e.printStackTrace(); - } - }); - } -} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/BDEngineConversionListener.java b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/BDEngineConversionListener.java index 7c6c95c6..625eff20 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/BDEngineConversionListener.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/bdengine/BDEngineConversionListener.java @@ -1,6 +1,7 @@ package net.donnypz.displayentityutils.listeners.bdengine; import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; +import net.donnypz.displayentityutils.utils.bdengine.convert.common.BDECommandConverter; import net.donnypz.displayentityutils.utils.bdengine.convert.datapack.BDEngineDPConverter; import org.bukkit.entity.Display; import org.bukkit.entity.Entity; @@ -45,8 +46,13 @@ public static void createNewGroup(Display master){ pendingGroups.put(master.getUniqueId(), new SpawnedDisplayEntityGroup(master)); } - public static SpawnedDisplayEntityGroup removeCreatedGroup(UUID masterEntityUUID){ - return pendingGroups.remove(masterEntityUUID); + public static SpawnedDisplayEntityGroup removeCreatedGroup(BDECommandConverter converter){ + SpawnedDisplayEntityGroup group = pendingGroups.remove(converter.getMasterEntityUUID()); + if (group != null){ + group.setTag(converter.getGroupSaveTag()); + group.seedPartUUIDs(SpawnedDisplayEntityGroup.DEFAULT_PART_UUID_SEED); + } + return group; } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java index 2cc51504..9bc1e2ba 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandlerImpl.java @@ -1,7 +1,13 @@ package net.donnypz.displayentityutils.utils.bdengine.convert.common; import net.donnypz.displayentityutils.listeners.bdengine.BDEngineConversionListener; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; +import net.donnypz.displayentityutils.utils.bdengine.convert.datapack.BDEngineDPConverter; +import org.bukkit.Location; import org.bukkit.entity.Display; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public class BDEConversionHandlerImpl implements BDEConversionHandler{ @@ -10,4 +16,15 @@ public void createConversionGroup(Display display) { BDEngineConversionListener.createNewGroup(display); } + @Override + public SpawnedDisplayEntityGroup removeCreatedGroup(BDECommandConverter converter) { + return BDEngineConversionListener.removeCreatedGroup(converter); + } + + @Override + public void convertDatapack(@NotNull String datapackName, @Nullable String conversionId, @Nullable Player player, @NotNull Location spawnLocation, @NotNull String groupSaveTag, @NotNull String animationSavePrefix, boolean saveGroup, boolean saveAnimations, boolean despawnAfter) { + new BDEngineDPConverter( + datapackName, conversionId, player, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); + } + } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java index a5b790b1..52195df0 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java @@ -28,35 +28,12 @@ public class BDEngineDPConverter extends BDECommandConverter { public static final String CONVERT_DELETE_SUB_PARENT_TAG = "deu_delete_sub_parent"; - public static final String CONVERSION_SCOREBOARD_PREFIX = "deu_dp_conversion_"; static final String FUNCTION_FOLDER = VersionUtils.IS_1_21 ? "function" : "functions"; private static final String CREATE_MODEL_PATH = "/create.mcfunction"; private final LinkedHashMap> animations = new LinkedHashMap<>(); private final ZipFile zipFile; - - public BDEngineDPConverter(@NotNull String datapackName, - @Nullable String conversionId, - @NotNull Location spawnLocation, - @NotNull String groupSaveTag, - @NotNull String animationSavePrefix, - boolean saveGroup, - boolean saveAnimations, - boolean despawnAfter) { - this(datapackName, conversionId, null, spawnLocation, groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); - } - - public BDEngineDPConverter(@NotNull String datapackName, - @Nullable Player player, - @NotNull String groupSaveTag, - @NotNull String animationSavePrefix, - boolean saveGroup, - boolean saveAnimations, - boolean despawnAfter) { - this(datapackName, null, player, player.getLocation(), groupSaveTag, animationSavePrefix, saveGroup, saveAnimations, despawnAfter); - } - public BDEngineDPConverter(@NotNull String datapackName, @Nullable String conversionId, @Nullable Player player, @@ -79,7 +56,7 @@ public BDEngineDPConverter(@NotNull String datapackName, catch (IOException e) { this.zipFile = null; super.sendMessage(DisplayAPI.pluginPrefix - .append(Component.text("Failed to find datapack with the provided name! Ensure that the datapack is placed in the \"bdenginedatapacks\" folder of this plugin, as a .zip file", NamedTextColor.RED))); + .append(Component.text("Failed to find a datapack with the provided name! The datapack must be in the plugin's \"bdenginedatapacks\" folder, as a .zip file", NamedTextColor.RED))); return; } @@ -118,9 +95,6 @@ else if (!isSoundPath(entryName) && entryName.contains("/keyframe_") && entryNam addFrame(entry); } } - - //Save Model and Animations - DisplayAPI.getScheduler().runLater(this::saveAndConvert, 1); } private void addFrame(ZipEntry entry){ @@ -129,48 +103,49 @@ private void addFrame(ZipEntry entry){ frames.add(entry); } - private void saveAndConvert(){ - SpawnedDisplayEntityGroup createdGroup = BDEngineConversionListener.removeCreatedGroup(masterEntityUUID); - if (createdGroup == null){ - super.sendMessage(Component.text("Failed to find model/group created from datapack!", NamedTextColor.RED)); - super.sendMessage(Component.text("| The datapack may have been generated for a different game version or of an older BDEngine format", NamedTextColor.GRAY, TextDecoration.ITALIC)); - return; + @Override + protected void onConversionCompleted(){ + try{ + zipFile.close(); } + catch(IOException ignored){} + } - createdGroup.setTag(super.getGroupSaveTag()); - createdGroup.seedPartUUIDs(SpawnedDisplayEntityGroup.DEFAULT_PART_UUID_SEED); + @Override + protected Collection getAnimationNames() { + return animations.sequencedKeySet(); + } - super.sendMessage(Component.empty()); + @Override + protected int getFrameCount(@NotNull String animationName) { + List frames = animations.get(animationName); + return frames == null ? 0 : frames.size(); + } - int delay = 0; - for (String animName : animations.sequencedKeySet()){ - List frames = animations.get(animName); - DisplayAPI.getScheduler().runLater(() -> { - super.sendMessage(MiniMessage.miniMessage().deserialize("Converting Animation: "+animName)); - processAnimation(createdGroup, animName); - }, delay); - delay+=(frames.size()*2); - } + @Override + protected int getLastFrameId(@NotNull String animationName) { + return Math.max(0, getFrameCount(animationName)-1); + } - //Despawn group after animation conversions - DisplayAPI.getScheduler().runLater(() -> { - if (saveGroup){ - DisplayGroupManager.saveDisplayEntityGroup(LoadMethod.LOCAL, createdGroup.toDisplayEntityGroup(), player); - super.sendMessage(MiniMessage.miniMessage().deserialize("Group Tag: "+createdGroup.getTag())); - } - else{ - super.sendMessage(Component.text("| The group was not saved.", NamedTextColor.GRAY)); - } + @Override + protected SpawnedDisplayAnimationFrame executeFrameCommands(String animationName, int frameId, int lastAddedFrameId) { + List frames = animations.get(animationName); + ZipEntry zipEntry = frames.get(frameId); + SpawnedDisplayAnimationFrame frame = new SpawnedDisplayAnimationFrame(0, 2); - if (despawnAfter){ - DisplayAPI.getScheduler().run(() -> createdGroup.unregister(true, true)); - } + try(InputStream stream = zipFile.getInputStream(zipEntry); + BufferedReader br = new BufferedReader(new InputStreamReader(stream))){ - try{ - zipFile.close(); + String line; + while ((line = br.readLine()) != null){ + super.executeFrameCommand(frame, line); } - catch(IOException ignored){} - }, delay+2); + return frame; + } + catch (IOException e){ + super.sendMessage(Component.text("Animation conversion failed! Read console")); + throw new RuntimeException("Failed to execute command from ZipEntry: "+zipEntry.getName(), e); + } } @@ -202,7 +177,7 @@ private void executeCommands(ZipEntry zipEntry){ //Sub-Master Parts else if (line.contains("@s")){ line = line.split("@s run ", 2)[1]; - line = line.substring(0, line.length()-2)+",\""+CONVERSION_SCOREBOARD_PREFIX+masterEntityUUID+"\"]}"; + line = line.substring(0, line.length()-2)+",\""+getSubMasterScoreboardTag()+"\"]}"; } if (!line.startsWith("ride")) { line = line.substring(0, line.length()-2)+",\""+CONVERT_DELETE_SUB_PARENT_TAG+"\"]}"; @@ -216,19 +191,8 @@ else if (line.startsWith("schedule")) { continue; } - String coordinates = ConversionUtils.getCoordinateString(SPAWN_LOCATION); - World w = SPAWN_LOCATION.getWorld(); - String worldName = ConversionUtils.getExecuteCommandWorldName(w); - - String finalCommand = String.format( - "execute at %s positioned %s in %s run %s", - masterEntityUUID, - coordinates, - worldName, - line - ); try{ - Bukkit.dispatchCommand(SILENT_SENDER, finalCommand); + super.executeCommandAsMasterEntity(line); } catch(CommandException e){ super.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Animation conversion failed! Error in console...", NamedTextColor.RED))); @@ -249,38 +213,11 @@ private boolean isAnimationEntry(String entryName){ } private boolean isSoundPath(String entryName){ - return entryName.contains("/"+ FUNCTION_FOLDER +"/k_s"); + return entryName.contains("/"+ FUNCTION_FOLDER +"/k_s") || entryName.contains("/"+ FUNCTION_FOLDER +"/a_s"); } private String getAnimationName(String entryName, String folder){ String animName = entryName.split(FUNCTION_FOLDER +"/"+folder+"/")[1]; return animName.endsWith(".mcfunction") ? animName.split("/")[0] : animName.substring(0, animName.length()-1); } - - @Override - protected int getFrameCount(@NotNull String animationName) { - List entries = animations.get(animationName); - return entries == null ? 0 : entries.size(); - } - - @Override - protected SpawnedDisplayAnimationFrame executeFrameCommands(String animationName, int frameId) { - List frames = animations.get(animationName); - ZipEntry zipEntry = frames.get(frameId); - SpawnedDisplayAnimationFrame frame = new SpawnedDisplayAnimationFrame(0, 2); - - try(InputStream stream = zipFile.getInputStream(zipEntry); - BufferedReader br = new BufferedReader(new InputStreamReader(stream))){ - - String line; - while ((line = br.readLine()) != null){ - super.executeFrameCommand(frame, line); - } - return frame; - } - catch (IOException e){ - super.sendMessage(Component.text("Animation conversion failed! Read console")); - throw new RuntimeException("Failed to execute command from ZipEntry: "+zipEntry.getName(), e); - } - } } From f27f769353fc5da162b9329a653bcb871a5e2810 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 02:54:51 -0500 Subject: [PATCH 18/43] Added events for BDE conversions --- .../events/BDEAPIConvertEvent.java | 26 +++++ .../events/BDEConvertEvent.java | 107 ++++++++++++++++++ .../events/BDEDatapackConvertEvent.java | 38 +++++++ .../utils/bdengine/BDEngineUtils.java | 4 + .../bdengine/convert/api/BDEAPIConverter.java | 15 ++- .../utils/bdengine/convert/api/BDEResult.java | 4 + .../convert/common/BDECommandConverter.java | 10 +- .../skript/events/SimpleEvents.java | 20 +++- .../convert/datapack/BDEngineDPConverter.java | 19 +++- 9 files changed, 232 insertions(+), 11 deletions(-) create mode 100644 api/src/main/java/net/donnypz/displayentityutils/events/BDEAPIConvertEvent.java create mode 100644 api/src/main/java/net/donnypz/displayentityutils/events/BDEConvertEvent.java create mode 100644 api/src/main/java/net/donnypz/displayentityutils/events/BDEDatapackConvertEvent.java diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/BDEAPIConvertEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/BDEAPIConvertEvent.java new file mode 100644 index 00000000..1c47ddde --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/events/BDEAPIConvertEvent.java @@ -0,0 +1,26 @@ +package net.donnypz.displayentityutils.events; + +import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; +import net.donnypz.displayentityutils.utils.bdengine.convert.api.BDEResult; +import org.bukkit.Location; +import org.bukkit.entity.Player; + +import java.util.List; + +/** + * Called sometime after {@link BDEResult#convert(String, Player, Location, String, String, boolean, boolean, boolean)} + * or similar is called, converting an import BDEngine project into a group and animations. This contains all information related to the conversion. + */ +public class BDEAPIConvertEvent extends BDEConvertEvent{ + public BDEAPIConvertEvent(Player player, + String conversionId, + DisplayEntityGroup savedGroup, + SpawnedDisplayEntityGroup spawnedGroup, + List animations, + boolean isSavedGroup, + boolean isSavedAnimations) { + super(player, conversionId, savedGroup, spawnedGroup, animations, isSavedGroup, isSavedAnimations); + } +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/BDEConvertEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/BDEConvertEvent.java new file mode 100644 index 00000000..5523ba0f --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/events/BDEConvertEvent.java @@ -0,0 +1,107 @@ +package net.donnypz.displayentityutils.events; + +import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Collections; +import java.util.List; + +class BDEConvertEvent extends Event { + + private static final HandlerList handlers = new HandlerList(); + + private final Player player; + private final String conversionId; + private final DisplayEntityGroup savedGroup; + private final SpawnedDisplayEntityGroup spawnedGroup; + private final List animations; + private final boolean isSavedGroup; + private final boolean isSavedAnimations; + + BDEConvertEvent(Player player, + String conversionId, + DisplayEntityGroup savedGroup, + SpawnedDisplayEntityGroup spawnedGroup, + List animations, + boolean isSavedGroup, + boolean isSavedAnimations) { + this.player = player; + this.conversionId = conversionId; + this.savedGroup = savedGroup; + this.spawnedGroup = spawnedGroup; + this.animations = Collections.unmodifiableList(animations); + this.isSavedGroup = isSavedGroup; + this.isSavedAnimations = isSavedAnimations; + } + + /** + * Get the player who initiated the conversion, if done through commands + * @return a {@link Player} or null + */ + public @Nullable Player getPlayer() { + return player; + } + + /** + * Get the id used to identify a conversion process + * @return a string or null + */ + public @Nullable String getConversionId() { + return conversionId; + } + + /** + * Get the {@link DisplayEntityGroup} representing the group created from the conversion + * return a {@link DisplayEntityGroup} + */ + public @NotNull DisplayEntityGroup getGroup(){ + return savedGroup; + } + + /** + * Get the group that created as a result of the conversion. Null if the group was despawned after conversion + * @return a {@link SpawnedDisplayEntityGroup} or null + */ + public @Nullable SpawnedDisplayEntityGroup getSpawnedGroup() { + return spawnedGroup; + } + + /** + * Get the animations that were created as a result of the conversion + * @return an unmodifiable list of {@link SpawnedDisplayAnimation}s + */ + public @NotNull List getAnimations() { + return animations; + } + + /** + * Get whether the created group were saved after conversion + * @return a boolean + */ + public boolean isSavingGroups(){ + return isSavedGroup; + } + + /** + * Get whether the created animations were saved after conversion + * @return a boolean + */ + public boolean isSavingAnimations(){ + return isSavedAnimations; + } + + @Override + public HandlerList getHandlers() { + return handlers; + } + + public static HandlerList getHandlerList() { + return handlers; + } +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/BDEDatapackConvertEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/BDEDatapackConvertEvent.java new file mode 100644 index 00000000..a66ad593 --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/events/BDEDatapackConvertEvent.java @@ -0,0 +1,38 @@ +package net.donnypz.displayentityutils.events; + +import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; +import net.donnypz.displayentityutils.utils.bdengine.BDEngineUtils; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +/** + * Called sometime after {@link BDEngineUtils#convertDatapack(String, Player, String, String, boolean, boolean, boolean)} + * or similar is called, converting a datapack into a group and animations. This contains all information related to the conversion. + */ +public class BDEDatapackConvertEvent extends BDEConvertEvent{ + + private final String datapackName; + public BDEDatapackConvertEvent(String datapackName, + Player player, + String conversionId, + DisplayEntityGroup savedGroup, + SpawnedDisplayEntityGroup spawnedGroup, + List animations, + boolean isSavedGroup, + boolean isSavedAnimations) { + super(player, conversionId, savedGroup, spawnedGroup, animations, isSavedGroup, isSavedAnimations); + this.datapackName = datapackName; + } + + /** + * Get the name of the datapack that was converted + * @return a string + */ + public @NotNull String getDatapackName() { + return datapackName; + } +} diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineUtils.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineUtils.java index dea6f0f0..078067be 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineUtils.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/BDEngineUtils.java @@ -3,6 +3,7 @@ import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.events.BDEDatapackConvertEvent; import net.donnypz.displayentityutils.utils.bdengine.convert.api.BDEResult; import net.donnypz.displayentityutils.utils.bdengine.convert.file.BDEModel; import net.donnypz.displayentityutils.utils.bdengine.convert.file.BDEngineReader; @@ -67,6 +68,7 @@ public static BDEResult importProject(int exportID) throws IOException, Interrup /** * Convert a BDEngine datapack project into a group/model & animation format, usable for DisplayEntityUtils. + *
The {@link BDEDatapackConvertEvent} will be called after successful conversion completion. *
This method should be run synchronously. * @param datapackName the name of the datapack to be converted * @param spawnLocation where the conversion should take place. This should be in a loaded chunk @@ -88,6 +90,7 @@ public static void convertDatapack(@NotNull String datapackName, /** * Convert a BDEngine datapack project into a group/model & animation format, usable for DisplayEntityUtils. + *
The {@link BDEDatapackConvertEvent} will be called after successful conversion completion. *
This method should be run synchronously. * @param datapackName the name of the datapack to be converted * @param player the player involved in the conversion. typically supplied when using conversion commands @@ -109,6 +112,7 @@ public static void convertDatapack(@NotNull String datapackName, /** * Convert a BDEngine datapack project into a group/model & animation format, usable for DisplayEntityUtils. + *
The {@link BDEDatapackConvertEvent} will be called after successful conversion completion. *
This method should be run synchronously. * @param datapackName the name of the datapack to be converted * @param conversionId the id used to reference this conversion later through events. diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEAPIConverter.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEAPIConverter.java index 767eccdb..dfd27316 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEAPIConverter.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEAPIConverter.java @@ -1,6 +1,9 @@ package net.donnypz.displayentityutils.utils.bdengine.convert.api; +import net.donnypz.displayentityutils.events.BDEAPIConvertEvent; +import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup; import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimationFrame; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; import net.donnypz.displayentityutils.utils.bdengine.convert.common.BDECommandConverter; import net.kyori.adventure.text.Component; import org.bukkit.Location; @@ -39,7 +42,17 @@ private void spawnModel(){ } @Override - protected void onConversionCompleted() {} + protected void onConversionCompleted(DisplayEntityGroup savedGroup, SpawnedDisplayEntityGroup group) { + new BDEAPIConvertEvent( + player, + conversionId, + savedGroup, + group, + convertedAnimations, + saveGroup, + saveAnimations + ).callEvent(); + } @Override protected Collection getAnimationNames() { diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResult.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResult.java index 03ca870f..aa2d57ae 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResult.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResult.java @@ -1,6 +1,7 @@ package net.donnypz.displayentityutils.utils.bdengine.convert.api; import com.google.gson.*; +import net.donnypz.displayentityutils.events.BDEAPIConvertEvent; import net.donnypz.displayentityutils.utils.bdengine.BDEngineUtils; import org.bukkit.Location; import org.bukkit.entity.Player; @@ -64,6 +65,7 @@ public static BDEResult create(@NotNull String json) { /** * Convert the retrieved BDEngine project into a group/model & animation format, usable for DisplayEntityUtils. + *
The {@link BDEAPIConvertEvent} will be called after successful conversion completion. *
This method should be run synchronously. * @param spawnLocation where the conversion should take place. This should be in a loaded chunk * @param groupSaveTag the group's tag @@ -78,6 +80,7 @@ public void convert(@NotNull Location spawnLocation, @NotNull String groupSaveTa /** * Convert the retrieved BDEngine project into a group/model & animation format, usable for DisplayEntityUtils. + *
The {@link BDEAPIConvertEvent} will be called after successful conversion completion. *
This method should be run synchronously. * @param player the player involved in the conversion. typically supplied when using conversion commands * @param groupSaveTag the group's tag @@ -92,6 +95,7 @@ public void convert(@NotNull Player player, @NotNull String groupSaveTag, @NotNu /** * Convert the retrieved BDEngine project into a group/model & animation format, usable for DisplayEntityUtils. + *
The {@link BDEAPIConvertEvent} will be called after successful conversion completion. *
This method should be run synchronously. * @param conversionId the id used to reference this conversion later through events. * @param player the player involved in the conversion. typically supplied when using conversion commands diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java index e45df6da..6e00da67 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java @@ -111,16 +111,18 @@ private void convertAndSave(){ delay+=getAnimationWaitDelay(animName); } - //Despawn group after animation conversions + //Cleanup and save after animation conversions DisplayAPI.getScheduler().runLater(() -> { + DisplayEntityGroup savedGroup = createdGroup.toDisplayEntityGroup(); if (saveGroup){ - DisplayGroupManager.saveDisplayEntityGroup(LoadMethod.LOCAL, createdGroup.toDisplayEntityGroup(), player); + DisplayGroupManager.saveDisplayEntityGroup(LoadMethod.LOCAL, savedGroup, player); this.sendMessage(MiniMessage.miniMessage().deserialize("Group Tag: "+createdGroup.getTag())); } else{ this.sendMessage(Component.text("| The group was not saved.", NamedTextColor.GRAY)); } + if (despawnAfter){ DisplayAPI.getScheduler().run(() -> createdGroup.unregister(true, true)); } @@ -132,12 +134,12 @@ private void convertAndSave(){ createdGroup.unregister(true, true); } } - onConversionCompleted(); + onConversionCompleted(savedGroup, despawnAfter ? null : createdGroup); }, delay+2); } - protected abstract void onConversionCompleted(); + protected abstract void onConversionCompleted(DisplayEntityGroup savedGroup, SpawnedDisplayEntityGroup group); protected void processAnimation(SpawnedDisplayEntityGroup createdGroup, String animationName) { DisplayAPI.getScheduler().partRunTimer(createdGroup.getMasterPart(), new Scheduler.SchedulerRunnable() { diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/skript/events/SimpleEvents.java b/plugin/src/main/java/net/donnypz/displayentityutils/skript/events/SimpleEvents.java index 12c45078..f3a124ee 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/skript/events/SimpleEvents.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/skript/events/SimpleEvents.java @@ -289,8 +289,24 @@ public SkriptTypes.FrameId convert(PacketAnimationFrameEndEvent from) { EventValues.registerEventValue(GroupRideEntityEvent.class, SpawnedDisplayEntityGroup.class, GroupRideEntityEvent::getGroup); EventValues.registerEventValue(GroupRideEntityEvent.class, Entity.class, GroupRideEntityEvent::getEntity); + //BDEAPIConvert + Skript.registerEvent("BDEngine API Converted", SimpleEvents.class, BDEAPIConvertEvent.class, "bde (api|import) converted") + .description("Called when an imported BDEngine project is converted to a format usable by this plugin") + .since("3.5.3"); + EventValues.registerEventValue(BDEAPIConvertEvent.class, DisplayEntityGroup.class, BDEAPIConvertEvent::getGroup); + EventValues.registerEventValue(BDEAPIConvertEvent.class, SpawnedDisplayEntityGroup.class, BDEAPIConvertEvent::getSpawnedGroup); + EventValues.registerEventValue(BDEAPIConvertEvent.class, Player.class, BDEAPIConvertEvent::getPlayer); + EventValues.registerEventValue(BDEAPIConvertEvent.class, SpawnedDisplayAnimation[].class, e -> e.getAnimations().toArray(new SpawnedDisplayAnimation[0])); + + //BDEDatapackConvert + Skript.registerEvent("BDEngine Datapack Converted", SimpleEvents.class, BDEDatapackConvertEvent.class, "bde datapack converted") + .description("Called when a BDEngine datapack is converted to a format usable by this plugin") + .since("3.5.3"); + EventValues.registerEventValue(BDEDatapackConvertEvent.class, DisplayEntityGroup.class, BDEDatapackConvertEvent::getGroup); + EventValues.registerEventValue(BDEDatapackConvertEvent.class, SpawnedDisplayEntityGroup.class, BDEDatapackConvertEvent::getSpawnedGroup); + EventValues.registerEventValue(BDEDatapackConvertEvent.class, Player.class, BDEDatapackConvertEvent::getPlayer); + EventValues.registerEventValue(BDEDatapackConvertEvent.class, SpawnedDisplayAnimation[].class, e -> e.getAnimations().toArray(new SpawnedDisplayAnimation[0])); + } } - - } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java index 52195df0..d6c6c04d 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/datapack/BDEngineDPConverter.java @@ -1,16 +1,14 @@ package net.donnypz.displayentityutils.utils.bdengine.convert.datapack; import net.donnypz.displayentityutils.DisplayAPI; -import net.donnypz.displayentityutils.listeners.bdengine.BDEngineConversionListener; +import net.donnypz.displayentityutils.events.BDEDatapackConvertEvent; import net.donnypz.displayentityutils.managers.*; -import net.donnypz.displayentityutils.utils.ConversionUtils; import net.donnypz.displayentityutils.utils.DisplayEntities.*; import net.donnypz.displayentityutils.utils.bdengine.convert.common.BDECommandConverter; import net.donnypz.displayentityutils.utils.version.VersionUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; -import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.*; import org.bukkit.command.CommandException; import org.bukkit.entity.Player; @@ -31,6 +29,8 @@ public class BDEngineDPConverter extends BDECommandConverter { static final String FUNCTION_FOLDER = VersionUtils.IS_1_21 ? "function" : "functions"; private static final String CREATE_MODEL_PATH = "/create.mcfunction"; + + private final String datapackName; private final LinkedHashMap> animations = new LinkedHashMap<>(); private final ZipFile zipFile; @@ -48,6 +48,7 @@ public BDEngineDPConverter(@NotNull String datapackName, if (!datapackName.endsWith(".zip")){ datapackName = datapackName+".zip"; } + this.datapackName = datapackName; ZipFile zip; try{ @@ -104,11 +105,21 @@ private void addFrame(ZipEntry entry){ } @Override - protected void onConversionCompleted(){ + protected void onConversionCompleted(DisplayEntityGroup savedGroup, SpawnedDisplayEntityGroup group){ try{ zipFile.close(); } catch(IOException ignored){} + new BDEDatapackConvertEvent( + datapackName, + player, + conversionId, + savedGroup, + group, + convertedAnimations, + saveGroup, + saveAnimations + ).callEvent(); } @Override From 708ef5ceaa6ba9bcebc95a7c5b8c5c9635a31465 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 04:09:13 -0500 Subject: [PATCH 19/43] Add a default frame point to animation frames --- .../DisplayAnimationFrame.java | 14 +++++- .../utils/DisplayEntities/FramePoint.java | 44 ++++++++++++++++++- .../utils/DisplayEntities/RelativePoint.java | 8 +++- .../SpawnedDisplayAnimationFrame.java | 40 ++++++++++------- 4 files changed, 85 insertions(+), 21 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DisplayAnimationFrame.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DisplayAnimationFrame.java index f280e0bd..697c2e85 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DisplayAnimationFrame.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DisplayAnimationFrame.java @@ -22,6 +22,7 @@ public final class DisplayAnimationFrame implements Serializable { HashMap displayTransformations = new HashMap<>(); HashMap interactionTranslations = new HashMap<>(); AnimationCamera camera; + FramePoint defaultFramePoint; int delay; int duration; @@ -72,6 +73,7 @@ public SpawnedDisplayAnimationFrame toSpawnedDisplayAnimationFrame(){ if (camera != null){ frame.camera = new AnimationCamera(camera); } + frame.defaultFramePoint = defaultFramePoint != null ? new FramePoint(defaultFramePoint) : null; //Old Sound Maps if (startSounds != null || endSounds != null){ @@ -106,7 +108,7 @@ public SpawnedDisplayAnimationFrame toSpawnedDisplayAnimationFrame(){ if (frameStartParticles != null){ for (AnimationParticle particle : frameStartParticles){ String pointTag = OLD_ANIM_PARTICLE+animationParticle; - FramePoint point = new FramePoint(pointTag, particle.getVector(), particle.getGroupYawAtCreation(), particle.getGroupPitchAtCreation()); + FramePoint point = new FramePoint(pointTag, particle.getVector(), particle.getGroupYawAtCreation(), particle.getGroupPitchAtCreation(), false); point.addParticle(particle); frame.addFramePoint(point); animationParticle++; @@ -117,7 +119,7 @@ public SpawnedDisplayAnimationFrame toSpawnedDisplayAnimationFrame(){ if (frameEndParticles != null){ for (AnimationParticle particle : frameEndParticles){ String pointTag = OLD_ANIM_PARTICLE+animationParticle; - FramePoint point = new FramePoint(pointTag, particle.getVector(), particle.getGroupYawAtCreation(), particle.getGroupPitchAtCreation()); + FramePoint point = new FramePoint(pointTag, particle.getVector(), particle.getGroupYawAtCreation(), particle.getGroupPitchAtCreation(), false); particle.setDelayInTicks(duration); point.addParticle(particle); frame.addFramePoint(point); @@ -165,6 +167,14 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo return frameEndParticles == null ? new HashSet<>() : new HashSet<>(frameEndParticles); } + /** + * Get a copy of this frame's default frame point + * @return a {@link FramePoint} or null + */ + public @Nullable FramePoint getDefaultFramePoint(){ + return defaultFramePoint == null ? null : new FramePoint(defaultFramePoint); + } + public @NotNull Set getFramePoints(){ return framePoints == null ? new HashSet<>() : new HashSet<>(framePoints.values()); } diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java index cebeffb4..5c10daa0 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java @@ -1,6 +1,7 @@ package net.donnypz.displayentityutils.utils.DisplayEntities; import net.donnypz.displayentityutils.utils.DisplayEntities.particles.AnimationParticle; +import net.donnypz.displayentityutils.utils.DisplayUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; @@ -20,25 +21,32 @@ import java.util.*; public class FramePoint extends RelativePoint implements Serializable { + public static final String DEFAULT_FRAME_POINT_TAG = "@DEU_DEFAULT_FP"; Set particles = new HashSet<>(); Map sounds = new HashMap<>(); + private final boolean isDefault; //false by default @Serial private static final long serialVersionUID = 99L; public FramePoint(@NotNull String pointTag, @NotNull ActiveGroup group, @NotNull Location location) { super(pointTag, group, location); + this.isDefault = false; } FramePoint(@NotNull String pointTag, @NotNull Vector vector, float initialYaw, float initialPitch) { super(pointTag, vector, initialYaw, initialPitch); + this.isDefault = false; } - FramePoint(@NotNull String pointTag, @NotNull Vector3f vector, float initialYaw, float initialPitch) { + + FramePoint(@NotNull String pointTag, @NotNull Vector3f vector, float initialYaw, float initialPitch, boolean isDefault) { super(pointTag, vector, initialYaw, initialPitch); + this.isDefault = isDefault; } + public FramePoint(@NotNull FramePoint point) { super(point); for (AnimationParticle p : point.particles){ @@ -47,6 +55,40 @@ public FramePoint(@NotNull FramePoint point) { for (Map.Entry entry : point.sounds.entrySet()){ this.sounds.put(entry.getKey(), entry.getValue().clone()); } + this.isDefault = point.isDefault; + } + + static FramePoint createDefault(){ + return new FramePoint(DEFAULT_FRAME_POINT_TAG, new Vector3f(), 0, 0, true); + } + + /** + * Set the tag of this {@link FramePoint} + * @param pointTag the tag + * @return this + * @throws IllegalStateException if the point is an animation frame's default frame point + * @throws IllegalArgumentException if the tag is invalid, per {@link DisplayUtils#isValidTag(String)} + */ + @Override + public FramePoint setTag(@Nullable String pointTag){ + if (isDefault){ + throw new IllegalStateException("Cannot set the point tag of an animation frame's default frame point"); + } + super.setTag(pointTag); + return this; + } + + /** + * {@inheritDoc} + * @throws IllegalStateException if the point is an animation frame's default frame point + */ + @Override + public @NotNull FramePoint setLocation(@NotNull ActiveGroup group, @NotNull Location location){ + if (isDefault){ + throw new IllegalStateException("Cannot set the location of an animation frame's default frame point"); + } + super.setLocation(group, location); + return this; } /** diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/RelativePoint.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/RelativePoint.java index b341fc85..2cd048c2 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/RelativePoint.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/RelativePoint.java @@ -57,10 +57,14 @@ protected RelativePoint(@NotNull RelativePoint point){ /** * Set the tag of this {@link RelativePoint} - * @param pointTag + * @param pointTag the tag * @return this + * @throws IllegalArgumentException if the tag is invalid, per {@link DisplayUtils#isValidTag(String)} */ - public RelativePoint setTag(String pointTag){ + public RelativePoint setTag(@Nullable String pointTag){ + if (pointTag != null && !DisplayUtils.isValidTag(tag)){ + throw new IllegalArgumentException("Invalid tag"); + } this.tag = pointTag; return this; } diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java index 4b21bfdf..ffa171fc 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java @@ -1,8 +1,6 @@ package net.donnypz.displayentityutils.utils.DisplayEntities; -import net.donnypz.displayentityutils.utils.ConversionUtils; import net.donnypz.displayentityutils.utils.DisplayUtils; -import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.ApiStatus; @@ -16,6 +14,7 @@ public final class SpawnedDisplayAnimationFrame implements Cloneable{ HashMap displayTransformations = new HashMap<>(); //Part UUIDS HashMap interactionTransformations = new HashMap<>(); //Part UUIDS AnimationCamera camera; + FramePoint defaultFramePoint; int delay; int duration; @@ -247,12 +246,22 @@ public boolean hasFramePoints(){ return !framePoints.isEmpty(); } + /** + * Get the default frame point of this frame + * @return a {@link FramePoint} + */ + public @NotNull FramePoint getDefaultFramePoint(){ + if (this.defaultFramePoint == null) this.defaultFramePoint = FramePoint.createDefault(); + return defaultFramePoint; + } + /** * Play the sounds assigned to a {@link FramePoint} contained in this frame, at a specified location * @param location the location to play the sound */ public void playSounds(@NotNull Location location){ + if (defaultFramePoint != null) defaultFramePoint.playSounds(location); for (FramePoint framePoint : framePoints.values()){ framePoint.playSounds(location); } @@ -273,6 +282,7 @@ public void playSounds(@NotNull ActiveGroup group){ * @param limited whether the effects should only be played to players who can see the group */ public void playSounds(@NotNull ActiveGroup group, @Nullable DisplayAnimator animator, boolean limited){ + if (defaultFramePoint != null) defaultFramePoint.playSounds(group, animator, limited); for (FramePoint framePoint : framePoints.values()){ framePoint.playSounds(group, animator, limited); } @@ -284,6 +294,7 @@ public void playSounds(@NotNull ActiveGroup group, @Nullable DisplayAnimator * @param group the relative group */ public void playSounds(@NotNull Player player, @NotNull ActiveGroup group){ + if (defaultFramePoint != null) defaultFramePoint.playSounds(group, player); for (FramePoint framePoint : framePoints.values()){ framePoint.playSounds(group, player); } @@ -295,6 +306,7 @@ public void playSounds(@NotNull Player player, @NotNull ActiveGroup group){ * @param group the relative group */ public void playSounds(@NotNull Collection players, @NotNull ActiveGroup group){ + if (defaultFramePoint != null) defaultFramePoint.playSounds(group, players); for (FramePoint framePoint : framePoints.values()){ framePoint.playSounds(group, players); } @@ -306,6 +318,7 @@ public void playSounds(@NotNull Collection players, @NotNull ActiveGroup * @param location the location to display the particles */ public void showParticles(@NotNull Location location){ + if (defaultFramePoint != null) defaultFramePoint.showParticles(location); for (FramePoint framePoint : framePoints.values()){ framePoint.showParticles(location); } @@ -327,6 +340,7 @@ public void showParticles(@NotNull ActiveGroup group){ * @param limited whether the effects should only be played to players who can see the group */ public void showParticles(@NotNull ActiveGroup group, @Nullable DisplayAnimator animator, boolean limited){ + if (defaultFramePoint != null) defaultFramePoint.showParticles(group, animator, limited); for (FramePoint framePoint : framePoints.values()){ framePoint.showParticles(group, animator, limited); } @@ -334,10 +348,11 @@ public void showParticles(@NotNull ActiveGroup group, @Nullable DisplayAnimat /** * Show the particles that will be displayed at the start of this frame - * @param player + * @param player the player who should see the particles * @param group the group that the particles will spawn around, respecting the group's yaw and pitch */ public void showParticles(@NotNull Player player, @NotNull ActiveGroup group){ + if (defaultFramePoint != null) defaultFramePoint.showParticles(group, player); for (FramePoint framePoint : framePoints.values()){ framePoint.showParticles(group, player); } @@ -345,27 +360,16 @@ public void showParticles(@NotNull Player player, @NotNull ActiveGroup group) /** * Show the particles that will be displayed at the start of this frame - * @param players + * @param players the players who should see the particles * @param group the group that the particles will spawn around, respecting the group's yaw and pitch */ public void showParticles(@NotNull Collection players, @NotNull ActiveGroup group){ + if (defaultFramePoint != null) defaultFramePoint.showParticles(group, players); for (FramePoint framePoint : framePoints.values()){ framePoint.showParticles(group, players); } } - - private void executeCommands(Location location, List commands){ - if (location == null || !location.isChunkLoaded() || commands.isEmpty()) { - return; - } - String coordinates = ConversionUtils.getCoordinateString(location); - String worldName = ConversionUtils.getExecuteCommandWorldName(location.getWorld()); - for (String s : commands){ - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "execute positioned "+coordinates+" in "+worldName+" run "+s); - } - } - /** * Play all effects that are contained within every {@link FramePoint} and the commands of this frame. * Effects include sounds, particles, commands. @@ -374,6 +378,7 @@ private void executeCommands(Location location, List commands){ * @param limited whether the effects should only be played to players who can see the group */ public void playEffects(@NotNull ActiveGroup group, @Nullable DisplayAnimator animator, boolean limited){ + if (defaultFramePoint != null) defaultFramePoint.showParticles(group, animator, limited); for (FramePoint point : framePoints.values()){ point.playEffects(group, animator, limited); } @@ -386,6 +391,7 @@ public void playEffects(@NotNull ActiveGroup group, @Nullable DisplayAnimator * @param group the group to play these effects for */ public void playEffects(@NotNull Player player, @NotNull ActiveGroup group){ + if (defaultFramePoint != null) defaultFramePoint.showParticles(group, player); for (FramePoint point : framePoints.values()){ point.playEffects(group, player); } @@ -397,6 +403,7 @@ public void playEffects(@NotNull Player player, @NotNull ActiveGroup group){ * @param group the group to play these effects for */ public void playEffects(@NotNull Collection players, @NotNull ActiveGroup group){ + if (defaultFramePoint != null) defaultFramePoint.showParticles(group, players); for (FramePoint point : framePoints.values()){ point.playEffects(group, players); } @@ -440,6 +447,7 @@ public DisplayAnimationFrame toDisplayAnimationFrame(){ if (camera != null){ frame.setCamera(new AnimationCamera(camera)); } + frame.defaultFramePoint = defaultFramePoint != null ? new FramePoint(defaultFramePoint) : null; return frame; } From 4e3f302f14a7bcc194c95ea16a378175b80b19a9 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 06:14:35 -0500 Subject: [PATCH 20/43] Add "/deu anim adddefaultparticle" --- .../particles/AnimationParticleBuilder.java | 75 ++++++++++++---- .../anim/AnimAddDefaultParticleCMD.java | 46 ++++++++++ .../command/anim/AnimCMD.java | 7 +- .../AnimationParticleDialogs.java | 16 ++-- .../AnimationParticleSelectDialog.java | 90 ++++++++++++------- .../BlockParticleDialog.java | 17 +++- .../DustOptionParticleDialog.java | 16 +++- .../DustTransitionParticleDialog.java | 16 +++- .../EntityEffectParticleDialog.java | 16 +++- .../FlashParticleDialog.java | 16 +++- .../GeneralParticleDialog.java | 17 +++- .../ItemParticleDialog.java | 17 +++- .../animationparticles/ParticleDialog.java | 40 ++++++--- .../TintedLeavesParticleDialog.java | 16 +++- 14 files changed, 301 insertions(+), 104 deletions(-) create mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/particles/AnimationParticleBuilder.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/particles/AnimationParticleBuilder.java index e0025207..ac661cd8 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/particles/AnimationParticleBuilder.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/particles/AnimationParticleBuilder.java @@ -15,12 +15,14 @@ import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; -import java.lang.reflect.Constructor; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; @ApiStatus.Internal public class AnimationParticleBuilder extends ParticleBuilder{ Player player; - FramePoint framePoint; + Collection framePoints; Step step; int delayInTicks = 0; AnimationParticle editParticle = null; @@ -38,11 +40,21 @@ public class AnimationParticleBuilder extends ParticleBuilder{ private static final Component delayMSG = prefix.append(Component.text("Enter the amount of delay (in ticks) before the particle should be shown", NamedTextColor.YELLOW)); private static final Component separatedMSG = Component.text("All values should be entered separated by spaces.", NamedTextColor.GRAY, TextDecoration.ITALIC); + + @ApiStatus.Internal + public AnimationParticleBuilder(@NotNull Player player, @NotNull Collection framePoints){ + super(Particle.FLAME); + this.player = player; + this.framePoints = new HashSet<>(framePoints); + DEUUser.getOrCreateUser(player).setAnimationParticleBuilder(this); + advanceStep(Step.PARTICLE); + } + @ApiStatus.Internal public AnimationParticleBuilder(@NotNull Player player, @NotNull FramePoint framePoint){ super(Particle.FLAME); this.player = player; - this.framePoint = framePoint; + this.framePoints = List.of(framePoint); DEUUser.getOrCreateUser(player).setAnimationParticleBuilder(this); advanceStep(Step.PARTICLE); } @@ -57,13 +69,22 @@ public AnimationParticleBuilder(@NotNull Player player, @NotNull AnimationPartic } private AnimationParticleBuilder(FramePoint framePoint, Particle particle){ + this(List.of(framePoint), particle); + } + + private AnimationParticleBuilder(Collection framePoints, Particle particle){ super(particle); - this.framePoint = framePoint; + this.framePoints = new HashSet<>(framePoints); } @ApiStatus.Internal public static AnimationParticleBuilder create(@NotNull FramePoint framePoint, @NotNull Particle particle, int count, double xOffset, double yOffset, double zOffset, double extra, Object data){ - AnimationParticleBuilder builder = new AnimationParticleBuilder(framePoint, particle); + return create(List.of(framePoint), particle, count, xOffset, yOffset, zOffset, extra, data); + } + + @ApiStatus.Internal + public static AnimationParticleBuilder create(@NotNull Collection framePoints, @NotNull Particle particle, int count, double xOffset, double yOffset, double zOffset, double extra, Object data){ + AnimationParticleBuilder builder = new AnimationParticleBuilder(framePoints, particle); builder .count(count) .extra(extra) @@ -180,23 +201,18 @@ public Step getStep() { @ApiStatus.Internal public void remove(){ editParticle = null; - framePoint = null; + if (framePoints != null) framePoints.clear(); + framePoints = null; player = null; } public AnimationParticle build(){ - Class clazz = getAnimationParticleClass(particle()); - try { - Constructor constructor = clazz.getConstructor(AnimationParticleBuilder.class, Object.class); - - AnimationParticle animParticle = constructor.newInstance(this, data()); - animParticle.setDelayInTicks(delayInTicks); - framePoint.addParticle(animParticle); - return animParticle; - - } catch (Exception e) { - throw new RuntimeException("Could not create particle: " + clazz.getSimpleName(), e); + AnimationParticle animParticle = getAnimationParticle(); + for (FramePoint fp : framePoints){ + fp.addParticle(animParticle.clone()); } + return animParticle; + } public static Class getAnimationParticleClass(@NotNull String particleName){ @@ -208,6 +224,31 @@ public static Class getAnimationParticleClass(@NotN } } + AnimationParticle getAnimationParticle(){ + if (isBlockDataParticle()){ + return new BlockAnimationParticle(this, data()); + } + else if (isItemParticle()){ + return new ItemStackAnimationParticle(this, data()); + } + else if (isDustOptionParticle()){ + return new DustOptionAnimationParticle(this, data()); + } + else if (isDustTransitionParticle()) { + return new DustTransitionAnimationParticle(this, data()); + } + else if (particle() == VersionUtils.getEntityEffectParticle()) { + return new EntityEffectAnimationParticle(this, data()); + } + else if (particle() == Particle.FLASH) { + return new FlashAnimationParticle(this, data()); + } + else { + return new GeneralAnimationParticle(this, particle()); + } + + } + public static Class getAnimationParticleClass(@NotNull Particle particle){ if (isBlockDataParticle(particle)){ return BlockAnimationParticle.class; diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java new file mode 100644 index 00000000..3e3cde2d --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java @@ -0,0 +1,46 @@ +package net.donnypz.displayentityutils.command.anim; + +import net.donnypz.displayentityutils.command.DEUSubCommand; +import net.donnypz.displayentityutils.command.Permission; +import net.donnypz.displayentityutils.command.PlayerSubCommand; +import net.donnypz.displayentityutils.managers.DisplayAnimationManager; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimationFrame; +import net.donnypz.displayentityutils.utils.DisplayEntities.particles.AnimationParticleBuilder; +import net.donnypz.displayentityutils.utils.command.DEUCommandUtils; +import net.donnypz.displayentityutils.utils.dialogs.animationparticles.AnimationParticleSelectDialog; +import net.donnypz.displayentityutils.utils.version.VersionUtils; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; + +class AnimAddDefaultParticleCMD extends PlayerSubCommand { + AnimAddDefaultParticleCMD(@NotNull DEUSubCommand parentSubCommand) { + super("adddefaultparticle", parentSubCommand, Permission.ANIM_ADD_PARTICLE); + setTabComplete(2, List.of("", "")); + } + + @Override + public void execute(Player player, String[] args) { + SpawnedDisplayAnimation anim = DisplayAnimationManager.getSelectedSpawnedAnimation(player); + if (anim == null) { + AnimCMD.noAnimationSelection(player); + return; + } + + Collection framePoints = DEUCommandUtils.getFrames(args[2], anim) + .stream() + .map(SpawnedDisplayAnimationFrame::getDefaultFramePoint) + .toList(); + + if (VersionUtils.canViewDialogs(player, false)){ + AnimationParticleSelectDialog.sendDialog(player, framePoints); + } + else{ + new AnimationParticleBuilder(player, framePoints); + } + } +} \ No newline at end of file diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java index 4288ea17..d91106be 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java @@ -41,6 +41,7 @@ public AnimCMD(){ new AnimAddSoundCMD(this); new AnimRemoveSoundCMD(this); new AnimAddParticleCMD(this); + new AnimAddDefaultParticleCMD(this); new AnimReverseCMD(this); new AnimScaleRespectCMD(this); new AnimSetTagCMD(this); @@ -109,19 +110,19 @@ else if (page == 4){ CMDUtils.sendCMD(sender, "/deu anim movepoint", "Move a frame point to your location, relative to your selected group"); CMDUtils.sendCMD(sender, "/deu anim addsound ", "Add a sound to play at a frame point"); CMDUtils.sendCMD(sender, "/deu anim removesound ", "Remove a sound from a frame point"); - CMDUtils.sendCMD(sender, "/deu anim addparticle", "Add a particle to play at a frame point"); - CMDUtils.sendCMD(sender, "/deu anim reverse", "Reverse the order of frames in your selected animation"); } else if (page == 5){ + CMDUtils.sendCMD(sender, "/deu anim addparticle", "Add a particle to play at a frame point"); + CMDUtils.sendCMD(sender, "/deu anim adddefaultparticle ", "Add a particle to play at a frame's default frame point (group origin)"); CMDUtils.sendCMD(sender, "/deu anim togglescalerespect", "Toggle whether your selected animation should respect the group's scale"); CMDUtils.sendCMD(sender, "/deu anim showframe ", "Displays a frame on your selected group"); CMDUtils.sendCMD(sender, "/deu anim previewframe ", "Preview a frame on your selected group, without changing group entity data"); + CMDUtils.sendCMD(sender, "/deu anim reverse", "Reverse the order of frames in your selected animation"); CMDUtils.sendCMD(sender, "/deu anim play [-loop] [-packet] [-camera] [-nodata]", "Play your selected animation on your selected group." + " \n\"-loop\" will make the animation loop." + " \n\"-packet\" will play the animation using packets." + " \n\"-camera\" will set your view to the animation's camera, if present" + " \n\"-nodata\" will disable texture changes for item/block displays and text display text changes"); - CMDUtils.sendCMD(sender, "/deu anim listanims", "List the animations actively playing for a group"); } else{ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/AnimationParticleDialogs.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/AnimationParticleDialogs.java index c00864b4..2bc8b323 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/AnimationParticleDialogs.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/AnimationParticleDialogs.java @@ -2,12 +2,12 @@ final class AnimationParticleDialogs { - static final ParticleDialog BLOCK = new BlockParticleDialog(); - static final ParticleDialog ITEM = new ItemParticleDialog(); - static final ParticleDialog DUST_OPTION = new DustOptionParticleDialog(); - static final ParticleDialog DUST_TRANSITION = new DustTransitionParticleDialog(); - static final ParticleDialog ENTITY_EFFECT = new EntityEffectParticleDialog(); - static final ParticleDialog TINTED_LEAVES = new TintedLeavesParticleDialog(); - static final ParticleDialog FLASH = new FlashParticleDialog(); - static final ParticleDialog GENERAL = new GeneralParticleDialog(); + static final ParticleDialog BLOCK = new BlockParticleDialog(null); + static final ParticleDialog ITEM = new ItemParticleDialog(null); + static final ParticleDialog DUST_OPTION = new DustOptionParticleDialog(null); + static final ParticleDialog DUST_TRANSITION = new DustTransitionParticleDialog(null); + static final ParticleDialog ENTITY_EFFECT = new EntityEffectParticleDialog(null); + static final ParticleDialog TINTED_LEAVES = new TintedLeavesParticleDialog(null); + static final ParticleDialog FLASH = new FlashParticleDialog(null); + static final ParticleDialog GENERAL = new GeneralParticleDialog(null); } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/AnimationParticleSelectDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/AnimationParticleSelectDialog.java index 24b6622f..ae52ae4c 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/AnimationParticleSelectDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/AnimationParticleSelectDialog.java @@ -5,27 +5,26 @@ import io.papermc.paper.registry.data.dialog.DialogBase; import io.papermc.paper.registry.data.dialog.action.DialogAction; import io.papermc.paper.registry.data.dialog.type.DialogType; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; import net.donnypz.displayentityutils.utils.version.VersionUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.event.ClickCallback; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.entity.Player; -import org.jetbrains.annotations.ApiStatus; import org.jetbrains.annotations.NotNull; +import java.time.Duration; import java.util.ArrayList; +import java.util.Collection; import java.util.List; -@ApiStatus.Internal public class AnimationParticleSelectDialog { - private static final ClickCallback.Options CALLBACK_OPTIONS = ClickCallback.Options.builder().uses(ClickCallback.UNLIMITED_USES).build(); + private static final ClickCallback.Options CALLBACK_OPTIONS = ClickCallback.Options.builder() + .lifetime(Duration.ofMinutes(15)) + .uses(ClickCallback.UNLIMITED_USES) + .build(); - /** - * Send this dialog to a player - * @param player the player - */ - @ApiStatus.Internal public static void sendDialog(@NotNull Player player){ Dialog dialog = Dialog.create(builder -> { builder.empty() @@ -34,108 +33,131 @@ public static void sendDialog(@NotNull Player player){ .pause(false) .afterAction(DialogBase.DialogAfterAction.NONE) .build()) - .type(DialogType.multiAction(getActionButtons()) + .type(DialogType.multiAction(getActionButtons(null)) .build()); }); player.showDialog(dialog); } + public static void sendDialog(@NotNull Player player, Collection framePoints){ + Dialog dialog = Dialog.create(builder -> { + builder.empty() + .base(DialogBase.builder(Component.text("Select an Animation Particle")) + //2 lines below required to stop flicker and centering of mouse + .pause(false) + .afterAction(DialogBase.DialogAfterAction.NONE) + .build()) + .type(DialogType.multiAction(getActionButtons(framePoints)) + .build()); + }); + player.showDialog(dialog); + } - private static List getActionButtons(){ + + private static List getActionButtons(Collection framePoints){ List buttons = new ArrayList<>(); - buttons.add(getBlockParticleAction()); - buttons.add(getItemstackAction()); - buttons.add(getDustOptionAction()); - buttons.add(getDustTransitionAction()); + buttons.add(getBlockParticleAction(framePoints)); + buttons.add(getItemstackAction(framePoints)); + buttons.add(getDustOptionAction(framePoints)); + buttons.add(getDustTransitionAction(framePoints)); //V_1_21_5 Particles //Not checked for v1_20_5 since dialogs aren't even viewable on versions that low - buttons.add(getEntityEffectAction()); - buttons.add(getTintedLeavesAction()); + buttons.add(getEntityEffectAction(framePoints)); + buttons.add(getTintedLeavesAction(framePoints)); - if (VersionUtils.IS_1_21_9) buttons.add(getFlashAction()); + if (VersionUtils.IS_1_21_9) buttons.add(getFlashAction(framePoints)); - buttons.add(getGeneralAction()); + buttons.add(getGeneralAction(framePoints)); return buttons; } - private static ActionButton getBlockParticleAction(){ + private static ActionButton getBlockParticleAction(final Collection framePoints){ return ActionButton .builder(Component.text("Block Particle")) .tooltip(Component.text("Create an Animation Particle from a block's texture", NamedTextColor.YELLOW)) .action(DialogAction.customClick((view, audience) -> { - AnimationParticleDialogs.BLOCK.sendDialog((Player) audience); + createOrSendExisting(framePoints, (Player) audience, AnimationParticleDialogs.BLOCK); }, CALLBACK_OPTIONS)) .build(); } - private static ActionButton getItemstackAction(){ + private static ActionButton getItemstackAction(final Collection framePoints){ return ActionButton .builder(Component.text("Item Particle")) .tooltip(Component.text("Create an Animation Particle from an item's texture", NamedTextColor.YELLOW)) .action(DialogAction.customClick((view, audience) -> { - AnimationParticleDialogs.ITEM.sendDialog((Player) audience); + createOrSendExisting(framePoints, (Player) audience, AnimationParticleDialogs.ITEM); }, CALLBACK_OPTIONS)) .build(); } - private static ActionButton getDustOptionAction(){ + private static ActionButton getDustOptionAction(final Collection framePoints){ return ActionButton .builder(Component.text("Dust")) .tooltip(Component.text("Create a Dust Option Animation Particle", NamedTextColor.YELLOW)) .action(DialogAction.customClick((view, audience) -> { - AnimationParticleDialogs.DUST_OPTION.sendDialog((Player) audience); + createOrSendExisting(framePoints, (Player) audience, AnimationParticleDialogs.DUST_OPTION); }, CALLBACK_OPTIONS)) .build(); } - private static ActionButton getDustTransitionAction(){ + private static ActionButton getDustTransitionAction(final Collection framePoints){ return ActionButton .builder(Component.text("Dust Transition")) .tooltip(Component.text("Create a Dust Transition Animation Particle", NamedTextColor.YELLOW)) .action(DialogAction.customClick((view, audience) -> { - AnimationParticleDialogs.DUST_TRANSITION.sendDialog((Player) audience); + createOrSendExisting(framePoints, (Player) audience, AnimationParticleDialogs.DUST_TRANSITION); }, CALLBACK_OPTIONS)) .build(); } - private static ActionButton getEntityEffectAction(){ + private static ActionButton getEntityEffectAction(final Collection framePoints){ return ActionButton .builder(Component.text("Entity Effect")) .tooltip(Component.text("Create an Entity Effect Animation Particle", NamedTextColor.YELLOW)) .action(DialogAction.customClick((view, audience) -> { - AnimationParticleDialogs.ENTITY_EFFECT.sendDialog((Player) audience); + createOrSendExisting(framePoints, (Player) audience, AnimationParticleDialogs.ENTITY_EFFECT); }, CALLBACK_OPTIONS)) .build(); } - private static ActionButton getTintedLeavesAction(){ + private static ActionButton getTintedLeavesAction(final Collection framePoints){ return ActionButton .builder(Component.text("Tinted Leaves")) .tooltip(Component.text("Create a Tinted Leaves Animation Particle", NamedTextColor.YELLOW)) .action(DialogAction.customClick((view, audience) -> { - AnimationParticleDialogs.TINTED_LEAVES.sendDialog((Player) audience); + createOrSendExisting(framePoints, (Player) audience, AnimationParticleDialogs.TINTED_LEAVES); }, CALLBACK_OPTIONS)) .build(); } - private static ActionButton getFlashAction(){ + private static ActionButton getFlashAction(final Collection framePoints){ return ActionButton .builder(Component.text("Flash")) .tooltip(Component.text("Create a Flash Animation Particle", NamedTextColor.YELLOW)) .action(DialogAction.customClick((view, audience) -> { - AnimationParticleDialogs.FLASH.sendDialog((Player) audience); + createOrSendExisting(framePoints, (Player) audience, AnimationParticleDialogs.FLASH); }, CALLBACK_OPTIONS)) .build(); } - private static ActionButton getGeneralAction(){ + private static ActionButton getGeneralAction(final Collection framePoints){ return ActionButton .builder(Component.text("General Particle")) .tooltip(Component.text("Create an Animation Particle from a basic particle without additional data", NamedTextColor.YELLOW)) .action(DialogAction.customClick((view, audience) -> { - AnimationParticleDialogs.GENERAL.sendDialog((Player) audience); + createOrSendExisting(framePoints, (Player) audience, AnimationParticleDialogs.GENERAL); }, CALLBACK_OPTIONS)) .build(); } + + private static void createOrSendExisting(Collection framePoints, Player player, ParticleDialog dialog){ + if (framePoints == null){ + dialog.sendDialog(player); + } + else{ + dialog.create(framePoints).sendDialog(player); + } + } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/BlockParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/BlockParticleDialog.java index 06f89318..7a0c2cc5 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/BlockParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/BlockParticleDialog.java @@ -2,6 +2,7 @@ import io.papermc.paper.registry.data.dialog.action.DialogActionCallback; import io.papermc.paper.registry.data.dialog.input.DialogInput; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -9,18 +10,26 @@ import org.bukkit.Registry; import org.bukkit.block.BlockType; +import java.util.Collection; import java.util.List; class BlockParticleDialog extends ParticleDialog { private static final String BLOCK_ID = "deu_particle_builder_block"; - BlockParticleDialog() { - super(Component.text("Block Animation Particle"), List.of(DialogInput.text(BLOCK_ID, Component.text("Block Id")).build())); + BlockParticleDialog(Collection framePoints) { + super(Component.text("Block Animation Particle"), + List.of(DialogInput.text(BLOCK_ID, Component.text("Block Id")).build()), + framePoints); } @Override - protected DialogActionCallback buildConfirmCallback() { + ParticleDialog create(Collection framePoints) { + return new BlockParticleDialog(framePoints); + } + + @Override + protected DialogActionCallback buildConfirmCallback(Collection framePoints) { return (view, audience) -> { String blockId = view.getText(BLOCK_ID); BlockType blockType = Registry.BLOCK.get(Key.key("minecraft", blockId)); @@ -28,7 +37,7 @@ protected DialogActionCallback buildConfirmCallback() { audience.sendMessage(Component.text("Failed to use block with the given id: "+blockId, NamedTextColor.RED)); return; } - this.buildParticle(view, audience, Particle.BLOCK, blockType.createBlockData()); + this.buildParticle(view, audience, Particle.BLOCK, blockType.createBlockData(), framePoints); }; } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/DustOptionParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/DustOptionParticleDialog.java index 22d7830a..cddb1a54 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/DustOptionParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/DustOptionParticleDialog.java @@ -3,11 +3,13 @@ import io.papermc.paper.registry.data.dialog.action.DialogActionCallback; import io.papermc.paper.registry.data.dialog.input.DialogInput; import net.donnypz.displayentityutils.utils.ConversionUtils; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Color; import org.bukkit.Particle; +import java.util.Collection; import java.util.List; class DustOptionParticleDialog extends ParticleDialog { @@ -15,7 +17,7 @@ class DustOptionParticleDialog extends ParticleDialog { private static final String COLOR = "deu_particle_builder_dust_color"; private static final String SIZE = "deu_particle_builder_dust_size"; - DustOptionParticleDialog() { + DustOptionParticleDialog(Collection framePoints) { super(Component.text("Dust Option Animation Particle"), List.of( DialogInput .text(COLOR, Component.text("Color (Minecraft Color or Hex)")) @@ -24,11 +26,17 @@ class DustOptionParticleDialog extends ParticleDialog { .numberRange(SIZE, Component.text("Size"), 0.0f, 100.0f) .step(0.5f) .initial(0.0f) - .build())); + .build()), + framePoints); } @Override - protected DialogActionCallback buildConfirmCallback() { + ParticleDialog create(Collection framePoints) { + return null; + } + + @Override + protected DialogActionCallback buildConfirmCallback(Collection framePoints) { return (view, audience) -> { float size = view.getFloat(SIZE); String colorString = view.getText(COLOR); @@ -37,7 +45,7 @@ protected DialogActionCallback buildConfirmCallback() { audience.sendMessage(Component.text("Failed to read the given color: "+colorString, NamedTextColor.RED)); return; } - this.buildParticle(view, audience, Particle.DUST, new Particle.DustOptions(color, size)); + this.buildParticle(view, audience, Particle.DUST, new Particle.DustOptions(color, size), framePoints); }; } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/DustTransitionParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/DustTransitionParticleDialog.java index bd7c14f2..605800f9 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/DustTransitionParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/DustTransitionParticleDialog.java @@ -3,11 +3,13 @@ import io.papermc.paper.registry.data.dialog.action.DialogActionCallback; import io.papermc.paper.registry.data.dialog.input.DialogInput; import net.donnypz.displayentityutils.utils.ConversionUtils; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Color; import org.bukkit.Particle; +import java.util.Collection; import java.util.List; class DustTransitionParticleDialog extends ParticleDialog { @@ -16,7 +18,7 @@ class DustTransitionParticleDialog extends ParticleDialog { private static final String END_COLOR = "deu_particle_builder_dust_color_2"; private static final String SIZE = "deu_particle_builder_dust_size"; - DustTransitionParticleDialog() { + DustTransitionParticleDialog(Collection framePoints) { super(Component.text("Dust Transition Animation Particle"), List.of( DialogInput .text(START_COLOR, Component.text("Start Color (Minecraft Color or Hex)")) @@ -28,11 +30,17 @@ class DustTransitionParticleDialog extends ParticleDialog { .numberRange(SIZE, Component.text("Size"), 0.0f, 100.0f) .step(0.5f) .initial(0.0f) - .build())); + .build()), + framePoints); } @Override - protected DialogActionCallback buildConfirmCallback() { + ParticleDialog create(Collection framePoints) { + return new DustTransitionParticleDialog(framePoints); + } + + @Override + protected DialogActionCallback buildConfirmCallback(Collection framePoints) { return (view, audience) -> { float size = view.getFloat(SIZE); @@ -49,7 +57,7 @@ protected DialogActionCallback buildConfirmCallback() { audience.sendMessage(Component.text("Failed to read the given end color: "+color2String, NamedTextColor.RED)); return; } - this.buildParticle(view, audience, Particle.DUST_COLOR_TRANSITION, new Particle.DustTransition(color1, color2, size)); + this.buildParticle(view, audience, Particle.DUST_COLOR_TRANSITION, new Particle.DustTransition(color1, color2, size), framePoints); }; } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/EntityEffectParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/EntityEffectParticleDialog.java index 2697cd5c..0d3356dd 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/EntityEffectParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/EntityEffectParticleDialog.java @@ -3,26 +3,34 @@ import io.papermc.paper.registry.data.dialog.action.DialogActionCallback; import io.papermc.paper.registry.data.dialog.input.DialogInput; import net.donnypz.displayentityutils.utils.ConversionUtils; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Color; import org.bukkit.Particle; +import java.util.Collection; import java.util.List; class EntityEffectParticleDialog extends ParticleDialog { private static final String COLOR = "deu_particle_builder_entity_effect_color"; - EntityEffectParticleDialog() { + EntityEffectParticleDialog(Collection framePoints) { super(Component.text("Dust Option Animation Particle"), List.of( DialogInput .text(COLOR, Component.text("Color (Minecraft Color or Hex)")) - .build())); + .build()), + framePoints); } @Override - protected DialogActionCallback buildConfirmCallback() { + ParticleDialog create(Collection framePoints) { + return new EntityEffectParticleDialog(framePoints); + } + + @Override + protected DialogActionCallback buildConfirmCallback(Collection framePoints) { return (view, audience) -> { String colorString = view.getText(COLOR); Color color = ConversionUtils.getColorFromText(colorString); @@ -30,7 +38,7 @@ protected DialogActionCallback buildConfirmCallback() { audience.sendMessage(Component.text("Failed to read the given color: "+colorString, NamedTextColor.RED)); return; } - this.buildParticle(view, audience, Particle.ENTITY_EFFECT, color); + this.buildParticle(view, audience, Particle.ENTITY_EFFECT, color, framePoints); }; } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/FlashParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/FlashParticleDialog.java index 64abd7fb..d5f81266 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/FlashParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/FlashParticleDialog.java @@ -3,26 +3,34 @@ import io.papermc.paper.registry.data.dialog.action.DialogActionCallback; import io.papermc.paper.registry.data.dialog.input.DialogInput; import net.donnypz.displayentityutils.utils.ConversionUtils; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Color; import org.bukkit.Particle; +import java.util.Collection; import java.util.List; class FlashParticleDialog extends ParticleDialog { private static final String COLOR = "deu_particle_builder_flash_color"; - FlashParticleDialog() { + FlashParticleDialog(Collection framePoints) { super(Component.text("Flash Animation Particle"), List.of( DialogInput .text(COLOR, Component.text("Color (Minecraft Color or Hex)")) - .build())); + .build()), + framePoints); } @Override - protected DialogActionCallback buildConfirmCallback() { + ParticleDialog create(Collection framePoints) { + return new FlashParticleDialog(framePoints); + } + + @Override + protected DialogActionCallback buildConfirmCallback(Collection framePoints) { return (view, audience) -> { String colorString = view.getText(COLOR); Color color = ConversionUtils.getColorFromText(colorString); @@ -30,7 +38,7 @@ protected DialogActionCallback buildConfirmCallback() { audience.sendMessage(Component.text("Failed to read the given color: "+colorString, NamedTextColor.RED)); return; } - this.buildParticle(view, audience, Particle.FLASH, color); + this.buildParticle(view, audience, Particle.FLASH, color, framePoints); }; } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/GeneralParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/GeneralParticleDialog.java index 6ea1ed2a..e34bae22 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/GeneralParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/GeneralParticleDialog.java @@ -2,24 +2,33 @@ import io.papermc.paper.registry.data.dialog.action.DialogActionCallback; import io.papermc.paper.registry.data.dialog.input.DialogInput; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Particle; import org.bukkit.Registry; +import java.util.Collection; import java.util.List; class GeneralParticleDialog extends ParticleDialog { private static final String PARTICLE = "deu_particle_builder_particle"; - GeneralParticleDialog() { - super(Component.text("General Animation Particle"), List.of(DialogInput.text(PARTICLE, Component.text("Particle")).build())); + GeneralParticleDialog(Collection framePoints) { + super(Component.text("General Animation Particle"), + List.of(DialogInput.text(PARTICLE, Component.text("Particle")).build()), + framePoints); } @Override - protected DialogActionCallback buildConfirmCallback() { + ParticleDialog create(Collection framePoints) { + return new GeneralParticleDialog(framePoints); + } + + @Override + protected DialogActionCallback buildConfirmCallback(Collection framePoints) { return (view, audience) -> { String particleId = view.getText(PARTICLE); Particle particle = Registry.PARTICLE_TYPE.get(Key.key("minecraft", particleId)); @@ -27,7 +36,7 @@ protected DialogActionCallback buildConfirmCallback() { audience.sendMessage(Component.text("Failed to find particle: "+particleId, NamedTextColor.RED)); return; } - this.buildParticle(view, audience, particle, null); + this.buildParticle(view, audience, particle, null, framePoints); }; } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ItemParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ItemParticleDialog.java index 682abaec..6265f5af 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ItemParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ItemParticleDialog.java @@ -2,6 +2,7 @@ import io.papermc.paper.registry.data.dialog.action.DialogActionCallback; import io.papermc.paper.registry.data.dialog.input.DialogInput; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; import net.kyori.adventure.key.Key; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; @@ -9,18 +10,26 @@ import org.bukkit.Registry; import org.bukkit.inventory.ItemType; +import java.util.Collection; import java.util.List; class ItemParticleDialog extends ParticleDialog { private static final String ITEM_ID = "deu_particle_builder_item"; - ItemParticleDialog() { - super(Component.text("Block Animation Particle"), List.of(DialogInput.text(ITEM_ID, Component.text("Item Id")).build())); + ItemParticleDialog(Collection framePoints) { + super(Component.text("Item Animation Particle"), + List.of(DialogInput.text(ITEM_ID, Component.text("Item Id")).build()), + framePoints); } @Override - protected DialogActionCallback buildConfirmCallback() { + ParticleDialog create(Collection framePoints) { + return new ItemParticleDialog(framePoints); + } + + @Override + protected DialogActionCallback buildConfirmCallback(Collection framePoints) { return (view, audience) -> { String itemId = view.getText(ITEM_ID); ItemType itemType = Registry.ITEM.get(Key.key("minecraft", itemId)); @@ -28,7 +37,7 @@ protected DialogActionCallback buildConfirmCallback() { audience.sendMessage(Component.text("Failed to use item with the given id: "+itemId, NamedTextColor.RED)); return; } - this.buildParticle(view, audience, Particle.ITEM, itemType.createItemStack()); + this.buildParticle(view, audience, Particle.ITEM, itemType.createItemStack(), framePoints); }; } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ParticleDialog.java index d63d8f9a..fb61247f 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ParticleDialog.java @@ -22,6 +22,7 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; +import java.util.Collection; import java.util.List; public abstract class ParticleDialog { @@ -44,12 +45,14 @@ public abstract class ParticleDialog { .append(Component.text("Animation Particle creation cancelled!", NamedTextColor.RED))); }, CALLBACK_OPTIONS)) .build(); + private final Component dialogTitle; - ParticleDialog(Component dialogTitle, @Nullable List additionalInputs){ + ParticleDialog(Component dialogTitle, @Nullable List additionalInputs, Collection points){ + this.dialogTitle = dialogTitle; this.dialog = Dialog.create(builder -> { builder.empty() - .type(buildDialogType(buildConfirmCallback())) - .base(DialogBase.builder(dialogTitle) + .type(buildDialogType(buildConfirmCallback(points))) + .base(DialogBase.builder(this.dialogTitle) .inputs(getInputs(additionalInputs)) .build()); }); @@ -59,22 +62,39 @@ void sendDialog(Player player){ player.showDialog(dialog); } - protected void buildParticle(DialogResponseView view, Audience audience, Particle particle, Object data){ + abstract ParticleDialog create(Collection framePoints); + + protected void buildParticle(DialogResponseView view, Audience audience, Particle particle, Object data, Collection points){ int count = Math.round(view.getFloat(COUNT)); double extra = (double) view.getFloat(EXTRA); double xOffset = (double) view.getFloat(X_OFFSET); double yOffset = (double) view.getFloat(Y_OFFSET); double zOffset = (double) view.getFloat(Z_OFFSET); Player p = (Player) audience; - FramePointSelector display = (FramePointSelector) RelativePointUtils.getRelativePointSelector(p); - FramePoint framePoint = display.getRelativePoint(); - AnimationParticleBuilder builder = AnimationParticleBuilder.create(framePoint, particle, count, xOffset, yOffset, zOffset, extra, data); - builder.build(); + AnimationParticleBuilder builder; + if (points == null){ + FramePointSelector display = (FramePointSelector) RelativePointUtils.getRelativePointSelector(p); + FramePoint framePoint = display.getRelativePoint(); + builder = AnimationParticleBuilder.create(framePoint, particle, count, xOffset, yOffset, zOffset, extra, data); + } + else{ + builder = AnimationParticleBuilder.create(points, particle, count, xOffset, yOffset, zOffset, extra, data); + } + + try{ + builder.build(); + p.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Animation Particle created for frame(s)!", NamedTextColor.GREEN))); + } + catch(RuntimeException ex){ + p.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed to create animation particle! See console.", NamedTextColor.RED))); + ex.printStackTrace(); + } + builder.remove(); - p.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Animation Particle creation successful!", NamedTextColor.GREEN))); + } - protected abstract DialogActionCallback buildConfirmCallback(); + protected abstract DialogActionCallback buildConfirmCallback(Collection points); private List getInputs(@Nullable List additionalInputs){ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/TintedLeavesParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/TintedLeavesParticleDialog.java index c467bf1b..b4f5b476 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/TintedLeavesParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/TintedLeavesParticleDialog.java @@ -3,26 +3,34 @@ import io.papermc.paper.registry.data.dialog.action.DialogActionCallback; import io.papermc.paper.registry.data.dialog.input.DialogInput; import net.donnypz.displayentityutils.utils.ConversionUtils; +import net.donnypz.displayentityutils.utils.DisplayEntities.FramePoint; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import org.bukkit.Color; import org.bukkit.Particle; +import java.util.Collection; import java.util.List; class TintedLeavesParticleDialog extends ParticleDialog { private static final String COLOR = "deu_particle_builder_tinted_leaves_color"; - TintedLeavesParticleDialog() { + TintedLeavesParticleDialog(Collection framePoints) { super(Component.text("Tinted Leaves Animation Particle"), List.of( DialogInput .text(COLOR, Component.text("Color (Minecraft Color or Hex)")) - .build())); + .build()), + framePoints); } @Override - protected DialogActionCallback buildConfirmCallback() { + ParticleDialog create(Collection framePoints) { + return new TintedLeavesParticleDialog(framePoints); + } + + @Override + protected DialogActionCallback buildConfirmCallback(Collection framePoints) { return (view, audience) -> { String colorString = view.getText(COLOR); Color color = ConversionUtils.getColorFromText(colorString); @@ -30,7 +38,7 @@ protected DialogActionCallback buildConfirmCallback() { audience.sendMessage(Component.text("Failed to read the given color: "+colorString, NamedTextColor.RED)); return; } - this.buildParticle(view, audience, Particle.TINTED_LEAVES, color); + this.buildParticle(view, audience, Particle.TINTED_LEAVES, color, framePoints); }; } } From c37bfd916576f9d64f02d12b028af0909fb20e91 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 17:39:30 -0500 Subject: [PATCH 21/43] No changes --- .../displayentityutils/utils/version/VersionUtils.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/version/VersionUtils.java b/api/src/main/java/net/donnypz/displayentityutils/utils/version/VersionUtils.java index b05282c2..1b4777b8 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/version/VersionUtils.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/version/VersionUtils.java @@ -30,15 +30,6 @@ public static boolean canViewDialogs(@NotNull Player player, boolean sendErrorMe } return false; } - else if (IS_1_21_6){ - if (!serverHasDialogs()){ - if (sendErrorMessage){ - player.sendMessage(DisplayAPI.pluginPrefix - .append(Component.text("You can only view Dialog Menus when playing on version 1.21.6 or higher!", NamedTextColor.RED))); - } - } - return false; - } return true; } From ea5d5a2bda32f3f63a9323ccaec9d7e8e6e045b8 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 18:01:03 -0500 Subject: [PATCH 22/43] Update range and step of delta and extra of particle dialogs --- .../animationparticles/ParticleDialog.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ParticleDialog.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ParticleDialog.java index fb61247f..a34d9d96 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ParticleDialog.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/dialogs/animationparticles/ParticleDialog.java @@ -106,23 +106,23 @@ private List getInputs(@Nullable List additionalInputs .initial(1.0f) .build()); inputs.add(DialogInput - .numberRange(EXTRA, Component.text("Extra"), 0.0f, 100.0f) - .step(1.0f) + .numberRange(EXTRA, Component.text("Extra"), 0.0f, 15.0f) + .step(0.1f) .initial(0.0f) .build()); inputs.add(DialogInput - .numberRange(X_OFFSET, Component.text("Delta X (Offset)"), 0.0f, 100.0f) - .step(0.5f) + .numberRange(X_OFFSET, Component.text("Delta X (Offset)"), 0.0f, 15.0f) + .step(0.1f) .initial(0.0f) .build()); inputs.add(DialogInput - .numberRange(Y_OFFSET, Component.text("Delta Y (Offset)"), 0.0f, 100.0f) - .step(0.5f) + .numberRange(Y_OFFSET, Component.text("Delta Y (Offset)"), 0.0f, 15.0f) + .step(0.1f) .initial(0.0f) .build()); inputs.add(DialogInput - .numberRange(Z_OFFSET, Component.text("Delta Z (Offset)"), 0.0f, 100.0f) - .step(0.5f) + .numberRange(Z_OFFSET, Component.text("Delta Z (Offset)"), 0.0f, 15.0f) + .step(0.1f) .initial(0.0f) .build()); return inputs; From 94fc2e659551c4b982acf645e973e4771e560878 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 20:22:33 -0500 Subject: [PATCH 23/43] Added PacketDisplayEntityGroup#getNearbyGroups and WorldUtils --- .../managers/DisplayGroupManager.java | 29 +--------- .../PacketDisplayEntityGroup.java | 22 +++++++ .../displayentityutils/utils/WorldUtils.java | 58 +++++++++++++++++++ 3 files changed, 83 insertions(+), 26 deletions(-) create mode 100644 api/src/main/java/net/donnypz/displayentityutils/utils/WorldUtils.java diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java index 40777e99..7a98ec96 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java +++ b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java @@ -11,6 +11,7 @@ import net.donnypz.displayentityutils.utils.DisplayEntities.*; import net.donnypz.displayentityutils.utils.DisplayUtils; import net.donnypz.displayentityutils.utils.GroupResult; +import net.donnypz.displayentityutils.utils.WorldUtils; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; @@ -168,7 +169,7 @@ public static void updateSpawnedGroup(Location lastLoc, Location newLoc, Spawned if (holder == null) return Collections.emptySet(); Set groups = new HashSet<>(); - Set chunks = getNearbyChunks(location, radius); + Set chunks = WorldUtils.getNearbyChunks(location, radius); double radiusSquared = radius*radius; for (Chunk c : chunks){ for (SpawnedDisplayEntityGroup group : holder.getGroups(c.getChunkKey())){ @@ -189,7 +190,7 @@ public static void updateSpawnedGroup(Location lastLoc, Location newLoc, Spawned SpawnedDisplayEntityGroup nearest = null; double lastDistSq = Double.MAX_VALUE; - Set chunks = getNearbyChunks(location, radius); + Set chunks = WorldUtils.getNearbyChunks(location, radius); double radiusSquared = radius*radius; for (Chunk c : chunks){ @@ -215,30 +216,6 @@ public static void updateSpawnedGroup(Location lastLoc, Location newLoc, Spawned return nearest; } - private static Set getNearbyChunks(Location loc, double radiusInBlocks) { - World world = loc.getWorld(); - - double minX = loc.getX() - radiusInBlocks; - double maxX = loc.getX() + radiusInBlocks; - double minZ = loc.getZ() - radiusInBlocks; - double maxZ = loc.getZ() + radiusInBlocks; - - int minChunkX = (int) minX >> 4; - int maxChunkX = (int) maxX >> 4; - int minChunkZ = (int) minZ >> 4; - int maxChunkZ = (int) maxZ >> 4; - - Set chunks = new HashSet<>(); - - for (int cx = minChunkX; cx <= maxChunkX; cx++) { - for (int cz = minChunkZ; cz <= maxChunkZ; cz++) { - chunks.add(world.getChunkAt(cx, cz)); - } - } - - return chunks; - } - /** * Get the {@link SpawnedDisplayEntityGroup} that has been registered during the current game session from a display entity. *
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketDisplayEntityGroup.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketDisplayEntityGroup.java index 3f0799e8..ee84027c 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketDisplayEntityGroup.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketDisplayEntityGroup.java @@ -103,6 +103,28 @@ public static boolean hasGroups(@NotNull World world) { return data != null ? data.getGroups(chunkKey) : Collections.emptySet(); } + public static @NotNull Set getNearbyGroups(@NotNull Location location, double radius){ + WorldData data = allPacketGroups.get(location.getWorld().getName()); + if (data == null) return Collections.emptySet(); + + Set groups = new HashSet<>(); + double radiusSquared = radius*radius; + + Set nearbyChunks = WorldUtils.getNearbyChunkKeys(location, radius); + + for (Long chunkKey : nearbyChunks){ + for (PacketDisplayEntityGroup group : data.getGroups(chunkKey)){ + Location groupLoc = group.getLocation(); + if (groupLoc == null || location.distanceSquared(groupLoc) > radiusSquared){ + continue; + } + groups.add(group); + } + } + return groups; + } + + public static boolean hasPassengerGroups(@NotNull Entity entity) { return hasPassengerGroups(entity.getUniqueId()); } diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/WorldUtils.java b/api/src/main/java/net/donnypz/displayentityutils/utils/WorldUtils.java new file mode 100644 index 00000000..0ec9ac94 --- /dev/null +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/WorldUtils.java @@ -0,0 +1,58 @@ +package net.donnypz.displayentityutils.utils; + +import org.bukkit.Chunk; +import org.bukkit.Location; +import org.bukkit.World; + +import java.util.HashSet; +import java.util.Set; + +public class WorldUtils { + + + public static Set getNearbyChunks(Location loc, double radiusInBlocks) { + World world = loc.getWorld(); + + double minX = loc.getX() - radiusInBlocks; + double maxX = loc.getX() + radiusInBlocks; + double minZ = loc.getZ() - radiusInBlocks; + double maxZ = loc.getZ() + radiusInBlocks; + + int minChunkX = (int) minX >> 4; + int maxChunkX = (int) maxX >> 4; + int minChunkZ = (int) minZ >> 4; + int maxChunkZ = (int) maxZ >> 4; + + Set chunks = new HashSet<>(); + + for (int cx = minChunkX; cx <= maxChunkX; cx++) { + for (int cz = minChunkZ; cz <= maxChunkZ; cz++) { + chunks.add(world.getChunkAt(cx, cz)); + } + } + + return chunks; + } + + public static Set getNearbyChunkKeys(Location loc, double radiusInBlocks){ + double minX = loc.getX() - radiusInBlocks; + double maxX = loc.getX() + radiusInBlocks; + double minZ = loc.getZ() - radiusInBlocks; + double maxZ = loc.getZ() + radiusInBlocks; + + int minChunkX = (int) minX >> 4; + int maxChunkX = (int) maxX >> 4; + int minChunkZ = (int) minZ >> 4; + int maxChunkZ = (int) maxZ >> 4; + + Set chunkKeys = new HashSet<>(); + + for (int cx = minChunkX; cx <= maxChunkX; cx++) { + for (int cz = minChunkZ; cz <= maxChunkZ; cz++) { + chunkKeys.add(ConversionUtils.getChunkKey(cx, cz)); + } + } + + return chunkKeys; + } +} From e31c920716ef90730cca61314edc98b3cf70603e Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 20:32:02 -0500 Subject: [PATCH 24/43] Added "/deu group spawnat" and "/deu group despawnat" --- .../command/anim/AnimSelectCMD.java | 2 +- .../command/group/GroupCMD.java | 44 +++- .../command/group/GroupDespawnAtCMD.java | 189 ++++++++++++++++++ .../command/group/GroupSpawnAtCMD.java | 138 +++++++++++++ .../command/group/GroupSpawnCMD.java | 65 +++--- 5 files changed, 396 insertions(+), 42 deletions(-) create mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDespawnAtCMD.java create mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnAtCMD.java diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimSelectCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimSelectCMD.java index 60ce7999..c12d89a5 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimSelectCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimSelectCMD.java @@ -36,7 +36,7 @@ public void execute(Player player, String[] args) { static void getAnimation(Player p, String tag, String storage){ if (storage.equals("all")){ p.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempting to select animation from all storage locations", NamedTextColor.YELLOW))); - GroupSpawnCMD.attemptAll(p, tag, LoadMethod.LOCAL, false); + GroupSpawnCMD.attemptAll(p, p.getLocation(), tag, LoadMethod.LOCAL, false); return; } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java index 4c54b0e2..09fa1699 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java @@ -29,8 +29,10 @@ public GroupCMD(){ new GroupShowPersistentPacketGroupsCMD(this); new GroupDeleteCMD(this); new GroupSpawnCMD(this); + new GroupSpawnAtCMD(this); new GroupSpawnJSONCMD(this); new GroupDespawnCMD(this); + new GroupDespawnAtCMD(this); new GroupInfoCMD(this); new GroupSetTagCMD(this); new GroupYawCMD(this); @@ -98,52 +100,72 @@ static void groupHelp(CommandSender sender, int page){ } else if (page == 2) { CMDUtils.sendCMD(sender, "/deu group spawn [-packet]", "Spawn a saved display entity group/model from a storage location. \"-packet\" will spawn the group/model using packets"); - CMDUtils.sendCMD(sender, "/deu group spawnjson [-packet]", "Spawn a JSON saved display entity group/model from a local storage. \"-packet\" will spawn the group/model using packets"); + CMDUtils.sendCMD(sender, "/deu group spawnat [-world ][-packet] [-force]", + """ + Spawn a saved display entity group/model from a storage location at a specified location. \ + + "-world " will spawn the group in the provided world\ + + "-packet" will spawn the group/model using packets\ + + "-force" will force the location's chunk to load, if unloaded. Unneeded if spawned using packets + """); + CMDUtils.sendUnsafeCMD(sender, "/deu group spawnjson [-packet]", "Spawn a JSON saved display entity group/model from a local storage. \"-packet\" will spawn the group/model using packets"); CMDUtils.sendCMD(sender, "/deu group despawn", "Despawn your selected group"); + CMDUtils.sendCMD(sender, "/deu group despawnat [-world ] [-packet ] [-force]", + """ + Despawn groups within a given distance from a location\ + + "-world " will despawn groups in the provided world\ + + "-packet " will optionally include packet-based groups or only look for packet-based groups when despawning\ + + "-force" will force the location's chunk to load, if unloaded. Unneeded if only packet groups are searched for + """); CMDUtils.sendCMD(sender, "/deu group save ", "Save your selected group"); CMDUtils.sendUnsafeCMD(sender, "/deu group savejson", "Save your selected group as a JSON file. Spawning groups from JSON files will always be slower"); + } + else if (page == 3){ CMDUtils.sendCMD(sender, "/deu group delete ", "Delete a saved group from a storage location"); CMDUtils.sendCMD(sender, "/deu group topacket [-confirm] [-keep]", "Make your selected group packet-based, making it unselectable. \"-confirm\" confirms the action." + " \"-keep\" keeps the non-packet based version of your group spawned."); - } - else if (page == 3){ CMDUtils.sendCMD(sender, "/deu group markpacketgroups", "Create markers for all packet groups stored in your current chunk"); CMDUtils.sendCMD(sender, "/deu group showpacketgroups [-self]", "Show all persistent packet-based groups in your current chunk. \n\"-self\" shows the group only for you"); CMDUtils.sendCMD(sender, "/deu group hidepacketgroups [-self]", "Hide all persistent packet-based groups in your current chunk. \n\"-self\" hides the group only for you"); CMDUtils.sendCMD(sender, "/deu group addtarget", "Add a targeted interaction entity to your group"); CMDUtils.sendCMD(sender, "/deu group ungroupinteractions", "Remove all interactions from your group"); - CMDUtils.sendCMD(sender, "/deu group settag ", "Set this group's tag, or identifier"); - CMDUtils.sendCMD(sender, "/deu group yaw [-pivot]","Set your selected group's yaw, \"-pivot\" pivots interaction entities around the group"); } else if (page == 4){ + CMDUtils.sendCMD(sender, "/deu group settag ", "Set this group's tag, or identifier"); + CMDUtils.sendCMD(sender, "/deu group yaw [-pivot]","Set your selected group's yaw, \"-pivot\" pivots interaction entities around the group"); CMDUtils.sendCMD(sender, "/deu group pitch ", "Set your selected group's pitch"); CMDUtils.sendCMD(sender, "/deu group scale ", "Scale your selected group with a given multiplier"); CMDUtils.sendCMD(sender, "/deu group brightness ", "Set your selected group's brightness. Enter values between 0-15. -1 resets"); CMDUtils.sendCMD(sender, "/deu group clone", "Spawn a cloned group at your selected group's location"); CMDUtils.sendCMD(sender, "/deu group clonehere", "Spawn a cloned group at your location"); - CMDUtils.sendCMD(sender, "/deu group move [tick-duration]", "Change the actual location of your selected group, with an optional duration"); - CMDUtils.sendCMD(sender, "/deu group movehere", "Change your selected group's actual location to your location"); } else if (page == 5){ + CMDUtils.sendCMD(sender, "/deu group move [tick-duration]", "Change the actual location of your selected group, with an optional duration"); + CMDUtils.sendCMD(sender, "/deu group movehere", "Change your selected group's actual location to your location"); CMDUtils.sendCMD(sender, "/deu group translate ","Changes your selected group's translation, use \"move\" instead if this group uses animations"); CMDUtils.sendCMD(sender, "/deu group merge ","Merge groups near your selected group"); CMDUtils.sendCMD(sender, "/deu group billboard ", "Set the billboard of all parts in this group"); CMDUtils.sendCMD(sender, "/deu group glowcolor ", "Set the glow color for all parts in this group"); CMDUtils.sendCMD(sender, "/deu group glow", "Make all parts in this group glow"); + } + else if (page == 6){ CMDUtils.sendCMD(sender, "/deu group unglow", "Remove the glowing effect from all parts in this group"); CMDUtils.sendCMD(sender, "/deu group ride <-target | player-name | entity-uuid> [group-tag] [storage] [controller-id]", "Make a group ride an entity. Values in brackets [] are optional"); CMDUtils.sendCMD(sender, "/deu group ridedespawn <-target | player-name | entity-uuid> [group-tag] [storage] [controller-id]", "Make a group ride an entity, despawning the group after the ridden entity despawns/disconnects. The group will not be persistent. Values in brackets [] are optional"); - } - else if (page == 6){ CMDUtils.sendCMD(sender, "/deu group safedismount <-target | -selected | player-name | entity-uuid>", "Safely dismount a group from an entity"); CMDUtils.sendCMD(sender, "/deu group dismount <-target | -selected | player-name | entity-uuid> [-despawn]", "Dismount a group from an entity, with optional despawning"); CMDUtils.sendCMD(sender, "/deu group setspawnanim ", "Set an animation to play when this group is spawned/loaded"); CMDUtils.sendCMD(sender, "/deu group unsetspawnanim", "Remove the spawn animation that's set on your selected group"); + } + else{ CMDUtils.sendCMD(sender, "/deu group viewrange ", "Set the view range multiplier for your selected group"); CMDUtils.sendCMD(sender, "/deu group autocull", "Calculate and set culling bounds for every part in your selected group"); CMDUtils.sendCMD(sender, "/deu group removecull", "Remove the culling bounds for every part in your selected group"); - } - else{ CMDUtils.sendCMD(sender, "/deu group togglepersist", "Toggle if your group should persist after a server shutdown"); CMDUtils.sendCMD(sender, "/deu group togglepersistoverride", "Toggle if your group's persistence can be overriden when loaded by a chunk, " + "only if \"persistenceOverride\" is enabled in the config"); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDespawnAtCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDespawnAtCMD.java new file mode 100644 index 00000000..e8b12c57 --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDespawnAtCMD.java @@ -0,0 +1,189 @@ +package net.donnypz.displayentityutils.command.group; + +import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.command.ConsoleUsableSubCommand; +import net.donnypz.displayentityutils.command.DEUSubCommand; +import net.donnypz.displayentityutils.command.Permission; +import net.donnypz.displayentityutils.managers.DisplayGroupManager; +import net.donnypz.displayentityutils.utils.DisplayEntities.ActiveGroup; +import net.donnypz.displayentityutils.utils.DisplayEntities.PacketDisplayEntityGroup; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class GroupDespawnAtCMD extends ConsoleUsableSubCommand { + GroupDespawnAtCMD(@NotNull DEUSubCommand parentSubCommand) { + super("despawnat", parentSubCommand, Permission.GROUP_DESPAWN); + setTabComplete(2, ""); + setTabComplete(3, ""); + setTabComplete(4, ""); + setTabComplete(5, ""); + setTabComplete(6, List.of("-world ", "-packet ", "-force")); + setTabComplete(7, List.of("-world ", "-packet ", "-force")); + setTabComplete(8, List.of("-world ", "-packet ", "-force")); + setTabComplete(9, List.of("-world ", "-packet ", "-force")); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if (args.length < 6) { + sender.sendMessage(Component.text("Incorrect Usage! /deu group despawnat [-world ] [-packet ] [-force]", NamedTextColor.RED)); + return; + } + try{ + double x = getCoordinate(sender, args[2], 'x'); + double y = getCoordinate(sender, args[3], 'y'); + double z = getCoordinate(sender, args[4], 'z'); + double distance = Double.parseDouble(args[5]); + + OptionalArgs optionalArgs = new OptionalArgs(sender, args); + if (!optionalArgs.valid) return; + World w; + if (optionalArgs.worldName == null){ + if (sender instanceof Player player){ + w = player.getWorld(); + } + else{ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("You must supply a world name!", NamedTextColor.RED))); + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("| Ex: \"/deu group despawnat -world world_name", NamedTextColor.GRAY))); + return; + } + } + else{ + w = Bukkit.getWorld(optionalArgs.worldName); + } + + if (w == null){ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("World with the given name is not loaded or does not exist!", NamedTextColor.RED))); + return; + } + + Location searchLocation = new Location(w, x, y, z); + boolean hasGroups = false; + if (optionalArgs.packetIncluded){ + Set groups = PacketDisplayEntityGroup.getNearbyGroups(searchLocation, distance); + hasGroups = !groups.isEmpty(); + if (hasGroups) groups.forEach(PacketDisplayEntityGroup::unregister); + } + + if (!optionalArgs.packetOnly){ + Set groups = DisplayGroupManager.getNearbySpawnedGroups(searchLocation, distance); + if (!groups.isEmpty()){ + hasGroups = true; + groups.forEach(sg -> sg.unregister(true, optionalArgs.force)); + } + } + if (!hasGroups){ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("There are no groups within the given range", NamedTextColor.RED))); + return; + } + + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Despawned groups within the given range!", NamedTextColor.GRAY))); + }catch(NumberFormatException e){ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Enter valid numbers for the coordinates and distance!", NamedTextColor.RED))); + } + catch (IllegalArgumentException e){ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("You cannot use \"~\" in the console!", NamedTextColor.RED))); + } + } + + private double getCoordinate(CommandSender sender, String userInput, char coordinate){ + if (userInput.equals("~")){ + if (sender instanceof Player p ){ + Location pLoc = p.getLocation(); + if (coordinate == 'x') return pLoc.x(); + else if (coordinate == 'y') return pLoc.y(); + else return pLoc.z(); + } + else{ + throw new IllegalArgumentException(); + } + } + else{ + return Double.parseDouble(userInput); + } + } + + static class OptionalArgs{ + boolean valid = true; + String worldName = null; + boolean packetOnly = false; + boolean packetIncluded = false; + boolean force = false; + + OptionalArgs(CommandSender sender, String[] args){ + + + for (int i = 6; i < args.length; i++) { + String arg = args[i]; + + switch (arg.toLowerCase()) { + case "-packet" -> { + if (packetOnly || packetIncluded) { + sender.sendMessage(Component.text( + "-packet was specified multiple times.", + NamedTextColor.RED)); + return; + } + if (i + 1 >= args.length) { + sender.sendMessage(Component.text( + "You must specify 'include' or 'only' after -packet.", + NamedTextColor.RED)); + valid = false; + return; + } + + String mode = args[++i].toLowerCase(); + + switch (mode) { + case "include" -> packetIncluded = true; + case "only" -> packetOnly = true; + default -> { + sender.sendMessage(Component.text( + "Packet mode must be 'include' or 'only'.", + NamedTextColor.RED)); + return; + } + } + } + case "-force" -> force = true; + + case "-world" -> { + if (worldName != null) { + sender.sendMessage(Component.text( + "-world was specified multiple times.", + NamedTextColor.RED)); + return; + } + + if (i + 1 >= args.length) { + sender.sendMessage(Component.text( + "You must specify a world after -world.", + NamedTextColor.RED)); + return; + } + + worldName = args[++i]; + } + + default -> { + sender.sendMessage(Component.text( + "Unknown option: " + arg, + NamedTextColor.RED)); + return; + } + } + } + } + } +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnAtCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnAtCMD.java new file mode 100644 index 00000000..1554efa4 --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnAtCMD.java @@ -0,0 +1,138 @@ +package net.donnypz.displayentityutils.command.group; + +import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.command.ConsoleUsableSubCommand; +import net.donnypz.displayentityutils.command.DEUSubCommand; +import net.donnypz.displayentityutils.command.Permission; +import net.donnypz.displayentityutils.command.PlayerSubCommand; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.World; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class GroupSpawnAtCMD extends ConsoleUsableSubCommand { + GroupSpawnAtCMD(@NotNull DEUSubCommand parentSubCommand) { + super("spawnat", parentSubCommand, Permission.GROUP_SPAWN); + setTabComplete(2, ""); + setTabComplete(3, ""); + setTabComplete(4, ""); + setTabComplete(5, ""); + setTabComplete(6, TabSuggestion.STORAGES); + setTabComplete(7, List.of("-world ", "-packet", "-force")); + setTabComplete(8, List.of("-world ", "-packet", "-force")); + setTabComplete(9, List.of("-world ", "-packet", "-force")); + setTabComplete(10, List.of("-world ", "-packet", "-force")); + } + + @Override + public void execute(CommandSender sender, String[] args) { + if (args.length < 7) { + sender.sendMessage(Component.text("Incorrect Usage! /deu group spawnat [-world ] [-packet] [-force]", NamedTextColor.RED)); + return; + } + try{ + double x = getCoordinate(sender, args[2], 'x'); + double y = getCoordinate(sender, args[3], 'y'); + double z = getCoordinate(sender, args[4], 'z'); + String tag = args[5]; + String storage = args[6]; + + OptionalArgs optionalArgs = new OptionalArgs(sender, args); + World w; + if (optionalArgs.worldName == null){ + if (sender instanceof Player player){ + w = player.getWorld(); + } + else{ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("You must supply a world name!", NamedTextColor.RED))); + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("| Ex: \"/deu group spawnat -world world_name", NamedTextColor.GRAY))); + return; + } + } + else{ + w = Bukkit.getWorld(optionalArgs.worldName); + } + + if (w == null){ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("World with the given name is not loaded or does not exist!", NamedTextColor.RED))); + return; + } + + Location spawnLocation = new Location(w, x, y, z); + GroupSpawnCMD.spawnGroup(sender, spawnLocation, tag, storage, optionalArgs.packet); + }catch(NumberFormatException e){ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Enter a number for each coordinate!", NamedTextColor.RED))); + } + catch (IllegalArgumentException e){ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("You cannot use \"~\" in the console!", NamedTextColor.RED))); + } + } + + private double getCoordinate(CommandSender sender, String userInput, char coordinate){ + if (userInput.equals("~")){ + if (sender instanceof Player p ){ + Location pLoc = p.getLocation(); + if (coordinate == 'x') return pLoc.x(); + else if (coordinate == 'y') return pLoc.y(); + else return pLoc.z(); + + } + else{ + throw new IllegalArgumentException(); + } + } + else{ + return Double.parseDouble(userInput); + } + } + + class OptionalArgs{ + String worldName = null; + boolean packet = false; + boolean force = false; + + OptionalArgs(CommandSender sender, String[] args){ + + + for (int i = 7; i < args.length; i++) { + String arg = args[i]; + + switch (arg.toLowerCase()) { + case "-packet" -> packet = true; + case "-force" -> force = true; + + case "-world" -> { + if (worldName != null) { + sender.sendMessage(Component.text( + "-world was specified multiple times.", + NamedTextColor.RED)); + return; + } + + if (i + 1 >= args.length) { + sender.sendMessage(Component.text( + "You must specify a world after -world.", + NamedTextColor.RED)); + return; + } + + worldName = args[++i]; + } + + default -> { + sender.sendMessage(Component.text( + "Unknown option: " + arg, + NamedTextColor.RED)); + return; + } + } + } + } + } +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnCMD.java index fa0beb77..c93fa4c4 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnCMD.java @@ -17,6 +17,7 @@ import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Bukkit; import org.bukkit.Location; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -37,13 +38,13 @@ public void execute(Player player, String[] args) { String tag = args[2]; String storage = args[3]; boolean isPacket = args.length > 4 && args[4].equalsIgnoreCase("-packet"); - spawnGroup(player, tag, storage, isPacket); + spawnGroup(player, player.getLocation(), tag, storage, isPacket); } - private static void spawnGroup(Player p, String tag, String storage, boolean isPacket){ + static void spawnGroup(CommandSender sender, Location spawnLoc, String tag, String storage, boolean isPacket){ if (storage.equals("all")){ - p.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempting to spawn display entity group from all storage locations", NamedTextColor.YELLOW))); - attemptAll(p, tag, LoadMethod.LOCAL, true); + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempting to spawn display entity group from all storage locations", NamedTextColor.YELLOW))); + attemptAll(sender, spawnLoc, tag, LoadMethod.LOCAL, true); return; } @@ -52,60 +53,60 @@ private static void spawnGroup(Player p, String tag, String storage, boolean isP loadMethod = LoadMethod.valueOf(storage.toUpperCase()); } catch(IllegalArgumentException e){ - p.sendMessage(Component.text("Invalid Storage Method!", NamedTextColor.RED)); - p.sendMessage(Component.text("Valid storage methods are local, mongodb, or mysql", NamedTextColor.GRAY)); + sender.sendMessage(Component.text("Invalid Storage Method!", NamedTextColor.RED)); + sender.sendMessage(Component.text("Valid storage methods are local, mongodb, or mysql", NamedTextColor.GRAY)); return; } if (!loadMethod.isEnabled()){ - p.sendMessage(Component.text("- Storage location is disabled and cannot be checked!", NamedTextColor.GRAY)); + sender.sendMessage(Component.text("- Storage location is disabled and cannot be checked!", NamedTextColor.GRAY)); return; } DisplayEntityGroup group = DisplayGroupManager.getGroup(loadMethod, tag); if (group == null){ - p.sendMessage(Component.text("- Failed to find saved display entity group in that storage location!", NamedTextColor.RED)); + sender.sendMessage(Component.text("- Failed to find saved display entity group in that storage location!", NamedTextColor.RED)); return; } - Location spawnLoc = p.getLocation(); + if (isPacket){ DisplayGroupManager.addPersistentPacketGroup(spawnLoc, group, true, GroupSpawnedEvent.SpawnReason.COMMAND); - p.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Spawned a packet-based display entity group at your location! (Tagged: "+tag+")"))); + sender.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Spawned a packet-based display entity group at your location! (Tagged: "+tag+")"))); } else{ group.spawn(spawnLoc, GroupSpawnedEvent.SpawnReason.COMMAND); - p.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Spawned a display entity group at your location! (Tagged: "+tag+")"))); + sender.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Spawned a display entity group at your location! (Tagged: "+tag+")"))); } } - public static void attemptAll(Player p, String tag, LoadMethod storage, boolean isGroup){ + public static void attemptAll(CommandSender sender, Location spawnLoc, String tag, LoadMethod storage, boolean isGroup){ LoadMethod nextStorage; if (storage == LoadMethod.LOCAL){ nextStorage = LoadMethod.MONGODB; if (isGroup){ - p.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Attempting to spawn group (Tagged: "+tag+")"))); + sender.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Attempting to spawn group (Tagged: "+tag+")"))); } else{ - p.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Attempting to select animation (Tagged: "+tag+")"))); + sender.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Attempting to select animation (Tagged: "+tag+")"))); } if (!DisplayConfig.isLocalEnabled()){ - p.sendMessage(Component.text("- Local storage is disabled, checking MongoDB...", NamedTextColor.GRAY)); - attemptAll(p, tag, nextStorage, isGroup); + sender.sendMessage(Component.text("- Local storage is disabled, checking MongoDB...", NamedTextColor.GRAY)); + attemptAll(sender, spawnLoc, tag, nextStorage, isGroup); return; } } else if (storage == LoadMethod.MONGODB){ nextStorage = LoadMethod.MYSQL; if (!DisplayConfig.isMongoEnabled()){ - p.sendMessage(Component.text("- MongoDB storage is disabled, checking MYSQL...", NamedTextColor.GRAY)); - attemptAll(p, tag, nextStorage, isGroup); + sender.sendMessage(Component.text("- MongoDB storage is disabled, checking MYSQL...", NamedTextColor.GRAY)); + attemptAll(sender, spawnLoc, tag, nextStorage, isGroup); } } else{ nextStorage = null; if (!DisplayConfig.isMYSQLEnabled()){ - p.sendMessage(Component.text("- MYSQL storage is disabled.", NamedTextColor.GRAY)); + sender.sendMessage(Component.text("- MYSQL storage is disabled.", NamedTextColor.GRAY)); return; } } @@ -115,14 +116,13 @@ else if (storage == LoadMethod.MONGODB){ DisplayEntityGroup group = DisplayGroupManager.getGroup(storage, tag); if (group == null){ if (nextStorage != null){ - p.sendMessage(Component.text("- Failed to find saved display entity group in "+storage.getDisplayName()+" database! Checking "+nextStorage.getDisplayName()+"...", NamedTextColor.RED)); - attemptAll(p, tag, nextStorage, true); + sender.sendMessage(Component.text("- Failed to find saved display entity group in "+storage.getDisplayName()+" database! Checking "+nextStorage.getDisplayName()+"...", NamedTextColor.RED)); + attemptAll(sender, spawnLoc, tag, nextStorage, true); } return; } - p.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Successfully spawned display entity group at your location! (Tagged: "+tag+")"))); - Location spawnLoc = p.getLocation(); + sender.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Successfully spawned display entity group at your location! (Tagged: "+tag+")"))); DisplayAPI.getScheduler().run(() -> { if (!spawnLoc.isChunkLoaded()){ @@ -131,23 +131,28 @@ else if (storage == LoadMethod.MONGODB){ } SpawnedDisplayEntityGroup g = group.spawn(spawnLoc, GroupSpawnedEvent.SpawnReason.COMMAND); - if (p.isConnected() && DisplayConfig.autoSelectGroups()){ - g.addPlayerSelection(p); - p.sendMessage(Component.text("Spawned group has been automatically selected", NamedTextColor.GRAY)); + if (sender instanceof Player player){ + if (player.isConnected() && DisplayConfig.autoSelectGroups()){ + g.addPlayerSelection(player); + sender.sendMessage(Component.text("Spawned group has been automatically selected", NamedTextColor.GRAY)); + } } + }); } else{ DisplayAnimation anim = DisplayAnimationManager.getAnimation(LoadMethod.LOCAL, tag); if (anim == null){ if (nextStorage != null){ - p.sendMessage(Component.text("- Failed to find saved display animation in "+storage.getDisplayName()+" database! Checking "+nextStorage.getDisplayName()+"...", NamedTextColor.RED)); - attemptAll(p, tag, nextStorage, false); + sender.sendMessage(Component.text("- Failed to find saved display animation in "+storage.getDisplayName()+" database! Checking "+nextStorage.getDisplayName()+"...", NamedTextColor.RED)); + attemptAll(sender, spawnLoc, tag, nextStorage, false); } return; } - DisplayAnimationManager.setSelectedSpawnedAnimation(p, anim.toSpawnedDisplayAnimation()); - p.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Animation selected! (Tagged: "+tag+")"))); + if (sender instanceof Player player){ + DisplayAnimationManager.setSelectedSpawnedAnimation(player, anim.toSpawnedDisplayAnimation()); + sender.sendMessage(DisplayAPI.pluginPrefix.append(MiniMessage.miniMessage().deserialize("Animation selected! (Tagged: "+tag+")"))); + } } }); } From 9bf99b38fd79452aa84cfa742522a030bc162f3f Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 22:19:08 -0500 Subject: [PATCH 25/43] Added optional arguments to commands, instead of args checking each command --- .../command/DEUSubCommand.java | 81 +++++++++++++++++-- .../command/DisplayEntityPluginCommand.java | 48 ++++++++++- 2 files changed, 122 insertions(+), 7 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/DEUSubCommand.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/DEUSubCommand.java index 17e95716..5d9cb19d 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/DEUSubCommand.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/DEUSubCommand.java @@ -1,20 +1,23 @@ package net.donnypz.displayentityutils.command; +import net.donnypz.displayentityutils.DisplayAPI; import net.donnypz.displayentityutils.utils.Direction; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Display; import org.bukkit.entity.ItemDisplay; import org.bukkit.entity.Pose; import org.bukkit.entity.TextDisplay; import org.jetbrains.annotations.NotNull; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; public abstract class DEUSubCommand { private final Permission permission; - protected final Map tabCompleteSuggestions = new HashMap<>(); + protected final TreeMap tabCompleteSuggestions = new TreeMap<>(); + protected final Set flags = new HashSet<>(); + protected final HashMap> options = new HashMap<>(); protected final HashMap subCommands = new HashMap<>(); DEUSubCommand(@NotNull Permission permission){ @@ -45,6 +48,18 @@ protected void setTabComplete(int index, TabSuggestion suggestion){ tabCompleteSuggestions.put(index, suggestion); } + protected void addFlag(@NotNull String flag){ + flags.add(flag); + } + + protected void addOption(@NotNull String option, @NotNull String inputPlaceholder){ + addOption(option, List.of(inputPlaceholder)); + } + + protected void addOption(@NotNull String option, @NotNull List inputPlaceholders){ + options.put(option, new ArrayList<>(inputPlaceholders)); + } + public Permission getPermission() { return permission; } @@ -53,6 +68,62 @@ public boolean isHelpCommand(){ return permission == Permission.HELP; } + protected @NotNull OptionalArguments getOptionalArguments(CommandSender sender, String[] args){ + if (tabCompleteSuggestions.isEmpty()){ + OptionalArguments oArgs = new OptionalArguments(); + oArgs.isValid = false; + return oArgs; + } + int startIndex = tabCompleteSuggestions.sequencedKeySet().getLast()+1; + return getOptionalArguments(sender, args, startIndex); + } + + protected @NotNull OptionalArguments getOptionalArguments(CommandSender sender, String[] args, int startIndex){ + OptionalArguments oArgs = new OptionalArguments(); + for (int i = startIndex; i < args.length; i++) { + String arg = args[i]; + + if (flags.contains(arg)){ + oArgs.flags.add(arg); + continue; + } + if (options.containsKey(arg)){ + if (i + 1 >= args.length) { + List placeholders = options.get(arg); + String expected = placeholders.size() == 1 ? placeholders.getFirst() : "<"+String.join(" | ", placeholders)+">"; + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text( + String.format("Incorrect Usage! \"%s\" expects %s.", arg, expected), + NamedTextColor.RED))); + oArgs.isValid = false; + return oArgs; + } + oArgs.options.put(arg, args[++i]); + } + } + + return oArgs; + } + + protected static class OptionalArguments{ + Set flags = new HashSet<>(); + Map options = new HashMap<>(); + boolean isValid = true; + + private OptionalArguments(){} + + public boolean hasFlag(@NotNull String flag){ + return flags.contains(flag); + } + + public @NotNull String getOption(@NotNull String option){ + return option.startsWith("-") ? options.getOrDefault(option, "") : options.getOrDefault("-"+option, ""); + } + + public boolean isValid() { + return isValid; + } + } + protected static class TabSuggestion{ public static final TabSuggestion STORAGES = new TabSuggestion(List.of("local", "mysql", "mongodb")) .suggestUsingCurrentString(); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/DisplayEntityPluginCommand.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/DisplayEntityPluginCommand.java index 5ed1663a..7a1b13ca 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/DisplayEntityPluginCommand.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/DisplayEntityPluginCommand.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; @ApiStatus.Internal @@ -78,8 +79,50 @@ private List getTabComplete(String commandType, String subCommand, Strin cmd = cmd.subCommands.get(subCommand); if (cmd == null) return List.of(); - String current = args[args.length-1]; - DEUSubCommand.TabSuggestion indexSuggestions = cmd.tabCompleteSuggestions.get(args.length-1); + + int requiredArgs = cmd.tabCompleteSuggestions.isEmpty() + ? 0 + : cmd.tabCompleteSuggestions.sequencedKeySet().getLast() + 1; + + int currentIndex = args.length-1; + + String current = args[currentIndex]; + + //Optional Flags/Options + if (currentIndex >= requiredArgs) { + HashSet used = new HashSet<>(); + for (String arg : args) { + if (cmd.flags.contains(arg.toLowerCase())) { + used.add(arg); + } + + if (cmd.options.containsKey(arg.toLowerCase())) { + used.add(arg); + } + } + + //Show Option Placeholders + int previousIndex = currentIndex-1; + String previous = args[previousIndex]; + if (previousIndex != requiredArgs-1 && cmd.options.containsKey(previous.toLowerCase())){ + return cmd.options.get(previous); + } + + //Show available options/flags + List suggestions = new ArrayList<>(); + for (String s : cmd.flags){ + if (current.toLowerCase().equals(s)) return List.of(); + if (!used.contains(s)) suggestions.add(s); + } + + for (String s : cmd.options.keySet()){ + if (current.toLowerCase().equals(s)) return List.of(); + if (!used.contains(s)) suggestions.add(s); + } + return suggestions; + } + + DEUSubCommand.TabSuggestion indexSuggestions = cmd.tabCompleteSuggestions.get(currentIndex); if (indexSuggestions == null) return List.of(); List tabCompletes = indexSuggestions.suggestions; @@ -88,6 +131,7 @@ private List getTabComplete(String commandType, String subCommand, Strin if (!indexSuggestions.suggestUsingCurrentString){ return tabCompletes; } + List list = new ArrayList<>(); for (String s : tabCompletes){ if (s.toLowerCase().startsWith(current.toLowerCase())){ From 6103747468668d9638ce695cd6fccfc059d14399 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 22:20:33 -0500 Subject: [PATCH 26/43] List correct command --- .../net/donnypz/displayentityutils/command/group/GroupCMD.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java index 09fa1699..0e2350f5 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java @@ -91,7 +91,7 @@ static void groupHelp(CommandSender sender, int page){ sender.sendMessage(DisplayAPI.pluginPrefixLong); if (page <= 1){ CMDUtils.sendCMD(sender, "/deu group help ", "Get help for groups"); - CMDUtils.sendCMD(sender, "/deu anim list [page-number]", "List all saved display entity groups/models"); + CMDUtils.sendCMD(sender, "/deu group list [page-number]", "List all saved display entity groups/models"); CMDUtils.sendCMD(sender, "/deu group select ", "Select from nearby groups within the given distance"); CMDUtils.sendCMD(sender, "/deu group selectnearest ", "Select the nearest group within the given distance"); CMDUtils.sendCMD(sender, "/deu group selectplaced", "Select a group placed by a player's held item"); From b1bd37181d06d3f843a345833f535a4b44743c23 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 22:51:27 -0500 Subject: [PATCH 27/43] improve cmd output for /deu group hidepacketgroups and showpacketgroups --- .../command/group/GroupHidePersistentPacketGroupsCMD.java | 8 ++++++-- .../command/group/GroupShowPersistentPacketGroupsCMD.java | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupHidePersistentPacketGroupsCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupHidePersistentPacketGroupsCMD.java index 610f872c..f24d66b3 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupHidePersistentPacketGroupsCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupHidePersistentPacketGroupsCMD.java @@ -31,7 +31,11 @@ public void execute(Player player, String[] args) { } } } - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Hiding all persistent packet-based groups in this chunk.", NamedTextColor.YELLOW))); - if (hideForSelf) player.sendMessage(Component.text("For only you, the groups are only hidden until you are re-sent this chunk")); + + String self = hideForSelf ? " (For self)" : ""; + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Hiding all persistent packet-based groups in this chunk."+self, NamedTextColor.YELLOW))); + if (hideForSelf){ + player.sendMessage(Component.text("| The groups will only be hidden until you are re-sent this chunk", NamedTextColor.GRAY)); + } } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupShowPersistentPacketGroupsCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupShowPersistentPacketGroupsCMD.java index e3b9aa96..c14f3a61 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupShowPersistentPacketGroupsCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupShowPersistentPacketGroupsCMD.java @@ -31,7 +31,11 @@ public void execute(Player player, String[] args) { } } } - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Showing all persistent, packet-based groups in this chunk.", NamedTextColor.GREEN))); - if (showForSelf) player.sendMessage(Component.text("For only you, and if the groups are hidden for others, the groups are only revealed until you are re-sent this chunk")); + + String self = showForSelf ? " (For self)" : ""; + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Showing all persistent, packet-based groups in this chunk."+self, NamedTextColor.GREEN))); + if (showForSelf){ + player.sendMessage(Component.text("| If the chunks in this group are hidden by default, the groups will be hidden again once you re-sent this chunk", NamedTextColor.GRAY)); + } } } From ebd87060db66c469b07ad2f7b9ac70e52a49bcef Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 22:52:15 -0500 Subject: [PATCH 28/43] search for optional args at first index if there's no tab suggestions --- .../command/DEUSubCommand.java | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/DEUSubCommand.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/DEUSubCommand.java index 5d9cb19d..f619ae15 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/DEUSubCommand.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/DEUSubCommand.java @@ -69,12 +69,9 @@ public boolean isHelpCommand(){ } protected @NotNull OptionalArguments getOptionalArguments(CommandSender sender, String[] args){ - if (tabCompleteSuggestions.isEmpty()){ - OptionalArguments oArgs = new OptionalArguments(); - oArgs.isValid = false; - return oArgs; - } - int startIndex = tabCompleteSuggestions.sequencedKeySet().getLast()+1; + int startIndex = tabCompleteSuggestions.isEmpty() + ? 0 + : tabCompleteSuggestions.sequencedKeySet().getLast() + 1; return getOptionalArguments(sender, args, startIndex); } @@ -94,7 +91,7 @@ public boolean isHelpCommand(){ sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text( String.format("Incorrect Usage! \"%s\" expects %s.", arg, expected), NamedTextColor.RED))); - oArgs.isValid = false; + oArgs.isValidOptions = false; return oArgs; } oArgs.options.put(arg, args[++i]); @@ -107,7 +104,7 @@ public boolean isHelpCommand(){ protected static class OptionalArguments{ Set flags = new HashSet<>(); Map options = new HashMap<>(); - boolean isValid = true; + boolean isValidOptions = true; private OptionalArguments(){} @@ -119,8 +116,8 @@ public boolean hasFlag(@NotNull String flag){ return option.startsWith("-") ? options.getOrDefault(option, "") : options.getOrDefault("-"+option, ""); } - public boolean isValid() { - return isValid; + public boolean isValidOptions() { + return isValidOptions; } } From f2d5424885f3c691b749e9da3c2a4bb671493c6e Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Sun, 31 May 2026 23:39:37 -0500 Subject: [PATCH 29/43] utilize optional args where applicable --- .../displayentityutils/command/CMDUtils.java | 4 +- .../command/anim/AnimCMD.java | 2 +- .../command/anim/AnimPlayCMD.java | 51 ++++----- .../command/anim/AnimPreviewPlayCMD.java | 4 +- .../command/anim/AnimUseFilterCMD.java | 4 +- .../command/group/GroupCMD.java | 2 +- .../command/group/GroupDespawnAtCMD.java | 107 +++--------------- .../command/group/GroupDismountCMD.java | 4 +- .../GroupHidePersistentPacketGroupsCMD.java | 4 +- .../GroupShowPersistentPacketGroupsCMD.java | 4 +- .../command/group/GroupSpawnCMD.java | 5 +- .../command/group/GroupSpawnJSONCMD.java | 4 +- .../command/group/GroupWorldEditCMD.java | 4 +- .../command/group/GroupYawCMD.java | 9 +- .../interaction/InteractionPivotCMD.java | 4 +- .../interaction/InteractionSpawnCMD.java | 4 +- .../command/mannequin/MannequinSpawnCMD.java | 6 +- 17 files changed, 68 insertions(+), 154 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/CMDUtils.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/CMDUtils.java index 3cf4296c..649e714a 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/CMDUtils.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/CMDUtils.java @@ -37,9 +37,9 @@ public static void sendUnsafeCMD(CommandSender sender, String command, String de sender.sendMessage(msg); } - public static void tryAddEntityToGroup(Player player, Entity entity, String[] args, int groupArg){ + public static void tryAddEntityToGroup(Player player, String[] args, Entity entity, DEUSubCommand subCommand, String flag){ String entityTypeName = entity.getType().getKey().getKey(); - if (args.length >= groupArg+1 && args[groupArg].equalsIgnoreCase("-g")){ + if (subCommand.getOptionalArguments(player, args).hasFlag(flag)){ ActiveGroup group = DEUUser.getOrCreateUser(player).getSelectedGroup(); if (group == null) { player.sendMessage(Component.text("- You must have a group selected to add the "+entityTypeName+" to a group", NamedTextColor.YELLOW)); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java index d91106be..f20f2c78 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java @@ -80,7 +80,7 @@ static void animationHelp(CommandSender sender, int page){ sender.sendMessage(MiniMessage.miniMessage().deserialize("Convert animations from block-display.com with " + "\"/deu bdengine convertanim\"")); sender.sendMessage(Component.text("Commands allowing multiple are comma separated", NamedTextColor.GRAY)); - CMDUtils.sendCMD(sender,"/deu anim help ", "Get help for animations"); + CMDUtils.sendCMD(sender,"/deu anim help [page-number]", "Get help for animations"); CMDUtils.sendCMD(sender, "/deu anim list [page-number]", "List all saved animations"); CMDUtils.sendCMD(sender, "/deu anim select ", "Select a saved animation"); CMDUtils.sendCMD(sender, "/deu anim selectjson ", "Select a JSON saved animation"); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPlayCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPlayCMD.java index 5b01b0e4..6be95c6e 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPlayCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPlayCMD.java @@ -15,15 +15,13 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.util.List; - class AnimPlayCMD extends PlayerSubCommand { AnimPlayCMD(@NotNull DEUSubCommand parentSubCommand) { super("play", parentSubCommand, Permission.ANIM_PLAY); - setTabComplete(2, List.of("-loop", "-packet", "-camera", "-nodata")); - setTabComplete(3, List.of("-loop", "-packet", "-camera", "-nodata")); - setTabComplete(4, List.of("-loop", "-packet", "-camera", "-nodata")); - setTabComplete(5, List.of("-loop", "-packet", "-camera", "-nodata")); + addFlag("-loop"); + addFlag("-packet"); + addFlag("-camera"); + addFlag("-nodata"); } @Override @@ -49,31 +47,23 @@ public void execute(Player player, String[] args) { AnimCMD.hasNoFrames(player); return; } - DisplayAnimator.AnimationType animationType = DisplayAnimator.AnimationType.LINEAR; - boolean packet = false; - boolean camera = false; - boolean dataChange = true; - Component optionResult = Component.empty(); - for (int i = 2; i < args.length; i++){ - String arg = args[i]; - if (arg.equalsIgnoreCase("-loop") && animationType != DisplayAnimator.AnimationType.LOOP){ - animationType = DisplayAnimator.AnimationType.LOOP; - optionResult = optionResult.append(Component.text(" (LOOPING)", NamedTextColor.YELLOW)); - } - else if (arg.equalsIgnoreCase("-packet") && !packet){ - packet = true; - optionResult = optionResult.append(Component.text(" (PACKET-BASED)", NamedTextColor.LIGHT_PURPLE)); - } - else if (arg.equalsIgnoreCase("-camera") && !camera){ - camera = true; - optionResult.append(Component.text(" (CAMERA VIEW)", NamedTextColor.AQUA)); - } - else if (arg.equalsIgnoreCase("-nodata")){ - dataChange = false; - } - } + + OptionalArguments optionalArgs = getOptionalArguments(player, args); + DisplayAnimator.AnimationType animationType = optionalArgs.hasFlag("-loop") + ? DisplayAnimator.AnimationType.LOOP + : DisplayAnimator.AnimationType.LINEAR; + + Component optionResultComp = Component.empty(); + + boolean packet = optionalArgs.hasFlag("-packet"); + boolean camera = optionalArgs.hasFlag("-camera"); + boolean dataChange = !optionalArgs.hasFlag("-nodata"); + + if (packet) optionResultComp = optionResultComp.append(Component.text(" (PACKET-BASED)", NamedTextColor.LIGHT_PURPLE)); + if (!dataChange) optionResultComp = optionResultComp.append(Component.text(" (NO DATA CHANGES)", NamedTextColor.GOLD)); if (animationType == DisplayAnimator.AnimationType.LOOP){ + optionResultComp = optionResultComp.append(Component.text(" (LOOPING)", NamedTextColor.YELLOW)); if (packet){ DisplayAnimator.playUsingPackets(group, anim, DisplayAnimator.AnimationType.LOOP, dataChange); } @@ -91,8 +81,9 @@ else if (arg.equalsIgnoreCase("-nodata")){ } if (camera){ DisplayAnimator.playCamera(player, group, anim, animationType); + optionResultComp = optionResultComp.append(Component.text(" (CAMERA VIEW)", NamedTextColor.AQUA)); } player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Playing Animation!", NamedTextColor.GREEN)) - .append(optionResult)); + .append(optionResultComp)); } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPreviewPlayCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPreviewPlayCMD.java index 2e5184b7..59da8021 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPreviewPlayCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimPreviewPlayCMD.java @@ -20,7 +20,7 @@ class AnimPreviewPlayCMD extends PlayerSubCommand { AnimPreviewPlayCMD(@NotNull DEUSubCommand parentSubCommand) { super("previewplay", parentSubCommand, Permission.ANIM_PREVIEW); - setTabComplete(2, List.of("-camera")); + addFlag("-camera"); } @Override @@ -51,7 +51,7 @@ public void execute(Player player, String[] args) { player.sendMessage(Component.text("| Only you can see the animation being played", NamedTextColor.GRAY)); DisplayAnimator.play(player, group, anim, DisplayAnimator.AnimationType.LINEAR); - if (args.length >= 3 && args[2].equalsIgnoreCase("-camera")){ + if (getOptionalArguments(player, args).hasFlag("-camera")){ DisplayAnimator.playCamera(player, group, anim, DisplayAnimator.AnimationType.LINEAR); player.sendMessage(Component.text("| Previewing with camera", NamedTextColor.GRAY)); } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimUseFilterCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimUseFilterCMD.java index 535fec15..faee0c8c 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimUseFilterCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimUseFilterCMD.java @@ -17,7 +17,7 @@ class AnimUseFilterCMD extends PlayerSubCommand { AnimUseFilterCMD(@NotNull DEUSubCommand parentSubCommand) { super("usefilter", parentSubCommand, Permission.ANIM_USE_FILTER); - setTabComplete(2, "-trim"); + addFlag("-trim"); } @Override @@ -43,7 +43,7 @@ public void execute(Player player, String[] args) { return; } - boolean trim = args.length > 0 && args[2].equalsIgnoreCase("-trim"); + boolean trim = getOptionalArguments(player, args).hasFlag("-trim"); anim.setFilter((MultiPartSelection) selection, trim); player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Your selected animation will use your part section's filter", NamedTextColor.GREEN))); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java index 0e2350f5..928f451d 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java @@ -90,7 +90,7 @@ static void groupToPacketInfo(Player player){ static void groupHelp(CommandSender sender, int page){ sender.sendMessage(DisplayAPI.pluginPrefixLong); if (page <= 1){ - CMDUtils.sendCMD(sender, "/deu group help ", "Get help for groups"); + CMDUtils.sendCMD(sender, "/deu group help [page-number]", "Get help for groups"); CMDUtils.sendCMD(sender, "/deu group list [page-number]", "List all saved display entity groups/models"); CMDUtils.sendCMD(sender, "/deu group select ", "Select from nearby groups within the given distance"); CMDUtils.sendCMD(sender, "/deu group selectnearest ", "Select the nearest group within the given distance"); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDespawnAtCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDespawnAtCMD.java index e8b12c57..4268c5f1 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDespawnAtCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDespawnAtCMD.java @@ -5,7 +5,6 @@ import net.donnypz.displayentityutils.command.DEUSubCommand; import net.donnypz.displayentityutils.command.Permission; import net.donnypz.displayentityutils.managers.DisplayGroupManager; -import net.donnypz.displayentityutils.utils.DisplayEntities.ActiveGroup; import net.donnypz.displayentityutils.utils.DisplayEntities.PacketDisplayEntityGroup; import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; import net.kyori.adventure.text.Component; @@ -17,21 +16,19 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.util.HashSet; import java.util.List; import java.util.Set; public class GroupDespawnAtCMD extends ConsoleUsableSubCommand { GroupDespawnAtCMD(@NotNull DEUSubCommand parentSubCommand) { super("despawnat", parentSubCommand, Permission.GROUP_DESPAWN); - setTabComplete(2, ""); - setTabComplete(3, ""); - setTabComplete(4, ""); + setTabComplete(2, List.of("", "~")); + setTabComplete(3, List.of("", "~")); + setTabComplete(4, List.of("", "~")); setTabComplete(5, ""); - setTabComplete(6, List.of("-world ", "-packet ", "-force")); - setTabComplete(7, List.of("-world ", "-packet ", "-force")); - setTabComplete(8, List.of("-world ", "-packet ", "-force")); - setTabComplete(9, List.of("-world ", "-packet ", "-force")); + addFlag("-force"); + addOption("-world", ""); + addOption("-packet", List.of("include", "only")); } @Override @@ -46,10 +43,12 @@ public void execute(CommandSender sender, String[] args) { double z = getCoordinate(sender, args[4], 'z'); double distance = Double.parseDouble(args[5]); - OptionalArgs optionalArgs = new OptionalArgs(sender, args); - if (!optionalArgs.valid) return; + OptionalArguments optionalArgs = getOptionalArguments(sender, args); + if (!optionalArgs.isValidOptions()) return; + World w; - if (optionalArgs.worldName == null){ + String worldName = optionalArgs.getOption("-world"); + if (worldName.isEmpty()){ if (sender instanceof Player player){ w = player.getWorld(); } @@ -60,7 +59,7 @@ public void execute(CommandSender sender, String[] args) { } } else{ - w = Bukkit.getWorld(optionalArgs.worldName); + w = Bukkit.getWorld(worldName); } if (w == null){ @@ -70,19 +69,22 @@ public void execute(CommandSender sender, String[] args) { Location searchLocation = new Location(w, x, y, z); boolean hasGroups = false; - if (optionalArgs.packetIncluded){ + String packetOption = optionalArgs.getOption("-packet"); + + if (packetOption.equals("included") || packetOption.equals("only")){ Set groups = PacketDisplayEntityGroup.getNearbyGroups(searchLocation, distance); hasGroups = !groups.isEmpty(); if (hasGroups) groups.forEach(PacketDisplayEntityGroup::unregister); } - if (!optionalArgs.packetOnly){ + if (!packetOption.equals("only")){ Set groups = DisplayGroupManager.getNearbySpawnedGroups(searchLocation, distance); if (!groups.isEmpty()){ hasGroups = true; - groups.forEach(sg -> sg.unregister(true, optionalArgs.force)); + groups.forEach(sg -> sg.unregister(true, optionalArgs.hasFlag("-force"))); } } + if (!hasGroups){ sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("There are no groups within the given range", NamedTextColor.RED))); return; @@ -113,77 +115,4 @@ private double getCoordinate(CommandSender sender, String userInput, char coordi return Double.parseDouble(userInput); } } - - static class OptionalArgs{ - boolean valid = true; - String worldName = null; - boolean packetOnly = false; - boolean packetIncluded = false; - boolean force = false; - - OptionalArgs(CommandSender sender, String[] args){ - - - for (int i = 6; i < args.length; i++) { - String arg = args[i]; - - switch (arg.toLowerCase()) { - case "-packet" -> { - if (packetOnly || packetIncluded) { - sender.sendMessage(Component.text( - "-packet was specified multiple times.", - NamedTextColor.RED)); - return; - } - if (i + 1 >= args.length) { - sender.sendMessage(Component.text( - "You must specify 'include' or 'only' after -packet.", - NamedTextColor.RED)); - valid = false; - return; - } - - String mode = args[++i].toLowerCase(); - - switch (mode) { - case "include" -> packetIncluded = true; - case "only" -> packetOnly = true; - default -> { - sender.sendMessage(Component.text( - "Packet mode must be 'include' or 'only'.", - NamedTextColor.RED)); - return; - } - } - } - case "-force" -> force = true; - - case "-world" -> { - if (worldName != null) { - sender.sendMessage(Component.text( - "-world was specified multiple times.", - NamedTextColor.RED)); - return; - } - - if (i + 1 >= args.length) { - sender.sendMessage(Component.text( - "You must specify a world after -world.", - NamedTextColor.RED)); - return; - } - - worldName = args[++i]; - } - - default -> { - sender.sendMessage(Component.text( - "Unknown option: " + arg, - NamedTextColor.RED)); - return; - } - } - } - } - } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDismountCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDismountCMD.java index c25b748d..bc22058d 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDismountCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupDismountCMD.java @@ -25,7 +25,7 @@ class GroupDismountCMD extends ConsoleUsableSubCommand { GroupDismountCMD(@NotNull DEUSubCommand parentSubCommand) { super("dismount", parentSubCommand, Permission.GROUP_DISMOUNT); setTabComplete(2, List.of("-target", "-selected", "player-name", "entity-uuid")); - setTabComplete(3, "-despawn"); + addFlag("-despawn"); } @Override @@ -41,7 +41,7 @@ public void execute(CommandSender sender, String[] args) { return; } - boolean despawn = args.length == 4 && args[3].equalsIgnoreCase("-despawn"); + boolean despawn = getOptionalArguments(sender, args).hasFlag("-despawn"); String type = args[2]; if (type.equalsIgnoreCase("-selected")){ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupHidePersistentPacketGroupsCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupHidePersistentPacketGroupsCMD.java index f24d66b3..8be66959 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupHidePersistentPacketGroupsCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupHidePersistentPacketGroupsCMD.java @@ -13,12 +13,12 @@ class GroupHidePersistentPacketGroupsCMD extends PlayerSubCommand { GroupHidePersistentPacketGroupsCMD(@NotNull DEUSubCommand parentSubCommand) { super("hidepacketgroups", parentSubCommand, Permission.GROUP_CHUNK_PACKET_GROUP_VISIBILITY); - setTabComplete(2, "-self"); + addFlag("-self"); } @Override public void execute(Player player, String[] args) { - boolean hideForSelf = args.length > 2 && args[2].equalsIgnoreCase("-self"); + boolean hideForSelf = getOptionalArguments(player, args).hasFlag("-self"); for (PacketDisplayEntityGroup pg : PacketDisplayEntityGroup.getGroups(player.getChunk())){ if (pg.isPersistent()){ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupShowPersistentPacketGroupsCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupShowPersistentPacketGroupsCMD.java index c14f3a61..57099908 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupShowPersistentPacketGroupsCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupShowPersistentPacketGroupsCMD.java @@ -14,12 +14,12 @@ class GroupShowPersistentPacketGroupsCMD extends PlayerSubCommand { GroupShowPersistentPacketGroupsCMD(@NotNull DEUSubCommand parentSubCommand) { super("showpacketgroups", parentSubCommand, Permission.GROUP_CHUNK_PACKET_GROUP_VISIBILITY); - setTabComplete(2, "-self"); + addFlag("-self"); } @Override public void execute(Player player, String[] args) { - boolean showForSelf = args.length > 2 && args[2].equalsIgnoreCase("-self"); + boolean showForSelf = getOptionalArguments(player, args).hasFlag("-self"); for (PacketDisplayEntityGroup pg : PacketDisplayEntityGroup.getGroups(player.getChunk())){ if (pg.isPersistent()){ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnCMD.java index c93fa4c4..8ea80a9a 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnCMD.java @@ -26,7 +26,7 @@ public class GroupSpawnCMD extends PlayerSubCommand { super("spawn", parentSubCommand, Permission.GROUP_SPAWN); setTabComplete(2, ""); setTabComplete(3, TabSuggestion.STORAGES); - setTabComplete(4, "-packet"); + addFlag("-packet"); } @Override @@ -37,7 +37,8 @@ public void execute(Player player, String[] args) { } String tag = args[2]; String storage = args[3]; - boolean isPacket = args.length > 4 && args[4].equalsIgnoreCase("-packet"); + + boolean isPacket = getOptionalArguments(player, args).hasFlag("-packet"); spawnGroup(player, player.getLocation(), tag, storage, isPacket); } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnJSONCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnJSONCMD.java index f98a4a97..e248eb84 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnJSONCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupSpawnJSONCMD.java @@ -21,7 +21,7 @@ public class GroupSpawnJSONCMD extends PlayerSubCommand { GroupSpawnJSONCMD(@NotNull DEUSubCommand parentSubCommand) { super("spawnjson", parentSubCommand, Permission.GROUP_SPAWN); setTabComplete(2, ""); - setTabComplete(3, "-packet"); + addFlag("-packet"); } @Override @@ -31,7 +31,7 @@ public void execute(Player player, String[] args) { return; } String tag = args[2]; - boolean isPacket = args.length > 3 && args[3].equalsIgnoreCase("-packet"); + boolean isPacket = getOptionalArguments(player, args).hasFlag("-packet"); spawnGroup(player, tag, isPacket); } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupWorldEditCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupWorldEditCMD.java index e8f32740..150dfc45 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupWorldEditCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupWorldEditCMD.java @@ -16,7 +16,7 @@ class GroupWorldEditCMD extends PlayerSubCommand { GroupWorldEditCMD(@NotNull DEUSubCommand parentSubCommand) { super("wetogroup", parentSubCommand, Permission.GROUP_WORLD_EDIT); - setTabComplete(2, "-removeblocks"); + addFlag("-removeblocks"); } @Override @@ -26,7 +26,7 @@ public void execute(Player player, String[] args) { return; } - boolean removeBlocks = args.length >= 3 && args[2].equals("-removeblocks"); + boolean removeBlocks = getOptionalArguments(player, args).hasFlag("-removeblocks"); SpawnedDisplayEntityGroup g = WorldEditUtils.createGroupFromSelection(player, removeBlocks); if (g == null){ player.sendMessage(Component.text("Failed to convert WorldEdit selection to a spawned group! Ensure that:", NamedTextColor.RED)); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupYawCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupYawCMD.java index 17803bbd..f8e26ab7 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupYawCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupYawCMD.java @@ -16,7 +16,7 @@ class GroupYawCMD extends GroupSubCommand { GroupYawCMD(@NotNull DEUSubCommand parentSubCommand) { super("yaw", parentSubCommand, Permission.GROUP_TRANSFORM, 3, true); setTabComplete(2, ""); - setTabComplete(3, "-pivot"); + addFlag("-pivot"); } @Override @@ -33,12 +33,7 @@ protected void execute(@NotNull Player player, @Nullable ActiveGroup group, @ try{ float yaw = Float.parseFloat(args[2]); - boolean pivot = false; - if (args.length > 3){ - if (args[3].equalsIgnoreCase("-pivot")){ - pivot = true; - } - } + boolean pivot = getOptionalArguments(player, args).hasFlag("-pivot"); double oldYaw = group.getLocation().getYaw(); group.setYaw(yaw, pivot); player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Yaw set!", NamedTextColor.GREEN))); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/interaction/InteractionPivotCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/interaction/InteractionPivotCMD.java index 3942b1eb..c38571b0 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/interaction/InteractionPivotCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/interaction/InteractionPivotCMD.java @@ -17,7 +17,7 @@ class InteractionPivotCMD extends PlayerSubCommand { InteractionPivotCMD(@NotNull DEUSubCommand parentSubCommand) { super("pivot", parentSubCommand, Permission.INTERACTION_PIVOT); setTabComplete(2, ""); - setTabComplete(3, "-all"); + addFlag("-all"); } @Override @@ -54,7 +54,7 @@ public void execute(Player player, String[] args) { MultiPartSelection selection = (MultiPartSelection) sel; - boolean isAll = args.length >= 4 && args[3].equalsIgnoreCase("-all"); + boolean isAll = getOptionalArguments(player, args).hasFlag("-all"); if (isAll){ for (ActivePart p : selection.getSelectedParts()){ if (p.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/interaction/InteractionSpawnCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/interaction/InteractionSpawnCMD.java index 7b10f786..8ec0dfca 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/interaction/InteractionSpawnCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/interaction/InteractionSpawnCMD.java @@ -18,7 +18,7 @@ class InteractionSpawnCMD extends PlayerSubCommand { super("spawn", parentSubCommand, Permission.INTERACTION_SPAWN); setTabComplete(2, ""); setTabComplete(3, ""); - setTabComplete(4, "-g"); + addFlag("-g"); } @Override @@ -29,7 +29,7 @@ public void execute(Player player, String[] args) { } Interaction interaction = spawnInteraction(player, player.getLocation(), args); - CMDUtils.tryAddEntityToGroup(player, interaction, args, 4); + CMDUtils.tryAddEntityToGroup(player, args, interaction, this, "-g"); } private Interaction spawnInteraction(Player player, Location spawnLoc, String[] args){ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/mannequin/MannequinSpawnCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/mannequin/MannequinSpawnCMD.java index 81812924..eaa1c841 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/mannequin/MannequinSpawnCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/mannequin/MannequinSpawnCMD.java @@ -16,12 +16,10 @@ import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; -import java.util.List; - class MannequinSpawnCMD extends PlayerSubCommand { MannequinSpawnCMD(@NotNull DEUSubCommand parentSubCommand) { super("spawn", parentSubCommand, Permission.MANNEQUIN_SPAWN); - setTabComplete(2, List.of("-g")); + addFlag("-g"); } protected void sendIncorrectUsage(@NotNull Player player) { @@ -40,6 +38,6 @@ public void execute(Player player, String[] args) { player.sendMessage(DisplayAPI.pluginPrefix .append(MiniMessage.miniMessage().deserialize("A new mannequin has been spawned at your location!"))); - CMDUtils.tryAddEntityToGroup(player, mannequin, args, 2); + CMDUtils.tryAddEntityToGroup(player, args, mannequin, this, "-g"); } } From 91850e8f00f61f455f4369b2b59f34ca486a0d14 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 00:16:58 -0500 Subject: [PATCH 30/43] fix NPE when attempting to remove an invalid part selection --- .../net/donnypz/displayentityutils/managers/DEUUser.java | 5 +++-- .../utils/DisplayEntities/PacketPartSelection.java | 1 + .../utils/DisplayEntities/SinglePartSelection.java | 1 + .../utils/DisplayEntities/SpawnedDisplayEntityPart.java | 9 +++++---- .../utils/DisplayEntities/SpawnedPartSelection.java | 4 +--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/DEUUser.java b/api/src/main/java/net/donnypz/displayentityutils/managers/DEUUser.java index 9fef50d5..dd740d64 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/managers/DEUUser.java +++ b/api/src/main/java/net/donnypz/displayentityutils/managers/DEUUser.java @@ -135,8 +135,8 @@ public boolean setSelectedGroup(@NotNull ActiveGroup activeGroup) { */ public void setSelectedPartSelection(@NotNull ActivePartSelection selection, boolean setGroup) { deselectPartSelection(); - if (selection instanceof MultiPartSelection newSel && setGroup){ - selectedGroup = newSel.getGroup(); + if (selection instanceof MultiPartSelection multiSel && setGroup){ + selectedGroup = multiSel.getGroup(); } selectedPartSelection = selection; } @@ -186,6 +186,7 @@ public void deselectSpawnedAnimation(){ public void deselectPartSelection(){ if (selectedPartSelection != null){ selectedPartSelection.remove(); + selectedPartSelection = null; } selectedGroup = null; diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketPartSelection.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketPartSelection.java index e3e5f84c..34cdf2e2 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketPartSelection.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketPartSelection.java @@ -79,6 +79,7 @@ public PacketDisplayEntityGroup getGroup() { */ @Override public void remove() { + if (!isValid()) return; reset(false); group = null; } diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SinglePartSelection.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SinglePartSelection.java index 5d628c56..a84eac06 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SinglePartSelection.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SinglePartSelection.java @@ -23,6 +23,7 @@ public SinglePartSelection(@NotNull SpawnedDisplayEntityPart part){ @Override public void remove() { + if (!isValid()) return; selectedPart.remove(false); selectedPart = null; } diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayEntityPart.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayEntityPart.java index e11b673c..289f2f82 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayEntityPart.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayEntityPart.java @@ -39,7 +39,7 @@ public final class SpawnedDisplayEntityPart extends ActivePart implements Spawne private SpawnedDisplayEntityGroup group; private Entity entity; - private UUID entityUUID; + final UUID entityUUID; private final boolean isSingle; SpawnedDisplayEntityPart(SpawnedDisplayEntityGroup group, Entity entity, Random random){ @@ -104,8 +104,6 @@ private void applyData(Random random, Entity entity){ } ActivePartRegistry.register(this); - this.entityUUID = entity.getUniqueId(); - setPartUUID(random); group.groupParts.put(partUUID, this); entity.setPersistent(group.isPersistent()); @@ -1369,6 +1367,7 @@ public boolean isOfType(@NotNull Entity entity){ * @return Returns the part's entity. */ public Entity remove(boolean kill) { + if (!isValid()) return getEntity(); this.removeFromGroup(); return groupUnregisterRemove(kill); } @@ -1390,13 +1389,15 @@ public void removeFromGroup() { Entity groupUnregisterRemove(boolean despawn){ this.group = null; + Entity entity = getEntity(); + this.unregister(); + if (despawn && entity != null) { if (!entity.isDead()){ entity.remove(); } } - this.unregister(); this.entity = null; return entity; } diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedPartSelection.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedPartSelection.java index e037ce71..f4e8749d 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedPartSelection.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedPartSelection.java @@ -1,7 +1,5 @@ package net.donnypz.displayentityutils.utils.DisplayEntities; -import net.donnypz.displayentityutils.managers.DisplayGroupManager; -import net.donnypz.displayentityutils.utils.DisplayUtils; import org.bukkit.Material; import org.bukkit.entity.BlockDisplay; import org.bukkit.entity.ItemDisplay; @@ -76,7 +74,7 @@ public boolean removePart(@NotNull SpawnedDisplayEntityPart part){ */ @Override public void remove(){ - if (group == null) return; + if (!isValid()) return; ((SpawnedDisplayEntityGroup) group).removePartSelection(this); removeSilent(); } From 06a8ca7c6f70a9ade4cbc66d1658e8a393fa4d68 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 00:18:09 -0500 Subject: [PATCH 31/43] update command feedback --- .../displayentityutils/command/parts/PartsRemoveCMD.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/parts/PartsRemoveCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/parts/PartsRemoveCMD.java index d9100463..ac149922 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/parts/PartsRemoveCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/parts/PartsRemoveCMD.java @@ -34,7 +34,8 @@ else if (part instanceof SpawnedDisplayEntityPart p){ p.remove(true); } } - player.sendMessage(Component.text("Despawned all selected parts!", NamedTextColor.GREEN)); + + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Despawned all selected parts!", NamedTextColor.YELLOW))); removeGroupIfEmpty(player, group); return true; } @@ -51,7 +52,8 @@ protected boolean executeSinglePartAction(@NotNull Player player, @Nullable Acti else if (selectedPart instanceof PacketDisplayEntityPart pp){ pp.remove(); } - player.sendMessage(Component.text("Despawned your selected part!", NamedTextColor.GREEN)); + + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Despawned your selected part!", NamedTextColor.YELLOW))); removePartSelectionIfEmpty(player, selection); removeGroupIfEmpty(player, group); return true; @@ -62,7 +64,7 @@ private void removePartSelectionIfEmpty(Player player, ActivePartSelection se selection.remove(); if (!(selection instanceof SinglePartSelection)){ - player.sendMessage(Component.text("Part selection reset! (No parts remaining)", NamedTextColor.RED)); + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Part selection reset! (No parts remaining)", NamedTextColor.RED))); } } From f9f77a7cf5bdf325ae146117fd633c3d43b08e7f Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 00:56:06 -0500 Subject: [PATCH 32/43] Remove "clonehere" cmd and add "-here" flag to "clone" --- .../command/group/GroupCMD.java | 10 ++++---- .../command/group/GroupCloneCMD.java | 10 +++++--- .../command/group/GroupCloneHereCMD.java | 23 ------------------- 3 files changed, 11 insertions(+), 32 deletions(-) delete mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCloneHereCMD.java diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java index 928f451d..3d5bb787 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java @@ -46,7 +46,6 @@ public GroupCMD(){ new GroupMergeCMD(this); new GroupAddTargetCMD(this); new GroupCloneCMD(this); - new GroupCloneHereCMD(this); new GroupGlowCMD(this); new GroupUnglowCMD(this); new GroupGlowColorCMD(this); @@ -141,29 +140,28 @@ else if (page == 4){ CMDUtils.sendCMD(sender, "/deu group pitch ", "Set your selected group's pitch"); CMDUtils.sendCMD(sender, "/deu group scale ", "Scale your selected group with a given multiplier"); CMDUtils.sendCMD(sender, "/deu group brightness ", "Set your selected group's brightness. Enter values between 0-15. -1 resets"); - CMDUtils.sendCMD(sender, "/deu group clone", "Spawn a cloned group at your selected group's location"); - CMDUtils.sendCMD(sender, "/deu group clonehere", "Spawn a cloned group at your location"); + CMDUtils.sendCMD(sender, "/deu group clone [-here]", "Spawn a cloned group at your selected group's location \n\"-here\" clones the group at your location"); + CMDUtils.sendCMD(sender, "/deu group move [tick-duration]", "Change the actual location of your selected group, with an optional duration"); } else if (page == 5){ - CMDUtils.sendCMD(sender, "/deu group move [tick-duration]", "Change the actual location of your selected group, with an optional duration"); CMDUtils.sendCMD(sender, "/deu group movehere", "Change your selected group's actual location to your location"); CMDUtils.sendCMD(sender, "/deu group translate ","Changes your selected group's translation, use \"move\" instead if this group uses animations"); CMDUtils.sendCMD(sender, "/deu group merge ","Merge groups near your selected group"); CMDUtils.sendCMD(sender, "/deu group billboard ", "Set the billboard of all parts in this group"); CMDUtils.sendCMD(sender, "/deu group glowcolor ", "Set the glow color for all parts in this group"); CMDUtils.sendCMD(sender, "/deu group glow", "Make all parts in this group glow"); + CMDUtils.sendCMD(sender, "/deu group unglow", "Remove the glowing effect from all parts in this group"); } else if (page == 6){ - CMDUtils.sendCMD(sender, "/deu group unglow", "Remove the glowing effect from all parts in this group"); CMDUtils.sendCMD(sender, "/deu group ride <-target | player-name | entity-uuid> [group-tag] [storage] [controller-id]", "Make a group ride an entity. Values in brackets [] are optional"); CMDUtils.sendCMD(sender, "/deu group ridedespawn <-target | player-name | entity-uuid> [group-tag] [storage] [controller-id]", "Make a group ride an entity, despawning the group after the ridden entity despawns/disconnects. The group will not be persistent. Values in brackets [] are optional"); CMDUtils.sendCMD(sender, "/deu group safedismount <-target | -selected | player-name | entity-uuid>", "Safely dismount a group from an entity"); CMDUtils.sendCMD(sender, "/deu group dismount <-target | -selected | player-name | entity-uuid> [-despawn]", "Dismount a group from an entity, with optional despawning"); CMDUtils.sendCMD(sender, "/deu group setspawnanim ", "Set an animation to play when this group is spawned/loaded"); CMDUtils.sendCMD(sender, "/deu group unsetspawnanim", "Remove the spawn animation that's set on your selected group"); + CMDUtils.sendCMD(sender, "/deu group viewrange ", "Set the view range multiplier for your selected group"); } else{ - CMDUtils.sendCMD(sender, "/deu group viewrange ", "Set the view range multiplier for your selected group"); CMDUtils.sendCMD(sender, "/deu group autocull", "Calculate and set culling bounds for every part in your selected group"); CMDUtils.sendCMD(sender, "/deu group removecull", "Remove the culling bounds for every part in your selected group"); CMDUtils.sendCMD(sender, "/deu group togglepersist", "Toggle if your group should persist after a server shutdown"); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCloneCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCloneCMD.java index 8aa9645a..c0b651f1 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCloneCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCloneCMD.java @@ -4,10 +4,10 @@ import net.donnypz.displayentityutils.command.*; import net.donnypz.displayentityutils.managers.DisplayGroupManager; import net.donnypz.displayentityutils.utils.DisplayEntities.ActiveGroup; -import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.Location; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -15,6 +15,7 @@ class GroupCloneCMD extends GroupSubCommand { GroupCloneCMD(@NotNull DEUSubCommand parentSubCommand) { super("clone", parentSubCommand, Permission.GROUP_CLONE, 0, false); + addFlag("-here"); } @Override @@ -22,10 +23,13 @@ protected void sendIncorrectUsage(@NotNull Player player) {} @Override protected void execute(@NotNull Player player, @Nullable ActiveGroup group, @NotNull String[] args) { - clone(player, group.clone(group.getLocation())); + Location cloneLoc = getOptionalArguments(player, args).hasFlag("-here") + ? player.getLocation() + : group.getLocation(); + clone(player, group.clone(cloneLoc)); } - static void clone(Player p, ActiveGroup clonedGroup){ + private void clone(Player p, ActiveGroup clonedGroup){ if (clonedGroup == null){ p.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed to clone your selected group!", NamedTextColor.RED))); } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCloneHereCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCloneHereCMD.java deleted file mode 100644 index d43153bd..00000000 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCloneHereCMD.java +++ /dev/null @@ -1,23 +0,0 @@ -package net.donnypz.displayentityutils.command.group; - -import net.donnypz.displayentityutils.command.*; -import net.donnypz.displayentityutils.managers.DisplayGroupManager; -import net.donnypz.displayentityutils.utils.DisplayEntities.ActiveGroup; -import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -class GroupCloneHereCMD extends GroupSubCommand { - GroupCloneHereCMD(@NotNull DEUSubCommand parentSubCommand) { - super("clonehere", parentSubCommand, Permission.GROUP_CLONE, 0, false); - } - - @Override - protected void sendIncorrectUsage(@NotNull Player player) {} - - @Override - protected void execute(@NotNull Player player, @Nullable ActiveGroup group, @NotNull String[] args) { - GroupCloneCMD.clone(player, group.clone(player.getLocation())); - } -} From d4facd90b422623b23544a2f921c5ed9965e7026 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 02:24:08 -0500 Subject: [PATCH 33/43] Add #removeParticle and #removeAllParticles to FramePoint --- .../utils/DisplayEntities/FramePoint.java | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java index 5c10daa0..0c2a08d5 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java @@ -1,5 +1,6 @@ package net.donnypz.displayentityutils.utils.DisplayEntities; +import net.donnypz.displayentityutils.DisplayAPI; import net.donnypz.displayentityutils.utils.DisplayEntities.particles.AnimationParticle; import net.donnypz.displayentityutils.utils.DisplayUtils; import net.kyori.adventure.text.Component; @@ -8,6 +9,7 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Location; +import org.bukkit.Particle; import org.bukkit.Sound; import org.bukkit.entity.Player; import org.bukkit.util.Vector; @@ -372,7 +374,7 @@ public void playSounds(@NotNull Location location){ /** * Add a {@link DEUSound} to play at this frame point - * @param sound + * @param sound the sound * @return this */ public FramePoint addSound(@NotNull DEUSound sound){ @@ -380,6 +382,46 @@ public FramePoint addSound(@NotNull DEUSound sound){ return this; } + /** + * Remove all {@link AnimationParticle}s from this frame point + * @return this + */ + public FramePoint removeAllParticles(){ + particles.clear(); + return this; + } + + + /** + * Remove {@link AnimationParticle}s that would be played at this point during an animation frame. + * This removes all instances of the given particle + * @param particle the particle + * @return true if an instance of the particle was removed + */ + public boolean removeParticle(@NotNull Particle particle){ + Iterator iterator = particles.iterator(); + boolean hasInstance = false; + while (iterator.hasNext()){ + AnimationParticle p = iterator.next(); + if (p.getParticle() == particle){ + hasInstance = true; + iterator.remove(); + } + } + return hasInstance; + } + + + + /** + * Remove an {@link AnimationParticle} that would be played at this point during an animation frame. + * @param particle the particle + * @return true if the particle was contained and removed + */ + public boolean removeParticle(@NotNull AnimationParticle particle){ + return particles.remove(particle); + } + /** * Remove all {@link DEUSound}s from this frame point * @return this @@ -391,7 +433,7 @@ public FramePoint removeAllSounds(){ /** * Remove a {@link DEUSound} that would be played at this point during an animation frame - * @param sound + * @param sound the sound * @return true if the sound was removed */ public boolean removeSound(@NotNull DEUSound sound){ From 388c9d1c6fbdcd9448a01e48e7d109576d7a0916 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 02:47:43 -0500 Subject: [PATCH 34/43] Add removal of particles from frame points --- .../utils/DisplayEntities/DEUSound.java | 5 +-- .../utils/DisplayEntities/FramePoint.java | 31 +++++++++++++------ .../particles/AnimationParticle.java | 30 ++++++++++++++++-- .../entity/DEUInteractionListener.java | 2 +- 4 files changed, 51 insertions(+), 17 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java index 097fd128..505f7295 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java @@ -256,10 +256,7 @@ public void sendInfo(Player player, Consumer clickRemovalAction){ public static void sendInfo(Collection sounds, Player player, String soundListTitle, Consumer clickRemovalAction){ if (soundListTitle == null) soundListTitle = "Sounds"; player.sendMessage(MiniMessage.miniMessage().deserialize(soundListTitle+": "+sounds.size())); - if (sounds.isEmpty()){ - player.sendMessage(Component.text("| NONE", NamedTextColor.GRAY)); - } - else{ + if (!sounds.isEmpty()){ for (DEUSound sound : sounds){ sound.sendInfo(player, clickRemovalAction); } diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java index 0c2a08d5..eaec2089 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java @@ -502,17 +502,30 @@ public void sendInfo(Player player){ //Particles player.sendMessage(MiniMessage.miniMessage().deserialize("Particles: "+particles.size())); - if (particles.isEmpty()){ - player.sendMessage(Component.text("| NONE", NamedTextColor.GRAY)); - } - else{ + if (!particles.isEmpty()){ player.sendMessage(Component.text("Click a particle to edit it", NamedTextColor.AQUA)); for (AnimationParticle particle : particles){ - player.sendMessage(Component.text("- "+particle.getParticleName(), NamedTextColor.LIGHT_PURPLE) - .clickEvent(ClickEvent.callback(click -> { - particle.sendInfo((Player) click); - })) - .hoverEvent(HoverEvent.showText(Component.text("Click to edit", NamedTextColor.YELLOW)))); + Component c1 = Component.text("- "+particle.getParticleName(), NamedTextColor.LIGHT_PURPLE) + .clickEvent(ClickEvent.callback(click -> { + particle.sendInfo((Player) click); + })) + .hoverEvent(HoverEvent.showText(Component.text("Click to edit", NamedTextColor.YELLOW) + .appendNewline() + .append(particle.getInfoComponent()))); + + Component c2 = Component.text("[REMOVE]", NamedTextColor.RED) + .hoverEvent(HoverEvent.showText(Component.text("Click to remove this particle", NamedTextColor.RED))) + .clickEvent(ClickEvent.callback(click -> { + if (removeParticle(particle)) { + player.sendMessage(DisplayAPI.pluginPrefix + .append(Component.text("Particle Removed!", NamedTextColor.YELLOW))); + } + else{ + player.sendMessage(DisplayAPI.pluginPrefix + .append(Component.text("That particle has already been removed!", NamedTextColor.RED))); + } + })); + player.sendMessage(c1.appendSpace().append(c2)); } } diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/particles/AnimationParticle.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/particles/AnimationParticle.java index 95c80bfa..da059a56 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/particles/AnimationParticle.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/particles/AnimationParticle.java @@ -170,6 +170,28 @@ public void initializeParticle(){ protected abstract void initalize(); + @ApiStatus.Internal + public Component getInfoComponent(){ + Component comp = Component.empty(); + comp = comp.appendNewline(); + comp = comp.append(Component.text("Particle Info:", NamedTextColor.AQUA)); + comp = comp.appendNewline(); + comp = comp.append(Component.text("| Count: "+count, NamedTextColor.GREEN)); + comp = comp.appendNewline(); + comp = comp.append(Component.text("| Extra: "+extra, NamedTextColor.GREEN)); + comp = comp.appendNewline(); + comp = comp.append(Component.text("| Delay: "+delayInTicks, NamedTextColor.GREEN)); + comp = comp.appendNewline(); + comp = comp.append(Component.text(getOffsetsString(), NamedTextColor.GREEN)); + + Component unique = getUniqueInfo(); + if (unique != null){ + comp = comp.appendNewline(); + comp = comp.append(unique); + } + return comp; + } + public void sendInfo(Player player){ player.sendMessage(Component.empty()); player.sendMessage(Component.text("Particle Info:", NamedTextColor.AQUA)); @@ -178,9 +200,7 @@ public void sendInfo(Player player){ sendEditMSG(player, "| Count: "+count, AnimationParticleBuilder.Step.COUNT); sendEditMSG(player, "| Extra: "+extra, AnimationParticleBuilder.Step.EXTRA); sendEditMSG(player, "| Delay: "+delayInTicks, AnimationParticleBuilder.Step.DELAY); - - String xyzOffset = xOffset+", "+yOffset+", "+zOffset; - sendEditMSG(player, "| Offsets: "+xyzOffset, AnimationParticleBuilder.Step.OFFSETS); + sendEditMSG(player, getOffsetsString(), AnimationParticleBuilder.Step.OFFSETS); Component unique = getUniqueInfo(); if (unique != null){ @@ -188,6 +208,10 @@ public void sendInfo(Player player){ } } + private String getOffsetsString(){ + return String.format("| Offsets: %s, %s, %s", xOffset, yOffset, zOffset); + } + private void sendEditMSG(Player player, String info, AnimationParticleBuilder.Step step){ player.sendMessage(getEditMSG(info, step)); } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/entity/DEUInteractionListener.java b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/entity/DEUInteractionListener.java index b5ba5f19..0eb443f3 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/listeners/entity/DEUInteractionListener.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/listeners/entity/DEUInteractionListener.java @@ -81,7 +81,7 @@ public void onPacketReceive(PacketReceiveEvent event) { player.sendMessage(Component.empty()); player.sendMessage(Component.text("-------=Point Info=-------", NamedTextColor.AQUA, TextDecoration.BOLD)); pointDisplay.sendInfo(player); - player.sendMessage(MiniMessage.miniMessage().deserialize("Sneak+Right Click to DELETE")); + player.sendMessage(Component.text("Sneak+Right Click to DELETE", NamedTextColor.RED)); player.sendMessage(Component.text("| Run \"/deu hidepoints\" to stop viewing points", NamedTextColor.GRAY)); RelativePointUtils.selectRelativePoint(player, pointDisplay); return; From ec4d499adec8440839cfe4547bf59a5e16dccdd6 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 03:10:12 -0500 Subject: [PATCH 35/43] send error messages when user input for a collection of framepoints is invalid --- .../command/DisplayEntityPluginCommand.java | 12 +++++----- .../anim/AnimAddDefaultParticleCMD.java | 23 +++++++++++-------- .../command/anim/AnimCopyPointCMD.java | 6 ++--- .../command/anim/AnimEditFrameCMD.java | 6 ++--- .../utils/command/DEUCommandUtils.java | 16 +++++++++++-- 5 files changed, 37 insertions(+), 26 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/DisplayEntityPluginCommand.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/DisplayEntityPluginCommand.java index 7a1b13ca..58578db4 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/DisplayEntityPluginCommand.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/DisplayEntityPluginCommand.java @@ -176,14 +176,14 @@ public static void noPartSelection(Player player){ player.sendMessage(Component.text("/deu parts select ", NamedTextColor.GRAY)); } - public static void invalidTag(Player player, String tag){ - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed to add tag: "+tag, NamedTextColor.RED))); - invalidTagRestrictions(player); + public static void invalidTag(CommandSender sender, String tag){ + sender.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Failed to add tag: "+tag, NamedTextColor.RED))); + invalidTagRestrictions(sender); } - public static void invalidTagRestrictions(Player player){ - player.sendMessage(Component.text("| Valid tags do not start with an \"!\" and do not contain commas.", NamedTextColor.GRAY, TextDecoration.ITALIC)); - player.sendMessage(Component.text("| The tag may also already exist or be set", NamedTextColor.GRAY, TextDecoration.ITALIC)); + public static void invalidTagRestrictions(CommandSender sender){ + sender.sendMessage(Component.text("| Valid tags do not start with an \"!\" and do not contain commas.", NamedTextColor.GRAY, TextDecoration.ITALIC)); + sender.sendMessage(Component.text("| The tag may also already exist or be set", NamedTextColor.GRAY, TextDecoration.ITALIC)); } public static void invalidStorage(CommandSender sender){ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java index 3e3cde2d..b3ae8aed 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java @@ -31,16 +31,19 @@ public void execute(Player player, String[] args) { return; } - Collection framePoints = DEUCommandUtils.getFrames(args[2], anim) - .stream() - .map(SpawnedDisplayAnimationFrame::getDefaultFramePoint) - .toList(); + try{ + Collection framePoints = DEUCommandUtils.getFrames(player, args[2], anim) + .stream() + .map(SpawnedDisplayAnimationFrame::getDefaultFramePoint) + .toList(); + + if (VersionUtils.canViewDialogs(player, false)){ + AnimationParticleSelectDialog.sendDialog(player, framePoints); + } + else{ + new AnimationParticleBuilder(player, framePoints); + } + }catch(IllegalArgumentException e){} - if (VersionUtils.canViewDialogs(player, false)){ - AnimationParticleSelectDialog.sendDialog(player, framePoints); - } - else{ - new AnimationParticleBuilder(player, framePoints); - } } } \ No newline at end of file diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCopyPointCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCopyPointCMD.java index 0add0024..87767ffc 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCopyPointCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCopyPointCMD.java @@ -54,7 +54,7 @@ public void execute(Player player, String[] args) { try { String arg = args[2]; - Collection frames = DEUCommandUtils.getFrames(arg, anim); + Collection frames = DEUCommandUtils.getFrames(player, arg, anim); for (SpawnedDisplayAnimationFrame frame : frames){ frame.addFramePoint(new FramePoint(framePoint)); @@ -75,8 +75,6 @@ public void execute(Player player, String[] args) { } catch (NumberFormatException e) { player.sendMessage(Component.text("Invalid value entered! Enter whole numbers >= 0", NamedTextColor.RED)); } - catch (IllegalArgumentException e){ - player.sendMessage(Component.text("Invalid Frame ID(s) or Frame Tag", NamedTextColor.RED)); - } + catch (IllegalArgumentException e){} } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditFrameCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditFrameCMD.java index cca65659..f5400553 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditFrameCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditFrameCMD.java @@ -44,7 +44,7 @@ public void execute(Player player, String[] args) { return; } try { - Collection frames = DEUCommandUtils.getFrames(args[2], anim); + Collection frames = DEUCommandUtils.getFrames(player, args[2], anim); int delay = Integer.parseInt(args[3]); int duration = Integer.parseInt(args[4]); if (delay < 0 || duration < 0) { @@ -61,8 +61,6 @@ public void execute(Player player, String[] args) { } catch (NumberFormatException e) { player.sendMessage(Component.text("Invalid value entered! Enter whole numbers >= 0", NamedTextColor.RED)); } - catch (IllegalArgumentException e){ - player.sendMessage(Component.text("Invalid Frame ID(s) or Frame Tag", NamedTextColor.RED)); - } + catch (IllegalArgumentException e){} } } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/command/DEUCommandUtils.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/command/DEUCommandUtils.java index 66e62c4f..3a9aa7ee 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/command/DEUCommandUtils.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/command/DEUCommandUtils.java @@ -1,6 +1,7 @@ package net.donnypz.displayentityutils.utils.command; import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.command.DisplayEntityPluginCommand; import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimationFrame; import net.donnypz.displayentityutils.utils.DisplayUtils; @@ -18,6 +19,7 @@ import org.bukkit.Registry; import org.bukkit.block.Block; import org.bukkit.block.data.BlockData; +import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; import org.bukkit.util.RayTraceResult; @@ -53,7 +55,15 @@ public static int[] commaSeparatedIDs(String idString) throws IllegalArgumentExc return arr; } - public static Collection getFrames(String arg, SpawnedDisplayAnimation animation) throws IllegalArgumentException{ + /** + * Get the frames provided by a user, either comma separated or by a frame tag. Automatically sends error messages + * @param sender the sender + * @param arg the user-input + * @param animation the animation + * @return a collection of frames + * @throws IllegalArgumentException on failure + */ + public static Collection getFrames(CommandSender sender, String arg, SpawnedDisplayAnimation animation) throws IllegalArgumentException{ //Single Frame ID in arg try{ int index = Integer.parseInt(arg); @@ -63,7 +73,8 @@ public static Collection getFrames(String arg, Spa catch(NumberFormatException ignored){} catch(IndexOutOfBoundsException e){ //Single Frame ID Out of Bounds - throw new IllegalArgumentException(e); + sender.sendMessage(Component.text("Invalid Frame ID(s) or Frame Tag", NamedTextColor.RED)); + throw new IllegalArgumentException(); } //Multiple Frame IDs @@ -81,6 +92,7 @@ public static Collection getFrames(String arg, Spa //Single Frame Tag catch (IllegalArgumentException ex){ if (!DisplayUtils.isValidTag(arg)){ + DisplayEntityPluginCommand.invalidTag(sender, arg); throw ex; } From 228bfbead0f59e7836895833a5bb06ea1da7e631 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 04:35:29 -0500 Subject: [PATCH 36/43] Add "/deu anim showpoints" and "/deu anim adddefaultsound" --- .../utils/DisplayEntities/FramePoint.java | 10 ++- .../command/anim/AnimAddDefaultSoundCMD.java | 73 ++++++++++++++++++ .../command/anim/AnimCMD.java | 6 +- .../command/anim/AnimFrameInfoCMD.java | 1 - .../command/anim/AnimShowPointsCMD.java | 77 +++++++++++++++++++ .../relativepoints/RelativePointUtils.java | 1 + 6 files changed, 164 insertions(+), 4 deletions(-) create mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultSoundCMD.java create mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimShowPointsCMD.java diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java index eaec2089..7d655fcb 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java @@ -7,6 +7,7 @@ import net.kyori.adventure.text.event.ClickEvent; import net.kyori.adventure.text.event.HoverEvent; import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; import net.kyori.adventure.text.minimessage.MiniMessage; import org.bukkit.Location; import org.bukkit.Particle; @@ -498,7 +499,12 @@ public void initialize(){ @ApiStatus.Internal public void sendInfo(Player player){ - player.sendMessage(MiniMessage.miniMessage().deserialize("Tag: "+tag)); + if (tag.equals(DEFAULT_FRAME_POINT_TAG)) { + player.sendMessage(Component.text("| Default Point", NamedTextColor.YELLOW, TextDecoration.ITALIC)); + } + else{ + player.sendMessage(MiniMessage.miniMessage().deserialize("Tag: "+tag)); + } //Particles player.sendMessage(MiniMessage.miniMessage().deserialize("Particles: "+particles.size())); @@ -534,7 +540,7 @@ public void sendInfo(Player player){ //Sounds DEUSound.sendInfo(sounds.values(), player, null, null); - player.sendMessage(MiniMessage.miniMessage().deserialize("RIGHT click to preview effects")); + if (!tag.equals(DEFAULT_FRAME_POINT_TAG)) player.sendMessage(MiniMessage.miniMessage().deserialize("RIGHT click to preview effects")); } } \ No newline at end of file diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultSoundCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultSoundCMD.java new file mode 100644 index 00000000..e5aaf413 --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultSoundCMD.java @@ -0,0 +1,73 @@ +package net.donnypz.displayentityutils.command.anim; + +import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.command.DEUSubCommand; +import net.donnypz.displayentityutils.command.Permission; +import net.donnypz.displayentityutils.command.PlayerSubCommand; +import net.donnypz.displayentityutils.managers.DisplayAnimationManager; +import net.donnypz.displayentityutils.utils.DisplayEntities.DEUSound; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimationFrame; +import net.donnypz.displayentityutils.utils.command.DEUCommandUtils; +import net.donnypz.displayentityutils.utils.version.VersionUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.minimessage.MiniMessage; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; +import java.util.List; + +class AnimAddDefaultSoundCMD extends PlayerSubCommand { + AnimAddDefaultSoundCMD(@NotNull DEUSubCommand parentSubCommand) { + super("adddefaultsound", parentSubCommand, Permission.ANIM_ADD_SOUND); + setTabComplete(2, ""); + setTabComplete(3, ""); + setTabComplete(4, ""); + setTabComplete(5, ""); + setTabComplete(6, List.of("", "")); + } + + @Override + public void execute(Player player, String[] args) { + + SpawnedDisplayAnimation anim = DisplayAnimationManager.getSelectedSpawnedAnimation(player); + if (anim == null) { + AnimCMD.noAnimationSelection(player); + return; + } + + if (args.length < 6) { + player.sendMessage(Component.text("Incorrect Usage! /deu anim adddefaultsound ", NamedTextColor.RED)); + return; + } + + try { + String soundStr = args[2]; + float volume = Float.parseFloat(args[3]); + float pitch = Float.parseFloat(args[4]); + int delayInTicks = Integer.parseInt(args[5]); + + + Collection frames = DEUCommandUtils.getFrames(player, args[6], anim); + for (SpawnedDisplayAnimationFrame frame : frames){ + frame.getDefaultFramePoint().addSound(new DEUSound(soundStr, volume, pitch, delayInTicks)); + } + + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Sound Added to "+frames.size()+" frames' default frame points", NamedTextColor.GREEN))); + player.sendMessage(MiniMessage.miniMessage().deserialize("| Sound: "+soundStr)); + player.sendMessage(MiniMessage.miniMessage().deserialize("| Volume: "+volume)); + player.sendMessage(MiniMessage.miniMessage().deserialize("| Pitch: "+pitch)); + player.sendMessage(MiniMessage.miniMessage().deserialize("| Delay: "+delayInTicks)); + + if (VersionUtils.getSound(args[2]) == null){ + player.sendMessage(Component.text("| The provided sound is not a vanilla Minecraft sound, or does not exist in this game version!", NamedTextColor.GRAY)); + } + } catch (NumberFormatException | IndexOutOfBoundsException e) { + player.sendMessage(Component.text("Invalid number entered! Enter a number >= 0", NamedTextColor.RED)); + player.sendMessage(Component.text("| Delay must be a whole number", NamedTextColor.GRAY)); + } + catch (IllegalArgumentException e){} + } +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java index f20f2c78..9ddc8417 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java @@ -32,6 +32,7 @@ public AnimCMD(){ new AnimEditFrameCMD(this); new AnimEditAllFramesCMD(this); new AnimAddPointCMD(this); + new AnimShowPointsCMD(this); new AnimDrawPointsCMD(this); new AnimDrawPosCMD(this); new AnimCopyPointCMD(this); @@ -39,6 +40,7 @@ public AnimCMD(){ new AnimShowFrameCMD(this); new AnimPreviewFrameCMD(this); new AnimAddSoundCMD(this); + new AnimAddDefaultSoundCMD(this); new AnimRemoveSoundCMD(this); new AnimAddParticleCMD(this); new AnimAddDefaultParticleCMD(this); @@ -102,13 +104,15 @@ else if (page == 3){ CMDUtils.sendCMD(sender, "/deu anim editframe ", "Edit properties of a frame"); CMDUtils.sendCMD(sender, "/deu anim editallframes ", "Edit properties of all frames"); CMDUtils.sendCMD(sender, "/deu anim addpoint ", "Add a point relative to a group and your location for a frame)"); - CMDUtils.sendUnsafeCMD(sender, "/deu anim drawpos <1 | 2 | 3>", "Set where frame points should be drawn between. Set 3 when drawing arcs"); + CMDUtils.sendCMD(sender, "/deu anim showpoints [-default]", "Show the frame points of a frame. Use \"-default\" to view a frame's default point info"); } else if (page == 4){ + CMDUtils.sendUnsafeCMD(sender, "/deu anim drawpos <1 | 2 | 3>", "Set where frame points should be drawn between. Set 3 when drawing arcs"); CMDUtils.sendUnsafeCMD(sender, "/deu anim drawpoints [points-per-frame]", "Draw a straight/arc'd line of frame points between frames"); CMDUtils.sendCMD(sender, "/deu anim copypoint ", "Copy a selected frame point to other frames"); CMDUtils.sendCMD(sender, "/deu anim movepoint", "Move a frame point to your location, relative to your selected group"); CMDUtils.sendCMD(sender, "/deu anim addsound ", "Add a sound to play at a frame point"); + CMDUtils.sendCMD(sender, "/deu anim adddefaultsound ", "Add a sound to play at a frame's default frame point (group origin)"); CMDUtils.sendCMD(sender, "/deu anim removesound ", "Remove a sound from a frame point"); } else if (page == 5){ diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimFrameInfoCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimFrameInfoCMD.java index 0a43af9b..9b867157 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimFrameInfoCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimFrameInfoCMD.java @@ -65,7 +65,6 @@ public void execute(Player player, String[] args) { return; } player.sendMessage(Component.empty()); - player.playSound(player, Sound.ENTITY_ITEM_FRAME_PLACE, 1, 2); RelativePointUtils.spawnFramePointDisplays(group, player, frame); })); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimShowPointsCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimShowPointsCMD.java new file mode 100644 index 00000000..c2cbce07 --- /dev/null +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimShowPointsCMD.java @@ -0,0 +1,77 @@ +package net.donnypz.displayentityutils.command.anim; + +import net.donnypz.displayentityutils.DisplayAPI; +import net.donnypz.displayentityutils.command.DEUSubCommand; +import net.donnypz.displayentityutils.command.Permission; +import net.donnypz.displayentityutils.command.PlayerSubCommand; +import net.donnypz.displayentityutils.managers.DisplayAnimationManager; +import net.donnypz.displayentityutils.managers.DisplayGroupManager; +import net.donnypz.displayentityutils.utils.DisplayEntities.ActiveGroup; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; +import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimationFrame; +import net.donnypz.displayentityutils.utils.relativepoints.RelativePointUtils; +import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.format.NamedTextColor; +import net.kyori.adventure.text.format.TextDecoration; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +public class AnimShowPointsCMD extends PlayerSubCommand { + + AnimShowPointsCMD(@NotNull DEUSubCommand parentSubCommand) { + super("showpoint", parentSubCommand, Permission.ANIM_FRAME_INFO); + setTabComplete(2, ""); + addFlag("-default"); + } + + @Override + public void execute(Player player, String[] args) { + ActiveGroup group = DisplayGroupManager.getSelectedGroup(player); + if (group == null) { + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("You must have a group selected to do this animation command!", NamedTextColor.RED))); + return; + } + + SpawnedDisplayAnimation anim = DisplayAnimationManager.getSelectedSpawnedAnimation(player); + if (anim == null) { + AnimCMD.noAnimationSelection(player); + return; + } + + if (RelativePointUtils.isViewingRelativePoints(player)){ + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("You cannot play do that while viewing points!", NamedTextColor.RED))); + return; + } + + if (args.length < 3) { + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Incorrect Usage! /deu anim showpoints ", NamedTextColor.RED))); + return; + } + + try { + int id = Integer.parseInt(args[2]); + if (id < 0) { + throw new NumberFormatException(); + } + if (id >= anim.getFrames().size()) { + player.sendMessage(Component.text("Invalid ID! The ID cannot be >= the amount of frames!", NamedTextColor.RED)); + return; + } + SpawnedDisplayAnimationFrame frame = anim.getFrame(id); + boolean isDefault = getOptionalArguments(player, args).hasFlag("-default"); + if (isDefault){ + player.sendMessage(Component.empty()); + player.sendMessage(Component.text("-------=Point Info=-------", NamedTextColor.AQUA, TextDecoration.BOLD)); + frame.getDefaultFramePoint().sendInfo(player); + } + else{ + RelativePointUtils.spawnFramePointDisplays(group, player, frame); + } + + } catch (NumberFormatException e) { + player.sendMessage(Component.text("Invalid ID! ID's must be >= 0", NamedTextColor.RED)); + } + + + } +} diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/relativepoints/RelativePointUtils.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/relativepoints/RelativePointUtils.java index 3cba78df..31336f8f 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/relativepoints/RelativePointUtils.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/relativepoints/RelativePointUtils.java @@ -36,6 +36,7 @@ public static void spawnFramePointDisplays(ActiveGroup group, Player player, displays.add(pd); } setDisplays(player, displays, true); + player.playSound(player, Sound.ENTITY_ITEM_FRAME_PLACE, 1, 2); } From 58b5b594c3f58f937fd84042030ff7f51aba3e85 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 04:35:46 -0500 Subject: [PATCH 37/43] Update incorrect usage msgs --- .../donnypz/displayentityutils/command/anim/AnimSetTagCMD.java | 2 +- .../displayentityutils/command/anim/AnimShowFrameCMD.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimSetTagCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimSetTagCMD.java index fb6df5ae..3e73e057 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimSetTagCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimSetTagCMD.java @@ -20,7 +20,7 @@ class AnimSetTagCMD extends PlayerSubCommand { @Override public void execute(Player player, String[] args) { if (args.length < 3) { - player.sendMessage(Component.text("Incorrect Usage! /deu anim settag ", NamedTextColor.RED)); + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Incorrect Usage! /deu anim settag ", NamedTextColor.RED))); return; } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimShowFrameCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimShowFrameCMD.java index 3735f8b9..baf1aef8 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimShowFrameCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimShowFrameCMD.java @@ -28,7 +28,7 @@ class AnimShowFrameCMD extends GroupSubCommand { @Override protected void sendIncorrectUsage(@NotNull Player player) { - player.sendMessage(Component.text("/deu anim showframe ", NamedTextColor.RED)); + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Incorrect Usage! /deu anim showframe ", NamedTextColor.RED))); player.sendMessage(Component.text("First frame is 0, Second frame is 1, and so on...", NamedTextColor.GRAY)); } From 0a0839e7f3a4be15640780bc2da226b1b16677a5 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 04:39:28 -0500 Subject: [PATCH 38/43] fix sounds not playing from default frame point during animations --- .../utils/DisplayEntities/SpawnedDisplayAnimationFrame.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java index ffa171fc..3f30f88b 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java @@ -378,7 +378,7 @@ public void showParticles(@NotNull Collection players, @NotNull ActiveGr * @param limited whether the effects should only be played to players who can see the group */ public void playEffects(@NotNull ActiveGroup group, @Nullable DisplayAnimator animator, boolean limited){ - if (defaultFramePoint != null) defaultFramePoint.showParticles(group, animator, limited); + if (defaultFramePoint != null) defaultFramePoint.playEffects(group, animator, limited); for (FramePoint point : framePoints.values()){ point.playEffects(group, animator, limited); } @@ -391,7 +391,7 @@ public void playEffects(@NotNull ActiveGroup group, @Nullable DisplayAnimator * @param group the group to play these effects for */ public void playEffects(@NotNull Player player, @NotNull ActiveGroup group){ - if (defaultFramePoint != null) defaultFramePoint.showParticles(group, player); + if (defaultFramePoint != null) defaultFramePoint.playEffects(group, player); for (FramePoint point : framePoints.values()){ point.playEffects(group, player); } @@ -403,7 +403,7 @@ public void playEffects(@NotNull Player player, @NotNull ActiveGroup group){ * @param group the group to play these effects for */ public void playEffects(@NotNull Collection players, @NotNull ActiveGroup group){ - if (defaultFramePoint != null) defaultFramePoint.showParticles(group, players); + if (defaultFramePoint != null) defaultFramePoint.playEffects(group, players); for (FramePoint point : framePoints.values()){ point.playEffects(group, players); } From 0417042a3d5da00a3fcbf70d082232bdfb049021 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 05:14:11 -0500 Subject: [PATCH 39/43] Added -all parameter to anim cmds that accept frame-ids/frame-tag --- .../command/anim/AnimAddDefaultParticleCMD.java | 2 +- .../command/anim/AnimAddDefaultSoundCMD.java | 4 ++-- .../displayentityutils/command/anim/AnimCMD.java | 8 ++++---- .../command/anim/AnimCopyPointCMD.java | 4 ++-- .../command/anim/AnimEditFrameCMD.java | 4 ++-- .../utils/command/DEUCommandUtils.java | 12 ++++++++++-- 6 files changed, 21 insertions(+), 13 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java index b3ae8aed..0d578c88 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java @@ -20,7 +20,7 @@ class AnimAddDefaultParticleCMD extends PlayerSubCommand { AnimAddDefaultParticleCMD(@NotNull DEUSubCommand parentSubCommand) { super("adddefaultparticle", parentSubCommand, Permission.ANIM_ADD_PARTICLE); - setTabComplete(2, List.of("", "")); + setTabComplete(2, List.of("", "", "-all")); } @Override diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultSoundCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultSoundCMD.java index e5aaf413..05e8a209 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultSoundCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultSoundCMD.java @@ -26,7 +26,7 @@ class AnimAddDefaultSoundCMD extends PlayerSubCommand { setTabComplete(3, ""); setTabComplete(4, ""); setTabComplete(5, ""); - setTabComplete(6, List.of("", "")); + setTabComplete(6, List.of("", "", "-all")); } @Override @@ -39,7 +39,7 @@ public void execute(Player player, String[] args) { } if (args.length < 6) { - player.sendMessage(Component.text("Incorrect Usage! /deu anim adddefaultsound ", NamedTextColor.RED)); + player.sendMessage(Component.text("Incorrect Usage! /deu anim adddefaultsound ", NamedTextColor.RED)); return; } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java index 9ddc8417..fed01a42 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java @@ -101,7 +101,7 @@ else if (page == 3){ CMDUtils.sendCMD(sender, "/deu anim frameinfo ", "List information about a frame in your animation"); CMDUtils.sendCMD(sender, "/deu anim removeframe ", "Remove a frame from your selected animation"); CMDUtils.sendCMD(sender, "/deu anim overwriteframe ", "Overwrite the transformation data of a frame"); - CMDUtils.sendCMD(sender, "/deu anim editframe ", "Edit properties of a frame"); + CMDUtils.sendCMD(sender, "/deu anim editframe ", "Edit properties of a frame"); CMDUtils.sendCMD(sender, "/deu anim editallframes ", "Edit properties of all frames"); CMDUtils.sendCMD(sender, "/deu anim addpoint ", "Add a point relative to a group and your location for a frame)"); CMDUtils.sendCMD(sender, "/deu anim showpoints [-default]", "Show the frame points of a frame. Use \"-default\" to view a frame's default point info"); @@ -109,15 +109,15 @@ else if (page == 3){ else if (page == 4){ CMDUtils.sendUnsafeCMD(sender, "/deu anim drawpos <1 | 2 | 3>", "Set where frame points should be drawn between. Set 3 when drawing arcs"); CMDUtils.sendUnsafeCMD(sender, "/deu anim drawpoints [points-per-frame]", "Draw a straight/arc'd line of frame points between frames"); - CMDUtils.sendCMD(sender, "/deu anim copypoint ", "Copy a selected frame point to other frames"); + CMDUtils.sendCMD(sender, "/deu anim copypoint ", "Copy a selected frame point to other frames"); CMDUtils.sendCMD(sender, "/deu anim movepoint", "Move a frame point to your location, relative to your selected group"); CMDUtils.sendCMD(sender, "/deu anim addsound ", "Add a sound to play at a frame point"); - CMDUtils.sendCMD(sender, "/deu anim adddefaultsound ", "Add a sound to play at a frame's default frame point (group origin)"); + CMDUtils.sendCMD(sender, "/deu anim adddefaultsound ", "Add a sound to play at a frame's default frame point (group origin)"); CMDUtils.sendCMD(sender, "/deu anim removesound ", "Remove a sound from a frame point"); } else if (page == 5){ CMDUtils.sendCMD(sender, "/deu anim addparticle", "Add a particle to play at a frame point"); - CMDUtils.sendCMD(sender, "/deu anim adddefaultparticle ", "Add a particle to play at a frame's default frame point (group origin)"); + CMDUtils.sendCMD(sender, "/deu anim adddefaultparticle ", "Add a particle to play at a frame's default frame point (group origin)"); CMDUtils.sendCMD(sender, "/deu anim togglescalerespect", "Toggle whether your selected animation should respect the group's scale"); CMDUtils.sendCMD(sender, "/deu anim showframe ", "Displays a frame on your selected group"); CMDUtils.sendCMD(sender, "/deu anim previewframe ", "Preview a frame on your selected group, without changing group entity data"); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCopyPointCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCopyPointCMD.java index 87767ffc..a458ba09 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCopyPointCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCopyPointCMD.java @@ -23,7 +23,7 @@ class AnimCopyPointCMD extends PlayerSubCommand { AnimCopyPointCMD(@NotNull DEUSubCommand parentSubCommand) { super("copypoint", parentSubCommand, Permission.ANIM_COPY_FRAME_POINT); - setTabComplete(2, List.of("", "")); + setTabComplete(2, List.of("", "", "-all")); } @Override @@ -42,7 +42,7 @@ public void execute(Player player, String[] args) { FramePoint framePoint = display.getRelativePoint(); if (args.length < 3) { - player.sendMessage(Component.text("Incorrect Usage! /deu anim copypoint ", NamedTextColor.RED)); + player.sendMessage(Component.text("Incorrect Usage! /deu anim copypoint ", NamedTextColor.RED)); player.sendMessage(Component.text("| Enter a frame-tag, a single frame-id, or multiple comma separated ids.", NamedTextColor.GRAY)); return; } diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditFrameCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditFrameCMD.java index f5400553..cbbfced6 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditFrameCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditFrameCMD.java @@ -19,7 +19,7 @@ class AnimEditFrameCMD extends PlayerSubCommand { AnimEditFrameCMD(@NotNull DEUSubCommand parentSubCommand) { super("editframe", parentSubCommand, Permission.ANIM_EDIT_FRAME); - setTabComplete(2, List.of("", "")); + setTabComplete(2, List.of("", "", "-all")); setTabComplete(3, ""); setTabComplete(4, ""); } @@ -33,7 +33,7 @@ public void execute(Player player, String[] args) { } if (args.length < 5) { - player.sendMessage(Component.text("Incorrect Usage! /deu anim editframe ", NamedTextColor.RED)); + player.sendMessage(Component.text("Incorrect Usage! /deu anim editframe ", NamedTextColor.RED)); player.sendMessage(Component.text("| Enter a frame-tag, a single frame-id, or multiple comma separated ids.", NamedTextColor.GRAY)); player.sendMessage(Component.text("| First frame is 0, Second frame is 1, and so on...", NamedTextColor.GRAY)); return; diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/utils/command/DEUCommandUtils.java b/plugin/src/main/java/net/donnypz/displayentityutils/utils/command/DEUCommandUtils.java index 3a9aa7ee..35467201 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/utils/command/DEUCommandUtils.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/utils/command/DEUCommandUtils.java @@ -69,14 +69,20 @@ public static Collection getFrames(CommandSender s int index = Integer.parseInt(arg); return Set.of(animation.getFrame(index)); } - - catch(NumberFormatException ignored){} catch(IndexOutOfBoundsException e){ //Single Frame ID Out of Bounds sender.sendMessage(Component.text("Invalid Frame ID(s) or Frame Tag", NamedTextColor.RED)); throw new IllegalArgumentException(); } + if (arg.equalsIgnoreCase("-all")){ + if (!animation.hasFrames()){ + sender.sendMessage(Component.text("Your selected animation has no frames!", NamedTextColor.RED)); + throw new IllegalArgumentException(); + } + return animation.getFrames(); + } + //Multiple Frame IDs try{ int[] ids = commaSeparatedIDs(arg); @@ -87,6 +93,7 @@ public static Collection getFrames(CommandSender s } catch(IndexOutOfBoundsException ignored1){} } + if (frames.isEmpty()) sender.sendMessage(Component.text("Your selected animation has no frames!", NamedTextColor.RED)); return frames; } //Single Frame Tag @@ -102,6 +109,7 @@ public static Collection getFrames(CommandSender s frames.add(frame); } } + if (frames.isEmpty()) sender.sendMessage(Component.text("Your selected animation has no frames!", NamedTextColor.RED)); return frames; } } From 887c0ddfccb796bcebbb2f0cbb792dac3de22ea3 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 05:15:47 -0500 Subject: [PATCH 40/43] Remove "/deu anim editallframes" --- .../command/anim/AnimCMD.java | 11 ++-- .../command/anim/AnimEditAllFramesCMD.java | 58 ------------------- 2 files changed, 4 insertions(+), 65 deletions(-) delete mode 100644 plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditAllFramesCMD.java diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java index fed01a42..f9a09bab 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCMD.java @@ -30,7 +30,6 @@ public AnimCMD(){ new AnimRemoveFrameCMD(this); new AnimOverwriteFrameCMD(this); new AnimEditFrameCMD(this); - new AnimEditAllFramesCMD(this); new AnimAddPointCMD(this); new AnimShowPointsCMD(this); new AnimDrawPointsCMD(this); @@ -102,21 +101,20 @@ else if (page == 3){ CMDUtils.sendCMD(sender, "/deu anim removeframe ", "Remove a frame from your selected animation"); CMDUtils.sendCMD(sender, "/deu anim overwriteframe ", "Overwrite the transformation data of a frame"); CMDUtils.sendCMD(sender, "/deu anim editframe ", "Edit properties of a frame"); - CMDUtils.sendCMD(sender, "/deu anim editallframes ", "Edit properties of all frames"); CMDUtils.sendCMD(sender, "/deu anim addpoint ", "Add a point relative to a group and your location for a frame)"); CMDUtils.sendCMD(sender, "/deu anim showpoints [-default]", "Show the frame points of a frame. Use \"-default\" to view a frame's default point info"); + CMDUtils.sendUnsafeCMD(sender, "/deu anim drawpos <1 | 2 | 3>", "Set where frame points should be drawn between. Set 3 when drawing arcs"); } else if (page == 4){ - CMDUtils.sendUnsafeCMD(sender, "/deu anim drawpos <1 | 2 | 3>", "Set where frame points should be drawn between. Set 3 when drawing arcs"); CMDUtils.sendUnsafeCMD(sender, "/deu anim drawpoints [points-per-frame]", "Draw a straight/arc'd line of frame points between frames"); CMDUtils.sendCMD(sender, "/deu anim copypoint ", "Copy a selected frame point to other frames"); CMDUtils.sendCMD(sender, "/deu anim movepoint", "Move a frame point to your location, relative to your selected group"); CMDUtils.sendCMD(sender, "/deu anim addsound ", "Add a sound to play at a frame point"); CMDUtils.sendCMD(sender, "/deu anim adddefaultsound ", "Add a sound to play at a frame's default frame point (group origin)"); CMDUtils.sendCMD(sender, "/deu anim removesound ", "Remove a sound from a frame point"); + CMDUtils.sendCMD(sender, "/deu anim addparticle", "Add a particle to play at a frame point"); } else if (page == 5){ - CMDUtils.sendCMD(sender, "/deu anim addparticle", "Add a particle to play at a frame point"); CMDUtils.sendCMD(sender, "/deu anim adddefaultparticle ", "Add a particle to play at a frame's default frame point (group origin)"); CMDUtils.sendCMD(sender, "/deu anim togglescalerespect", "Toggle whether your selected animation should respect the group's scale"); CMDUtils.sendCMD(sender, "/deu anim showframe ", "Displays a frame on your selected group"); @@ -127,11 +125,10 @@ else if (page == 5){ " \n\"-packet\" will play the animation using packets." + " \n\"-camera\" will set your view to the animation's camera, if present" + " \n\"-nodata\" will disable texture changes for item/block displays and text display text changes"); - - } - else{ CMDUtils.sendCMD(sender, "/deu anim previewplay [-camera]", "Preview your selected animation on your selected group, without changing group entity data." + " \n\"-camera\" will set your view to the animation's camera, if present"); + } + else{ CMDUtils.sendCMD(sender, "/deu anim restore", "Restore your selected group to its previous state before previewing frames/animations"); CMDUtils.sendCMD(sender, "/deu anim stop", "Stop an animation playing on a group"); CMDUtils.sendCMD(sender, "/deu anim save ", "Save your selected animation and any changes made"); diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditAllFramesCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditAllFramesCMD.java deleted file mode 100644 index 2653bda0..00000000 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimEditAllFramesCMD.java +++ /dev/null @@ -1,58 +0,0 @@ -package net.donnypz.displayentityutils.command.anim; - -import net.donnypz.displayentityutils.command.DEUSubCommand; -import net.donnypz.displayentityutils.command.Permission; -import net.donnypz.displayentityutils.command.PlayerSubCommand; -import net.donnypz.displayentityutils.managers.DisplayAnimationManager; -import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimation; -import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayAnimationFrame; -import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.format.NamedTextColor; -import org.bukkit.entity.Player; -import org.jetbrains.annotations.NotNull; - -import java.util.List; - -class AnimEditAllFramesCMD extends PlayerSubCommand { - AnimEditAllFramesCMD(@NotNull DEUSubCommand parentSubCommand) { - super("editallframes", parentSubCommand, Permission.ANIM_EDIT_FRAME); - setTabComplete(2, ""); - setTabComplete(3, ""); - } - - @Override - public void execute(Player player, String[] args) { - SpawnedDisplayAnimation anim = DisplayAnimationManager.getSelectedSpawnedAnimation(player); - if (anim == null) { - AnimCMD.noAnimationSelection(player); - return; - } - - if (args.length < 4) { - player.sendMessage(Component.text("/deu anim editallframes ", NamedTextColor.RED)); - return; - } - - List frames = anim.getFrames(); - if (frames.isEmpty()) { - AnimCMD.hasNoFrames(player); - return ; - } - try { - int delay = Integer.parseInt(args[2]); - int duration = Integer.parseInt(args[3]); - if (delay < 0 || duration < 0) { - throw new NumberFormatException(); - } - for (SpawnedDisplayAnimationFrame frame : frames){ - frame.setDelay(delay); - frame.setDuration(duration); - } - player.sendMessage(Component.text("All frames have been edited!", NamedTextColor.GREEN)); - player.sendMessage(Component.text("Delay: " + delay, NamedTextColor.GRAY)); - player.sendMessage(Component.text("Duration: " + duration, NamedTextColor.GRAY)); - } catch (NumberFormatException e) { - player.sendMessage(Component.text("Invalid value entered! Enter whole numbers >= 0", NamedTextColor.RED)); - } - } -} From 01e146de3ebaafd0745742534941c7df2a523177 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 05:24:59 -0500 Subject: [PATCH 41/43] Click to remove framepoint's deusound when viewing frameinfo --- .../displayentityutils/utils/DisplayEntities/DEUSound.java | 2 +- .../displayentityutils/utils/DisplayEntities/FramePoint.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java index 505f7295..2ef98079 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java @@ -241,7 +241,7 @@ public void sendInfo(Player player, Consumer clickRemovalAction){ if (clickRemovalAction != null){ hoverComp = hoverComp.append(Component.newline()) - .append(Component.text("Click to remove this sound", NamedTextColor.YELLOW)); + .append(Component.text("Click to remove this sound", NamedTextColor.RED)); msgComp = msgComp.clickEvent(ClickEvent.callback(a -> { clickRemovalAction.accept(this); a.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Sound Removed!", NamedTextColor.YELLOW))); diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java index 7d655fcb..007a5a71 100644 --- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java +++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/FramePoint.java @@ -538,7 +538,7 @@ public void sendInfo(Player player){ player.sendMessage(Component.empty()); //Sounds - DEUSound.sendInfo(sounds.values(), player, null, null); + DEUSound.sendInfo(sounds.values(), player, null, this::removeSound); if (!tag.equals(DEFAULT_FRAME_POINT_TAG)) player.sendMessage(MiniMessage.miniMessage().deserialize("RIGHT click to preview effects")); } From c84414cccfa4148eacbf4322f9a27494b2a07368 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 17:45:47 -0500 Subject: [PATCH 42/43] 3.5.3 --- api/pom.xml | 2 +- plugin/pom.xml | 2 +- pom.xml | 16 +--------------- 3 files changed, 3 insertions(+), 17 deletions(-) diff --git a/api/pom.xml b/api/pom.xml index 5a33ffab..8f42b049 100644 --- a/api/pom.xml +++ b/api/pom.xml @@ -6,7 +6,7 @@ net.donnypz.displayentityutils deu - 3.5.2 + 3.5.3 api diff --git a/plugin/pom.xml b/plugin/pom.xml index d00be887..47420436 100644 --- a/plugin/pom.xml +++ b/plugin/pom.xml @@ -6,7 +6,7 @@ net.donnypz.displayentityutils deu - 3.5.2 + 3.5.3 plugin diff --git a/pom.xml b/pom.xml index a5b3a8df..1bd767dc 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ net.donnypz.displayentityutils deu - 3.5.2 + 3.5.3 pom DisplayEntityUtils @@ -39,20 +39,6 @@ - - org.apache.maven.plugins - maven-shade-plugin - 3.5.3 - - - package - - shade - - - - - org.apache.maven.plugins maven-javadoc-plugin From 4c0ae380be0e0f372feaec43294567cf6d7c0fb4 Mon Sep 17 00:00:00 2001 From: Jay <76460079+PZDonny@users.noreply.github.com> Date: Mon, 1 Jun 2026 19:00:10 -0500 Subject: [PATCH 43/43] (3.5.3) Despawn imported project after conversion --- .../command/bdengine/BDEngineImportCMD.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportCMD.java index c8af1561..9e85089f 100644 --- a/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportCMD.java +++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportCMD.java @@ -54,8 +54,8 @@ public void execute(Player player, String[] args) { !saveAnimations ? "" : animPrefix, saveGroups, saveAnimations, - false); - player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempted to spawn model at your location!", NamedTextColor.GREEN))); + true); + player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempting to import project at your location!", NamedTextColor.GREEN))); }); } catch(NumberFormatException e){