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/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java b/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java
index b090233f..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,8 +54,9 @@ 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;
- private static boolean isFolia;
+ static boolean isFolia;
private DisplayAPI(){}
@@ -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;
}
@@ -236,13 +242,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/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/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/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/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);
+ }
}
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..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
@@ -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);
@@ -211,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)));
@@ -226,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/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/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());
}
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..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
@@ -1,12 +1,16 @@
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;
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;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
@@ -20,25 +24,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 +58,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;
}
/**
@@ -330,7 +375,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){
@@ -338,6 +383,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
@@ -349,7 +434,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){
@@ -414,30 +499,48 @@ 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()));
- 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));
}
}
player.sendMessage(Component.empty());
//Sounds
- DEUSound.sendInfo(sounds.values(), player, null, null);
+ DEUSound.sendInfo(sounds.values(), player, null, this::removeSound);
- 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/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/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/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/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/SpawnedDisplayAnimationFrame.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayAnimationFrame.java
index 25ffd83f..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
@@ -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.playEffects(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.playEffects(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.playEffects(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;
}
@@ -451,6 +459,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 +469,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
+ );
}
}
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();
}
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/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 extends AnimationParticle> clazz = getAnimationParticleClass(particle());
- try {
- Constructor extends AnimationParticle> 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 extends AnimationParticle> getAnimationParticleClass(@NotNull String particleName){
@@ -208,6 +224,31 @@ public static Class extends AnimationParticle> 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 extends AnimationParticle> getAnimationParticleClass(@NotNull Particle particle){
if (isBlockDataParticle(particle)){
return BlockAnimationParticle.class;
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..35eb62e8 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
- * @return a boolean
+ * @param tag the tag
+ * @return false if the tag is blank, contains commas, starts with an "!", or contains spaces
*/
public static boolean isValidTag(@NotNull String tag){
- return !tag.contains(",") && !tag.startsWith("!") && !tag.isBlank();
+ return !tag.isBlank() && !tag.contains(",") && !tag.startsWith("!") && !tag.contains(" ");
}
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;
+ }
+}
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..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
@@ -1,11 +1,18 @@
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.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;
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 +25,125 @@
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.
+ *
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
+ * @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.
+ *
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
+ * @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.
+ *
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.
+ * @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..dfd27316
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEAPIConverter.java
@@ -0,0 +1,99 @@
+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;
+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(DisplayEntityGroup savedGroup, SpawnedDisplayEntityGroup group) {
+ new BDEAPIConvertEvent(
+ player,
+ conversionId,
+ savedGroup,
+ group,
+ convertedAnimations,
+ saveGroup,
+ saveAnimations
+ ).callEvent();
+ }
+
+ @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..aa2d57ae
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/api/BDEResult.java
@@ -0,0 +1,129 @@
+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;
+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.
+ *
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
+ * @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.
+ *
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
+ * @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.
+ *
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
+ * @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
new file mode 100644
index 00000000..6e00da67
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDECommandConverter.java
@@ -0,0 +1,287 @@
+package net.donnypz.displayentityutils.utils.bdengine.convert.common;
+
+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;
+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.*;
+
+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 -> {
+ });
+
+ 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 convertedAnimations = new ArrayList<>();
+
+ protected String projectName;
+
+ protected 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);
+
+ //Save Model and Animations
+ DisplayAPI.getScheduler().runLater(this::convertAndSave, 1);
+ }
+
+ public UUID getMasterEntityUUID(){
+ return masterEntityUUID;
+ }
+
+ public String getSubMasterScoreboardTag(){
+ return CONVERSION_SCOREBOARD_PREFIX+masterEntityUUID;
+ }
+
+ protected void setProjectName(@NotNull String projectName) {
+ this.projectName = projectName;
+ }
+
+ 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);
+ }
+
+ //Cleanup and save after animation conversions
+ DisplayAPI.getScheduler().runLater(() -> {
+ DisplayEntityGroup savedGroup = createdGroup.toDisplayEntityGroup();
+ if (saveGroup){
+ 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));
+ }
+ else{
+ if (createdGroup.getParts().size() > 1){
+ createdGroup.setPersistent(true);
+ }
+ else{
+ createdGroup.unregister(true, true);
+ }
+ }
+ onConversionCompleted(savedGroup, despawnAfter ? null : createdGroup);
+
+ }, delay+2);
+ }
+
+ protected abstract void onConversionCompleted(DisplayEntityGroup savedGroup, SpawnedDisplayEntityGroup group);
+
+ 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);
+ 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, 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);
+ }
+
+ anim.forceAddFrame(frame);
+ lastAddedFrameId = frameId;
+ }
+ frameId++;
+ }
+
+ if (frameId > finalFrame) {
+ 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);
+ convertedAnimations.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);
+ convertedAnimations.add(anim);
+ }
+ sendMessage(Component.empty());
+ });
+ cancel();
+ }
+ }
+ }, 0, 2); //BDEngine Animation Frame Duration is 2 ticks
+ }
+
+ protected abstract Collection getAnimationNames();
+
+ protected abstract int getFrameCount(@NotNull String animationName);
+
+ 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, int lastAddedFrameId);
+
+ 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);
+ }
+ }
+ executeCommandAsMasterEntity(command);
+
+ }
+
+ protected void executeCommandAsMasterEntity(String command){
+ String worldName = ConversionUtils.getExecuteCommandWorldName(SPAWN_LOCATION.getWorld());
+ 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) {
+ 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..3c7fb325
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/common/BDEConversionHandler.java
@@ -0,0 +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/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;
}
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/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java b/plugin/src/main/java/net/donnypz/displayentityutils/DisplayEntityPlugin.java
index 9951cf31..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;
@@ -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);
@@ -78,7 +80,7 @@ public void onEnable() {
registerListeners();
initializeNamespacedKeys();
initializeBStats();
- DisplayAPI.checkFolia();
+ checkFolia();
getServer().getConsoleSender().sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Plugin Enabled!", NamedTextColor.GREEN)));
}
@@ -88,6 +90,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");
@@ -164,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/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/DEUSubCommand.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/DEUSubCommand.java
index 17e95716..f619ae15 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,59 @@ public boolean isHelpCommand(){
return permission == Permission.HELP;
}
+ protected @NotNull OptionalArguments getOptionalArguments(CommandSender sender, String[] args){
+ int startIndex = tabCompleteSuggestions.isEmpty()
+ ? 0
+ : 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.isValidOptions = false;
+ return oArgs;
+ }
+ oArgs.options.put(arg, args[++i]);
+ }
+ }
+
+ return oArgs;
+ }
+
+ protected static class OptionalArguments{
+ Set flags = new HashSet<>();
+ Map options = new HashMap<>();
+ boolean isValidOptions = 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 isValidOptions() {
+ return isValidOptions;
+ }
+ }
+
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..58578db4 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())){
@@ -132,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/Permission.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/Permission.java
index c3838266..70bdab6c 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"),
@@ -133,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/anim/AnimAddDefaultParticleCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java
new file mode 100644
index 00000000..0d578c88
--- /dev/null
+++ b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimAddDefaultParticleCMD.java
@@ -0,0 +1,49 @@
+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("", "", "-all"));
+ }
+
+ @Override
+ public void execute(Player player, String[] args) {
+ SpawnedDisplayAnimation anim = DisplayAnimationManager.getSelectedSpawnedAnimation(player);
+ if (anim == null) {
+ AnimCMD.noAnimationSelection(player);
+ return;
+ }
+
+ 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){}
+
+ }
+}
\ 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..05e8a209
--- /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("", "", "-all"));
+ }
+
+ @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 4288ea17..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,8 +30,8 @@ public AnimCMD(){
new AnimRemoveFrameCMD(this);
new AnimOverwriteFrameCMD(this);
new AnimEditFrameCMD(this);
- new AnimEditAllFramesCMD(this);
new AnimAddPointCMD(this);
+ new AnimShowPointsCMD(this);
new AnimDrawPointsCMD(this);
new AnimDrawPosCMD(this);
new AnimCopyPointCMD(this);
@@ -39,8 +39,10 @@ public AnimCMD(){
new AnimShowFrameCMD(this);
new AnimPreviewFrameCMD(this);
new AnimAddSoundCMD(this);
+ new AnimAddDefaultSoundCMD(this);
new AnimRemoveSoundCMD(this);
new AnimAddParticleCMD(this);
+ new AnimAddDefaultParticleCMD(this);
new AnimReverseCMD(this);
new AnimScaleRespectCMD(this);
new AnimSetTagCMD(this);
@@ -79,7 +81,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");
@@ -98,35 +100,35 @@ 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 editallframes ", "Edit properties of all frames");
+ CMDUtils.sendCMD(sender, "/deu anim editframe ", "Edit properties of a frame");
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 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 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 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{
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/AnimCopyPointCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/anim/AnimCopyPointCMD.java
index 0add0024..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;
}
@@ -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/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));
- }
- }
-}
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..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;
@@ -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/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/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 d5c5c587..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
@@ -48,9 +48,10 @@ 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")){
+ 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/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/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));
}
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/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/bdengine/BDEngineCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineCMD.java
index 8a8231ef..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,8 +11,7 @@ public final class BDEngineCMD extends ConsoleUsableSubCommand {
public BDEngineCMD(){
super(Permission.HELP, new BDEngineHelpCMD());
new BDEngineConvertDatapackCMD(this);
- new BDEngineConvertLegacyDatapackCMD(this);
- new BDEngineImportModelCMD(this);
+ new BDEngineImportCMD(this);
new BDEngineSpawnModelCMD(this);
}
@@ -38,11 +37,9 @@ 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 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/BDEngineConvertDatapackCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineConvertDatapackCMD.java
index c02dddaa..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,16 +22,26 @@ 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;
}
String datapackName = args[2];
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(player, datapackName, groupTag, animPrefix);
+ BDEngineUtils.convertDatapack(
+ datapackName,
+ player,
+ !saveGroups ? "" : groupTag,
+ !saveAnimations ? "" : animPrefix,
+ saveGroups,
+ saveAnimations,
+ true
+ );
}
}
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/command/bdengine/BDEngineImportCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/bdengine/BDEngineImportCMD.java
new file mode 100644
index 00000000..9e85089f
--- /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,
+ true);
+ player.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Attempting to import project 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/command/group/GroupCMD.java b/plugin/src/main/java/net/donnypz/displayentityutils/command/group/GroupCMD.java
index 4c54b0e2..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
@@ -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);
@@ -44,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);
@@ -88,8 +89,8 @@ 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 anim list [page-number]", "List all saved display entity groups/models");
+ 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");
CMDUtils.sendCMD(sender, "/deu group selectplaced", "Select a group placed by a player's held item");
@@ -98,52 +99,71 @@ 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 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");
- 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 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