diff --git a/README.md b/README.md
index 8b9be4c5..4eeeae71 100644
--- a/README.md
+++ b/README.md
@@ -3,54 +3,57 @@
[Join the Discord Here!](https://discord.gg/k3wtdG5fRZ)
-DisplayEntityUtils is a extensive plugin designed to make the usage of Display/Interaction entities simplier. It also supports BDEngine, a modeling/animation engine designed for display entities without the need for resource packs or mods.
+DisplayEntityUtils makes handling customizable display entities simpler.
-## What you'll need
-### Dependencies:
+
+### Supported Entities
+- **Block Displays**
+- **Text Displays**
+- **Item Displays**
+- **Interactions**
+- **Mannequins**
+
+### Dependencies
- **[PacketEvents](https://modrinth.com/plugin/packetevents)**
- - **_This dependency allows for usage of packet-based display entities and animations, and is used throughout the plugin_**
-### BDEngine (Recommended)
-BDEngine is what you'll use to create models and animations that can be transferred into your game world. Info on BDEngine and the Block Display Place, the collection of BDEngine models and animations, can be found [HERE](block-display.com)
-> DISCLAIMER: I DO NOT OWN BDModels/BDEngine! ALL ISSUES AND SUGGESTIONS RELATED TO THAT PROJECT SHOULD BE BROUGHT TO THE OWNER, [ILLYSTRAY](https://illystray.com), IN THE BDENGINE DISCORD FOUND [HERE](https://discord.com/invite/VCeHfSd6Xa)
+### Supports BDEngine (Recommended)
+- **[BDEngine](block-display.com)** is a modeling and animation engine designed for display entities,
+ without the need for resource packs or mods
+- The created models and animations can be transferred into your game world and reused with **DisplayEntityUtils**.
+> DISCLAIMER: I DO NOT OWN **BDEngine**! ALL ISSUES AND SUGGESTIONS RELATED TO THAT PROJECT SHOULD BE BROUGHT TO THE OWNER, [ILLYSTRAY](https://illystray.com), IN THE BDENGINE DISCORD FOUND [HERE](https://discord.com/invite/VCeHfSd6Xa)
## What can DisplayEntityUtils do?
-- Manipulate Individual Display/Interaction entities
-- Manipulate Groups (BDEngine Models)
-- Manipulate every part (Display Entity) within a model
+- Manipulate Display Entities
- Manipulate Interaction Entities
+- Manipulate Mannequin Entities
+- Save and load BDEngine models
+- Manipulate entities in a model/group
- Integration with [Skript](https://github.com/SkriptLang/Skript)
[](http://skripthub.net/docs/?addon=DisplayEntityUtils)
-- Create and Play Animations through the plugin
-- Convert Datapack Animations from BDEngine (preferred over creating in-game)
-- Include Interaction Entities as part of groups/models
-- Execute commands through Interaction Entities
-- Retrieve models from BDEngine and spawn them in-game
+- Save/Load/Play BDEngine Animations
+- Interaction entity click commands
- Intergration with **MythicMobs**
- Create Animation State Machines
- Save groups and animations through Local Storage, MySQL, or MongoDB
- And so much more!
## What more can it do for API/Skript users?
-- Tools to manipulate Display and Interaction entity as individual entities
-- Show/Hide groups to/from certain players
+- Manipulate Display/Interaction/Mannequin entities
+- Manage group visibility for players
- Mount groups on entities (or vice-versa)
- Request models from BDEngine's website
-- Create packets based groups/entities
-- Play packet-based animations
-- Theres too many things to list!
+- Play animations
## Showcases
-- Display Controllers (Using Models as cosmetic equipment) - With(out) MythicMobs
+- ### Display Controllers (Using Models as cosmetic equipment) - With(out) MythicMobs
> 
-- Display Controllers (Creating Animation State Machines) - With(out) MythicMobs *(Credit goes to [Yegor_Mechanic](https://block-display.com/author/yegor_mechanic/) for all Dodo Bird Models/Animations)*
+- ### Display Controllers (Creating Animation State Machines) - With(out) MythicMobs
> 
-
-- API: Display Entities rotating smoothly with yaw changes, and Interaction entities pivoting around the center
-> 
+ >
+ > *Dodo Bird Model and Animations by [Yegor_Mechanic](https://block-display.com/author/yegor_mechanic/)*
## WIKI
### Access the wiki [HERE](https://github.com/PZDonny/DisplayEntityUtils/wiki)
diff --git a/api/pom.xml b/api/pom.xml
index 294e8694..51976498 100644
--- a/api/pom.xml
+++ b/api/pom.xml
@@ -6,7 +6,7 @@
net.donnypz.displayentityutilsdeu
- 3.3.8
+ 3.4.0api
@@ -45,6 +45,13 @@
7.3.5provided
+
+
+ com.jeff-media
+ custom-block-data
+ 2.2.4
+ compile
+
\ No newline at end of file
diff --git a/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java b/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java
index ecee2a1f..ae4cc008 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/DisplayAPI.java
@@ -27,6 +27,17 @@ public final class DisplayAPI {
static NamespacedKey spawnAnimationTypeKey;
static NamespacedKey spawnAnimationLoadMethodKey;
static NamespacedKey chunkPacketGroupsKey;
+ static NamespacedKey placeableGroupKey;
+ static NamespacedKey placeableGroupPermissionKey;
+ static NamespacedKey placeableGroupRespectFacingKey;
+ static NamespacedKey placeableGroupRespectBlockFace;
+ static NamespacedKey placeableGroupPlaceSounds;
+ static NamespacedKey placeableGroupBreakSounds;
+ static NamespacedKey placeableGroupDropItem;
+ static NamespacedKey placeableGroupPlacerBreaksOnly;
+ static NamespacedKey placeableGroupItemStack;
+ static NamespacedKey placeableGroupPlacer;
+ static NamespacedKey placeableGroupId;
static boolean isMythicMobsInstalled;
static boolean isLibsDisguisesInstalled;
@@ -85,10 +96,54 @@ private DisplayAPI(){}
return chunkPacketGroupsKey;
}
- /**
- * Used for older versions of DisplayEntityUtils Plugin
- * This will NEVER have to be called manually
- */
+ public static @NotNull NamespacedKey getPlaceableGroupKey(){
+ return placeableGroupKey;
+ }
+
+
+ public static @NotNull NamespacedKey getPlaceableGroupPermissionKey(){
+ return placeableGroupPermissionKey;
+ }
+
+ public static @NotNull NamespacedKey getPlaceableGroupRespectPlayerFacing(){
+ return placeableGroupRespectFacingKey;
+ }
+
+ public static @NotNull NamespacedKey getPlaceableGroupRespectBlockFace(){
+ return placeableGroupRespectBlockFace;
+ }
+
+ public static @NotNull NamespacedKey getPlaceableGroupPlaceSounds(){
+ return placeableGroupPlaceSounds;
+ }
+
+ public static @NotNull NamespacedKey getPlaceableGroupBreakSounds(){
+ return placeableGroupBreakSounds;
+ }
+
+ public static @NotNull NamespacedKey getPlaceableGroupPlacerBreaksOnly() {
+ return placeableGroupPlacerBreaksOnly;
+ }
+
+ public static @NotNull NamespacedKey getPlaceableGroupDropItem() {
+ return placeableGroupDropItem;
+ }
+
+
+ public static @NotNull NamespacedKey getPlaceableGroupItemStack(){
+ return placeableGroupItemStack;
+ }
+
+ public static @NotNull NamespacedKey getPlaceableGroupId(){
+ return placeableGroupId;
+ }
+
+ public static @NotNull NamespacedKey getPlaceableGroupPlacer() {
+ return placeableGroupPlacer;
+ }
+
+
+
@ApiStatus.Internal
public static String getLegacyPartTagPrefix(){
return legacyPartTagPrefix;
@@ -104,7 +159,7 @@ public static boolean isMythicMobsInstalled() {
/**
* Get whether LibsDisguises is installed on this server
- * @return true if MythicMobs is present
+ * @return true if LibsDisguises is present
*/
public static boolean isLibsDisguisesInstalled() {
return isLibsDisguisesInstalled;
diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/ChunkAddGroupInteractionsEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/ChunkAddGroupEntitiesEvent.java
similarity index 50%
rename from api/src/main/java/net/donnypz/displayentityutils/events/ChunkAddGroupInteractionsEvent.java
rename to api/src/main/java/net/donnypz/displayentityutils/events/ChunkAddGroupEntitiesEvent.java
index d1761d47..0a6c9bd7 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/events/ChunkAddGroupInteractionsEvent.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/events/ChunkAddGroupEntitiesEvent.java
@@ -3,28 +3,29 @@
import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup;
import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityPart;
import org.bukkit.Chunk;
-import org.bukkit.entity.Interaction;
+import org.bukkit.entity.Entity;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.HashSet;
/**
- * Called when a Chunk adds a {@link SpawnedDisplayEntityPart} of the type {@link SpawnedDisplayEntityPart.PartType#INTERACTION} to an already registered {@link SpawnedDisplayEntityGroup}.
+ * Called when a Chunk adds a non-display {@link SpawnedDisplayEntityPart} to an already registered {@link SpawnedDisplayEntityGroup}.
*
- * It is not guaranteed that every expected Interaction entity for a group will be added with this event, it may only be a partial amount.
+ * It is not guaranteed that every entity expected to be in a group will be added with this event, due to config settings and loaded chunks.
*/
-public class ChunkAddGroupInteractionsEvent extends Event {
+public class ChunkAddGroupEntitiesEvent extends Event {
private static final HandlerList handlers = new HandlerList();
SpawnedDisplayEntityGroup spawnedDisplayEntityGroup;
- Collection interactions;
+ Collection entities;
Chunk chunk;
- public ChunkAddGroupInteractionsEvent(SpawnedDisplayEntityGroup group, Collection interactions, Chunk chunk){
+ public ChunkAddGroupEntitiesEvent(SpawnedDisplayEntityGroup group, Collection entities, Chunk chunk){
this.spawnedDisplayEntityGroup = group;
- this.interactions = interactions;
+ this.entities = entities;
this.chunk = chunk;
}
@@ -32,24 +33,24 @@ public ChunkAddGroupInteractionsEvent(SpawnedDisplayEntityGroup group, Collectio
* Get the {@link SpawnedDisplayEntityGroup} involved in this event
* @return a group
*/
- public SpawnedDisplayEntityGroup getGroup() {
+ public @NotNull SpawnedDisplayEntityGroup getGroup() {
return spawnedDisplayEntityGroup;
}
/**
- * Get the {@link Interaction} entities involved in this event
- *
Use {@link SpawnedDisplayEntityPart#getPart(Interaction)} to get the entities as parts
- * @return a collection of {@link Interaction} entities.
+ * Get the entities involved in this event
+ *
Use {@link SpawnedDisplayEntityPart#getEntity()} to get the entities as parts
+ * @return a collection of entities.
*/
- public Collection getInteractions() {
- return new HashSet<>(interactions);
+ public @NotNull Collection getEntities() {
+ return new HashSet<>(entities);
}
/**
* Get the chunk involved in this event
* @return a chunk
*/
- public Chunk getChunk() {
+ public @NotNull Chunk getChunk() {
return chunk;
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/ChunkRegisterGroupEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/ChunkRegisterGroupEvent.java
index fe289bf5..6cedd6e7 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/events/ChunkRegisterGroupEvent.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/events/ChunkRegisterGroupEvent.java
@@ -7,9 +7,10 @@
/**
* Called when a Chunk loads a {@link SpawnedDisplayEntityGroup} and registers it.
- *
It is not guaranteed that all Interaction entities in the given group will be added at group registration time.
+ *
It is not guaranteed that all non-display entities in the given group will be added at group registration time.
*
- * In rare cases, it is recommended to use the {@link ChunkAddGroupInteractionsEvent} if Interaction entities are expected, but are infrequently (or not at all) added found in a group, through this event.
+ * In rare cases, it is recommended to use the {@link ChunkAddGroupEntitiesEvent} if non-display entities are expected,
+ * but are infrequently (or not at all) added found in a group, through this event.
*/
public class ChunkRegisterGroupEvent extends Event {
private static final HandlerList handlers = new HandlerList();
@@ -38,8 +39,6 @@ public Chunk getChunk() {
return chunk;
}
-
-
@Override
public HandlerList getHandlers() {
return handlers;
diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/GroupSpawnedEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/GroupSpawnedEvent.java
index 95de4314..2fa33c7a 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/events/GroupSpawnedEvent.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/events/GroupSpawnedEvent.java
@@ -1,9 +1,15 @@
package net.donnypz.displayentityutils.events;
+import net.donnypz.displayentityutils.managers.PlaceableGroupManager;
import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup;
+import net.donnypz.displayentityutils.utils.DisplayEntities.PacketDisplayEntityGroup;
import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup;
+import org.bukkit.Location;
+import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
/**
@@ -54,7 +60,18 @@ public enum SpawnReason{
*/
PLAYER_SENT_CHUNK,
PLAYER_SENT_PASSENGER_GROUP,
+ /**
+ * This is only applicable to the {@link PacketDisplayEntityGroup} placed by an item, through {@link PlaceableGroupManager#spawnGroup(ItemStack, Location, Player)} or similar
+ */
+ @ApiStatus.Internal
+ ITEMSTACK,
+ /**
+ * This is only applicable to the {@link PacketDisplayEntityGroup} placed by an item, through {@link PlaceableGroupManager#spawnGroup(ItemStack, Location, Player)} or similar
+ */
+ @ApiStatus.Internal
+ CHUNK_LOAD_PLACED,
SKRIPT,
+ @ApiStatus.Internal
INTERNAL;
}
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/ItemPlaceGroupEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/ItemPlaceGroupEvent.java
new file mode 100644
index 00000000..61ffd54e
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/events/ItemPlaceGroupEvent.java
@@ -0,0 +1,62 @@
+package net.donnypz.displayentityutils.events;
+
+import net.donnypz.displayentityutils.managers.PlaceableGroupData;
+import net.donnypz.displayentityutils.utils.DisplayEntities.ActiveGroup;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Called when a player places an {@link ActiveGroup} using an item that
+ * has an assigned group with {@link PlaceableGroupData}.
+ */
+public class ItemPlaceGroupEvent extends Event {
+ private static final HandlerList handlers = new HandlerList();
+ ActiveGroup> activeGroup;
+ ItemStack itemStack;
+ Player player;
+
+ public ItemPlaceGroupEvent(@NotNull ActiveGroup> group, @NotNull ItemStack itemStack, @Nullable Player player){
+ super(!Bukkit.isPrimaryThread());
+ this.activeGroup = group;
+ this.itemStack = itemStack;
+ this.player = player;
+ }
+
+ /**
+ * Get the {@link ActiveGroup} involved in this event.
+ * @return a {@link ActiveGroup}
+ */
+ public @NotNull ActiveGroup> getGroup() {
+ return activeGroup;
+ }
+
+ /**
+ * Get the {@link ItemStack} invovled in this event.
+ * @return an item
+ */
+ public @NotNull ItemStack getItemStack() {
+ return itemStack;
+ }
+
+ /**
+ * Get the player involved in this event
+ * @return a player
+ */
+ public @Nullable Player getPlayer() {
+ return player;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/PartTranslateEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/PartTranslateEvent.java
index 80f4484c..d0aac4fa 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/events/PartTranslateEvent.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/events/PartTranslateEvent.java
@@ -11,12 +11,13 @@
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import org.bukkit.util.Transformation;
+import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
- * Called when a {@link Display} or {@link Interaction} changes its translation using methods within the {@link DisplayGroupManager}.
- * Can be cancelled
+ * Called when an entity changes its translation through the {@link DisplayUtils#translate(Display, Vector, double, int, int)} or similar methods.
+ * Can be cancelled
*/
public final class PartTranslateEvent extends Event implements Cancellable {
@@ -28,10 +29,6 @@ public final class PartTranslateEvent extends Event implements Cancellable {
Transformation oldTransformation;
Transformation newTransformation;
- /**
- * Called when a Display Entity or Interaction Entity changes its translation through the DisplayGroupManager.
- * Can be cancelled
- */
public PartTranslateEvent(@NotNull Entity entity, Location destination, Transformation oldTransformation, Transformation newTransformation){
this.entity = entity;
this.destination = destination;
@@ -61,12 +58,7 @@ public boolean isDisplay(){
* @return a {@link SpawnedDisplayEntityPart}, null if this entity is not a part
*/
public @Nullable SpawnedDisplayEntityPart getEntityAsSpawnedDisplayEntityPart(){
- if (entity instanceof Interaction i){
- return SpawnedDisplayEntityPart.getPart(i);
- }
- else{
- return SpawnedDisplayEntityPart.getPart((Display) entity);
- }
+ return SpawnedDisplayEntityPart.getPart(entity);
}
/**
@@ -93,19 +85,12 @@ public Location getDestination() {
return newTransformation;
}
-
-
/**
* Get the tag of this entity's group.
* @return group tag, null if not grouped
*/
public String getGroupTag(){
- if (entity instanceof Interaction i){
- return DisplayUtils.getGroupTag(i);
- }
- else{
- return DisplayUtils.getGroupTag((Display) entity);
- }
+ return DisplayUtils.getGroupTag(entity);
}
@Override
diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/PlacedGroupBreakEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/PlacedGroupBreakEvent.java
new file mode 100644
index 00000000..71fb9732
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/events/PlacedGroupBreakEvent.java
@@ -0,0 +1,68 @@
+package net.donnypz.displayentityutils.events;
+
+import net.donnypz.displayentityutils.utils.DisplayEntities.PacketDisplayEntityGroup;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * Called when a group placed by an item is broken by a player. Cancellable
+ */
+public class PlacedGroupBreakEvent extends Event implements Cancellable {
+
+ private static final HandlerList handlers = new HandlerList();
+ boolean cancelled = false;
+
+ Block block;
+ PacketDisplayEntityGroup packetDisplayEntityGroup;
+ Player player;
+
+ public PlacedGroupBreakEvent(Block block, PacketDisplayEntityGroup packetDisplayEntityGroup, Player player){
+ this.block = block;
+ this.packetDisplayEntityGroup = packetDisplayEntityGroup;
+ this.player = player;
+ }
+
+
+ /**
+ * Get the block involved in this event
+ * @return a block
+ */
+ public Block getBlock() {
+ return block;
+ }
+
+ /**
+ * Get the {@link PacketDisplayEntityGroup} involved in this event
+ * @return a packet-based group
+ */
+ public PacketDisplayEntityGroup getGroup() {
+ return packetDisplayEntityGroup;
+ }
+
+ /**
+ * Get the player involved in this event
+ * @return the player
+ */
+ public Player getPlayer() {
+ return player;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return cancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.cancelled = cancel;
+ }
+
+ @Override
+ public @NotNull HandlerList getHandlers() {
+ return handlers;
+ }
+}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/PreItemPlaceGroupEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/PreItemPlaceGroupEvent.java
new file mode 100644
index 00000000..d454c1db
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/events/PreItemPlaceGroupEvent.java
@@ -0,0 +1,72 @@
+package net.donnypz.displayentityutils.events;
+
+import net.donnypz.displayentityutils.managers.PlaceableGroupManager;
+import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+import org.bukkit.event.Cancellable;
+import org.bukkit.event.Event;
+import org.bukkit.event.HandlerList;
+import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+/**
+ * Called before the stored {@link DisplayEntityGroup} on an item can be spawned when a player places the item's block
+ */
+public class PreItemPlaceGroupEvent extends Event implements Cancellable {
+ private static final HandlerList handlers = new HandlerList();
+ ItemStack itemStack;
+ Player player;
+
+ private boolean isCancelled = false;
+
+ public PreItemPlaceGroupEvent(@NotNull ItemStack itemStack, @Nullable Player player){
+ super(!Bukkit.isPrimaryThread());
+ this.itemStack = itemStack;
+ this.player = player;
+ }
+
+ /**
+ * Get the group tag of the group involved in this event.
+ * @return the group tag or null
+ */
+ public @Nullable String getGroupTag() {
+ return PlaceableGroupManager.getGroupTag(itemStack);
+ }
+
+ /**
+ * Get the {@link ItemStack} involved in this event.
+ * @return an {@link ItemStack}
+ */
+ public @NotNull ItemStack getItemStack() {
+ return itemStack;
+ }
+
+ /**
+ * Get the player involved in this event
+ * @return a player
+ */
+ public @Nullable Player getPlayer() {
+ return player;
+ }
+
+ @Override
+ public HandlerList getHandlers() {
+ return handlers;
+ }
+
+ public static HandlerList getHandlerList() {
+ return handlers;
+ }
+
+ @Override
+ public boolean isCancelled() {
+ return isCancelled;
+ }
+
+ @Override
+ public void setCancelled(boolean cancel) {
+ this.isCancelled = cancel;
+ }
+}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/events/PrePacketGroupCreateEvent.java b/api/src/main/java/net/donnypz/displayentityutils/events/PrePacketGroupCreateEvent.java
index a542f3b6..80d51c29 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/events/PrePacketGroupCreateEvent.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/events/PrePacketGroupCreateEvent.java
@@ -3,6 +3,7 @@
import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup;
import net.donnypz.displayentityutils.utils.DisplayEntities.GroupSpawnSettings;
import net.donnypz.displayentityutils.utils.DisplayEntities.PacketDisplayEntityGroup;
+import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.event.Cancellable;
import org.bukkit.event.Event;
@@ -23,6 +24,7 @@ public class PrePacketGroupCreateEvent extends Event implements Cancellable {
private boolean isCancelled = false;
public PrePacketGroupCreateEvent(DisplayEntityGroup group, GroupSpawnedEvent.SpawnReason spawnReason){
+ super(!Bukkit.isPrimaryThread());
this.displayEntityGroup = group;
this.spawnReason = spawnReason;
}
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 dcba35bc..ae3ac37e 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/managers/DEUUser.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/managers/DEUUser.java
@@ -36,12 +36,13 @@ public class DEUUser {
private AnimationParticleBuilder particleBuilder;
private final Location[] pointPositions = new Location[3];
private final Set trackedPacketEntities = Collections.newSetFromMap(new ConcurrentHashMap<>());
+ private Integer armorEditMannequinEntityId;
private PreAnimationCameraData preAnimationCameraData;
private final Object animationCameraLock = new Object();
private UUID cameraPlayerUUID;
-
+ private boolean placedGroupBreakMode = false;
private DEUUser(UUID userUUID){
@@ -88,6 +89,21 @@ public boolean unsuppressIfEqual(int entityId, @NotNull Vector3f vector3f) {
return false;
}
+ @ApiStatus.Internal
+ public boolean isEditingMannequinArmor(){
+ return armorEditMannequinEntityId != null;
+ }
+
+ @ApiStatus.Internal
+ public int getEditingMannequin(){
+ return armorEditMannequinEntityId;
+ }
+
+ @ApiStatus.Internal
+ public void setEditingMannequinArmor(Integer armorEditMannequinEntityId){
+ this.armorEditMannequinEntityId = armorEditMannequinEntityId;
+ }
+
/**
* Set the selected {@link ActiveGroup} of a user
* @param activeGroup the group
@@ -306,6 +322,22 @@ public boolean isInAnimationCamera(@NotNull UUID cameraPlayerUUID){
}
}
+ /**
+ * Set whether this user should be able to break any placed group, regardless of who placed it
+ * @param breakMode whether the break mode should be enabled
+ */
+ public void setPlacedGroupBreakMode(boolean breakMode){
+ this.placedGroupBreakMode = breakMode;
+ }
+
+ /**
+ * Get whether this user can break any placed group, regardless of who placed it
+ * @return a boolean
+ */
+ public boolean isInPlacedGroupBreakMode(){
+ return this.placedGroupBreakMode;
+ }
+
/**
* Check if this user is tracking a {@link PacketDisplayEntityPart}
* @param part the {@link PacketDisplayEntityPart}
@@ -320,6 +352,7 @@ public int getTrackedPacketEntityCount(){
}
public @Nullable ActiveGroup> getSelectedGroup(){
+ if (selectedGroup != null && !selectedGroup.isRegistered()) selectedGroup = null;
return selectedGroup;
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationInputStream.java b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationInputStream.java
deleted file mode 100644
index 43547b59..00000000
--- a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationInputStream.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package net.donnypz.displayentityutils.managers;
-
-import net.donnypz.displayentityutils.utils.DisplayEntities.saved.OldSound;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.ObjectStreamClass;
-
-class DisplayAnimationInputStream extends DisplayObjectInputStream{
- DisplayAnimationInputStream(InputStream in) throws IOException {
- super(in);
- }
-
- @Override
- protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
- ObjectStreamClass desc = super.readClassDescriptor();
- if (desc.getName().equals("org.bukkit.Sound")){ //Because of old sound HashMaps using an enum
- //if (VersionUtils.is_1_21_2){
- return ObjectStreamClass.lookup(OldSound.class);
- //}
- }
- return desc;
- }
-}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationManager.java b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationManager.java
index c2e92657..28675070 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationManager.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayAnimationManager.java
@@ -252,7 +252,7 @@ private static void attemptCacheAnimation(String tag, SpawnedDisplayAnimation an
}
try(ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
GZIPInputStream gzipInputStream = new GZIPInputStream(byteStream);
- ObjectInputStream objIn = new DisplayAnimationInputStream(gzipInputStream)
+ ObjectInputStream objIn = new DisplayObjectInputStream(gzipInputStream)
){
DisplayAnimation anim = (DisplayAnimation) objIn.readObject();
@@ -277,7 +277,7 @@ private static void attemptCacheAnimation(String tag, SpawnedDisplayAnimation an
//Not Compressed (Will be an older file version, before gzip compression)
catch (ZipException z){
try(ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
- ObjectInputStream objIn = new DisplayAnimationInputStream(byteStream)
+ ObjectInputStream objIn = new DisplayObjectInputStream(byteStream)
){
DisplayAnimation anim = (DisplayAnimation) objIn.readObject();
anim.adaptOldSounds();
@@ -384,7 +384,7 @@ private static DisplayAnimation getAnimationFromJson(JsonObject jsonObject){
DisplayGroupManager.deserializeJsonElement(jsonObject);
jsonObject.remove(DisplayGroupManager.PLUGIN_VERSION_FIELD);
- return DEGJSONAdapter
+ return DEUJSONAdapter
.GSON
.fromJson(jsonObject, DisplayAnimation.class);
}
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 b995b99d..dc6671d7 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayGroupManager.java
@@ -136,7 +136,7 @@ public static void removeSpawnedGroup(SpawnedDisplayEntityGroup spawnedGroup, bo
Chunk mainChunk = spawnedGroup.getLocation().getChunk();
ticketChunk(mainChunk, chunks);
for (SpawnedDisplayEntityPart part : spawnedGroup.getParts()) {
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){ //Chunk may be different from main chunk
+ if (!part.isDisplay()){ //Chunk may be different from main chunk
Entity e = part.getEntity();
Chunk c = e.getChunk();
if (c != mainChunk){
@@ -208,7 +208,7 @@ public static boolean saveDisplayEntityGroupJson(@NotNull DisplayEntityGroup dis
saveFile.createNewFile();
FileWriter fileWriter = new FileWriter(saveFile);
- Gson gson = DEGJSONAdapter.GSON;
+ Gson gson = DEUJSONAdapter.GSON;
JsonElement jsonEl = gson.toJsonTree(displayEntityGroup);
JsonObject jsonObj = jsonEl.getAsJsonObject();
@@ -252,22 +252,6 @@ static void serializeJsonElement(JsonElement el) {
}
}
-// if (obj.has(PART_TAG_FIELD)){
-// JsonArray arr = obj.getAsJsonArray(PDC_FIELD);
-// if (arr != null){
-// byte[] bytes = new byte[arr.size()];
-// ItemStack stick = new ItemStack(Material.STICK);
-// PersistentDataContainer pdc = stick.getItemMeta().getPersistentDataContainer();
-// try{
-// pdc.readFromBytes(bytes);
-// List tags = pdc.get(DisplayAPI.getPartPDCTagKey(), PersistentDataType.LIST.strings());
-// obj.add(PART_TAG_FIELD, gson.toJsonTree(tags));
-// }
-// catch(IOException e){}
-// }
-// }
-
-
for (Map.Entry entry : obj.entrySet()) {
serializeJsonElement(entry.getValue());
}
@@ -488,7 +472,7 @@ private static DisplayEntityGroup getGroupFromJson(JsonObject jsonObject){
deserializeJsonElement(jsonObject);
jsonObject.remove(PLUGIN_VERSION_FIELD);
- return DEGJSONAdapter
+ return DEUJSONAdapter
.GSON
.fromJson(jsonObject, DisplayEntityGroup.class);
}
@@ -507,8 +491,8 @@ private static DisplayEntityGroup getGroupFromJson(JsonObject jsonObject){
* Get the {@link GroupResult} of a display entity containing its {@link SpawnedDisplayEntityGroup}, if applicable.
*
* If a group is created as a result of this, {@link GroupRegisteredEvent} will be called
- *
- * @param displayEntity The display entity within a group
+ * @param displayEntity The display entity that's in a group
+ * @return a {@link GroupResult} or null
*/
public static @Nullable GroupResult getSpawnedGroup(@NotNull Display displayEntity) {
//Check for already registered group
@@ -548,26 +532,38 @@ else if (displayEntity.getPassengers().isEmpty()) {
/**
- * Get a Spawned Display Entity Group through an interaction entity
- *
- * @param interaction The interaction entity part of a display entity group
- * @param radius The radius to check for a spawned display entity group
- * @return SpawnedDisplayEntityGroup containing the interaction entity. Null if not found.
+ * Get a {@link SpawnedDisplayEntityGroup} through an Interaction entity
+ * @param interaction The interaction
+ * @param radius The radius to search for the group
+ * @return a {@link SpawnedDisplayEntityGroup} containing the interaction. Null if not found.
*/
- public static SpawnedDisplayEntityGroup getSpawnedGroup(@NotNull Interaction interaction, double radius) {
+ public static @Nullable SpawnedDisplayEntityGroup getSpawnedGroup(@NotNull Interaction interaction, double radius){ //Keep this just to prevent breakage
+ return getSpawnedGroup((Entity) interaction, radius);
+ }
+
+
+ /**
+ * Get a {@link SpawnedDisplayEntityGroup} through an eligible part entity
+ * @param entity The entity that's in a group
+ * @param radius The radius to search for the group
+ * @return a {@link SpawnedDisplayEntityGroup} containing the entity. Null if not found.
+ */
+ public static @Nullable SpawnedDisplayEntityGroup getSpawnedGroup(@NotNull Entity entity, double radius) {
+ if (!DisplayUtils.isPartEntity(entity)) return null;
+
//Check for existing group
- SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(interaction);
+ SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(entity);
if (part != null && part.getGroup() != null) {
return part.getGroup();
}
//Get Nearby Groups
- List results = getSpawnedGroupsNearLocation(interaction.getLocation(), radius);
+ List results = getSpawnedGroupsNearLocation(entity.getLocation(), radius);
if (results.isEmpty()){
return null;
}
//Check if Interaction is part of group
- part = SpawnedDisplayEntityPart.getPart(interaction);
+ part = SpawnedDisplayEntityPart.getPart(entity);
return part == null ? null : part.getGroup();
}
@@ -635,7 +631,7 @@ public static SpawnedDisplayEntityGroup getSpawnedGroup(@NotNull Interaction int
continue;
}
if (addInteractions){
- result.group().addMissingInteractionEntities(DisplayConfig.getMaximumInteractionSearchRange());
+ result.group().addMissingEntities(DisplayConfig.getMaximumInteractionSearchRange());
}
results.add(result);
}
@@ -709,9 +705,15 @@ static void addPersistentPacketGroupSilent(@NotNull PacketDisplayEntityGroup gro
id = 1;
}
else{
- id = gson.fromJson(list.getLast(), PersistentPacketGroup.class).id+1;
+ PersistentPacketGroup ppg = gson.fromJson(list.getLast(), PersistentPacketGroup.class);
+ if (ppg == null){
+ id = list.size()+1;
+ }
+ else{
+ id = gson.fromJson(list.getLast(), PersistentPacketGroup.class).id+1;
+ }
}
- PersistentPacketGroup cpg = PersistentPacketGroup.create(id, location, displayEntityGroup, group.isAutoShow());
+ PersistentPacketGroup cpg = PersistentPacketGroup.create(id, location, displayEntityGroup, group.isAutoShow(), group.isPlaced());
if (cpg == null) return;
String json = gson.toJson(cpg);
@@ -735,7 +737,7 @@ public static PacketDisplayEntityGroup addPersistentPacketGroup(@NotNull Locatio
else{
id = gson.fromJson(list.getLast(), PersistentPacketGroup.class).id+1;
}
- PersistentPacketGroup cpg = PersistentPacketGroup.create(id, location, displayEntityGroup, autoShow);
+ PersistentPacketGroup cpg = PersistentPacketGroup.create(id, location, displayEntityGroup, autoShow, false);
if (cpg == null) return null;
String json = gson.toJson(cpg);
@@ -764,7 +766,7 @@ public static PacketDisplayEntityGroup addPersistentPacketGroup(@NotNull Locatio
PacketDisplayEntityGroup pdeg = displayEntityGroup.createPacketGroup(location, spawnReason, true, settings);
displayEntityGroup = pdeg.toDisplayEntityGroup();
- PersistentPacketGroup cpg = PersistentPacketGroup.create(id, location, displayEntityGroup, pdeg.isAutoShow());
+ PersistentPacketGroup cpg = PersistentPacketGroup.create(id, location, displayEntityGroup, pdeg.isAutoShow(), false);
if (cpg != null){
String json = gson.toJson(cpg);
list.add(json);
@@ -864,9 +866,17 @@ public static void removePersistentPacketGroups(@NotNull Chunk chunk){
@ApiStatus.Internal
public static void spawnPersistentPacketGroups(@NotNull Chunk chunk){
List list = getChunkList(chunk.getPersistentDataContainer());
- for (String json : list){
+ Iterator i = list.iterator();
+
+ while (i.hasNext()){
+ String json = i.next();
PersistentPacketGroup cpg = gson.fromJson(json, PersistentPacketGroup.class);
- cpg.spawn(chunk).setPersistentIds(cpg.id, chunk);
+ if (cpg == null){
+ i.remove();
+ }
+ else{
+ cpg.spawn(chunk).setPersistentIds(cpg.id, chunk);
+ }
}
}
@@ -899,10 +909,11 @@ static class PersistentPacketGroup {
String groupBase64;
String groupTag;
boolean autoShow = true; //Don't change
+ boolean isPlaced = false;
private PersistentPacketGroup(){}
- static PersistentPacketGroup create(int id, Location location, DisplayEntityGroup group, boolean autoShow){
+ static PersistentPacketGroup create(int id, Location location, DisplayEntityGroup group, boolean autoShow, boolean isPlaced){
PersistentPacketGroup cpg = new PersistentPacketGroup();
cpg.id = id;
cpg.x = location.x();
@@ -912,6 +923,7 @@ static PersistentPacketGroup create(int id, Location location, DisplayEntityGrou
cpg.pitch = location.getPitch();
cpg.groupTag = group.getTag();
cpg.autoShow = autoShow;
+ cpg.isPlaced = isPlaced;
cpg.setGroup(group);
return (cpg.groupBase64 == null) ? null : cpg;
}
@@ -961,7 +973,12 @@ public PacketDisplayEntityGroup spawn(Chunk chunk){
Location spawnLoc = getLocation(chunk);
DisplayEntityGroup g = getGroup();
if (spawnLoc == null || g == null) return null;
- return g.createPacketGroup(spawnLoc, GroupSpawnedEvent.SpawnReason.INTERNAL, true, autoShow);
+ if (isPlaced){
+ return g.createPacketGroup(spawnLoc, GroupSpawnedEvent.SpawnReason.CHUNK_LOAD_PLACED, true, autoShow);
+ }
+ else{
+ return g.createPacketGroup(spawnLoc, GroupSpawnedEvent.SpawnReason.INTERNAL, true, autoShow);
+ }
}
}
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayObjectInputStream.java b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayObjectInputStream.java
index 86fa2287..c3921046 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayObjectInputStream.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/managers/DisplayObjectInputStream.java
@@ -1,5 +1,8 @@
package net.donnypz.displayentityutils.managers;
+import net.donnypz.displayentityutils.utils.DisplayEntities.DEUSound;
+import net.donnypz.displayentityutils.utils.DisplayEntities.saved.OldSound;
+
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
@@ -7,7 +10,10 @@
class DisplayObjectInputStream extends ObjectInputStream {
- private static final String oldPackage = "com.pzdonny";
+ private static final String OLD_PACKAGE = "com.pzdonny";
+ private static final String NEW_PACKAGE = "net.donnypz";
+ private static final String BUKKIT_SOUND_ENUM = "org.bukkit.Sound";
+ private static final String ANIMATION_SOUND = "AnimationSound";
DisplayObjectInputStream(InputStream in) throws IOException {
super(in);
}
@@ -17,10 +23,22 @@ protected Class> resolveClass(ObjectStreamClass desc) throws ClassNotFoundExce
//Convert Old Serialized Objects with new package name
String name = desc.getName();
- if (name.startsWith(oldPackage)) {
- name = "net.donnypz" + name.substring(oldPackage.length());
+ if (name.startsWith(OLD_PACKAGE)) {
+ name = NEW_PACKAGE + name.substring(OLD_PACKAGE.length());
}
-
return Class.forName(name);
}
+
+ @Override
+ protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException {
+ ObjectStreamClass desc = super.readClassDescriptor();
+ String name = desc.getName();
+ if (name.equals(BUKKIT_SOUND_ENUM)){ //Because of old sound HashMaps using an enum
+ return ObjectStreamClass.lookup(OldSound.class);
+ }
+ if (name.endsWith(ANIMATION_SOUND)){
+ return ObjectStreamClass.lookup(DEUSound.class);
+ }
+ return desc;
+ }
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/PlaceableGroupData.java b/api/src/main/java/net/donnypz/displayentityutils/managers/PlaceableGroupData.java
new file mode 100644
index 00000000..b8a9d5f7
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/managers/PlaceableGroupData.java
@@ -0,0 +1,143 @@
+package net.donnypz.displayentityutils.managers;
+
+import net.donnypz.displayentityutils.DisplayAPI;
+import net.donnypz.displayentityutils.utils.DisplayEntities.DEUSound;
+import net.donnypz.displayentityutils.utils.DisplayEntities.DisplayEntityGroup;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.ArrayList;
+
+
+/**
+ * Assign a {@link DisplayEntityGroup} that will spawn where an {@link ItemStack}, of a block type, is placed.
+ */
+public class PlaceableGroupData {
+
+ String groupTag;
+ String permission;
+ boolean respectPlayerFacing = true;
+ boolean respectBlockFace = true;
+ boolean dropItemOnBreak = true;
+ boolean placerBreaksOnly = true;
+ ArrayList placeSounds = new ArrayList<>();
+ ArrayList breakSounds = new ArrayList<>();
+
+
+ public PlaceableGroupData(@NotNull String groupTag) {
+ this.groupTag = groupTag;
+ }
+
+ /**
+ * Set the group that should be spawned when the block is placed
+ * @param groupTag the group's tag
+ * @return this
+ */
+ public PlaceableGroupData setGroupTag(@NotNull String groupTag){
+ this.groupTag = groupTag;
+ return this;
+ }
+
+ /**
+ * Set the required permission to spawn the group when the block is placed
+ * @param permission the permission
+ * @return this
+ */
+ public PlaceableGroupData setPermission(@Nullable String permission) {
+ this.permission = permission;
+ return this;
+ }
+
+
+ /**
+ * Set whether the group should spawn with respect to the player's facing direction. True by default
+ * @param respectPlayerFacing whether the group should respect the player's facing direction
+ * @return this
+ */
+ public PlaceableGroupData setRespectPlayerFacing(boolean respectPlayerFacing) {
+ this.respectPlayerFacing = respectPlayerFacing;
+ return this;
+ }
+
+ /**
+ * Set whether the group associated with the given item should spawn with respect to the block face its placed on. True by default
+ * @param respectBlockFace whether the group should respect the block face its placed on
+ * @return this
+ */
+ public PlaceableGroupData setRespectBlockFace(boolean respectBlockFace) {
+ this.respectBlockFace = respectBlockFace;
+ return this;
+ }
+
+ /**
+ * Set whether the item used to place the group should be dropped when the group is destroyed
+ * @param dropItemOnBreak
+ * @return this
+ */
+ public PlaceableGroupData setDropItemOnBreak(boolean dropItemOnBreak){
+ this.dropItemOnBreak = dropItemOnBreak;
+ return this;
+ }
+
+ /**
+ * Set whether only the placer of the group is the one who can break it
+ * @param placerBreaksOnly
+ * @return this
+ */
+ public PlaceableGroupData setPlacerBreaksOnly(boolean placerBreaksOnly){
+ this.placerBreaksOnly = placerBreaksOnly;
+ return this;
+ }
+
+
+ /**
+ * Add a sound to play when the group is placed or removed/broken
+ * @param sound the sound
+ * @param isPlace whether the sound should play when placed or removed/broken
+ */
+ public PlaceableGroupData addSound(@NotNull DEUSound sound, boolean isPlace){
+ if (isPlace){
+ placeSounds.add(sound);
+ }
+ else{
+ breakSounds.add(sound);
+ }
+ return this;
+ }
+
+ /**
+ * Apply the data to a provided itemstack, spawning the group when placed
+ * @param itemStack
+ * @throws IllegalArgumentException if the itemstack is not of a block type
+ */
+ public void apply(@NotNull ItemStack itemStack){
+ if (!PlaceableGroupManager.isValidItem(itemStack)){
+ throw new IllegalArgumentException("The provided ItemStack is not of a block type");
+ }
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+
+ pdc.set(DisplayAPI.getPlaceableGroupKey(), PersistentDataType.STRING, groupTag);
+
+ if (permission != null){
+ pdc.set(DisplayAPI.getPlaceableGroupPermissionKey(), PersistentDataType.STRING, permission);
+ }
+
+ pdc.set(DisplayAPI.getPlaceableGroupRespectPlayerFacing(), PersistentDataType.BOOLEAN, respectPlayerFacing);
+ pdc.set(DisplayAPI.getPlaceableGroupRespectBlockFace(), PersistentDataType.BOOLEAN, respectBlockFace);
+
+
+ pdc.set(DisplayAPI.getPlaceableGroupPlacerBreaksOnly(), PersistentDataType.BOOLEAN, placerBreaksOnly);
+ pdc.set(DisplayAPI.getPlaceableGroupDropItem(), PersistentDataType.BOOLEAN, dropItemOnBreak);
+
+ pdc.set(DisplayAPI.getPlaceableGroupPlaceSounds(), PersistentDataType.BYTE_ARRAY,
+ PlaceableGroupManager.writeSoundList(placeSounds));
+ pdc.set(DisplayAPI.getPlaceableGroupBreakSounds(), PersistentDataType.BYTE_ARRAY,
+ PlaceableGroupManager.writeSoundList(breakSounds));
+ itemStack.setItemMeta(meta);
+ }
+}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/managers/PlaceableGroupManager.java b/api/src/main/java/net/donnypz/displayentityutils/managers/PlaceableGroupManager.java
new file mode 100644
index 00000000..01915938
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/managers/PlaceableGroupManager.java
@@ -0,0 +1,591 @@
+package net.donnypz.displayentityutils.managers;
+
+import com.jeff_media.customblockdata.CustomBlockData;
+import net.donnypz.displayentityutils.DisplayAPI;
+import net.donnypz.displayentityutils.events.GroupSpawnedEvent;
+import net.donnypz.displayentityutils.events.ItemPlaceGroupEvent;
+import net.donnypz.displayentityutils.events.PreItemPlaceGroupEvent;
+import net.donnypz.displayentityutils.utils.DisplayEntities.*;
+import org.bukkit.*;
+import org.bukkit.block.Block;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.jetbrains.annotations.ApiStatus;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.joml.Quaternionf;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.List;
+import java.util.UUID;
+
+public final class PlaceableGroupManager {
+
+ private PlaceableGroupManager(){}
+
+ /**
+ * Set the group that will spawn where an itemstack of a block type is placed.
+ * @param itemStack the itemstack
+ * @param group the group
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void setGroup(@NotNull ItemStack itemStack, @NotNull DisplayEntityGroup group){
+ setGroup(itemStack, group.getTag());
+ }
+
+ /**
+ * Set the group that will spawn where an itemstack of a block type is placed.
+ * @param itemStack the itemstack
+ * @param groupTag the group's tag
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied through a {@link PlaceableGroupData}
+ */
+ public static void setGroup(@NotNull ItemStack itemStack, @NotNull String groupTag){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ pdc.set(DisplayAPI.getPlaceableGroupKey(), PersistentDataType.STRING, groupTag);
+ itemStack.setItemMeta(meta);
+ }
+
+
+ /**
+ * Set the permission a player needs to place an itemstack's assigned group. Set the permission to {@code null} to unset it
+ * @param itemStack the item
+ * @param placePermission the permission
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void setPlacePermission(@NotNull ItemStack itemStack, @Nullable String placePermission){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ if (placePermission == null){
+ pdc.remove(DisplayAPI.getPlaceableGroupPermissionKey());
+ }
+ else{
+ pdc.set(DisplayAPI.getPlaceableGroupPermissionKey(), PersistentDataType.STRING, placePermission);
+ }
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Set whether the group associated with the given item should spawn with respect to the player's facing direction
+ * @param itemStack the item
+ * @param respect whether the group should respect the player's facing direction
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void setRespectPlayerFacing(@NotNull ItemStack itemStack, boolean respect){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ pdc.set(DisplayAPI.getPlaceableGroupRespectPlayerFacing(), PersistentDataType.BOOLEAN, respect);
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Set whether the group associated with the given item should spawn with respect to the block face its placed on
+ * @param itemStack the item
+ * @param respect whether the group should respect the block face its placed on
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void setRespectBlockFace(@NotNull ItemStack itemStack, boolean respect){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ pdc.set(DisplayAPI.getPlaceableGroupRespectBlockFace(), PersistentDataType.BOOLEAN, respect);
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Set whether the item used to place the group should be dropped when the group is broken
+ * @param itemStack the item
+ * @param dropItem
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void setDropItemOnBreak(@NotNull ItemStack itemStack, boolean dropItem){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ pdc.set(DisplayAPI.getPlaceableGroupDropItem(), PersistentDataType.BOOLEAN, dropItem);
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Set whether only the placer of the group can break the group
+ * @param itemStack the item
+ * @param placerBreaksOnly
+ */
+ public static void setPlacerBreaksOnly(@NotNull ItemStack itemStack, boolean placerBreaksOnly){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ pdc.set(DisplayAPI.getPlaceableGroupPlacerBreaksOnly(), PersistentDataType.BOOLEAN, placerBreaksOnly);
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Add a sound to play when the given item's assigned group is placed or removed/broken
+ * @param itemStack the sound's index
+ * @param sound the sound
+ * @param isPlace whether the sound should play when placed or removed/broken
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void addSound(@NotNull ItemStack itemStack, @NotNull DEUSound sound, boolean isPlace){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ List sounds = getSounds(pdc, isPlace);
+ sounds.add(sound);
+
+ pdc.set(getSoundKey(isPlace), PersistentDataType.BYTE_ARRAY, writeSoundList(sounds));
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Remove a sound to placed when the given item's assigned group is placed or removed/broken
+ * @param itemStack the item
+ * @param index the sound's index
+ * @param isPlace whether to remove a place or remove/break sound
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void removeSound(@NotNull ItemStack itemStack, int index, boolean isPlace){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ List sounds = getSounds(pdc, isPlace);
+ sounds.remove(index);
+
+ pdc.set(getSoundKey(isPlace), PersistentDataType.BYTE_ARRAY, writeSoundList(sounds));
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Remove a sound to placed when the given item's assigned group is placed or removed/broken
+ * @param itemStack the item
+ * @param sound the sound
+ * @param isPlace whether to remove a place or remove/break sound
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void removeSound(@NotNull ItemStack itemStack, @NotNull DEUSound sound, boolean isPlace){
+ removeSound(itemStack, sound.getSoundName(), sound.getVolume(), sound.getPitch(), isPlace);
+ }
+
+ /**
+ * Remove a sound to placed when the given item's assigned group is placed or removed/broken
+ * @param itemStack the item
+ * @param sound the sound
+ * @param isPlace whether to remove a place or remove/break sound
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void removeSound(@NotNull ItemStack itemStack, @NotNull Sound sound, boolean isPlace){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ List sounds = getSounds(pdc, isPlace);
+ sounds.removeIf(s -> sound == s.getSound());
+
+ pdc.set(getSoundKey(isPlace), PersistentDataType.BYTE_ARRAY, writeSoundList(sounds));
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Remove a sound to placed when the given item's assigned group is placed or removed/broken
+ * @param itemStack the item
+ * @param sound the sound
+ * @param isPlace whether to remove a place or remove/break sound
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void removeSound(@NotNull ItemStack itemStack, @NotNull String sound, boolean isPlace){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ List sounds = getSounds(pdc, isPlace);
+ sounds.removeIf(s -> s.getSoundName().equalsIgnoreCase(sound));
+
+ pdc.set(getSoundKey(isPlace), PersistentDataType.BYTE_ARRAY, writeSoundList(sounds));
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Remove a sound to placed when the given item's assigned group is placed or removed/broken
+ * @param itemStack the item
+ * @param sound the sound
+ * @param isPlace whether to remove a place or remove/break sound
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void removeSound(@NotNull ItemStack itemStack, @NotNull String sound, float volume, float pitch, boolean isPlace){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ List sounds = getSounds(pdc, isPlace);
+ sounds.removeIf(s ->
+ s.getSoundName().equalsIgnoreCase(sound)
+ && s.getVolume() == volume
+ && s.getPitch() == pitch
+ );
+
+ pdc.set(getSoundKey(isPlace), PersistentDataType.BYTE_ARRAY, writeSoundList(sounds));
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Remove a sound to placed when the given item's assigned group is placed or removed/broken
+ * @param itemStack the item
+ * @param isPlace whether to remove place or remove/break sounds
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void removeAllSounds(@NotNull ItemStack itemStack, boolean isPlace){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ pdc.remove(getSoundKey(isPlace));
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Get the {@link DEUSound}s added to an item that will play when the item is placed or removed/broken
+ * @param itemStack the item
+ * @param isPlace whether to get place or break sounds
+ * @return a {@link DEUSound} list
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static @NotNull List getSounds(@NotNull ItemStack itemStack, boolean isPlace){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+ return getSounds(pdc, isPlace);
+ }
+
+ private static List getSounds(PersistentDataContainer pdc, boolean isPlace){
+ NamespacedKey key = getSoundKey(isPlace);
+ byte[] soundArr = pdc.get(key, PersistentDataType.BYTE_ARRAY);
+ return soundArr == null ? new ArrayList<>() : readSoundList(soundArr);
+ }
+
+
+ /**
+ *
+ * @param itemStack the item
+ * @param location where the sounds should play
+ * @param isPlace whether to play place or break sounds
+ * @throws IllegalArgumentException if the provided itemstack did not already have placeable group data applied with {@link PlaceableGroupData}
+ */
+ public static void playSounds(@NotNull ItemStack itemStack, @NotNull Location location, boolean isPlace){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ checkExistingData(pdc);
+
+ for (DEUSound sound : getSounds(pdc, isPlace)){
+ sound.playSound(location);
+ }
+ }
+
+ private static NamespacedKey getSoundKey(boolean isPlace){
+ return isPlace ? DisplayAPI.getPlaceableGroupPlaceSounds() : DisplayAPI.getPlaceableGroupBreakSounds();
+ }
+
+ @ApiStatus.Internal
+ public static byte[] writeSoundList(List sounds) {
+ try {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ ObjectOutputStream out = new ObjectOutputStream(baos);
+
+ out.writeInt(sounds.size());
+ for (DEUSound sound : sounds) {
+ sound.writeExternal(out);
+ }
+
+ out.flush();
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to write DEUSound list", e);
+ }
+ }
+
+ private static List readSoundList(byte[] data) {
+ try {
+ ObjectInputStream in =
+ new ObjectInputStream(new ByteArrayInputStream(data));
+
+ int size = in.readInt();
+ List sounds = new ArrayList<>(size);
+
+ for (int i = 0; i < size; i++) {
+ DEUSound sound = new DEUSound();
+ sound.readExternal(in);
+ sounds.add(sound);
+ }
+
+ return sounds;
+ } catch (IOException | ClassNotFoundException e) {
+ throw new RuntimeException("Failed to read DEUSound list", e);
+ }
+ }
+
+ private static void checkExistingData(PersistentDataContainer pdc){
+ if (!pdc.has(DisplayAPI.getPlaceableGroupKey())) throw new IllegalArgumentException("ItemStack was never provided PlaceableGroupData");
+ }
+
+ /**
+ * Unassign a {@link DisplayEntityGroup} from an {@link ItemStack}
+ * @param itemStack the item
+ */
+ public static void unassign(@NotNull ItemStack itemStack){
+ ItemMeta meta = itemStack.getItemMeta();
+ PersistentDataContainer pdc = meta.getPersistentDataContainer();
+ pdc.remove(DisplayAPI.getPlaceableGroupKey());
+ pdc.remove(DisplayAPI.getPlaceableGroupPermissionKey());
+ pdc.remove(DisplayAPI.getPlaceableGroupRespectPlayerFacing());
+ pdc.remove(DisplayAPI.getPlaceableGroupRespectBlockFace());
+ pdc.remove(DisplayAPI.getPlaceableGroupPlaceSounds());
+ pdc.remove(DisplayAPI.getPlaceableGroupBreakSounds());
+ pdc.remove(DisplayAPI.getPlaceableGroupPlacerBreaksOnly());
+ pdc.remove(DisplayAPI.getPlaceableGroupDropItem());
+ itemStack.setItemMeta(meta);
+ }
+
+ /**
+ * Check if an item has an assigned {@link DisplayEntityGroup}
+ * @param itemStack the item
+ * @return a boolean
+ */
+ public static boolean hasData(@NotNull ItemStack itemStack){
+ PersistentDataContainer pdc = itemStack.getItemMeta().getPersistentDataContainer();
+ return pdc.has(DisplayAPI.getPlaceableGroupKey(), PersistentDataType.STRING);
+ }
+
+ /**
+ * Get the tag of the group assigned to an {@link ItemStack}
+ * @param itemStack the item
+ * @return the group's tag or null
+ */
+ public static @Nullable String getGroupTag(@NotNull ItemStack itemStack){
+ PersistentDataContainer pdc = itemStack.getItemMeta().getPersistentDataContainer();
+ return pdc.get(DisplayAPI.getPlaceableGroupKey(), PersistentDataType.STRING);
+ }
+
+ /**
+ * Get the {@link DisplayEntityGroup} assigned to an {@link ItemStack} based on the group tag stored on the item
+ * @param itemStack the item
+ * @return the {@link DisplayEntityGroup} or null if the group doesn't exist or if the item doesn't have placeable group data
+ */
+ public static @Nullable DisplayEntityGroup getGroup(@NotNull ItemStack itemStack){
+ String tag = getGroupTag(itemStack);
+ return tag == null ? null : DisplayGroupManager.getGroup(tag);
+ }
+
+
+
+ /**
+ * Check if an itemstack has a place permission, determining if a player can place its assigned group
+ * @param itemStack the itemstack
+ * @return a boolean
+ */
+ public static boolean hasPlacePermission(@NotNull ItemStack itemStack){
+ PersistentDataContainer pdc = itemStack.getItemMeta().getPersistentDataContainer();
+ return pdc.has(DisplayAPI.getPlaceableGroupPermissionKey(), PersistentDataType.STRING);
+ }
+
+ /**
+ * Get the permission that determines whether a player can place an itemstack's assigned group
+ * @param itemStack the item
+ * @return the permission or null if the item doesn't have placeable group data
+ */
+ public static @Nullable String getPlacePermission(@NotNull ItemStack itemStack){
+ PersistentDataContainer pdc = itemStack.getItemMeta().getPersistentDataContainer();
+ return pdc.get(DisplayAPI.getPlaceableGroupPermissionKey(), PersistentDataType.STRING);
+ }
+
+ /**
+ * Get whether the group associated with the given item will respect the player's facing direction
+ * @param itemStack the item
+ * @return a boolean
+ */
+ public static boolean isRespectingPlayerFacing(@NotNull ItemStack itemStack){
+ PersistentDataContainer pdc = itemStack.getItemMeta().getPersistentDataContainer();
+ return Boolean.TRUE.equals(pdc.get(DisplayAPI.getPlaceableGroupRespectPlayerFacing(), PersistentDataType.BOOLEAN));
+ }
+
+ /**
+ * Get whether the group associated with the given item will respect the block face its placed on
+ * @param itemStack the item
+ * @return a boolean
+ */
+ public static boolean isRespectingBlockFace(@NotNull ItemStack itemStack){
+ PersistentDataContainer pdc = itemStack.getItemMeta().getPersistentDataContainer();
+ return Boolean.TRUE.equals(pdc.get(DisplayAPI.getPlaceableGroupRespectBlockFace(), PersistentDataType.BOOLEAN));
+ }
+
+ /**
+ * Get whether only the placer of a placed group can break it
+ * @param itemStack the item
+ * @return a boolean
+ */
+ public static boolean isPlacerBreaksOnly(@NotNull ItemStack itemStack){
+ PersistentDataContainer pdc = itemStack.getItemMeta().getPersistentDataContainer();
+ return Boolean.TRUE.equals(pdc.get(DisplayAPI.getPlaceableGroupPlacerBreaksOnly(), PersistentDataType.BOOLEAN));
+ }
+
+ /**
+ * Get whether the itemstack used to place a group will drop when the group is broken
+ * @param itemStack the item
+ * @return a boolean
+ */
+ public static boolean isDropItem(@NotNull ItemStack itemStack){
+ PersistentDataContainer pdc = itemStack.getItemMeta().getPersistentDataContainer();
+ return Boolean.TRUE.equals(pdc.get(DisplayAPI.getPlaceableGroupDropItem(), PersistentDataType.BOOLEAN));
+ }
+
+ /**
+ * Check if the given player has permission to place the itemstack's assigned group
+ * @param itemStack the item
+ * @param player the player
+ * @return a boolean
+ */
+ public static boolean canPlace(@NotNull ItemStack itemStack, @NotNull Player player){
+ String placePerm = getPlacePermission(itemStack);
+ if (placePerm == null) return true;
+ return player.hasPermission(placePerm);
+ }
+
+
+ /**
+ * Spawn the placeable group stored on an item
+ * @param itemStack the item
+ * @param spawnLocation the spawn location
+ * @return a {@link PacketDisplayEntityGroup} or null if the itemstack does not contain placeable group data, or if the {@link PreItemPlaceGroupEvent} is cancelled
+ */
+ public static @Nullable PacketDisplayEntityGroup spawnGroup(@NotNull ItemStack itemStack, @NotNull Location spawnLocation, @Nullable Player itemHolder){
+ return spawnGroup(itemStack, spawnLocation, new Quaternionf(), itemHolder);
+ }
+
+ /**
+ * Spawn the placeable group stored on an item
+ * @param itemStack the item
+ * @param spawnLocation the spawn location
+ * @return a {@link PacketDisplayEntityGroup}, or null if the itemstack does not contain placeable group data or if the {@link PreItemPlaceGroupEvent} is cancelled
+ */
+ public static @Nullable PacketDisplayEntityGroup spawnGroup(@NotNull ItemStack itemStack, @NotNull Location spawnLocation, @NotNull Quaternionf rotation, @Nullable Player itemHolder){
+ String tag = getGroupTag(itemStack);
+ if (tag == null) return null;
+
+ DisplayEntityGroup group = DisplayGroupManager.getGroup(tag);
+ if (group == null) return null;
+
+ PacketDisplayEntityGroup pg = group.createPacketGroup(spawnLocation, GroupSpawnedEvent.SpawnReason.ITEMSTACK, true, true);
+ if (pg == null) return null;
+ pg.rotateDisplays(rotation);
+ pg.setPersistent(true);
+
+ Bukkit.getScheduler().runTask(DisplayAPI.getPlugin(), () -> {
+ setBlockData(itemHolder, spawnLocation.getBlock(), itemStack.clone(), pg.getPersistentGlobalId());
+ new ItemPlaceGroupEvent(pg, itemStack, itemHolder).callEvent();
+ });
+
+ return pg;
+ }
+
+
+ /**
+ * Get the UUID of the player who placed a group
+ * @param group the placed group
+ * @return a {@link UUID} or null if the group wasn't placed with an item or if a placer wasn't specified
+ */
+ public static @Nullable UUID getWhoPlaced(@NotNull PacketDisplayEntityGroup group){
+ if (!group.isPlaced()) return null;
+
+ Location loc = group.getLocation();
+ if (loc == null) return null;
+
+ return getWhoPlaced(loc.getBlock());
+ }
+
+ /**
+ * Get the UUID of the player who placed a group
+ * @param block the block where a placed group is
+ * @return a {@link UUID} or null if the group wasn't placed with an item or if a placer wasn't specified
+ */
+ public static @Nullable UUID getWhoPlaced(@NotNull Block block){
+ if (!CustomBlockData.hasCustomBlockData(block, DisplayAPI.getPlugin())) return null;
+ CustomBlockData data = new CustomBlockData(block, DisplayAPI.getPlugin());
+ String uuidStr = data.get(DisplayAPI.getPlaceableGroupPlacer(), PersistentDataType.STRING);
+ return uuidStr == null ? null : UUID.fromString(uuidStr);
+ }
+
+ /**
+ * Get the ItemStack used to place a group
+ * @param group the placed group
+ * @return an item or null if the group wasn't placed with an item
+ */
+ public static @Nullable ItemStack getItemStack(@NotNull PacketDisplayEntityGroup group){
+ if (!group.isPlaced()) return null;
+
+ Location loc = group.getLocation();
+ if (loc == null) return null;
+
+ return getItemStack(loc.getBlock());
+ }
+
+ /**
+ * Get the ItemStack used to place a group
+ * @param block the block where a placed group is
+ * @return an item or null if the group wasn't placed with an item
+ */
+ public static @Nullable ItemStack getItemStack(@NotNull Block block){
+ if (!CustomBlockData.hasCustomBlockData(block, DisplayAPI.getPlugin())) return null;
+ CustomBlockData data = new CustomBlockData(block, DisplayAPI.getPlugin());
+
+ String b64 = data.get(DisplayAPI.getPlaceableGroupItemStack(), PersistentDataType.STRING);
+ return ItemStack.deserializeBytes(Base64.getDecoder().decode(b64));
+ }
+
+ /**
+ * Get the {@link PacketDisplayEntityGroup} placed at a block
+ * @param block the block where a placed group is
+ * @return an {@link PacketDisplayEntityGroup} or null
+ */
+ public static @Nullable PacketDisplayEntityGroup getPlacedGroup(@NotNull Block block){
+ if (!CustomBlockData.hasCustomBlockData(block, DisplayAPI.getPlugin())) return null;
+ CustomBlockData data = new CustomBlockData(block, DisplayAPI.getPlugin());
+
+ String id = data.get(DisplayAPI.getPlaceableGroupId(), PersistentDataType.STRING);
+ return PacketDisplayEntityGroup.getGroup(id);
+ }
+
+ private static void setBlockData(Player itemHolder, Block block, ItemStack itemStack, String groupID){
+ block.setType(Material.BARRIER);
+ PersistentDataContainer pdc = new CustomBlockData(block, DisplayAPI.getPlugin());
+ pdc.set(DisplayAPI.getPlaceableGroupId(), PersistentDataType.STRING, groupID);
+
+ itemStack.setAmount(1);
+ String b64 = Base64.getEncoder().encodeToString(itemStack.serializeAsBytes());
+ pdc.set(DisplayAPI.getPlaceableGroupItemStack(), PersistentDataType.STRING, b64);
+ if (itemHolder != null) pdc.set(DisplayAPI.getPlaceableGroupPlacer(), PersistentDataType.STRING, itemHolder.getUniqueId().toString());
+ }
+
+
+ /**
+ * Get whether an itemstack can be used to place groups
+ * @param itemStack the itemstack
+ * @return a boolean
+ */
+ public static boolean isValidItem(@NotNull ItemStack itemStack){
+ return itemStack.getType() != Material.AIR && itemStack.getType().isBlock();
+ }
+}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/Direction.java b/api/src/main/java/net/donnypz/displayentityutils/utils/Direction.java
index 30cc3be0..0fc2195e 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/Direction.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/Direction.java
@@ -1,10 +1,9 @@
package net.donnypz.displayentityutils.utils;
import net.donnypz.displayentityutils.utils.DisplayEntities.ActivePart;
-import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityPart;
import org.bukkit.Location;
+import org.bukkit.entity.Display;
import org.bukkit.entity.Entity;
-import org.bukkit.entity.Interaction;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
@@ -89,8 +88,8 @@ else if (this != FORWARD && this != BACK){
* @return A vector
*/
public Vector getVector(@NotNull Entity entity, boolean localSpace) {
- boolean isInteraction = entity instanceof Interaction;
- return getVector(entity.getLocation(), localSpace && !isInteraction);
+ boolean isDisplay = entity instanceof Display;
+ return getVector(entity.getLocation(), localSpace && isDisplay);
}
/**
@@ -99,8 +98,7 @@ public Vector getVector(@NotNull Entity entity, boolean localSpace) {
* @return A vector
*/
public Vector getVector(@NotNull ActivePart part, boolean localSpace) {
- boolean isInteraction = part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION;
- return getVector(part.getLocation(), localSpace && !isInteraction);
+ return getVector(part.getLocation(), localSpace && part.isDisplay());
}
/**
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/ActiveGroup.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/ActiveGroup.java
index ded292db..284d522a 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/ActiveGroup.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/ActiveGroup.java
@@ -21,6 +21,7 @@
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
+import org.joml.Quaternionf;
import java.util.*;
@@ -60,6 +61,13 @@ public ActiveGroup addPlayerSelection(Player player){
*/
public abstract void addPart(T part);
+ /**
+ * Add an entity to this group as a part, when you don't know the type of entity you're dealing with
+ * @param entity the eligible entity to add
+ * @return a corresponding part or null if the entity is not an eligible part entity
+ */
+ public abstract @Nullable T addEntity(@NotNull Entity entity);
+
/**
* Get this group's tag
@@ -125,10 +133,10 @@ public void removeCulling(){
* Change the scale of all parts in this group by the given scale multiplier
* @param newScaleMultiplier the scale multiplier to apply to this group
* @param durationInTicks how long it should take for the group to scale
- * @param scaleInteractions whether interaction entities should be scaled
+ * @param scaleNonDisplays whether non-display entities should scale
* @throws IllegalArgumentException if newScaleMultiplier is less than or equal to 0
*/
- public abstract boolean scale(float newScaleMultiplier, int durationInTicks, boolean scaleInteractions);
+ public abstract boolean scale(float newScaleMultiplier, int durationInTicks, boolean scaleNonDisplays);
/**
* Change the true location of this group.
@@ -138,6 +146,31 @@ public void removeCulling(){
*/
public abstract boolean teleport(@NotNull Location location, boolean respectGroupDirection);
+
+ /**
+ * Teleport this group in the given direction.
+ * @param direction The direction to translate the group
+ * @param distance How far the group should be translated
+ * @return true if the teleport was successful
+ */
+ public boolean teleport(@NotNull Direction direction, double distance){
+ return teleport(direction.getVector(masterPart, false), distance);
+ }
+
+ /**
+ * Teleport this group in the given vector's direction.
+ * @param direction The direction to translate the group
+ * @param distance How far the group should be translated
+ * @return true if the teleport was successful
+ */
+ public boolean teleport(@NotNull Vector direction, double distance){
+ Location l = getLocation();
+ if (l == null) return false;
+ l.add(direction.clone().normalize().multiply(distance));
+ teleport(l, true);
+ return true;
+ }
+
/**
* Move the model through smooth teleportation of both interaction and display entities. Doing this multiple times in a short amount of time may bring unexpected results.
* @param direction The direction to translate the group
@@ -154,7 +187,7 @@ public void teleportMove(@NotNull Direction direction, double distance, int dura
* @param distance How far the group should be translated
* @param durationInTicks How long it should take for the translation to complete
*/
- public abstract void teleportMove(Vector direction, double distance, int durationInTicks);
+ public abstract void teleportMove(@NotNull Vector direction, double distance, int durationInTicks);
/**
* Set the teleportation duration of all parts in this group
@@ -188,15 +221,25 @@ public void setInterpolationDelay(int interpolationDelay){
}
}
+ /**
+ * Rotate the display entities in this group
+ * @param rotation the rotation
+ */
+ public void rotateDisplays(@NotNull Quaternionf rotation){
+ for (ActivePart p : groupParts.values()){
+ p.rotateDisplay(rotation, true);
+ }
+ }
+
/**
* Change the yaw of this group
* @param yaw The yaw to set for this group
- * @param pivotInteractions true if interactions should pivot around the group with the yaw change
+ * @param pivot whether non-display entities should pivot around the group
*/
@Override
- public void setYaw(float yaw, boolean pivotInteractions){
+ public void setYaw(float yaw, boolean pivot){
for (ActivePart part : groupParts.values()){
- part.setYaw(yaw, pivotInteractions);
+ part.setYaw(yaw, pivot);
}
}
@@ -300,9 +343,7 @@ public void glow(long durationInTicks){
@Override
public void glow(@NotNull Player player, long durationInTicks){
for (ActivePart part : groupParts.values()){
- if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION || part.type == SpawnedDisplayEntityPart.PartType.TEXT_DISPLAY){
- continue;
- }
+ if (!part.canGlow()) continue;
if (!part.isGlowing()){
part.glow(player, durationInTicks);
}
@@ -441,7 +482,7 @@ public List getParts(@NotNull SpawnedDisplayEntityPart.PartType partType){
public List getDisplayParts(){
List partList = new ArrayList<>();
for (T part : groupParts.sequencedValues()){
- if (part.type != SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (part.isDisplay()){
partList.add(part);
}
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/ActivePart.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/ActivePart.java
index 37a529bc..451b9628 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/ActivePart.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/ActivePart.java
@@ -1,5 +1,7 @@
package net.donnypz.displayentityutils.utils.DisplayEntities;
+import com.destroystokyo.paper.profile.PlayerProfile;
+import io.papermc.paper.datacomponent.item.ResolvableProfile;
import net.donnypz.displayentityutils.DisplayAPI;
import net.donnypz.displayentityutils.DisplayConfig;
import net.donnypz.displayentityutils.events.GroupSpawnedEvent;
@@ -15,19 +17,22 @@
import org.bukkit.Location;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.*;
+import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MainHand;
import org.bukkit.util.Transformation;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
+import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
public abstract class ActivePart implements Active{
- private static final ConcurrentHashMap partsById = new ConcurrentHashMap<>();
+ protected static final ConcurrentHashMap partsById = new ConcurrentHashMap<>();
protected SpawnedDisplayEntityPart.PartType type;
protected UUID partUUID;
private int entityId;
@@ -35,9 +40,9 @@ public abstract class ActivePart implements Active{
private boolean valid = true;
final Set clientAnimationPlayers = Collections.newSetFromMap(new ConcurrentHashMap<>());
- protected ActivePart(int entityId, boolean mapped){
+ protected ActivePart(int entityId, boolean autoMap){
this.entityId = entityId;
- if (mapped) {
+ if (autoMap) {
partsById.put(entityId, this);
}
}
@@ -157,6 +162,12 @@ public boolean hasTag(@NotNull String tag){
public abstract boolean hasGroup();
+ /**
+ * Teleport this part to the given location. This will fail if the part is a display entity, in a group, and is not the group's master part.
+ * @param location the teleport location
+ */
+ public abstract void teleport(@NotNull Location location);
+
public abstract @Nullable Location getLocation();
protected abstract void cull(float width, float height);
@@ -168,7 +179,7 @@ public boolean hasTag(@NotNull String tag){
* @param heightAdder The amount of height to be added to the culling range
*/
public void autoCull(float widthAdder, float heightAdder){
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
+ if (!isDisplay()) return;
Transformation transformation = getTransformation();
if (transformation == null) return;
DisplayAPI.getScheduler().partRunAsync(this, () -> {
@@ -187,13 +198,29 @@ public SpawnedDisplayEntityPart.PartType getType(){
return type;
}
+ /**
+ * Get whether this part will visually glow if glowing is applied to it
+ * @return a boolean
+ */
+ public boolean canGlow(){
+ return type != SpawnedDisplayEntityPart.PartType.TEXT_DISPLAY
+ && type != SpawnedDisplayEntityPart.PartType.INTERACTION;
+ }
+
+ public boolean isDisplay(){
+ return type == SpawnedDisplayEntityPart.PartType.BLOCK_DISPLAY
+ || type == SpawnedDisplayEntityPart.PartType.ITEM_DISPLAY
+ || type == SpawnedDisplayEntityPart.PartType.TEXT_DISPLAY;
+ }
+
/**
* Make this part glow for a player
* @param player the player
*/
public void glow(@NotNull Player player){
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
- PacketUtils.setGlowing(player, getEntityId(), true);
+ if (canGlow()){
+ PacketUtils.setGlowing(player, getEntityId(), true);
+ }
}
/**
@@ -202,9 +229,8 @@ public void glow(@NotNull Player player){
*/
@Override
public void glow(long durationInTicks){
- if (type == SpawnedDisplayEntityPart.PartType.BLOCK_DISPLAY || type == SpawnedDisplayEntityPart.PartType.ITEM_DISPLAY){
+ if (canGlow()){
glow();
-
DisplayAPI.getScheduler().partRunLater(this, this::unglow, durationInTicks);
}
}
@@ -217,7 +243,7 @@ public void glow(long durationInTicks){
*/
@Override
public void glow(@NotNull Player player, long durationInTicks){
- if (type == SpawnedDisplayEntityPart.PartType.BLOCK_DISPLAY || type == SpawnedDisplayEntityPart.PartType.ITEM_DISPLAY){
+ if (canGlow()){
if (durationInTicks <= -1){
PacketUtils.setGlowing(player, getEntityId(), true);
}
@@ -267,7 +293,7 @@ public void markInteraction(@NotNull Player player, long durationInTicks){
*/
@Override
public void unglow(@NotNull Player player) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
+ if (!canGlow()) return;
PacketUtils.setGlowing(player, getEntityId(), false);
}
@@ -309,34 +335,41 @@ public void unglow(@NotNull Player player) {
public abstract void setTransformationMatrix(@NotNull Matrix4f matrix);
/**
- * Change the X scale of this part
- * @param scale The X scale to set for this part
- * @return false if this part is an Interaction
+ * Change this display entity part's X scale
+ * @param scale The X scale to set
+ * @return false if this part is not a display entity
+ */
+ public abstract boolean setDisplayXScale(float scale);
+
+ /**
+ * Change this display entity part's Y scale
+ * @param scale The Y scale to set
+ * @return false if this part is not a display entity
*/
- public abstract boolean setXScale(float scale);
+ public abstract boolean setDisplayYScale(float scale);
/**
- * Change the Y scale of this part
- * @param scale The Y scale to set for this part
- * @return false if this part is an Interaction
+ * Change this display entity part's Z scale
+ * @param scale The Z scale to set
+ * @return false if this part is not a display entity
*/
- public abstract boolean setYScale(float scale);
+ public abstract boolean setDisplayZScale(float scale);
/**
- * Change the Z scale of this part
- * @param scale The Z scale to set for this part
- * @return false if this part is an Interaction
+ * Change this display entity part's scale
+ * @param x The X scale to set
+ * @param y The Y scale to set
+ * @param z The Z scale to set
+ * @return false if this part is not a display entity
*/
- public abstract boolean setZScale(float scale);
+ public abstract boolean setDisplayScale(float x, float y, float z);
/**
- * Change the scale of this part
- * @param x The X scale to set for this part
- * @param y The Y scale to set for this part
- * @param z The Z scale to set for this part
- * @return false if this part is an Interaction
+ * Rotate this display entity part by a given quaternion
+ * @param rotation the rotation
+ * @param rotateTranslation whether the display's translation should be rotated, pivoting it around the entity's location
*/
- public abstract boolean setScale(float x, float y, float z);
+ public abstract void rotateDisplay(@NotNull Quaternionf rotation, boolean rotateTranslation);
/**
* Set the text of this part if its type is {@link SpawnedDisplayEntityPart.PartType#TEXT_DISPLAY}.
@@ -410,6 +443,8 @@ public void unglow(@NotNull Player player) {
*/
public abstract void setItemDisplayItemGlint(boolean hasGlint);
+ public abstract boolean hasItemDisplayItemGlint();
+
public abstract @Nullable Component getTextDisplayText();
public abstract int getTextDisplayLineWidth();
@@ -473,11 +508,19 @@ public void unglow(@NotNull Player player) {
public abstract int getTeleportDuration();
/**
- * Get the interaction translation of this part, relative to its group's location
- * group's location only if the part's type is {@link SpawnedDisplayEntityPart.PartType#INTERACTION}.
- * @return a {@link Vector} or null if the part is not an Interaction, or if the part is ungrouped
+ * Get the translation of this non-display entity part, relative to its group's location
+ * @return a {@link Vector} or null if the part is a display entity, or if the part is ungrouped
*/
- public abstract @Nullable Vector getInteractionTranslation();
+ public abstract @Nullable Vector getNonDisplayTranslation();
+
+
+ public abstract void setCustomName(@Nullable Component text);
+
+ public abstract void setCustomNameVisible(boolean visible);
+
+ public abstract @Nullable Component getCustomName();
+
+ public abstract boolean isCustomNameVisible();
public abstract void setInteractionHeight(float height);
@@ -504,74 +547,145 @@ public void unglow(@NotNull Player player) {
public abstract boolean isInteractionResponsive();
/**
- * Adds a command to this part to execute when clicked, if its type is {@link SpawnedDisplayEntityPart.PartType#INTERACTION}
- * @param command The command to assign
- * @param isLeftClick whether the command is executed on left click
- * @param isConsole whether the command should be executed by console or the clicker
+ * Set the profile of this mannequin part effectively changing its skin, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @param profile the profile
*/
- public abstract void addInteractionCommand(@NotNull String command, boolean isLeftClick, boolean isConsole);
+ public abstract void setMannequinProfile(@NotNull PlayerProfile profile);
/**
- * Remove a command from this part, if its type is {@link SpawnedDisplayEntityPart.PartType#INTERACTION}
- * @param command The command to remove
+ * Set the profile of this mannequin part effectively changing its skin, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @param profile the profile
*/
- public abstract void removeInteractionCommand(@NotNull InteractionCommand command);
+ public abstract void setMannequinProfile(@NotNull ResolvableProfile profile);
- public abstract @NotNull List getInteractionCommands();
+ /**
+ * Set the profile of this mannequin part effectively changing its skin, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}.
+ * The profile change will only be visible for the provided player
+ * @param profile the profile
+ * @param player the player to send profile change to
+ */
+ public void setMannequinProfile(@NotNull PlayerProfile profile, @NotNull Player player){
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ setMannequinProfile(ResolvableProfile.resolvableProfile(profile), player);
+ }
- public abstract @NotNull List getInteractionCommandsWithData();
+ /**
+ * Set the profile of this mannequin part effectively changing its skin, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}.
+ * The profile change will only be visible for the provided player
+ * @param profile the profile
+ * @param player the player to send profile change to
+ */
+ public void setMannequinProfile(@NotNull ResolvableProfile profile, @NotNull Player player){
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ PacketUtils.setAttribute(player, entityId, DisplayAttributes.Mannequin.RESOLVABLE_PROFILE, profile);
+ }
- static class PartData {
+ /**
+ * Set the description/below name text of this mannequin part, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @param text the pose text
+ */
+ public abstract void setMannequinBelowName(@Nullable Component text);
- private final UUID entityUUID;
- private String worldName;
+ /**
+ * Set the pose of this mannequin part, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @param pose the pose
+ */
+ public abstract void setMannequinPose(Pose pose);
- PartData(@NotNull Entity entity) {
- this(entity.getUniqueId(), entity.getWorld().getName());
- }
+ /**
+ * Set the scale attribute of this mannequin part, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @param scale the scale
+ */
+ public abstract void setMannequinScale(double scale);
- PartData(@NotNull UUID entityUUID, @NotNull String worldName) {
- this.entityUUID = entityUUID;
- this.worldName = worldName;
- }
+ /**
+ * Set whether the mannequin part should be immovable, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @param immovable the immovability
+ */
+ public abstract void setMannequinImmovable(boolean immovable);
- void setWorldName(String worldName) {
- this.worldName = worldName;
- }
+ /**
+ * Set whether the mannequin part should have gravity, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @param gravity the gravity
+ */
+ public abstract void setMannequinGravity(boolean gravity);
- /**
- * Get the UUID of the entity this PartData represents
- * @return a UUID
- */
- public UUID getUUID() {
- return entityUUID;
- }
+ /**
+ * Set the mannequin part's main hand, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @param mainHand the main hand
+ */
+ public abstract void setMannequinMainHand(@NotNull MainHand mainHand);
- /**
- * Get the world name of the entity this PartData represents
- *
- * @return a string
- */
- public String getWorldName() {
- return worldName;
- }
+ /**
+ * Set the item in mannequin part's equipment slot, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @param slot the equipment slot
+ * @param itemStack the item to put in the slot
+ */
+ public abstract void setMannequinEquipment(@NotNull EquipmentSlot slot, @NotNull ItemStack itemStack);
- @Override
- public boolean equals(Object obj) {
- if (this == obj) {
- return true;
- }
- if (obj == null || getClass() != obj.getClass()) {
- return false;
- }
+ /**
+ * Get a mannequin part's profile, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @return a {@link ResolvableProfile} or null
+ */
+ public abstract ResolvableProfile getMannequinProfile();
- PartData data = (PartData) obj;
- return entityUUID.equals(data.entityUUID) && worldName.equals(data.worldName);
- }
+ /**
+ * Get a mannequin part's description/below name text, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @return a {@link Component} or null
+ */
+ public abstract @Nullable Component getMannequinBelowName();
- @Override
- public int hashCode() {
- return Objects.hash(entityUUID, worldName);
- }
- }
+ /**
+ * Get a mannequin part's pose, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @return a {@link Pose} or null
+ */
+ public abstract @Nullable Pose getMannequinPose();
+
+ /**
+ * Get a mannequin part's scale attribute, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @return a double or -1 if the part is not a mannequin
+ */
+ public abstract double getMannequinScale();
+
+ /**
+ * Get whether mannequin part is immovable, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @return a boolean, always false if the part is not a mannequin
+ */
+ public abstract boolean isMannequinImmovable();
+
+ /**
+ * Get whether mannequin part has gravity, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @return a boolean, always false if the part is not a mannequin
+ */
+ public abstract boolean hasMannequinGravity();
+
+ /**
+ * Get whether mannequin part has gravity, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @return a {@link MainHand} or null if the part is not a mannequin
+ */
+ public abstract @Nullable MainHand getMannequinMainHand();
+
+ /**
+ * Get the item in a mannequin part's equipment slot, if its type is {@link SpawnedDisplayEntityPart.PartType#MANNEQUIN}
+ * @return a {@link ItemStack} or null if the part is not a mannequin
+ */
+ public abstract @Nullable ItemStack getMannequinEquipment(@NotNull EquipmentSlot equipmentSlot);
+
+ /**
+ * Adds a command to this part to execute when clicked, if its type is {@link SpawnedDisplayEntityPart.PartType#INTERACTION}
+ * @param command The command to assign
+ * @param isLeftClick whether the command is executed on left click
+ * @param isConsole whether the command should be executed by console or the clicker
+ */
+ public abstract void addInteractionCommand(@NotNull String command, boolean isLeftClick, boolean isConsole);
+
+ /**
+ * Remove a command from this part, if its type is {@link SpawnedDisplayEntityPart.PartType#INTERACTION}
+ * @param command The command to remove
+ */
+ public abstract void removeInteractionCommand(@NotNull InteractionCommand command);
+
+ public abstract @NotNull List getInteractionCommands();
+
+ public abstract @NotNull List getInteractionCommandsWithData();
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationPlayer.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationPlayer.java
index c45717e3..5e973b52 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationPlayer.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationPlayer.java
@@ -187,7 +187,7 @@ void animateInteractions(Collection players, Location groupLoc, SpawnedD
continue;
}
- Vector currentVector = part.getInteractionTranslation();
+ Vector currentVector = part.getNonDisplayTranslation();
if (currentVector == null){
continue;
}
@@ -227,7 +227,7 @@ void animateInteractions(Collection players, Location groupLoc, SpawnedD
part.translate(moveVector, (float) moveVector.length(), frame.duration, 0);
}
else {
- PacketUtils.translateInteraction(players, part, moveVector, (float) moveVector.length(), frame.duration, 0);
+ PacketUtils.translateNonDisplay(players, part, moveVector, (float) moveVector.length(), frame.duration, 0);
}
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEGJSONAdapter.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUJSONAdapter.java
similarity index 57%
rename from api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEGJSONAdapter.java
rename to api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUJSONAdapter.java
index b083adce..09f0a66a 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEGJSONAdapter.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUJSONAdapter.java
@@ -3,13 +3,18 @@
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-public final class DEGJSONAdapter {
+public final class DEUJSONAdapter {
+
+ static final String PART_UUID_FIELD = "partUUID";
+ static final String PDC_FIELD = "persistentDataContainer";
public static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(DisplayEntity.class, new JSONAdapter_DisplayEntity())
+ .registerTypeAdapter(InteractionEntity.class, new JSONAdapter_InteractionEntity())
+ .registerTypeAdapter(MannequinEntity.class, new JSONAdapter_MannequinEntity())
.registerTypeHierarchyAdapter(DisplayEntitySpecifics.class, new JSONAdapter_DisplayEntitySpecifics())
.registerTypeHierarchyAdapter(FramePoint.class, new JSONAdapter_FramePoint())
.create();
- private DEGJSONAdapter(){}
+ private DEUJSONAdapter(){}
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationSound.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java
similarity index 60%
rename from api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationSound.java
rename to api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java
index 32a6c9b1..79614e6c 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/AnimationSound.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/DEUSound.java
@@ -2,6 +2,11 @@
import net.donnypz.displayentityutils.DisplayAPI;
import net.donnypz.displayentityutils.utils.version.VersionUtils;
+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.minimessage.MiniMessage;
import org.bukkit.*;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
@@ -10,9 +15,9 @@
import java.io.*;
import java.util.Collection;
+import java.util.function.Consumer;
-@ApiStatus.Internal
-public class AnimationSound implements Externalizable, Cloneable {
+public class DEUSound implements Externalizable, Cloneable { //i have no clue what i was doing when making this class, now its externalizable forever
transient Sound sound;
String soundName;
float volume;
@@ -24,27 +29,26 @@ public class AnimationSound implements Externalizable, Cloneable {
private static final long serialVersionUID = 0;
@ApiStatus.Internal
- public AnimationSound(){}
+ public DEUSound(){}
- public AnimationSound(@NotNull String soundName, float volume, float pitch, int delayInTicks){
+ public DEUSound(@NotNull String soundName, float volume, float pitch, int delayInTicks){
this.soundName = soundName;
this.volume = volume;
this.pitch = pitch;
this.delay = delayInTicks;
this.sound = VersionUtils.getSound(soundName);
- existsInGameVersion = this.sound != null;
+ this.existsInGameVersion = this.sound != null;
}
- public AnimationSound(@NotNull Sound sound, float volume, float pitch, int delayInTicks){
+ public DEUSound(@NotNull Sound sound, float volume, float pitch, int delayInTicks){
this.sound = sound;
this.soundName = sound.getKey().getKey();
this.volume = volume;
this.pitch = pitch;
this.delay = delayInTicks;
- this.existsInGameVersion = true;
}
- public AnimationSound(@NotNull AnimationSound sound){
+ public DEUSound(@NotNull DEUSound sound){
this.sound = sound.sound;
this.soundName = sound.soundName;
this.volume = sound.volume;
@@ -111,11 +115,22 @@ else if (group.isActiveAnimator(animator)){
}
public void playSound(@NotNull Location location){
- location.getWorld().playSound(location, sound, volume, pitch);
+ if (sound == null){
+ location.getWorld().playSound(location, soundName, volume, pitch);
+ }
+ else{
+ location.getWorld().playSound(location, sound, volume, pitch);
+ }
+
}
public void playSound(@NotNull Location location, Player player){
- player.playSound(location, sound, volume, pitch);
+ if (sound == null){
+ player.playSound(location, soundName, volume, pitch);
+ }
+ else{
+ player.playSound(location, sound, volume, pitch);
+ }
}
public void playSound(@NotNull Location location, Collection players){
@@ -175,11 +190,50 @@ public boolean existsInGameVersion() {
}
@Override
- public AnimationSound clone() {
+ public DEUSound clone() {
try {
- return (AnimationSound) super.clone();
+ return (DEUSound) super.clone();
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
+
+ @ApiStatus.Internal
+ public void sendInfo(Player player, Consumer clickRemovalAction){
+ Component msgComp = Component.text("- "+soundName, NamedTextColor.YELLOW);
+ Component hoverComp = Component.text("| Vol: "+volume+", Pitch: "+pitch, NamedTextColor.GRAY);
+ if (!existsInGameVersion){
+ msgComp = msgComp.append(Component.text(" [UNKNOWN]", NamedTextColor.GRAY));
+ hoverComp = hoverComp
+ .append(Component.newline())
+ .append(Component.text("This sound no longer exists or is a resource pack sound!", NamedTextColor.RED));
+ }
+
+ if (clickRemovalAction != null){
+ hoverComp = hoverComp.append(Component.newline())
+ .append(Component.text("Click to remove this sound", NamedTextColor.YELLOW));
+ msgComp = msgComp.clickEvent(ClickEvent.callback(a -> {
+ clickRemovalAction.accept(this);
+ a.sendMessage(DisplayAPI.pluginPrefix.append(Component.text("Sound Removed!", NamedTextColor.YELLOW)));
+ }));
+ }
+
+ msgComp = msgComp.hoverEvent(HoverEvent.showText(hoverComp));
+ player.sendMessage(msgComp);
+ }
+
+ @ApiStatus.Internal
+ 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{
+ for (DEUSound sound : sounds){
+ sound.sendInfo(player, clickRemovalAction);
+ }
+ }
+ player.sendMessage(Component.empty());
+ }
}
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 f4f1020f..f280e0bd 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
@@ -28,8 +28,8 @@ public final class DisplayAnimationFrame implements Serializable {
HashMap startSoundMap;
HashMap endSoundMap;
- HashMap startSounds;
- HashMap endSounds;
+ HashMap startSounds;
+ HashMap endSounds;
transient Set frameStartParticles;
transient Set frameEndParticles;
@@ -79,9 +79,9 @@ public SpawnedDisplayAnimationFrame toSpawnedDisplayAnimationFrame(){
//Start Sounds
if (startSounds != null){
- for (Map.Entry entry : startSounds.entrySet()){
+ for (Map.Entry entry : startSounds.entrySet()){
String soundName = entry.getKey();
- AnimationSound sound = entry.getValue();
+ DEUSound sound = entry.getValue();
sound.delay = 0;
point.sounds.put(soundName, sound);
}
@@ -89,11 +89,10 @@ public SpawnedDisplayAnimationFrame toSpawnedDisplayAnimationFrame(){
//End Sounds
if (endSounds != null){
- for (Map.Entry entry : endSounds.entrySet()){
- String soundName = entry.getKey();
- AnimationSound sound = entry.getValue();
- sound.delay = duration;
- point.sounds.put(soundName, sound);
+ for (Map.Entry entry : endSounds.entrySet()){
+ DEUSound sound = entry.getValue();
+ sound.delay = this.duration;
+ point.addSound(sound);
}
}
@@ -108,9 +107,8 @@ public SpawnedDisplayAnimationFrame toSpawnedDisplayAnimationFrame(){
for (AnimationParticle particle : frameStartParticles){
String pointTag = OLD_ANIM_PARTICLE+animationParticle;
FramePoint point = new FramePoint(pointTag, particle.getVector(), particle.getGroupYawAtCreation(), particle.getGroupPitchAtCreation());
- particle.setDelayInTicks(0);
- point.particles.add(particle);
- frame.framePoints.put(pointTag, point);
+ point.addParticle(particle);
+ frame.addFramePoint(point);
animationParticle++;
}
}
@@ -121,8 +119,8 @@ public SpawnedDisplayAnimationFrame toSpawnedDisplayAnimationFrame(){
String pointTag = OLD_ANIM_PARTICLE+animationParticle;
FramePoint point = new FramePoint(pointTag, particle.getVector(), particle.getGroupYawAtCreation(), particle.getGroupPitchAtCreation());
particle.setDelayInTicks(duration);
- point.particles.add(particle);
- frame.framePoints.put(pointTag, point);
+ point.addParticle(particle);
+ frame.addFramePoint(point);
animationParticle++;
}
}
@@ -130,7 +128,7 @@ public SpawnedDisplayAnimationFrame toSpawnedDisplayAnimationFrame(){
//Frame Points
if (framePoints != null){
for (FramePoint fp : framePoints.values()){
- frame.framePoints.put(fp.tag, new FramePoint(fp));
+ frame.addFramePoint(new FramePoint(fp));
}
}
@@ -177,25 +175,25 @@ private void readObject(ObjectInputStream stream) throws IOException, ClassNotFo
void repairOldSounds(){
- HashMap map1 = handleOldSoundMaps(startSoundMap, 0);
+ HashMap map1 = handleEnumSoundMaps(startSoundMap, 0);
if (!map1.isEmpty()){
if (startSounds == null) startSounds = new HashMap<>();
startSounds.putAll(map1);
}
- HashMap map2 = handleOldSoundMaps(endSoundMap, duration);
+ HashMap map2 = handleEnumSoundMaps(endSoundMap, duration);
if (!map2.isEmpty()){
if (endSounds == null) endSounds = new HashMap<>();
endSounds.putAll(map2);
}
}
- private HashMap handleOldSoundMaps(HashMap soundMap, int delayInTicks) {
+ private HashMap handleEnumSoundMaps(HashMap soundMap, int delayInTicks) {
if (soundMap == null || soundMap.isEmpty()){
return new HashMap<>();
}
- HashMap convertedMap = new HashMap<>();
+ HashMap convertedMap = new HashMap<>();
for (Map.Entry entry : soundMap.entrySet()) {
OldSound sound = entry.getKey();
Float[] values = entry.getValue();
@@ -204,10 +202,10 @@ private HashMap handleOldSoundMaps(HashMap displayEntities = new ArrayList<>();
private final ArrayList interactionEntities = new ArrayList<>();
+ private final ArrayList mannequinEntities = new ArrayList<>();
DisplayEntity masterEntity;
- private String tag;
+ private final String tag;
private Boolean isPersistent = true;
@Serial
@@ -35,9 +35,11 @@ public final class DisplayEntityGroup implements Serializable{
this.masterEntity = addDisplayEntity(spawnedMasterEntity).setMaster();
for (SpawnedDisplayEntityPart part : spawnedGroup.getParts()){
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
- Interaction i = (Interaction) part.getEntity();
- addInteractionEntity(i);
+ if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ addInteractionEntity((Interaction) part.getEntity());
+ }
+ else if (VersionUtils.IS_1_21_9 && part.type == SpawnedDisplayEntityPart.PartType.MANNEQUIN){
+ addMannequinEntity(part.getEntity());
}
else{
if (!part.isMaster()){
@@ -55,10 +57,12 @@ public final class DisplayEntityGroup implements Serializable{
this.masterEntity = addDisplayEntity(packetGroup.masterPart, packetGroup).setMaster();
for (PacketDisplayEntityPart part : packetGroup.getParts()){
-
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION){
addInteractionEntity(part);
}
+ else if (VersionUtils.IS_1_21_9 && part.type == SpawnedDisplayEntityPart.PartType.MANNEQUIN){
+ addMannequinEntity(part);
+ }
else{
if (!part.isMaster()){
addDisplayEntity(part, packetGroup);
@@ -105,81 +109,34 @@ private DisplayEntity addDisplayEntity(PacketDisplayEntityPart part, PacketDispl
return display;
}
- private InteractionEntity addInteractionEntity(Interaction entity){
- InteractionEntity interaction = new InteractionEntity(entity);
- interactionEntities.add(interaction);
- return interaction;
- }
-
- private InteractionEntity addInteractionEntity(PacketDisplayEntityPart part){
- InteractionEntity interaction = new InteractionEntity(part);
- interactionEntities.add(interaction);
- return interaction;
- }
-
-
- /**
- * Get the DisplayEntities in this group
- * @return DisplayEntity List containing the ones in this group
- */
- List getDisplayEntities() {
- return new ArrayList<>(displayEntities);
+ private void addInteractionEntity(Interaction entity){
+ interactionEntities.add(new InteractionEntity(entity));
}
- /**
- * Get the DisplayEntities in this group of the specified type
- * @return DisplayEntity List containing the ones in this group with the specified type
- */
- List getDisplayEntities(DisplayEntity.Type type){
- ArrayList displayEntities = new ArrayList<>();
- for (DisplayEntity entity : this.displayEntities){
- if (entity.getType() == type){
- displayEntities.add(entity);
- }
- }
- return displayEntities;
+ private void addInteractionEntity(PacketDisplayEntityPart part){
+ interactionEntities.add(new InteractionEntity(part));
}
- /**
- * Get the InteractionEntities in this group
- * @return InteractionEntity List containing the ones in this group
- */
- List getInteractionEntities() {
- return new ArrayList<>(interactionEntities);
+ private void addMannequinEntity(Entity entity){
+ mannequinEntities.add(SavedEntityBuilder.buildMannequin(entity));
}
- /**
- * Get whether this DisplayEntityGroup has display entities
- * @return boolean of whether the group has display entities
- */
- public boolean hasDisplayEntities(){
- return !displayEntities.isEmpty();
+ private void addMannequinEntity(PacketDisplayEntityPart part){
+ mannequinEntities.add(SavedEntityBuilder.buildMannequin(part));
}
- /**
- * Get whether this DisplayEntityGroup has display entities of the specified type
- * @return boolean of whether the group has display entities of the specified type
- */
- public boolean hasDisplayEntities(DisplayEntity.Type type){
- for (DisplayEntity entity : displayEntities){
- if (entity.getType() == type){
- return true;
- }
- }
- return false;
- }
/**
- * Get whether this DisplayEntityGroup has interaction entities
- * @return boolean of whether the group has interaction entities
+ * Get whether this group has interaction entities
+ * @return a boolean
*/
public boolean hasInteractionEntities(){
return !interactionEntities.isEmpty();
}
/**
- * Get the tag of this Display Entity Group
- * @return This DisplayEntityGroup's Tag
+ * Get this group's tag
+ * @return a string
*/
public String getTag() {
return tag;
@@ -199,7 +156,7 @@ public String getTag() {
* Spawns this {@link DisplayEntityGroup} at a specified location returning a {@link SpawnedDisplayEntityGroup} that represents this.
* @param location The location to spawn the group
* @param spawnReason The reason for this display entity group to spawn
- * @param settings The settings to apply to every display and interaction entity created from this. This may be overridden with the {@link PreGroupSpawnedEvent}.
+ * @param settings The settings to apply when spawning this group. This may be overridden with the {@link PreGroupSpawnedEvent}.
* @return A {@link SpawnedDisplayEntityGroup} representative of this. Null if the {@link PreGroupSpawnedEvent} is cancelled
*/
public @Nullable SpawnedDisplayEntityGroup spawn(@NotNull Location location, @NotNull GroupSpawnedEvent.SpawnReason spawnReason, @NotNull GroupSpawnSettings settings){
@@ -236,12 +193,19 @@ public String getTag() {
masterDisplay.getLocation(),
settings);
- SpawnedDisplayEntityPart part = group.addInteractionEntity(interaction);
+ SpawnedDisplayEntityPart part = group.addEntity(interaction);
if (entity.hasLegacyPartTags()){
part.adaptScoreboardTags(true);
}
}
+ if (mannequinEntities != null){
+ for (MannequinEntity entity : mannequinEntities){
+ Entity e = SavedEntityLoader.spawnMannequin(masterDisplay.getLocation(), settings, entity);
+ group.addEntity(e);
+ }
+ }
+
group.setPersistenceOverride(settings.persistenceOverride);
if (tag != null){
@@ -261,9 +225,6 @@ public String getTag() {
return group;
}
-
-
-
/**
* Spawns this {@link DisplayEntityGroup} at a specified location returning a {@link PacketDisplayEntityGroup} that represents this.
* @param spawnLocation The location where this group spawn be spawned for players
@@ -314,7 +275,7 @@ public String getTag() {
* @param spawnLocation The location where this group spawn be spawned for players
* @param spawnReason The reason for this display entity group to spawn
* @param playSpawnAnimation whether this packet group should automatically play its spawn animation when created
- * @param settings The settings to apply to every display and interaction entity created from this. This may be overridden with the {@link PrePacketGroupCreateEvent}. The persistence of the settings is ignored for packet-based groups
+ * @param settings The settings to apply when spawning this group. This may be overridden with the {@link PrePacketGroupCreateEvent}. The persistence of the settings is ignored for packet-based groups
* @return A {@link PacketDisplayEntityGroup} representative of this. Null if the {@link PrePacketGroupCreateEvent} is cancelled
*/
public @Nullable PacketDisplayEntityGroup createPacketGroup(@NotNull Location spawnLocation, @NotNull GroupSpawnedEvent.SpawnReason spawnReason, boolean playSpawnAnimation, @NotNull GroupSpawnSettings settings){
@@ -351,6 +312,14 @@ public String getTag() {
packetGroup.addPartSilent(part);
}
+ if (mannequinEntities != null){ //old models won't have this field
+ for (MannequinEntity entity : mannequinEntities){
+ PacketDisplayEntityPart part = entity.createPacketPart(spawnLocation, settings);
+ packetGroup.addPartSilent(part);
+ }
+ }
+
+
if (playSpawnAnimation){
packetGroup.playSpawnAnimation();
}
@@ -363,6 +332,10 @@ public String getTag() {
packetGroup.autoCull(widthCullingAdder, heightCullingAdder);
}
+ if (spawnReason == GroupSpawnedEvent.SpawnReason.CHUNK_LOAD_PLACED || spawnReason == GroupSpawnedEvent.SpawnReason.ITEMSTACK){
+ packetGroup.isPlaced = true;
+ }
+
return packetGroup;
}
}
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 6bdb532c..cebeffb4 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
@@ -22,7 +22,7 @@
public class FramePoint extends RelativePoint implements Serializable {
Set particles = new HashSet<>();
- Map sounds = new HashMap<>();
+ Map sounds = new HashMap<>();
@Serial
private static final long serialVersionUID = 99L;
@@ -44,7 +44,7 @@ public FramePoint(@NotNull FramePoint point) {
for (AnimationParticle p : point.particles){
this.particles.add(p.clone());
}
- for (Map.Entry entry : point.sounds.entrySet()){
+ for (Map.Entry entry : point.sounds.entrySet()){
this.sounds.put(entry.getKey(), entry.getValue().clone());
}
}
@@ -234,7 +234,7 @@ public void showParticles(@NotNull Location location){
* @param limited whether to limit the played audio only to players who can visibly see the group
*/
public void playSounds(@NotNull ActiveGroup> group, @Nullable DisplayAnimator animator, boolean limited){
- for (AnimationSound sound : sounds.values()){
+ for (DEUSound sound : sounds.values()){
if (limited){
sound.playSound(getLocation(group), group, animator, getPlayers(group));
}
@@ -252,7 +252,7 @@ public void playSounds(@NotNull ActiveGroup> group, @Nullable DisplayAnimator
* @param limited whether to limit the played audio only to players who can visibly see the group
*/
public void playSounds(@NotNull ActiveGroup> group, @NotNull Location location, @Nullable DisplayAnimator animator, boolean limited){
- for (AnimationSound sound : sounds.values()){
+ for (DEUSound sound : sounds.values()){
if (limited){
sound.playSound(location, group, animator, getPlayers(group));
}
@@ -284,7 +284,7 @@ public void playSounds(@NotNull ActiveGroup> group, boolean limited){
* @param player the player
*/
public void playSounds(@NotNull Location location, @NotNull Player player){
- for (AnimationSound sound : sounds.values()){
+ for (DEUSound sound : sounds.values()){
sound.playSound(location, player);
}
}
@@ -295,7 +295,7 @@ public void playSounds(@NotNull Location location, @NotNull Player player){
* @param players the players
*/
public void playSounds(@NotNull Location location, @NotNull Collection players){
- for (AnimationSound sound : sounds.values()){
+ for (DEUSound sound : sounds.values()){
sound.playSound(location, players);
}
}
@@ -323,23 +323,23 @@ public void playSounds(@NotNull ActiveGroup> group, @NotNull Collection getParticles(){
}
/**
- * Get the {@link AnimationSound}s that will be played at this frame point
- * @return a collection of {@link AnimationSound}
+ * Get the {@link DEUSound}s that will be played at this frame point
+ * @return a collection of {@link DEUSound}
*/
- public Collection getSounds(){
+ public Collection getSounds(){
return sounds.values();
}
@@ -435,27 +435,8 @@ public void sendInfo(Player player){
player.sendMessage(Component.empty());
//Sounds
- player.sendMessage(MiniMessage.miniMessage().deserialize("Sounds: "+sounds.size()));
- if (sounds.isEmpty()){
- player.sendMessage(Component.text("| NONE", NamedTextColor.GRAY));
- }
- else{
- for (AnimationSound sound : sounds.values()){
- Component msgComp = Component.text("- "+sound.getSoundName(), NamedTextColor.YELLOW);
- Component hoverComp = Component.text("| Vol: "+sound.getVolume()+", Pitch: "+sound.getPitch(), NamedTextColor.GRAY);
- if (!sound.existsInGameVersion()){
- msgComp = msgComp.append(Component.text(" [UNKNOWN]", NamedTextColor.GRAY));
- hoverComp = hoverComp
- .append(Component.newline())
- .append(Component.text("This sound no longer exists!", NamedTextColor.RED));
- }
-
- msgComp = msgComp.hoverEvent(HoverEvent.showText(hoverComp));
- player.sendMessage(msgComp);
- }
- }
+ DEUSound.sendInfo(sounds.values(), player, null, null);
- player.sendMessage(Component.empty());
player.sendMessage(MiniMessage.miniMessage().deserialize("RIGHTclick to preview effects"));
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupEntityFollower.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupEntityFollower.java
index 56d048de..33f89ab0 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupEntityFollower.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupEntityFollower.java
@@ -173,7 +173,7 @@ private void apply(Entity entity, MultiPartSelection> selection, float newYaw,
private void pivotDisplayPitch(ActivePart part, boolean zero, float newPitch){
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!part.isDisplay()){
return;
}
Transformation t = part.getTransformation();
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupPoint.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupPoint.java
deleted file mode 100644
index 9e2d5adb..00000000
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupPoint.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.donnypz.displayentityutils.utils.DisplayEntities;
-
-import org.bukkit.Location;
-import org.bukkit.util.Vector;
-import org.jetbrains.annotations.ApiStatus;
-import org.jetbrains.annotations.NotNull;
-import org.joml.Vector3f;
-
-import java.io.Serializable;
-
-@ApiStatus.Experimental
-public class GroupPoint extends RelativePoint implements Serializable {
- public GroupPoint(@NotNull String pointTag, @NotNull SpawnedDisplayEntityGroup group, @NotNull Location location) {
- super(pointTag, group, location);
- }
-
- GroupPoint(@NotNull String pointTag, @NotNull Vector vector, float initialYaw, float initialPitch) {
- super(pointTag, vector, initialYaw, initialPitch);
- }
-
- GroupPoint(@NotNull String pointTag, @NotNull Vector3f vector, float initialYaw, float initialPitch) {
- super(pointTag, vector, initialYaw, initialPitch);
- }
-
- protected GroupPoint(@NotNull GroupPoint point) {
- super(point);
- }
-}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupSpawnSettings.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupSpawnSettings.java
index be49aa02..6080f22e 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupSpawnSettings.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/GroupSpawnSettings.java
@@ -7,10 +7,7 @@
import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttributes;
import org.bukkit.Bukkit;
import org.bukkit.Location;
-import org.bukkit.entity.Display;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.Interaction;
-import org.bukkit.entity.Player;
+import org.bukkit.entity.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -154,7 +151,7 @@ boolean applyVisibility(PacketDisplayEntityPart part, Player player){
}
boolean applyAttributes(PacketDisplayEntityPart part){
- if (part.type != SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (part.isDisplay()){
//Teleport Duration
part.attributeContainer.setAttribute(DisplayAttributes.TELEPORTATION_DURATION, teleportationDuration);
@@ -285,21 +282,35 @@ void apply(Interaction interaction){
interaction.setPersistent(persistentByDefault);
}
- private void determineVisibleByDefault(Display display){
- for (String tag : hiddenPartTags.keySet()){
- if (DisplayUtils.hasPartTag(display, tag)){
- display.setVisibleByDefault(false);
- reveal(display, tag);
- break;
+ void apply(Mannequin mannequin){
+ //Determine Visibility
+ if (!visibleByDefault){
+ mannequin.setVisibleByDefault(false);
+ if (!visiblePlayers.isEmpty()){ //Reveal for players
+ for (UUID uuid : visiblePlayers){
+ Player player = Bukkit.getPlayer(uuid);
+ if (player != null && player.isOnline()){
+ player.showEntity(DisplayAPI.getPlugin(), mannequin);
+ }
+ }
+ }
+ }
+ else{
+ if (hiddenPartTags.isEmpty()){
+ mannequin.setVisibleByDefault(true);
+ }
+ else{
+ determineVisibleByDefault(mannequin);
}
}
}
- private void determineVisibleByDefault(Interaction interaction){
+
+ private void determineVisibleByDefault(Entity entity){
for (String tag : hiddenPartTags.keySet()){
- if (DisplayUtils.hasPartTag(interaction, tag)){
- interaction.setVisibleByDefault(false);
- reveal(interaction, tag);
+ if (DisplayUtils.hasPartTag(entity, tag)){
+ entity.setVisibleByDefault(false);
+ reveal(entity, tag);
break;
}
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/InteractionEntity.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/InteractionEntity.java
index 2a90bbc3..8cd51ca0 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/InteractionEntity.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/InteractionEntity.java
@@ -31,15 +31,16 @@ final class InteractionEntity implements Serializable {
UUID partUUID;
float height;
float width;
- private byte[] persistentDataContainer = null;
+ byte[] persistentDataContainer = null;
boolean isResponsive;
+ InteractionEntity(){}
+
InteractionEntity(Interaction interaction){
this.height = interaction.getInteractionHeight();
this.width = interaction.getInteractionWidth();
this.isResponsive = interaction.isResponsive();
- this.vector = DisplayUtils.getInteractionTranslation(interaction).toVector3f();
- this.partUUID = DisplayUtils.getPartUUID(interaction);
+ this.vector = DisplayUtils.getNonDisplayTranslation(interaction).toVector3f();
try{
persistentDataContainer = interaction.getPersistentDataContainer().serializeToBytes();
@@ -55,13 +56,13 @@ final class InteractionEntity implements Serializable {
this.height = c.getAttributeOrDefault(DisplayAttributes.Interaction.HEIGHT, 1f);
this.width = c.getAttributeOrDefault(DisplayAttributes.Interaction.WIDTH, 1f);
this.isResponsive = c.getAttributeOrDefault(DisplayAttributes.Interaction.RESPONSIVE, false);
- this.vector = part.getInteractionTranslation().toVector3f();
- this.partUUID = part.partUUID;
+ this.vector = part.getNonDisplayTranslation().toVector3f();
try{
ItemStack i = new ItemStack(Material.STICK);
PersistentDataContainer pdc = i.getItemMeta().getPersistentDataContainer();
pdc.set(DisplayAPI.getPartPDCTagKey(), PersistentDataType.LIST.strings(), new ArrayList<>(part.getTags()));
+ pdc.set(DisplayAPI.getPartUUIDKey(), PersistentDataType.STRING, part.partUUID.toString());
persistentDataContainer = pdc.serializeToBytes();
}
catch(IOException e){
@@ -92,7 +93,11 @@ Interaction createEntity(Location origin, GroupSpawnSettings settings){
}
if (partUUID != null){
- i.getPersistentDataContainer().set(DisplayAPI.getPartUUIDKey(), PersistentDataType.STRING, partUUID.toString());
+ i
+ .getPersistentDataContainer()
+ .set(DisplayAPI.getPartUUIDKey(),
+ PersistentDataType.STRING,
+ partUUID.toString());
}
settings.apply(i);
@@ -123,10 +128,10 @@ PacketDisplayEntityPart createPacketPart(Location origin, GroupSpawnSettings set
}
part.partTags = DisplayEntity.getSetFromPDC(pdc, DisplayAPI.getPartPDCTagKey());
- part.partUUID = DisplayEntity.getPDCPartUUID(pdc);
+ part.partUUID = partUUID != null ? partUUID : DisplayEntity.getPDCPartUUID(pdc);
part.interactionCommands = getInteractionCommands(pdc);
}
- settings.applyAttributes(part);
+ if (settings != null) settings.applyAttributes(part);
return part;
}
@@ -147,20 +152,4 @@ Vector getVector(){
boolean hasLegacyPartTags(){
return partTags != null && !partTags.isEmpty();
}
-
- ArrayList getLegacyPartTags() {
- return partTags;
- }
-
- float getHeight() {
- return height;
- }
-
- float getWidth() {
- return width;
- }
-
- boolean isReponsive(){
- return isResponsive;
- }
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/InteractionTransformation.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/InteractionTransformation.java
index 8fedab40..d794061e 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/InteractionTransformation.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/InteractionTransformation.java
@@ -30,6 +30,16 @@ public InteractionTransformation(){}
this.width = width;
}
+
+ InteractionTransformation(Vector vector, float groupYawAtCreation, float groupPitchAtCreation, float height, float width){
+ super(vector.toVector3f());
+ this.vector = new Vector(vector.getX(), vector.getY(), vector.getZ());
+ this.groupYawAtCreation = groupYawAtCreation;
+ this.groupPitchAtCreation = groupPitchAtCreation;
+ this.height = height;
+ this.width = width;
+ }
+
@ApiStatus.Internal
public void writeExternal(ObjectOutput out) throws IOException {
out.writeFloat(this.x);
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_DisplayEntity.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_DisplayEntity.java
index 68691b63..23218f84 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_DisplayEntity.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_DisplayEntity.java
@@ -10,6 +10,7 @@
import java.io.IOException;
import java.lang.reflect.Type;
+import java.util.Base64;
import java.util.List;
@ApiStatus.Internal
@@ -25,15 +26,22 @@ public DisplayEntity deserialize(JsonElement json, Type typeOfT, JsonDeserializa
entity.type = DisplayEntity.Type.valueOf(obj.get("type").getAsString().toUpperCase());
entity.isMaster = obj.get("isMaster").getAsBoolean();
- List list = ctx.deserialize(obj.get("persistentDataContainer"), List.class);
- byte[] bytes = new byte[list.size()];
- for (int i = 0; i < list.size(); i++) {
- bytes[i] = list.get(i).byteValue();
+ if (obj.has(DEUJSONAdapter.PDC_FIELD)){
+ try{
+ List list = ctx.deserialize(obj.get(DEUJSONAdapter.PDC_FIELD), List.class);
+ byte[] bytes = new byte[list.size()];
+ for (int i = 0; i < list.size(); i++) {
+ bytes[i] = list.get(i).byteValue();
+ }
+
+ entity.persistentDataContainer = bytes;
+ }
+ catch(JsonParseException e){
+ String base64 = ctx.deserialize(obj.get(DEUJSONAdapter.PDC_FIELD), String.class);
+ entity.persistentDataContainer = Base64.getDecoder().decode(base64);
+ }
}
- entity.persistentDataContainer = bytes;
-
-
JsonObject specificsJson = obj.getAsJsonObject("specifics");
switch (entity.type) {
case BLOCK:
@@ -59,6 +67,9 @@ public JsonElement serialize(DisplayEntity src, Type typeOfSrc, JsonSerializatio
//Convert Specifics, and remove legacy part tags array
json.add("specifics", ctx.serialize(src.specifics, DisplayEntitySpecifics.class));
+ //partUUID
+ json.addProperty(DEUJSONAdapter.PART_UUID_FIELD, src.specifics.getPartUUID().toString());
+
//Add part tags
try{
PersistentDataContainer pdc = new ItemStack(Material.STICK).getItemMeta().getPersistentDataContainer();
@@ -66,14 +77,13 @@ public JsonElement serialize(DisplayEntity src, Type typeOfSrc, JsonSerializatio
List tags = pdc.get(DisplayAPI.getPartPDCTagKey(), PersistentDataType.LIST.strings());
json.add("partTags", new Gson().toJsonTree(tags));
}
- catch(IOException e){}
+ catch(IOException | NullPointerException e){}
- //Convert byte arr to int arr
- JsonArray arr = new JsonArray();
- for (byte b : src.persistentDataContainer){
- arr.add((int) b);
+ //pdc to base64
+ byte[] pdc = src.persistentDataContainer;
+ if (pdc != null){
+ json.addProperty(DEUJSONAdapter.PDC_FIELD, Base64.getEncoder().encodeToString(pdc));
}
- json.add("persistentDataContainer", arr);
return json;
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_InteractionEntity.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_InteractionEntity.java
new file mode 100644
index 00000000..bcf83380
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_InteractionEntity.java
@@ -0,0 +1,76 @@
+package net.donnypz.displayentityutils.utils.DisplayEntities;
+
+import com.google.gson.*;
+import net.donnypz.displayentityutils.DisplayAPI;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.joml.Vector3f;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.Base64;
+import java.util.List;
+import java.util.UUID;
+
+public class JSONAdapter_InteractionEntity implements JsonSerializer, JsonDeserializer {
+
+ @Override
+ public InteractionEntity deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext ctx) throws JsonParseException {
+ JsonObject obj = json.getAsJsonObject();
+ InteractionEntity entity = new InteractionEntity();
+
+ entity.vector = ctx.deserialize(obj.get("vector"), Vector3f.class);
+ entity.partUUID = UUID.fromString(obj.get(DEUJSONAdapter.PART_UUID_FIELD).getAsString());
+ entity.height = obj.get("height").getAsFloat();
+ entity.width = obj.get("width").getAsFloat();
+ entity.isResponsive = obj.get("isResponsive").getAsBoolean();
+
+
+ //PDC
+ String base64 = ctx.deserialize(obj.get(DEUJSONAdapter.PDC_FIELD), String.class);
+ entity.persistentDataContainer = Base64.getDecoder().decode(base64);
+
+ return null;
+ }
+
+
+ @Override
+ public JsonElement serialize(InteractionEntity src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject json = new Gson().toJsonTree(src).getAsJsonObject();
+ PersistentDataContainer pdc = new ItemStack(Material.STICK).getItemMeta().getPersistentDataContainer();
+ byte[] pdcBytes = src.persistentDataContainer;
+
+
+
+ if (pdcBytes != null){
+
+ //Add part tags
+ try{
+ pdc.readFromBytes(pdcBytes);
+ List tags = pdc.get(DisplayAPI.getPartPDCTagKey(), PersistentDataType.LIST.strings());
+ json.add("partTags", new Gson().toJsonTree(tags));
+ }
+ catch(IOException | NullPointerException e){}
+
+ //partuuid
+ UUID partUUID;
+ if (src.partUUID != null){
+ partUUID = src.partUUID;
+ }
+ else{
+ partUUID = UUID.fromString(pdc.get(DisplayAPI.getPartUUIDKey(), PersistentDataType.STRING));
+ }
+ json.addProperty(DEUJSONAdapter.PART_UUID_FIELD, partUUID.toString());
+
+
+ //pdc to base 64
+ json.addProperty(DEUJSONAdapter.PDC_FIELD, Base64.getEncoder().encodeToString(pdcBytes));
+ }
+
+ return json;
+ }
+
+
+}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_MannequinEntity.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_MannequinEntity.java
new file mode 100644
index 00000000..8455fca6
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/JSONAdapter_MannequinEntity.java
@@ -0,0 +1,123 @@
+package net.donnypz.displayentityutils.utils.DisplayEntities;
+
+import com.google.gson.*;
+import net.donnypz.displayentityutils.DisplayAPI;
+import org.bukkit.Material;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.joml.Vector3f;
+
+import java.io.IOException;
+import java.lang.reflect.Type;
+import java.util.Base64;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class JSONAdapter_MannequinEntity implements JsonSerializer, JsonDeserializer {
+ Gson gson = new Gson();
+ Map AIR_AS_MAP = new ItemStack(Material.AIR).serialize();
+
+ JSONAdapter_MannequinEntity(){}
+
+
+ @Override
+ public JsonElement serialize(MannequinEntity src, Type typeOfSrc, JsonSerializationContext context) {
+ JsonObject json = gson.toJsonTree(src).getAsJsonObject();
+
+ //Equipment items as json objects
+ JsonArray equipmentArr = new JsonArray();
+ for (byte[] itemBytes : src.equipment){
+ Map itemStackMap;
+ if (itemBytes != null){
+ itemStackMap = ItemStack.deserializeBytes(itemBytes).serialize();
+ }
+ else{
+ itemStackMap = AIR_AS_MAP;
+ }
+ JsonElement jsonElement = gson.toJsonTree(itemStackMap);
+ equipmentArr.add(jsonElement);
+
+ }
+
+ json.add("equipment", equipmentArr);
+
+ //Add part tags
+ try{
+ PersistentDataContainer pdc = new ItemStack(Material.STICK).getItemMeta().getPersistentDataContainer();
+ pdc.readFromBytes(src.persistentDataContainer);
+ List tags = pdc.get(DisplayAPI.getPartPDCTagKey(), PersistentDataType.LIST.strings());
+ json.add("partTags", new Gson().toJsonTree(tags));
+ }
+ catch(IOException | NullPointerException e){}
+
+ //pdc to base 64
+ json.addProperty(DEUJSONAdapter.PDC_FIELD,
+ Base64.getEncoder().encodeToString(src.persistentDataContainer));
+ return json;
+ }
+
+ @Override
+ public MannequinEntity deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
+ JsonObject obj = json.getAsJsonObject();
+
+ MannequinEntity mannequin = new MannequinEntity();
+
+ mannequin.vector = context.deserialize(obj.get("vector"), Vector3f.class);
+
+ mannequin.customName = getString(obj, "customName");
+ mannequin.customNameVisible = getBoolean(obj, "customNameVisible");
+ mannequin.description = getString(obj, "description");
+
+ mannequin.profileName = getString(obj, "profileName");
+ mannequin.profileUUID = obj.has("profileUUID") && !obj.get("profileUUID").isJsonNull()
+ ? UUID.fromString(obj.get("profileUUID").getAsString())
+ : null;
+
+ mannequin.scale = getDouble(obj, "scale");
+ mannequin.pose = getString(obj, "pose");
+ mannequin.isRightMainHand = getBoolean(obj, "isRightMainHand");
+
+ //Equipment
+ if (obj.has("equipment") && obj.get("equipment").isJsonArray()) {
+ JsonArray arr = obj.getAsJsonArray("equipment");
+ mannequin.equipment = new byte[arr.size()][];
+
+ for (int i = 0; i < arr.size(); i++) {
+ JsonElement el = arr.get(i);
+ if (el == null || el.isJsonNull()) {
+ mannequin.equipment[i] = null;
+ continue;
+ }
+
+ Map map = gson.fromJson(el, Map.class);
+ ItemStack stack = ItemStack.deserialize(map);
+ try{
+ mannequin.equipment[i] = stack.serializeAsBytes();
+ }
+ catch(IllegalArgumentException e){} //thrown if itemstack is empty (air, or 0 count)
+ }
+ }
+
+ //PDC
+ if (obj.has("persistentDataContainer") && !obj.get("persistentDataContainer").isJsonNull()) {
+ String pdcBase64 = obj.get("persistentDataContainer").getAsString();
+ mannequin.persistentDataContainer = Base64.getDecoder().decode(pdcBase64);
+ }
+
+ return mannequin;
+ }
+
+ private static String getString(JsonObject obj, String key) {
+ return obj.has(key) && !obj.get(key).isJsonNull() ? obj.get(key).getAsString() : null;
+ }
+
+ private static boolean getBoolean(JsonObject obj, String key) {
+ return obj.has(key) && !obj.get(key).isJsonNull() && obj.get(key).getAsBoolean();
+ }
+
+ private static double getDouble(JsonObject obj, String key) {
+ return obj.has(key) && !obj.get(key).isJsonNull() ? obj.get(key).getAsDouble() : 0.0D;
+ }
+}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/MannequinEntity.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/MannequinEntity.java
new file mode 100644
index 00000000..c01d33f4
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/MannequinEntity.java
@@ -0,0 +1,160 @@
+package net.donnypz.displayentityutils.utils.DisplayEntities;
+
+import net.donnypz.displayentityutils.DisplayAPI;
+import net.donnypz.displayentityutils.utils.DisplayUtils;
+import net.donnypz.displayentityutils.utils.packet.PacketAttributeContainer;
+import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttributes;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.entity.Pose;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MainHand;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.util.Vector;
+import org.joml.Vector3f;
+
+import java.io.IOException;
+import java.io.Serial;
+import java.io.Serializable;
+import java.util.List;
+import java.util.UUID;
+
+
+//DTO
+final class MannequinEntity implements Serializable {
+ @Serial
+ private static final long serialVersionUID = 99L;
+
+ Vector3f vector;
+ String customName;
+ boolean customNameVisible;
+ String description;
+ String profileName;
+ UUID profileUUID;
+ List profileProperties;
+ String profileSkinPatchBody;
+ String profileSkinPatchCape;
+ String profileSkinPatchElytra;
+ String profileSkinPatchModel;
+
+ double scale;
+ String pose;
+ boolean isRightMainHand;
+ byte[][] equipment; //0,1,2,3,4,5 = helm,chest,legs,boots,main,off | x bytes for itemstack
+ byte[] persistentDataContainer;
+
+
+ MannequinEntity(){}
+
+ PacketDisplayEntityPart createPacketPart(Location origin, GroupSpawnSettings settings){
+ PacketAttributeContainer attributeContainer = new PacketAttributeContainer()
+ .setAttribute(DisplayAttributes.Mannequin.SCALE, (float) scale)
+ .setAttribute(DisplayAttributes.Mannequin.IMMOVABLE, true)
+ .setAttribute(DisplayAttributes.Mannequin.NO_GRAVITY, true)
+ .setAttribute(DisplayAttributes.CUSTOM_NAME_VISIBLE, customNameVisible)
+ .setAttribute(DisplayAttributes.Mannequin.POSE, Pose.valueOf(pose))
+ .setAttribute(DisplayAttributes.Mannequin.MAIN_HAND, isRightMainHand ? MainHand.RIGHT : MainHand.LEFT)
+ .setAttribute(DisplayAttributes.Equipment.HELMET, getHelmet())
+ .setAttribute(DisplayAttributes.Equipment.CHESTPLATE, getChestplate())
+ .setAttribute(DisplayAttributes.Equipment.LEGGINGS, getLeggings())
+ .setAttribute(DisplayAttributes.Equipment.BOOTS, getBoots())
+ .setAttribute(DisplayAttributes.Equipment.MAIN_HAND, getMainHand())
+ .setAttribute(DisplayAttributes.Equipment.OFF_HAND, getOffHand());
+
+ if (customName != null){
+ attributeContainer.setAttribute(DisplayAttributes.CUSTOM_NAME, MiniMessage.miniMessage().deserialize(customName));
+ }
+
+ if (description != null){
+ attributeContainer.setAttribute(DisplayAttributes.Mannequin.BELOW_NAME, MiniMessage.miniMessage().deserialize(description));
+ }
+
+ if (profileName != null || profileUUID != null){
+ attributeContainer
+ .setAttribute(
+ DisplayAttributes.Mannequin.RESOLVABLE_PROFILE,
+ SavedEntityLoader.getMannequinProfile(this)
+ );
+ }
+
+ Location spawnLoc = DisplayUtils.getPivotLocation(
+ vector,
+ origin,
+ origin.getYaw());
+
+ PacketDisplayEntityPart part = attributeContainer.createPart(SpawnedDisplayEntityPart.PartType.MANNEQUIN, spawnLoc);
+
+ ItemStack i = new ItemStack(Material.STICK);
+ PersistentDataContainer pdc = i.getItemMeta().getPersistentDataContainer();
+
+ try {
+ pdc.readFromBytes(persistentDataContainer);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ part.partTags = DisplayEntity.getSetFromPDC(pdc, DisplayAPI.getPartPDCTagKey());
+ part.partUUID = DisplayEntity.getPDCPartUUID(pdc);
+ if (settings != null) settings.applyAttributes(part);
+
+ return part;
+ }
+
+ ItemStack getHelmet(){
+ return getItemStack(equipment[0]);
+ }
+
+ ItemStack getChestplate(){
+ return getItemStack(equipment[1]);
+ }
+
+ ItemStack getLeggings(){
+ return getItemStack(equipment[2]);
+ }
+
+ ItemStack getBoots(){
+ return getItemStack(equipment[3]);
+ }
+
+ ItemStack getMainHand(){
+ return getItemStack(equipment[4]);
+ }
+
+ ItemStack getOffHand(){
+ return getItemStack(equipment[5]);
+ }
+
+ Vector getVector(){
+ return Vector.fromJOML(vector);
+ }
+
+ ItemStack getItemStack(byte[] itemStack){
+ if (itemStack == null) return new ItemStack(Material.AIR);
+ return ItemStack.deserializeBytes(itemStack);
+ }
+
+ static class ProfileProperty implements Serializable{
+
+ @Serial
+ private static final long serialVersionUID = 99L;
+
+ String name;
+ String value;
+ String signature;
+
+ ProfileProperty(com.destroystokyo.paper.profile.ProfileProperty property){
+ this(property.getName(), property.getValue(), property.getSignature());
+ }
+
+ ProfileProperty(String name, String value, String signature){
+ this.name = name;
+ this.value = value;
+ this.signature = signature;
+ }
+
+ com.destroystokyo.paper.profile.ProfileProperty toBukkitProperty(){
+ return new com.destroystokyo.paper.profile.ProfileProperty(name, value, signature);
+ }
+ }
+}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/MultiPartSelection.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/MultiPartSelection.java
index 64d93f0c..1ecf1d54 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/MultiPartSelection.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/MultiPartSelection.java
@@ -487,7 +487,7 @@ public void unglow(@NotNull Player player){
}
/**
- * Pivot all Interaction parts in this selection around the SpawnedDisplayEntityGroup's master part
+ * Pivot all non-display parts in this selection around this selection's group
* @param angleInDegrees the pivot angle
*/
@Override
@@ -500,11 +500,12 @@ public void pivot(float angleInDegrees){
/**
* Set the yaw of all parts in this selection
* @param yaw the yaw to set
+ * @param pivot whether non-display entities should pivot around the group
*/
@Override
- public void setYaw(float yaw, boolean pivotInteractions){
+ public void setYaw(float yaw, boolean pivot){
for (T part : selectedParts){
- part.setYaw(yaw, pivotInteractions);
+ part.setYaw(yaw, pivot);
}
}
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 5c9c42c6..c956b616 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
@@ -34,13 +34,13 @@ public class PacketDisplayEntityGroup extends ActiveGroup allPacketGroups = new ConcurrentHashMap<>();
private static final ConcurrentHashMap groupVehicles = new ConcurrentHashMap<>();
- int interactionCount;
int[] passengerIds;
UUID vehicleUUID;
boolean autoShow;
Predicate autoShowCondition;
int persistentLocalId = -1;
String persistentGlobalId;
+ boolean isPlaced;
PacketDisplayEntityGroup(String tag){
@@ -126,6 +126,11 @@ public static String buildPersistentGlobalId(@NotNull Chunk chunk, int localId){
return chunk.getWorld().getName()+"|"+chunk.getChunkKey()+"|"+localId; //world,chunkkey,localid
}
+ /**
+ * {@inheritDoc}
+ *
The group cannot become persistent if {@link #isRiding()} is true
+ * The group cannot become non-persistent if {@link #isPlaced()} is true
+ */
@Override
public void setPersistent(boolean persistent) {
if (persistent){
@@ -135,7 +140,7 @@ public void setPersistent(boolean persistent) {
}
}
else{
- if (isPersistent()){
+ if (isPersistent() && !isPlaced){
DisplayGroupManager.removePersistentPacketGroup(this, false);
setPersistentIds(-1, null);
}
@@ -151,6 +156,14 @@ public boolean isPersistent(){
return this.persistentLocalId != -1;
}
+ /**
+ * Get whether this group was placed by a player's held item
+ * @return a boolean
+ */
+ public boolean isPlaced(){
+ return this.isPlaced;
+ }
+
@ApiStatus.Internal
public static void removeWorld(@NotNull World world){
//Viewers are already removed since this is only called on unloaded worlds (Viewers are forced to a new world)
@@ -161,7 +174,7 @@ void updateChunkAndWorld(@NotNull Location location){
Location oldLoc = getLocation();
//Remove from previous
if (oldLoc != null){
- if (location.getWorld().equals(oldLoc.getWorld()) && location.getChunk().getChunkKey() == oldLoc.getChunk().getChunkKey()){
+ if (location.getWorld().equals(oldLoc.getWorld()) && location.getChunk().getChunkKey() == oldLoc.getChunk().getChunkKey() && vehicleUUID == null){
return;
}
String oldWorldName = oldLoc.getWorld().getName();
@@ -196,10 +209,22 @@ public void chunkUnloadLocation(){
@Override
public void addPart(@NotNull PacketDisplayEntityPart part){
addPartSilent(part);
- updatePartCount(part, true);
+ updatePassengerIds(part.getEntityId(), true);
}
- void addPartSilent(PacketDisplayEntityPart part){
+ /**
+ * {@inheritDoc}
+ * This will create a {@link PacketDisplayEntityPart} representative of the entity
+ */
+ @Override
+ public @Nullable PacketDisplayEntityPart addEntity(@NotNull Entity entity) {
+ PacketDisplayEntityPart part = PacketDisplayEntityPart.getPart(entity, true);
+ if (part == null) return null;
+ addPart(part);
+ return part;
+ }
+
+ void addPartSilent(PacketDisplayEntityPart part){
if (groupParts.get(part.partUUID) == part) return;
if (part.partUUID == null){
@@ -216,21 +241,7 @@ void addPartSilent(PacketDisplayEntityPart part){
}
}
- void updatePartCount(PacketDisplayEntityPart part, boolean add){
- if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION){
- if (add){
- interactionCount++;
- }
- else{
- interactionCount--;
- }
- }
- else{
- updatePassengerIds(part.getEntityId(), add);
- }
- }
-
- private void updatePassengerIds(int passengerId, boolean add){
+ void updatePassengerIds(int passengerId, boolean add){
int[] ids;
if (add){
ids = new int[passengerIds.length+1];
@@ -278,7 +289,7 @@ private void updatePassengerIds(int passengerId, boolean add){
@Override
- public boolean scale(float newScaleMultiplier, int durationInTicks, boolean scaleInteractions) {
+ public boolean scale(float newScaleMultiplier, int durationInTicks, boolean scaleNonDisplays) {
if (newScaleMultiplier <= 0){
throw new IllegalArgumentException("New Scale Multiplier cannot be <= 0");
}
@@ -286,11 +297,11 @@ public boolean scale(float newScaleMultiplier, int durationInTicks, boolean scal
return true;
}
- for (PacketDisplayEntityPart p : groupParts.values()){
+ for (PacketDisplayEntityPart part : groupParts.values()){
//Displays
- if (p.getType() != SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (part.isDisplay()){
DisplayAttributeMap attributeMap = new DisplayAttributeMap();
- Transformation transformation = p.getTransformation();
+ Transformation transformation = part.getTransformation();
//Reset Scale then multiply by newScaleMultiplier
Vector3f scale = transformation.getScale();
scale.x = (scale.x/scaleMultiplier)*newScaleMultiplier;
@@ -303,40 +314,46 @@ public boolean scale(float newScaleMultiplier, int durationInTicks, boolean scal
translationVector.y = (translationVector.y/scaleMultiplier)*newScaleMultiplier;
translationVector.z = (translationVector.z/scaleMultiplier)*newScaleMultiplier;
- if (!transformation.equals(p.getTransformation())){
+ if (!transformation.equals(part.getTransformation())){
attributeMap.add(DisplayAttributes.Interpolation.DURATION, durationInTicks)
.add(DisplayAttributes.Interpolation.DELAY, -1)
.addTransformation(transformation);
}
//Culling
if (DisplayConfig.autoCulling()){
- float[] values = DisplayUtils.getAutoCullValues(p, DisplayConfig.widthCullingAdder(), DisplayConfig.heightCullingAdder());
+ float[] values = DisplayUtils.getAutoCullValues(part, DisplayConfig.widthCullingAdder(), DisplayConfig.heightCullingAdder());
attributeMap.add(DisplayAttributes.Culling.HEIGHT, values[1])
.add(DisplayAttributes.Culling.WIDTH, values[0]);
}
- p.attributeContainer.setAttributesAndSend(attributeMap, p.getEntityId(), p.viewers);
+ part.attributeContainer.setAttributesAndSend(attributeMap, part.getEntityId(), part.viewers);
}
- //Interactions
- else if (scaleInteractions){
-
- //Reset Scale then multiply by newScaleMultiplier
- float newHeight = (p.getInteractionHeight()/scaleMultiplier)*newScaleMultiplier;
- float newWidth = (p.getInteractionWidth()/scaleMultiplier)*newScaleMultiplier;
- PacketUtils.scaleInteraction(p, newHeight, newWidth, durationInTicks, 0);
+ //Non Displays
+ else if (scaleNonDisplays){
+ if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ //Reset Scale then multiply by newScaleMultiplier
+ float newHeight = (part.getInteractionHeight()/scaleMultiplier)*newScaleMultiplier;
+ float newWidth = (part.getInteractionWidth()/scaleMultiplier)*newScaleMultiplier;
+ PacketUtils.scaleInteraction(part, newHeight, newWidth, durationInTicks, 0);
+
+ //Reset Translation then multiply by newScaleMultiplier
+ Vector translationVector = part.getNonDisplayTranslation();
+ if (translationVector == null){
+ continue;
+ }
+ Vector oldVector = new Vector(translationVector.getX(), translationVector.getY(), translationVector.getZ());
+ translationVector.setX((translationVector.getX()/scaleMultiplier)*newScaleMultiplier);
+ translationVector.setY((translationVector.getY()/scaleMultiplier)*newScaleMultiplier);
+ translationVector.setZ((translationVector.getZ()/scaleMultiplier)*newScaleMultiplier);
- //Reset Translation then multiply by newScaleMultiplier
- Vector translationVector = p.getInteractionTranslation();
- if (translationVector == null){
- continue;
+ Vector moveVector = oldVector.subtract(translationVector);
+ PacketUtils.translateNonDisplay(part, moveVector, moveVector.length(), durationInTicks, 0);
+ }
+ else if (part.type == SpawnedDisplayEntityPart.PartType.MANNEQUIN){
+ double scale = part.attributeContainer.getAttributeOrDefault(DisplayAttributes.Mannequin.SCALE, 1f);
+ scale = (scale/scaleMultiplier)*newScaleMultiplier;
+ part.attributeContainer.setAttributeAndSend(DisplayAttributes.Mannequin.SCALE, (float) scale, part.getEntityId(), part.viewers);
}
- Vector oldVector = new Vector(translationVector.getX(), translationVector.getY(), translationVector.getZ());
- translationVector.setX((translationVector.getX()/scaleMultiplier)*newScaleMultiplier);
- translationVector.setY((translationVector.getY()/scaleMultiplier)*newScaleMultiplier);
- translationVector.setZ((translationVector.getZ()/scaleMultiplier)*newScaleMultiplier);
-
- Vector moveVector = oldVector.subtract(translationVector);
- PacketUtils.translateInteraction(p, moveVector, moveVector.length(), durationInTicks, 0);
}
}
@@ -418,7 +435,7 @@ public void run() {
}
updateChunkAndWorld(entity.getLocation());
}
- }, 0, 30);
+ }, 0, 20);
}
return true;
}
@@ -529,23 +546,27 @@ public void setAttributes(@NotNull DisplayAttributeMap attributeMap, SpawnedDisp
}
@Override
- public void setRotation(float pitch, float yaw, boolean pivotIfInteraction){
+ public void setRotation(float pitch, float yaw, boolean pivot){
for (PacketDisplayEntityPart part : groupParts.values()){
- part.setRotation(pitch, yaw, pivotIfInteraction);
+ part.setRotation(pitch, yaw, pivot);
}
}
+ /**
+ * Pivot all non-display parts in this group around the group
+ * @param angleInDegrees the pivot angle
+ */
@Override
public void pivot(float angleInDegrees) {
for (PacketDisplayEntityPart part : groupParts.values()){
- if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!part.isDisplay()){
part.pivot(angleInDegrees);
}
}
}
@Override
- public void teleportMove(Vector direction, double distance, int durationInTicks) {
+ public void teleportMove(@NotNull Vector direction, double distance, int durationInTicks) {
Location destination = getLocation().add(direction.clone().normalize().multiply(distance));
double movementIncrement = distance/(double) Math.max(durationInTicks, 1);
@@ -555,8 +576,8 @@ public void teleportMove(Vector direction, double distance, int durationInTicks)
.multiply(movementIncrement);
for (PacketDisplayEntityPart part : groupParts.values()){
- if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION){
- PacketUtils.translateInteraction(part, direction, distance, durationInTicks, 0);
+ if (!part.isDisplay()){
+ PacketUtils.translateNonDisplay(part, direction, distance, durationInTicks, 0);
}
}
DisplayAPI.getScheduler().partRunTimerAsync(masterPart, new Scheduler.SchedulerRunnable() {
@@ -586,10 +607,11 @@ public void run() {
/**
* {@inheritDoc}
* It is not recommended to use this multiple times in the same tick, unexpected results may occur.
+ *
This will fail if {@link #isRiding()} or {@link #isPlaced()} is true
*/
@Override
public boolean teleport(@NotNull Location tpLocation, boolean respectGroupDirection){
- if (isRiding()) return false;
+ if (isRiding() || isPlaced) return false;
Location oldMasterLoc = getLocation();
attemptLocationUpdate(oldMasterLoc, tpLocation);
@@ -600,7 +622,7 @@ public boolean teleport(@NotNull Location tpLocation, boolean respectGroupDirect
}
masterPart.teleportUnsetPassengers(tpLocation);
for (PacketDisplayEntityPart part : groupParts.values()){
- if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!part.isDisplay()){
Vector vector = oldMasterLoc.toVector().subtract(part.getLocation().toVector());
Location interactionTpLoc = tpLocation.clone().subtract(vector);
part.teleport(interactionTpLoc);
@@ -706,23 +728,28 @@ public PacketDisplayEntityGroup clone(@NotNull Location location){
* @return a cloned {@link PacketDisplayEntityGroup}
*/
public PacketDisplayEntityGroup clone(@NotNull Location location, boolean playSpawnAnimation, boolean autoShow){
- //Reset Interaction pivot to 0 yaw
- HashMap oldYaws = new HashMap<>();
- for (ActivePart part : this.getParts(SpawnedDisplayEntityPart.PartType.INTERACTION)){
- float oldYaw = part.getYaw();
- oldYaws.put(part, oldYaw);
- part.pivot(-oldYaw);
+ //Reset pivot to 0
+ float groupYaw = getLocation().getYaw();
+ HashSet resettedParts = new HashSet<>();
+ for (ActivePart part : groupParts.values()){
+ if (part.isDisplay()) continue;
+ part.pivot(-groupYaw);
+ resettedParts.add(part);
}
+
DisplayEntityGroup group = toDisplayEntityGroup();
+ float newYaw = location.getYaw();
+ location = location.clone();
+ location.setYaw(0);
PacketDisplayEntityGroup clone = group.createPacketGroup(location, GroupSpawnedEvent.SpawnReason.CLONE, playSpawnAnimation, autoShow);
- //Restore Interaction Pivot
- for (Map.Entry entry : oldYaws.entrySet()){
- ActivePart part = entry.getKey();
- float oldYaw = entry.getValue();
- part.pivot(oldYaw);
+ //Restore pivot
+ for (ActivePart part : resettedParts){
+ part.pivot(groupYaw);
}
+
+ clone.setYaw(newYaw, true);
if (this.isPersistent()) clone.setPersistent(true);
return clone;
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketDisplayEntityPart.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketDisplayEntityPart.java
index ff6e5bb9..ab497f24 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketDisplayEntityPart.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/PacketDisplayEntityPart.java
@@ -1,9 +1,12 @@
package net.donnypz.displayentityutils.utils.DisplayEntities;
+import com.destroystokyo.paper.profile.PlayerProfile;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.util.Vector3d;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityHeadLook;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityRotation;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityTeleport;
+import io.papermc.paper.datacomponent.item.ResolvableProfile;
import net.donnypz.displayentityutils.DisplayAPI;
import net.donnypz.displayentityutils.events.GroupSpawnedEvent;
import net.donnypz.displayentityutils.managers.DEUUser;
@@ -16,17 +19,15 @@
import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttribute;
import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttributes;
import net.donnypz.displayentityutils.utils.packet.attributes.TextDisplayOptions;
+import net.donnypz.displayentityutils.utils.version.VersionUtils;
import net.kyori.adventure.text.Component;
-import org.bukkit.Bukkit;
-import org.bukkit.Color;
-import org.bukkit.Location;
-import org.bukkit.NamespacedKey;
+import org.bukkit.*;
import org.bukkit.block.data.BlockData;
-import org.bukkit.entity.Display;
-import org.bukkit.entity.ItemDisplay;
-import org.bukkit.entity.Player;
-import org.bukkit.entity.TextDisplay;
+import org.bukkit.entity.*;
+import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MainHand;
+import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.util.Transformation;
import org.bukkit.util.Vector;
import org.jetbrains.annotations.ApiStatus;
@@ -81,14 +82,39 @@ public PacketDisplayEntityPart(@NotNull SpawnedDisplayEntityPart.PartType partTy
setDefaultTransformValues();
}
+ /**
+ * Create a {@link PacketDisplayEntityPart} representative of the given entity
+ * @return a {@link PacketDisplayEntityPart} or null if the entity is not an eligible part entity
+ */
+ public static @Nullable PacketDisplayEntityPart getPart(@NotNull Entity entity, boolean removeExistingEntity){
+ SpawnedDisplayEntityPart.PartType pt = SpawnedDisplayEntityPart.PartType.getType(entity);
+ if (pt == null) return null;
+ PacketDisplayEntityPart part;
+ if (entity instanceof Display d){
+ part = new DisplayEntity(d, DisplayEntity.Type.fromPartType(pt))
+ .createPacketPart(null, entity.getLocation(), null);
+ }
+ else if (entity instanceof Interaction i){
+ part = new InteractionEntity(i).createPacketPart(entity.getLocation(), null);
+ }
+ else if (VersionUtils.canSpawnMannequins() && entity instanceof Mannequin){
+ MannequinEntity m = SavedEntityBuilder.buildMannequin(entity);
+ part = m.createPacketPart(entity.getLocation(), null);
+ }
+ else{
+ return null;
+ }
+ if (removeExistingEntity) entity.remove();
+ return part;
+ }
+
private void setDefaultTransformValues(){
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION){
- return;
+ if (isDisplay()){
+ attributeContainer.setAttributeIfAbsent(DisplayAttributes.Transform.TRANSLATION, new Vector3f());
+ attributeContainer.setAttributeIfAbsent(DisplayAttributes.Transform.SCALE, new Vector3f(1));
+ attributeContainer.setAttributeIfAbsent(DisplayAttributes.Transform.LEFT_ROTATION, new Quaternionf());
+ attributeContainer.setAttributeIfAbsent(DisplayAttributes.Transform.RIGHT_ROTATION, new Quaternionf());
}
- attributeContainer.setAttributeIfAbsent(DisplayAttributes.Transform.TRANSLATION, new Vector3f());
- attributeContainer.setAttributeIfAbsent(DisplayAttributes.Transform.SCALE, new Vector3f(1));
- attributeContainer.setAttributeIfAbsent(DisplayAttributes.Transform.LEFT_ROTATION, new Quaternionf());
- attributeContainer.setAttributeIfAbsent(DisplayAttributes.Transform.RIGHT_ROTATION, new Quaternionf());
}
/**
@@ -159,8 +185,8 @@ public void showToPlayer(@NotNull Player player, GroupSpawnedEvent.@NotNull Spaw
return;
}
DEUUser.getOrCreateUser(player).trackPacketEntity(this);
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION && hasGroup()){
- Vector translation = getInteractionTranslation(group.getLocation());
+ if (!isDisplay() && hasGroup()){
+ Vector translation = getNonDisplayTranslation(group.getLocation());
location = location.clone().add(translation);
}
attributeContainer.sendEntity(type, getEntityId(), player, location);
@@ -208,8 +234,8 @@ public void showToPlayers(@NotNull Collection players, GroupSpawnedEvent
DEUUser.getOrCreateUser(player).trackPacketEntity(this);
}
}
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION && hasGroup()){
- Vector translation = getInteractionTranslation(group.getLocation());
+ if (!isDisplay() && hasGroup()){
+ Vector translation = getNonDisplayTranslation(group.getLocation());
location = location.clone().add(translation);
}
attributeContainer.sendEntityUsingPlayers(type, getEntityId(), plrs, location);
@@ -289,19 +315,19 @@ public void hideFromPlayers(@NotNull Collection players) {
@Override
public void setTransformation(@NotNull Transformation transformation) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
+ if (!isDisplay()) return;
attributeContainer.setTransformationAndSend(transformation, getEntityId(), viewers);
}
@Override
public void setTransformationMatrix(@NotNull Matrix4f matrix) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
+ if (!isDisplay()) return;
attributeContainer.setTransformationMatrixAndSend(matrix, getEntityId(), viewers);
}
@Override
- public boolean setXScale(float scale) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return false;
+ public boolean setDisplayXScale(float scale) {
+ if (!isDisplay()) return false;
Vector3f vec = attributeContainer.getAttributeOrDefault(DisplayAttributes.Transform.SCALE, new Vector3f());
vec.x = scale;
attributeContainer.setAttributeAndSend(DisplayAttributes.Transform.SCALE, vec, getEntityId(), viewers);
@@ -309,8 +335,8 @@ public boolean setXScale(float scale) {
}
@Override
- public boolean setYScale(float scale) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return false;
+ public boolean setDisplayYScale(float scale) {
+ if (!isDisplay()) return false;
Vector3f vec = attributeContainer.getAttributeOrDefault(DisplayAttributes.Transform.SCALE, new Vector3f());
vec.y = scale;
attributeContainer.setAttributeAndSend(DisplayAttributes.Transform.SCALE, vec, getEntityId(), viewers);
@@ -318,8 +344,8 @@ public boolean setYScale(float scale) {
}
@Override
- public boolean setZScale(float scale) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return false;
+ public boolean setDisplayZScale(float scale) {
+ if (!isDisplay()) return false;
Vector3f vec = attributeContainer.getAttributeOrDefault(DisplayAttributes.Transform.SCALE, new Vector3f());
vec.z = scale;
attributeContainer.setAttributeAndSend(DisplayAttributes.Transform.SCALE, vec, getEntityId(), viewers);
@@ -327,8 +353,8 @@ public boolean setZScale(float scale) {
}
@Override
- public boolean setScale(float x, float y, float z) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return false;
+ public boolean setDisplayScale(float x, float y, float z) {
+ if (!isDisplay()) return false;
Vector3f vec = attributeContainer.getAttributeOrDefault(DisplayAttributes.Transform.SCALE, new Vector3f());
vec.x = x;
vec.y = y;
@@ -337,6 +363,24 @@ public boolean setScale(float x, float y, float z) {
return true;
}
+ @Override
+ public void rotateDisplay(@NotNull Quaternionf rotation, boolean rotateTranslation) {
+ if (!isDisplay()) return;
+ Vector3f translation = attributeContainer.getAttributeOrDefault(DisplayAttributes.Transform.TRANSLATION, new Vector3f());
+ Quaternionf originalRot = attributeContainer.getAttributeOrDefault(DisplayAttributes.Transform.LEFT_ROTATION, new Quaternionf());
+
+ //World Space Rot
+ if (rotateTranslation){
+ translation.rotate(rotation);
+ }
+
+ rotation.mul(originalRot, originalRot);
+
+ attributeContainer.setAttributesAndSend(new DisplayAttributeMap()
+ .add(DisplayAttributes.Transform.TRANSLATION, translation)
+ .add(DisplayAttributes.Transform.LEFT_ROTATION, originalRot), getEntityId(), viewers);
+ }
+
@Override
public void setTextDisplayText(@NotNull Component text) {
@@ -438,13 +482,24 @@ public void setItemDisplayItemGlint(boolean hasGlint) {
if (type != SpawnedDisplayEntityPart.PartType.ITEM_DISPLAY) return;
ItemStack item = getItemDisplayItem();
if (item != null){
- item.editMeta(meta -> {
- meta.setEnchantmentGlintOverride(hasGlint);
- });
+ ItemMeta meta = item.getItemMeta();
+ meta.setEnchantmentGlintOverride(hasGlint);
+ item.setItemMeta(meta);
setAndSend(DisplayAttributes.ItemDisplay.ITEMSTACK, item);
}
}
+ @Override
+ public boolean hasItemDisplayItemGlint() {
+ if (type == SpawnedDisplayEntityPart.PartType.TEXT_DISPLAY){
+ ItemStack i = attributeContainer.getAttribute(DisplayAttributes.ItemDisplay.ITEMSTACK);
+ if (i == null) return false;
+ return i.getItemMeta().getEnchantmentGlintOverride();
+ }
+ return false;
+ }
+
+
@Override
public @Nullable Component getTextDisplayText() {
if (type == SpawnedDisplayEntityPart.PartType.TEXT_DISPLAY){
@@ -531,6 +586,127 @@ public boolean isTextDisplayDefaultBackground() {
return attributeContainer.getAttribute(DisplayAttributes.ItemDisplay.ITEMSTACK);
}
+ @Override
+ public void setMannequinPose(Pose pose) {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ setAndSend(DisplayAttributes.Mannequin.POSE, pose);
+ }
+
+ @Override
+ public @Nullable Pose getMannequinPose() {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return null;
+ return attributeContainer.getAttribute(DisplayAttributes.Mannequin.POSE);
+ }
+
+ @Override
+ public void setMannequinScale(double scale) {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ setAndSend(DisplayAttributes.Mannequin.SCALE, (float) scale);
+ }
+
+ @Override
+ public double getMannequinScale() {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return -1;
+ return attributeContainer.getAttribute(DisplayAttributes.Mannequin.SCALE);
+ }
+
+ @Override
+ public void setMannequinImmovable(boolean immovable) {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ setAndSend(DisplayAttributes.Mannequin.IMMOVABLE, immovable);
+ }
+
+ @Override
+ public boolean isMannequinImmovable() {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return false;
+ return attributeContainer.getAttribute(DisplayAttributes.Mannequin.IMMOVABLE);
+ }
+
+ @Override
+ public void setMannequinGravity(boolean gravity) {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ setAndSend(DisplayAttributes.Mannequin.NO_GRAVITY, !gravity);
+ }
+
+ @Override
+ public boolean hasMannequinGravity() {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return false;
+ return !attributeContainer.getAttribute(DisplayAttributes.Mannequin.NO_GRAVITY);
+ }
+
+ @Override
+ public void setMannequinMainHand(@NotNull MainHand mainHand) {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ setAndSend(DisplayAttributes.Mannequin.MAIN_HAND, mainHand);
+ }
+
+ @Override
+ public @Nullable MainHand getMannequinMainHand() {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return null;
+ return attributeContainer.getAttribute(DisplayAttributes.Mannequin.MAIN_HAND);
+ }
+
+ @Override
+ public @Nullable ItemStack getMannequinEquipment(@NotNull EquipmentSlot equipmentSlot) {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return null;
+ return attributeContainer.getAttributeOrDefault(DisplayAttributes.Equipment.getAttribute(equipmentSlot), new ItemStack(Material.AIR));
+ }
+
+ @Override
+ public void setMannequinEquipment(@NotNull EquipmentSlot slot, @NotNull ItemStack itemStack) {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ setAndSend(DisplayAttributes.Equipment.getAttribute(slot), itemStack);
+ }
+
+ @Override
+ public void setMannequinProfile(@NotNull PlayerProfile profile) {
+ setMannequinProfile(ResolvableProfile.resolvableProfile(profile));
+ }
+
+ @Override
+ public void setMannequinProfile(@NotNull ResolvableProfile profile) {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ setAndSend(DisplayAttributes.Mannequin.RESOLVABLE_PROFILE, profile);
+ }
+
+ @Override
+ public void setCustomName(@Nullable Component text) {
+ setAndSend(DisplayAttributes.CUSTOM_NAME, text);
+ }
+
+ @Override
+ public void setCustomNameVisible(boolean visible) {
+ setAndSend(DisplayAttributes.CUSTOM_NAME_VISIBLE, visible);
+ }
+
+ @Override
+ public @Nullable Component getCustomName() {
+ return attributeContainer.getAttribute(DisplayAttributes.CUSTOM_NAME);
+ }
+
+ @Override
+ public boolean isCustomNameVisible(){
+ return attributeContainer.getAttributeOrDefault(DisplayAttributes.CUSTOM_NAME_VISIBLE, false);
+ }
+
+ @Override
+ public void setMannequinBelowName(@Nullable Component text) {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return;
+ setAndSend(DisplayAttributes.Mannequin.BELOW_NAME, text);
+ }
+
+ @Override
+ public ResolvableProfile getMannequinProfile() {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return null;
+ return attributeContainer.getAttribute(DisplayAttributes.Mannequin.RESOLVABLE_PROFILE);
+ }
+
+ @Override
+ public @Nullable Component getMannequinBelowName() {
+ if (type != SpawnedDisplayEntityPart.PartType.MANNEQUIN) return null;
+ return attributeContainer.getAttribute(DisplayAttributes.Mannequin.BELOW_NAME);
+ }
+
@Override
public void setAttribute(@NotNull DisplayAttribute attribute, T value) {
@@ -557,7 +733,7 @@ public void resendAttributes(@NotNull Player player){
@Override
public Transformation getTransformation(){
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!isDisplay()){
return null;
}
return new Transformation(
@@ -570,7 +746,7 @@ public Transformation getTransformation(){
@Override
public int getTeleportDuration() {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!isDisplay()){
return -1;
}
return attributeContainer.getAttributeOrDefault(DisplayAttributes.TELEPORTATION_DURATION, 0);
@@ -583,7 +759,7 @@ public int getTeleportDuration() {
@Override
public float getViewRange(){
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!isDisplay()){
return -1;
}
return attributeContainer.getAttributeOrDefault(DisplayAttributes.VIEW_RANGE, 1f);
@@ -591,12 +767,19 @@ public float getViewRange(){
@Override
- public @Nullable Vector getInteractionTranslation() {
- if (type != SpawnedDisplayEntityPart.PartType.INTERACTION) {
- return null;
- }
- if (group == null) return null;
- return getInteractionTranslation(group.getLocation());
+ public @Nullable Vector getNonDisplayTranslation() {
+ if (isDisplay() || group == null) return null;
+ return getNonDisplayTranslation(group.getLocation());
+ }
+
+ /**
+ * Get the Interaction's translation vector relative to a location
+ * @param referenceLocation the reference location
+ * @return A vector or null if the part is not an interaction
+ */
+ public Vector getNonDisplayTranslation(@NotNull Location referenceLocation){
+ if (isDisplay()) return null;
+ return referenceLocation.toVector().subtract(getLocation().toVector());
}
@Override
@@ -620,19 +803,6 @@ public void setInteractionResponsive(boolean responsive) {
}
}
- /**
- * Get the Interaction's translation vector relative to a location
- * @param referenceLocation the reference location
- * @return A vector or null if the part is not an interaction
- */
- public Vector getInteractionTranslation(@NotNull Location referenceLocation){
- if (type != SpawnedDisplayEntityPart.PartType.INTERACTION) {
- return null;
- }
- return referenceLocation.toVector().subtract(getLocation().toVector());
- }
-
-
@Override
public float getInteractionHeight() {
if (type != SpawnedDisplayEntityPart.PartType.INTERACTION) {
@@ -719,7 +889,7 @@ private NamespacedKey getInteractionCMDKey(boolean isLeftClick, boolean isConsol
@Override
protected void cull(float width, float height) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
+ if (!isDisplay()) return;
attributeContainer
.setAttributesAndSend(new DisplayAttributeMap()
.add(DisplayAttributes.Culling.HEIGHT, height)
@@ -748,25 +918,31 @@ public Collection getTrackingPlayers() {
@Override
public @Nullable Color getGlowColor() {
- return attributeContainer.getAttribute(DisplayAttributes.GLOW_COLOR_OVERRIDE);
+ if (isDisplay()){
+ return attributeContainer.getAttribute(DisplayAttributes.GLOW_COLOR_OVERRIDE);
+ }
+ return null;
}
@Override
public void setGlowColor(@Nullable Color color) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
- setAndSend(DisplayAttributes.GLOW_COLOR_OVERRIDE, color);
+ if (isDisplay()){
+ setAndSend(DisplayAttributes.GLOW_COLOR_OVERRIDE, color);
+ }
}
@Override
public void glow() {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
- setAndSend(DisplayAttributes.GLOWING, true);
+ if (canGlow()){
+ setAndSend(DisplayAttributes.GLOWING, true);
+ }
}
@Override
public void unglow() {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
- setAndSend(DisplayAttributes.GLOWING, false);
+ if (canGlow()){
+ setAndSend(DisplayAttributes.GLOWING, false);
+ }
}
/**
@@ -774,8 +950,9 @@ public void unglow() {
*/
@Override
public void setTeleportDuration(int teleportDuration) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
- setAndSend(DisplayAttributes.TELEPORTATION_DURATION, teleportDuration);
+ if (isDisplay()){
+ setAndSend(DisplayAttributes.TELEPORTATION_DURATION, teleportDuration);
+ }
}
/**
@@ -784,10 +961,9 @@ public void setTeleportDuration(int teleportDuration) {
*/
@Override
public void setInterpolationDuration(int interpolationDuration) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION){
- return;
+ if (isDisplay()){
+ setAndSend(DisplayAttributes.Interpolation.DURATION, interpolationDuration);
}
- setAndSend(DisplayAttributes.Interpolation.DURATION, interpolationDuration);
}
/**
@@ -796,39 +972,47 @@ public void setInterpolationDuration(int interpolationDuration) {
*/
@Override
public void setInterpolationDelay(int interpolationDelay) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION){
- return;
+ if (isDisplay()){
+ setAndSend(DisplayAttributes.Interpolation.DELAY, interpolationDelay);
}
- setAndSend(DisplayAttributes.Interpolation.DELAY, interpolationDelay);
}
@Override
public void setViewRange(float viewRangeMultiplier) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
- setAndSend(DisplayAttributes.VIEW_RANGE, viewRangeMultiplier);
+ if (isDisplay()){
+ setAndSend(DisplayAttributes.VIEW_RANGE, viewRangeMultiplier);
+ }
}
@Override
public void setBillboard(Display.@NotNull Billboard billboard) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
- setAndSend(DisplayAttributes.BILLBOARD, billboard);
+ if (isDisplay()){
+ setAndSend(DisplayAttributes.BILLBOARD, billboard);
+ }
}
@Override
public void setBrightness(Display.@Nullable Brightness brightness) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION) return;
- setAndSend(DisplayAttributes.BRIGHTNESS, brightness);
+ if (isDisplay()){
+ setAndSend(DisplayAttributes.BRIGHTNESS, brightness);
+ }
}
@Override
- public void setRotation(float pitch, float yaw, boolean pivotIfInteraction){
- if (pivotIfInteraction && type == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ public void setRotation(float pitch, float yaw, boolean pivot){
+ if (pivot && !isDisplay()){
pivot(yaw, pitch);
}
else if (!viewers.isEmpty()){
+ WrapperPlayServerEntityHeadLook headLook = getHeadLookPacket(yaw);
WrapperPlayServerEntityRotation rotPacket = new WrapperPlayServerEntityRotation(getEntityId(), yaw, pitch, false);
for (UUID uuid : getViewers()){
- PacketEvents.getAPI().getPlayerManager().sendPacket(Bukkit.getPlayer(uuid), rotPacket);
+ Player p = Bukkit.getPlayer(uuid);
+ if (p == null) continue;
+ PacketEvents.getAPI().getPlayerManager().sendPacket(p, rotPacket);
+ if (packetLocation.yaw != yaw && type == SpawnedDisplayEntityPart.PartType.MANNEQUIN){
+ PacketEvents.getAPI().getPlayerManager().sendPacket(p, headLook);
+ }
}
}
packetLocation.pitch = pitch;
@@ -841,6 +1025,11 @@ public void setPitch(float pitch) {
setRotation(pitch, getYaw(), false);
}
+ /**
+ * Change the yaw of this part
+ * @param yaw The yaw to set for this part
+ * @param pivot whether the part should pivot around its group's location, if it has one, and if the part is an Interaction
+ */
@Override
public void setYaw(float yaw, boolean pivot) {
setRotation(getPitch(), yaw, pivot);
@@ -857,12 +1046,12 @@ public float getYaw(){
}
/**
- * Pivot an Interaction Entity around its group's master part
+ * Pivot a non-display entity around its group\
* @param angleInDegrees the pivot angle
*/
@Override
public void pivot(float angleInDegrees) {
- if (type != SpawnedDisplayEntityPart.PartType.INTERACTION) return;
+ if (isDisplay() || group == null) return;
pivot(getYaw(), getPitch(), angleInDegrees);
}
@@ -871,27 +1060,36 @@ private void pivot(float yaw, float pitch){
}
private void pivot(float yaw, float pitch, float angleInDegrees){
+ if (group == null || isDisplay()) return;
Location groupLoc = group.getLocation();
Location pivotedLoc = DisplayUtils.getPivotLocation(getLocation(), groupLoc, angleInDegrees);
packetLocation.setCoordinates(pivotedLoc);
+ WrapperPlayServerEntityHeadLook headLook = getHeadLookPacket(yaw);
+ WrapperPlayServerEntityTeleport teleport = new WrapperPlayServerEntityTeleport(getEntityId(),
+ new Vector3d(pivotedLoc.x(), pivotedLoc.y(), pivotedLoc.z()),
+ yaw,
+ pitch,
+ false);
for (UUID uuid : getViewers()){
Player player = Bukkit.getPlayer(uuid);
if (player == null) continue;
- PacketEvents.getAPI().getPlayerManager().sendPacket(player, new WrapperPlayServerEntityTeleport(getEntityId(),
- new Vector3d(pivotedLoc.x(), pivotedLoc.y(), pivotedLoc.z()),
- yaw,
- pitch,
- false));
+ PacketEvents.getAPI().getPlayerManager().sendPacket(player, teleport);
+ if (type == SpawnedDisplayEntityPart.PartType.MANNEQUIN){
+ PacketEvents.getAPI().getPlayerManager().sendPacket(player, headLook);
+ }
}
}
/**
- * Set the location of this packet-based entity. The part should be hidden first with {@link #hide()} if being teleported to a different world.
+ * Set the location of this packet-based entity.
+ * {@inheritDoc}
+ * The part should be hidden first with {@link #hide()} if being teleported to a different world.
* @param location the location
*/
public void teleport(@NotNull Location location){
+ if (hasGroup() && isDisplay() && !isMaster) return;
packetLocation = new PacketLocation(location);
for (UUID uuid : getViewers()){
Player player = Bukkit.getPlayer(uuid);
@@ -900,10 +1098,7 @@ public void teleport(@NotNull Location location){
}
}
- /**
- * Set the location of this packet-based entity. The part should be hidden first with {@link #hide()} if being teleported to a different world.
- * @param location the location
- */
+
void teleportUnsetPassengers(@NotNull Location location){
packetLocation = new PacketLocation(location);
for (UUID uuid : getViewers()){
@@ -928,7 +1123,7 @@ void teleportUnsetPassengers(@NotNull Location location){
*/
@Override
public @Nullable Location getLocation(){
- if (!isMaster && group != null && type != SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!isMaster && group != null && isDisplay()){
return group.getLocation();
}
if (packetLocation != null){
@@ -966,8 +1161,8 @@ public boolean hasLocation(){
*/
@Override
public boolean translate(@NotNull Vector direction, float distance, int durationInTicks, int delayInTicks) {
- if (type == SpawnedDisplayEntityPart.PartType.INTERACTION){
- PacketUtils.translateInteraction(this, direction, distance, durationInTicks, delayInTicks);
+ if (!isDisplay()){
+ PacketUtils.translateNonDisplay(this, direction, distance, durationInTicks, delayInTicks);
}
else{
PacketUtils.translate(this, direction, distance, durationInTicks, delayInTicks);
@@ -1085,7 +1280,7 @@ public void removeFromGroup(boolean unregister){
if (!hasGroup()) return;
group.groupParts.remove(partUUID);
if (!isMaster){
- group.updatePartCount(this, false);
+ group.updatePassengerIds(getEntityId(), false);
}
group = null;
if (unregister){
@@ -1093,6 +1288,15 @@ public void removeFromGroup(boolean unregister){
}
}
+ private WrapperPlayServerEntityHeadLook getHeadLookPacket(float yaw){
+ return new WrapperPlayServerEntityHeadLook(getEntityId(), yaw);
+ }
+
+ private void sendHeadLookPacket(Player player, float yaw){
+ PacketEvents.getAPI().getPlayerManager().sendPacket(player, getHeadLookPacket(yaw));
+ }
+
+
static final class PacketLocation {
String worldName;
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 c301dcbb..e3e5f84c 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
@@ -84,9 +84,9 @@ public void remove() {
}
@Override
- public void setRotation(float pitch, float yaw, boolean pivotIfInteraction) {
+ public void setRotation(float pitch, float yaw, boolean pivot) {
for (PacketDisplayEntityPart part : selectedParts){
- part.setRotation(pitch, yaw, pivotIfInteraction);
+ part.setRotation(pitch, yaw, pivot);
}
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/Packeted.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/Packeted.java
index c054c043..164c5670 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/Packeted.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/Packeted.java
@@ -10,7 +10,7 @@
public interface Packeted{
- void setRotation(float pitch, float yaw, boolean pivotIfInteraction);
+ void setRotation(float pitch, float yaw, boolean pivot);
@Nullable Location getLocation();
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SavedEntityBuilder.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SavedEntityBuilder.java
new file mode 100644
index 00000000..7ef6902b
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SavedEntityBuilder.java
@@ -0,0 +1,150 @@
+package net.donnypz.displayentityutils.utils.DisplayEntities;
+
+import com.destroystokyo.paper.profile.ProfileProperty;
+import io.papermc.paper.datacomponent.item.ResolvableProfile;
+import net.donnypz.displayentityutils.DisplayAPI;
+import net.donnypz.displayentityutils.utils.DisplayUtils;
+import net.donnypz.displayentityutils.utils.packet.PacketAttributeContainer;
+import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttributes;
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import org.bukkit.Material;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Pose;
+import org.bukkit.inventory.EntityEquipment;
+import org.bukkit.inventory.EquipmentSlot;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MainHand;
+import org.bukkit.persistence.PersistentDataContainer;
+import org.bukkit.persistence.PersistentDataType;
+import org.bukkit.profile.PlayerTextures;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+class SavedEntityBuilder {
+
+ static MannequinEntity buildMannequin(Entity entity){ //Accept entity instead of mannequin, preventing issues on versions below 1.21.9
+ MannequinEntity mannequinEntity = new MannequinEntity();
+ org.bukkit.entity.Mannequin mannequin = (org.bukkit.entity.Mannequin) entity;
+
+ Component customName = mannequin.customName();
+ mannequinEntity.customName = customName != null ? MiniMessage.miniMessage().serialize(customName) : null;
+ mannequinEntity.customNameVisible = mannequin.isCustomNameVisible();
+
+ Component description = mannequin.getDescription();
+ mannequinEntity.description = description != null ? MiniMessage.miniMessage().serialize(description) : null;
+
+ setProfileFields(mannequinEntity, mannequin.getProfile());
+
+ mannequinEntity.scale = mannequin.getAttribute(Attribute.SCALE).getBaseValue();
+ mannequinEntity.pose = mannequin.getPose().name();
+
+ mannequinEntity.isRightMainHand = mannequin.getMainHand() == MainHand.RIGHT;
+ EntityEquipment equipment = mannequin.getEquipment();
+ mannequinEntity.equipment = new byte[][]{
+ serializeItemStack(equipment.getHelmet()),
+ serializeItemStack(equipment.getChestplate()),
+ serializeItemStack(equipment.getLeggings()),
+ serializeItemStack(equipment.getBoots()),
+ serializeItemStack(equipment.getItemInMainHand()),
+ serializeItemStack(equipment.getItemInOffHand())
+ };
+
+ mannequinEntity.vector = DisplayUtils.getNonDisplayTranslation(mannequin).toVector3f();
+
+ try{
+ mannequinEntity.persistentDataContainer = mannequin.getPersistentDataContainer().serializeToBytes();
+ }
+ catch(IOException e){
+ e.printStackTrace();
+ }
+ return mannequinEntity;
+ }
+
+ static MannequinEntity buildMannequin(PacketDisplayEntityPart part){
+ MannequinEntity mannequinEntity = new MannequinEntity();
+ PacketAttributeContainer c = part.attributeContainer;
+
+ Component customName = part.getCustomName();
+ mannequinEntity.customName = customName != null ? MiniMessage.miniMessage().serialize(customName) : null;
+ mannequinEntity.customNameVisible = part.isCustomNameVisible();
+
+ Component description = part.getMannequinBelowName();
+ mannequinEntity.description = description != null ? MiniMessage.miniMessage().serialize(description) : null;
+
+ setProfileFields(mannequinEntity, part.getMannequinProfile());
+
+ mannequinEntity.scale = c.getAttributeOrDefault(DisplayAttributes.Mannequin.SCALE, 1.0f);
+ Pose pose = part.getMannequinPose();
+ if (pose == null){
+ pose = Pose.STANDING;
+ }
+ mannequinEntity.pose = pose.name();
+
+ mannequinEntity.isRightMainHand = part.getMannequinMainHand() == MainHand.RIGHT;
+ mannequinEntity.equipment = new byte[][]{
+ serializeItemStack(part.getMannequinEquipment(EquipmentSlot.HEAD)),
+ serializeItemStack(part.getMannequinEquipment(EquipmentSlot.CHEST)),
+ serializeItemStack(part.getMannequinEquipment(EquipmentSlot.LEGS)),
+ serializeItemStack(part.getMannequinEquipment(EquipmentSlot.FEET)),
+ serializeItemStack(part.getMannequinEquipment(EquipmentSlot.HAND)),
+ serializeItemStack(part.getMannequinEquipment(EquipmentSlot.OFF_HAND))
+ };
+
+
+ mannequinEntity.vector = part.getNonDisplayTranslation().toVector3f();
+
+ try{
+ ItemStack i = new ItemStack(Material.STICK);
+ PersistentDataContainer pdc = i.getItemMeta().getPersistentDataContainer();
+ pdc.set(DisplayAPI.getPartPDCTagKey(), PersistentDataType.LIST.strings(), new ArrayList<>(part.getTags()));
+ pdc.set(DisplayAPI.getPartUUIDKey(), PersistentDataType.STRING, part.partUUID.toString());
+ mannequinEntity.persistentDataContainer = pdc.serializeToBytes();
+ }
+ catch(IOException e){
+ e.printStackTrace();
+ }
+ return mannequinEntity;
+ }
+
+ static byte[] serializeItemStack(ItemStack itemStack){
+ if (itemStack == null || itemStack.isEmpty()) return null;
+ return itemStack.serializeAsBytes();
+ }
+
+ private static void setProfileFields(MannequinEntity mannequinEntity, ResolvableProfile profile){
+ if (profile != null){
+ mannequinEntity.profileName = profile.name();
+ mannequinEntity.profileUUID = profile.uuid();
+
+ Collection bukkitProperties = profile.properties();
+ if (bukkitProperties != null){
+ List properties = new ArrayList<>();
+ for (ProfileProperty prop : bukkitProperties){
+ properties.add(new MannequinEntity.ProfileProperty(prop));
+ }
+ mannequinEntity.profileProperties = properties;
+ }
+
+ ResolvableProfile.SkinPatch skinPatch = profile.skinPatch();
+ if (skinPatch.isEmpty()) {
+ return;
+ }
+
+ Key body = skinPatch.body();
+ Key cape = skinPatch.cape();
+ Key elytra = skinPatch.elytra();
+ PlayerTextures.SkinModel model = skinPatch.model();
+
+ if (body != null) mannequinEntity.profileSkinPatchBody = body.asString();
+ if (cape != null) mannequinEntity.profileSkinPatchCape = cape.asString();
+ if (elytra != null) mannequinEntity.profileSkinPatchElytra = elytra.asString();
+ if (model != null) mannequinEntity.profileSkinPatchModel = model.name();
+ }
+ }
+}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SavedEntityLoader.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SavedEntityLoader.java
new file mode 100644
index 00000000..3615d418
--- /dev/null
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SavedEntityLoader.java
@@ -0,0 +1,92 @@
+package net.donnypz.displayentityutils.utils.DisplayEntities;
+
+import io.papermc.paper.datacomponent.item.ResolvableProfile;
+import net.donnypz.displayentityutils.utils.DisplayUtils;
+import net.kyori.adventure.key.Key;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import org.bukkit.Location;
+import org.bukkit.attribute.Attribute;
+import org.bukkit.entity.Mannequin;
+import org.bukkit.entity.Pose;
+import org.bukkit.inventory.EntityEquipment;
+import org.bukkit.inventory.MainHand;
+import org.bukkit.profile.PlayerTextures;
+
+import java.io.IOException;
+
+class SavedEntityLoader {
+
+ static Mannequin spawnMannequin(Location origin, GroupSpawnSettings settings, MannequinEntity mannequinEntity){
+ Location spawnLoc = DisplayUtils.getPivotLocation(
+ mannequinEntity.vector,
+ origin,
+ origin.getYaw());
+
+ return spawnLoc.getWorld().spawn(spawnLoc, org.bukkit.entity.Mannequin.class, m ->{
+ DisplayUtils.prepareMannequin(m);
+
+ m.customName(mannequinEntity.customName != null ? MiniMessage.miniMessage().deserialize(mannequinEntity.customName): null);
+ m.setDescription(mannequinEntity.description != null ? MiniMessage.miniMessage().deserialize(mannequinEntity.description) : null);
+
+ m.setProfile(getMannequinProfile(mannequinEntity));
+ m.getAttribute(Attribute.SCALE).setBaseValue(mannequinEntity.scale);
+ m.setPose(Pose.valueOf(mannequinEntity.pose));
+
+ m.setMainHand(mannequinEntity.isRightMainHand ? MainHand.RIGHT : MainHand.LEFT);
+
+ EntityEquipment equipment = m.getEquipment();
+ equipment.setItemInMainHand(mannequinEntity.getMainHand());
+ equipment.setItemInOffHand(mannequinEntity.getOffHand());
+ equipment.setHelmet(mannequinEntity.getHelmet());
+ equipment.setHelmet(mannequinEntity.getChestplate());
+ equipment.setHelmet(mannequinEntity.getLeggings());
+ equipment.setHelmet(mannequinEntity.getBoots());
+
+ if (mannequinEntity.persistentDataContainer != null){
+ try{
+ m.getPersistentDataContainer().readFromBytes(mannequinEntity.persistentDataContainer);
+ }
+ catch(IOException ignore){}
+ }
+
+ settings.apply(m);
+ });
+ }
+
+ static ResolvableProfile getMannequinProfile(MannequinEntity mannequin){
+ Key body = null;
+ Key cape = null;
+ Key elytra = null;
+ PlayerTextures.SkinModel model = null;
+ if (mannequin.profileSkinPatchBody != null){
+ body = Key.key(mannequin.profileSkinPatchBody);
+ }
+ if (mannequin.profileSkinPatchCape != null){
+ cape = Key.key(mannequin.profileSkinPatchCape);
+ }
+ if (mannequin.profileSkinPatchElytra != null){
+ elytra = Key.key(mannequin.profileSkinPatchElytra);
+ }
+ if (mannequin.profileSkinPatchModel != null){
+ model = PlayerTextures.SkinModel.valueOf(mannequin.profileSkinPatchModel);
+ }
+
+ ResolvableProfile.Builder builder = ResolvableProfile.resolvableProfile()
+ .name(mannequin.profileName)
+ .uuid(mannequin.profileUUID)
+ .skinPatch(ResolvableProfile.SkinPatch.skinPatch()
+ .body(body)
+ .cape(cape)
+ .elytra(elytra)
+ .model(model)
+ .build());
+
+ if (mannequin.profileProperties != null){
+ for (MannequinEntity.ProfileProperty prop : mannequin.profileProperties){
+ builder.addProperty(prop.toBukkitProperty());
+ }
+ }
+
+ return builder.build();
+ }
+}
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 2eda5174..5d628c56 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() {
+ selectedPart.remove(false);
selectedPart = null;
}
@@ -106,6 +107,10 @@ public void setYaw(float yaw, boolean pivot) {
selectedPart.setYaw(yaw, pivot);
}
+ /**
+ * Pivot a non-display entity around its group's master part
+ * @param angleInDegrees the pivot angle
+ */
@Override
public void pivot(float angleInDegrees) {
selectedPart.pivot(angleInDegrees);
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 44b2b846..25ffd83f 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
@@ -124,7 +124,7 @@ public SpawnedDisplayAnimationFrame setTransformation(@NotNull ActiveGroup> gr
if (p.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
InteractionTransformation transform = new InteractionTransformation(
- p.getInteractionTranslation().toVector3f(),
+ p.getNonDisplayTranslation(),
gLoc.getYaw(),
gLoc.getPitch(),
p.getInteractionHeight(),
@@ -157,7 +157,7 @@ public SpawnedDisplayAnimationFrame setTransformation(@NotNull ActiveGroup> gr
if (p.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
InteractionTransformation transform = new InteractionTransformation(
- p.getInteractionTranslation().toVector3f(),
+ p.getNonDisplayTranslation(),
gLoc.getYaw(),
gLoc.getPitch(),
p.getInteractionHeight(),
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayEntityGroup.java b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayEntityGroup.java
index 5aa85d0f..d5bd72b6 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayEntityGroup.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayEntities/SpawnedDisplayEntityGroup.java
@@ -14,6 +14,7 @@
import net.donnypz.displayentityutils.utils.version.folia.FoliaUtils;
import net.donnypz.displayentityutils.utils.version.folia.Scheduler;
import org.bukkit.*;
+import org.bukkit.attribute.Attribute;
import org.bukkit.entity.*;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
@@ -120,6 +121,28 @@ public void addPart(@NotNull SpawnedDisplayEntityPart part){
}
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public @Nullable SpawnedDisplayEntityPart addEntity(@NotNull Entity entity){
+ if (entity instanceof Display display){
+ return addDisplayEntity(display);
+ }
+
+ SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(entity);
+ if (part == null){
+ part = new SpawnedDisplayEntityPart(this, entity, partUUIDRandom);
+ }
+ else{
+ part.setGroup(this);
+ }
+ if (getVehicle() != null){
+ alignNonDisplayWithMountedGroup(part, getVehicle());
+ }
+ return part;
+ }
+
/**
* Add a display entity to this group. If this group already contains this display entity as a registered part it will return the existing
* {@link SpawnedDisplayEntityPart}. If it doesn't then it will return a new {@link SpawnedDisplayEntityPart}
@@ -152,98 +175,39 @@ else if (!groupParts.isEmpty()){
}
- /**
- * Add an interaction entity to this group. If this group already contains this interaction entity as a registered part it will return the existing
- * {@link SpawnedDisplayEntityPart}. If it doesn't then it will return a new {@link SpawnedDisplayEntityPart}
- * @param interactionEntity
- * @return a {@link SpawnedDisplayEntityPart} representing the Interaction entity
- */
- public SpawnedDisplayEntityPart addInteractionEntity(@NotNull Interaction interactionEntity){
- SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(interactionEntity);
- if (part == null){
- part = new SpawnedDisplayEntityPart(this, interactionEntity, partUUIDRandom);
- }
- else{
- part.setGroup(this);
- }
- if (getVehicle() != null){
- alignInteractionWithMountedGroup(part, getVehicle());
- }
- return part;
- }
-
- /**
- * Add a valid part entity (Display or Interaction) to this group, when you don't know the type of entity you're dealing with
- * @param entity the part entity to add
- * @return a corresponding {@link SpawnedDisplayEntityPart} or null if the entity is not a part entity
- */
- public SpawnedDisplayEntityPart addPartEntity(@NotNull Entity entity){
- if (entity instanceof Interaction interaction){
- return addInteractionEntity(interaction);
- }
- else if (entity instanceof Display display){
- return addDisplayEntity(display);
- }
- else{
- return null;
- }
- }
-
- /**
- * Check if this group and a Display entity share the same creation time. If this returns true this does not guarantee
- * that the part is registered to this group. Using {@link SpawnedDisplayEntityGroup#addDisplayEntity(Display)} will
- * add the display entity to the group if it is not added already
- * @param display
- * @return a boolean
- */
- public boolean hasSameCreationTime(Display display){
- return sameCreationTime(display);
- }
/**
- * Check if this group and an Interaction entity share the same creation time. If this returns true this does not guarantee
- * that the part is registered to this group. Using {@link SpawnedDisplayEntityGroup#addInteractionEntity(Interaction)} will
+ * Check if this group and an entity share the same creation time. If this returns true this does not guarantee
+ * that the part is registered to this group.
+ * Using {@link SpawnedDisplayEntityGroup#addEntity(Entity)} will
* add the interaction entity to the group if it is not added already
- * @param interaction
+ * @param entity the entity
* @return a boolean
*/
- public boolean hasSameCreationTime(Interaction interaction){
- return sameCreationTime(interaction);
- }
-
-
-
- private boolean sameCreationTime(Entity entity){
+ public boolean hasSameCreationTime(Entity entity){
PersistentDataContainer container = entity.getPersistentDataContainer();
if (!container.has(creationTimeKey, PersistentDataType.LONG)){
return false;
}
-
return creationTime == container.get(creationTimeKey, PersistentDataType.LONG);
}
/**
- * Add Interactions that are meant to be a part of this group
- * Usually these Interactions are unadded when a SpawnedDisplayEntityGroup is created during a new play session
- * @param searchRange Distance to search for Interaction entities from the group's location
- * @return a list of the interaction entities added to the group
+ * Add entities that are meant to be a part of this group.
+ * Usually these entities are unadded when a {@link SpawnedDisplayEntityGroup} is created during a new play session
+ * @param searchRange distance to search for entities from the group's location
+ * @return a list of the entities added to the group
*/
- public List addMissingInteractionEntities(double searchRange){
- List interactions = new ArrayList<>();
- //List existingInteractions = getSpawnedPartEntities(SpawnedDisplayEntityPart.PartType.INTERACTION);
+ public @NotNull List addMissingEntities(double searchRange){
+ List entities = new ArrayList<>();
for (Entity e : getMasterPart().getEntity().getNearbyEntities(searchRange, searchRange, searchRange)) {
- if (!(e instanceof Interaction i)){
- continue;
- }
- //if (!existingInteractions.contains(i) && sameCreationTime(i)){
- if (!sameCreationTime(i)){
- continue;
- }
+ if (!DisplayUtils.isPartEntity(e) || e instanceof Display) continue;
+ if (!hasSameCreationTime(e)) continue;
- SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(i);
+ SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(e);
if (part == null){
- new SpawnedDisplayEntityPart(this, i, partUUIDRandom);
+ new SpawnedDisplayEntityPart(this, e, partUUIDRandom);
}
else{
if (this == part.getGroup()){ //Already in this group
@@ -251,9 +215,9 @@ public List addMissingInteractionEntities(double searchRange){
}
part.setGroup(this);
}
- interactions.add(i);
+ entities.add(e);
}
- return interactions;
+ return entities;
}
@ApiStatus.Internal
@@ -275,8 +239,8 @@ public void seedPartUUIDs(long seed){
*/
@Override
public void showToPlayer(@NotNull Player player){
- for (ActivePart part : groupParts.values()){
- ((SpawnedDisplayEntityPart) part).showToPlayer(player);
+ for (SpawnedDisplayEntityPart part : groupParts.values()){
+ part.showToPlayer(player);
}
}
@@ -330,8 +294,7 @@ public List getUnaddedInteractionEntitiesInRange(double searchRange
if ((e instanceof Interaction interaction)){
if (!existingInteractions.contains(e)){
if (addToGroup){
- addInteractionEntity(interaction);
-
+ addEntity(interaction);
}
interactions.add(interaction);
}
@@ -451,7 +414,7 @@ public SpawnedDisplayEntityGroup setPersistenceOverride(boolean override){
}
@Override
- public boolean scale(float newScaleMultiplier, int durationInTicks, boolean scaleInteractions){
+ public boolean scale(float newScaleMultiplier, int durationInTicks, boolean scaleNonDisplays){
if (newScaleMultiplier <= 0){
throw new IllegalArgumentException("New Scale Multiplier cannot be <= 0");
}
@@ -467,10 +430,10 @@ public boolean scale(float newScaleMultiplier, int durationInTicks, boolean scal
return false;
}
- for (SpawnedDisplayEntityPart p : groupParts.values()){
+ for (SpawnedDisplayEntityPart part : groupParts.values()){
//Displays
- if (p.getType() != SpawnedDisplayEntityPart.PartType.INTERACTION){
- Display d = (Display) p.getEntity();
+ if (part.isDisplay()){
+ Display d = (Display) part.getEntity();
Transformation transformation = d.getTransformation();
//Reset Scale then multiply by newScaleMultiplier
@@ -493,30 +456,38 @@ public boolean scale(float newScaleMultiplier, int durationInTicks, boolean scal
}
//Culling
if (DisplayConfig.autoCulling()){
- p.autoCull(DisplayConfig.widthCullingAdder(), DisplayConfig.heightCullingAdder());
+ part.autoCull(DisplayConfig.widthCullingAdder(), DisplayConfig.heightCullingAdder());
}
}
//Interactions
- else if (scaleInteractions){
- Interaction i = (Interaction) p.getEntity();
-
- //Reset Scale then multiply by newScaleMultiplier
- float newHeight = (i.getInteractionHeight()/scaleMultiplier)*newScaleMultiplier;
- float newWidth = (i.getInteractionWidth()/scaleMultiplier)*newScaleMultiplier;
- DisplayUtils.scaleInteraction(i, newHeight, newWidth, durationInTicks, 0);
+ else if (scaleNonDisplays){
+ if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ Interaction i = (Interaction) part.getEntity();
+
+ //Reset Scale then multiply by newScaleMultiplier
+ float newHeight = (i.getInteractionHeight()/scaleMultiplier)*newScaleMultiplier;
+ float newWidth = (i.getInteractionWidth()/scaleMultiplier)*newScaleMultiplier;
+ DisplayUtils.scaleInteraction(i, newHeight, newWidth, durationInTicks, 0);
+
+ //Reset Translation then multiply by newScaleMultiplier
+ Vector translationVector = DisplayUtils.getNonDisplayTranslation(i);
+ if (translationVector == null){
+ continue;
+ }
+ Vector oldVector = new Vector(translationVector.getX(), translationVector.getY(), translationVector.getZ());
+ translationVector.setX((translationVector.getX()/scaleMultiplier)*newScaleMultiplier);
+ translationVector.setY((translationVector.getY()/scaleMultiplier)*newScaleMultiplier);
+ translationVector.setZ((translationVector.getZ()/scaleMultiplier)*newScaleMultiplier);
- //Reset Translation then multiply by newScaleMultiplier
- Vector translationVector = DisplayUtils.getInteractionTranslation(i);
- if (translationVector == null){
- continue;
+ Vector moveVector = oldVector.subtract(translationVector);
+ part.translateForce(moveVector, (float) moveVector.length(), durationInTicks, 0);
+ }
+ else if (part.type == SpawnedDisplayEntityPart.PartType.MANNEQUIN){
+ Mannequin m = (Mannequin) part.getEntity();
+ double scale = m.getAttribute(Attribute.SCALE).getBaseValue();
+ scale = (scale/scaleMultiplier)*newScaleMultiplier;
+ m.getAttribute(Attribute.SCALE).setBaseValue(scale);
}
- Vector oldVector = new Vector(translationVector.getX(), translationVector.getY(), translationVector.getZ());
- translationVector.setX((translationVector.getX()/scaleMultiplier)*newScaleMultiplier);
- translationVector.setY((translationVector.getY()/scaleMultiplier)*newScaleMultiplier);
- translationVector.setZ((translationVector.getZ()/scaleMultiplier)*newScaleMultiplier);
-
- Vector moveVector = oldVector.subtract(translationVector);
- p.translateForce(moveVector, (float) moveVector.length(), durationInTicks, 0);
}
}
@@ -548,57 +519,57 @@ private void teleportWithoutEvent(Location location, boolean respectGroupDirecti
}
FoliaUtils.teleport(master, location, TeleportFlag.EntityState.RETAIN_PASSENGERS);
- World w = location.getWorld();
-
for (SpawnedDisplayEntityPart part : this.getParts()){
part.getEntity().setRotation(location.getYaw(), location.getPitch());
- //Interaction Entity TP
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ //Non-Display TP
+ if (!part.isDisplay()){
Interaction interaction = (Interaction) part.getEntity();
Vector vector = oldMasterLoc.toVector().subtract(interaction.getLocation().toVector());
Location tpLocation = location.clone().subtract(vector);
FoliaUtils.teleport(part.getEntity(), tpLocation, TeleportFlag.EntityState.RETAIN_PASSENGERS);
}
-
- if (w != null && part.getEntity().getWorld() != w){ //Keep world name consistent within part's data
- part.getPartData().setWorldName(w.getName());
- }
}
}
- private static void translateInteractionEventless(@NotNull Interaction interaction, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
- Location destination = interaction.getLocation().clone().add(direction.clone().normalize().multiply(distance));
+ private static void translateEntityEventless(@NotNull Entity entity, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
+ DisplayUtils.translate(entity, direction, distance, durationInTicks, delayInTicks);
+ Location destination = entity.getLocation().clone().add(direction.clone().normalize().multiply(distance));
+
+ if (durationInTicks <= 0 && delayInTicks <= 0){
+ FoliaUtils.teleport(entity, destination);
+ return;
+ }
+
double movementIncrement = distance/(double) Math.max(durationInTicks, 1);
Vector incrementVector = direction
.clone()
.normalize()
.multiply(movementIncrement);
- DisplayAPI.getScheduler().entityRunTimer(interaction, new Scheduler.SchedulerRunnable(){
+ DisplayAPI.getScheduler().entityRunTimer(entity, new Scheduler.SchedulerRunnable() {
double currentDistance = 0;
- float lastYaw = interaction.getYaw();
+ float lastYaw = entity.getYaw();
@Override
public void run() {
- float newYaw = interaction.getYaw();
+ float newYaw = entity.getYaw();
if (newYaw != lastYaw){
incrementVector.rotateAroundY(Math.toRadians(lastYaw-newYaw));
lastYaw = newYaw;
}
currentDistance+=Math.abs(movementIncrement);
- Location tpLoc = interaction.getLocation().clone().add(incrementVector);
+ Location tpLoc = entity.getLocation().clone().add(incrementVector);
if (currentDistance >= distance){
- FoliaUtils.teleport(interaction, destination);
+ FoliaUtils.teleport(entity, destination);
cancel();
}
else{
- FoliaUtils.teleport(interaction, tpLoc);
+ FoliaUtils.teleport(entity, tpLoc);
}
}
}, delayInTicks, 1);
-
}
@Override
@@ -616,8 +587,8 @@ public void teleportMove(@NotNull Vector direction, double distance, int duratio
.multiply(movementIncrement);
for (SpawnedDisplayEntityPart part : groupParts.values()){
- if (part.type == SpawnedDisplayEntityPart.PartType.INTERACTION){
- translateInteractionEventless((Interaction) part.getEntity(), direction, distance, durationInTicks, 0);
+ if (!part.isDisplay()){
+ translateEntityEventless(part.getEntity(), direction, distance, durationInTicks, 0);
}
}
@@ -684,13 +655,13 @@ public List getTeleportMoveLocations(Vector direction, double distance
/**
- * Pivot all Interaction parts in this selection around this group's master part
+ * Pivot all non-display parts in this group around the group
* @param angleInDegrees the pivot angle
*/
@Override
public void pivot(float angleInDegrees){
for (ActivePart part : groupParts.values()){
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!part.isDisplay()){
part.pivot(angleInDegrees);
}
}
@@ -844,7 +815,7 @@ public boolean rideEntity(@NotNull Entity vehicle){
}
for (SpawnedDisplayEntityPart interactionPart: this.getParts(SpawnedDisplayEntityPart.PartType.INTERACTION)){
- alignInteractionWithMountedGroup(interactionPart, vehicle);
+ alignNonDisplayWithMountedGroup(interactionPart, vehicle);
}
return true;
}
@@ -871,7 +842,7 @@ public boolean isRiding(){
return getVehicle() != null;
}
- private void alignInteractionWithMountedGroup(SpawnedDisplayEntityPart part, Entity vehicle){
+ private void alignNonDisplayWithMountedGroup(SpawnedDisplayEntityPart part, Entity vehicle){
final Interaction interaction = (Interaction) part.getEntity();
DisplayAPI.getScheduler().entityRunTimer(interaction, new Scheduler.SchedulerRunnable() {
Location lastLoc = getLocation();
@@ -1156,12 +1127,13 @@ public SpawnedDisplayEntityGroup clone(@NotNull Location location){
* @return a cloned {@link SpawnedDisplayEntityGroup}
*/
public SpawnedDisplayEntityGroup clone(@NotNull Location location, @NotNull GroupSpawnSettings settings){
- //Reset Interaction pivot to 0 yaw
- HashMap oldYaws = new HashMap<>();
- for (SpawnedDisplayEntityPart part : this.getParts(SpawnedDisplayEntityPart.PartType.INTERACTION)){
- float oldYaw = part.getYaw();
- oldYaws.put(part, oldYaw);
- part.pivot(-oldYaw);
+ //Reset pivot to 0 yaw
+ float groupYaw = getLocation().getYaw();
+ HashSet resettedParts = new HashSet<>();
+ for (ActivePart part : groupParts.values()){
+ if (part.isDisplay()) continue;
+ part.pivot(-groupYaw);
+ resettedParts.add(part);
}
DisplayEntityGroup savedGroup = toDisplayEntityGroup();
@@ -1170,23 +1142,22 @@ public SpawnedDisplayEntityGroup clone(@NotNull Location location, @NotNull Grou
location.setYaw(0);
SpawnedDisplayEntityGroup cloned = savedGroup.spawn(location, GroupSpawnedEvent.SpawnReason.CLONE, settings);
- //Restore Interaction Pivot
- for (Map.Entry entry : oldYaws.entrySet()){
- SpawnedDisplayEntityPart part = entry.getKey();
- float oldYaw = entry.getValue();
- part.pivot(oldYaw);
+ //Restore pivot
+ for (ActivePart part : resettedParts){
+ part.pivot(groupYaw);
}
+
cloned.setYaw(newYaw, true);
return cloned;
}
public PacketDisplayEntityGroup toPacket(@NotNull Location location, boolean playSpawnAnimation, boolean autoShow, boolean persistent){
- //Reset Interaction pivot to 0 yaw
- HashMap oldYaws = new HashMap<>();
- for (SpawnedDisplayEntityPart part : this.getParts(SpawnedDisplayEntityPart.PartType.INTERACTION)){
- float oldYaw = part.getEntity().getYaw();
- oldYaws.put(part, oldYaw);
- part.pivot(-oldYaw);
+ //Reset pivot to 0 yaw
+ float groupYaw = getLocation().getYaw();
+ HashSet resettedParts = new HashSet<>();
+ for (ActivePart part : groupParts.values()){
+ part.pivot(-groupYaw);
+ resettedParts.add(part);
}
DisplayEntityGroup savedGroup = toDisplayEntityGroup();
@@ -1195,7 +1166,6 @@ public PacketDisplayEntityGroup toPacket(@NotNull Location location, boolean pla
location.setYaw(0);
PacketDisplayEntityGroup packetGroup;
-
if (persistent){
packetGroup = DisplayGroupManager.addPersistentPacketGroup(location, savedGroup, autoShow, GroupSpawnedEvent.SpawnReason.INTERNAL);
}
@@ -1203,11 +1173,9 @@ public PacketDisplayEntityGroup toPacket(@NotNull Location location, boolean pla
packetGroup = savedGroup.createPacketGroup(location, GroupSpawnedEvent.SpawnReason.INTERNAL, playSpawnAnimation, autoShow);
}
- //Restore Interaction Pivot
- for (Map.Entry entry : oldYaws.entrySet()){
- SpawnedDisplayEntityPart part = entry.getKey();
- float oldYaw = entry.getValue();
- part.pivot(oldYaw);
+ //Restore pivot
+ for (ActivePart part : resettedParts){
+ part.pivot(groupYaw);
}
packetGroup.setYaw(newYaw, true);
return packetGroup;
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 8de8e9cc..22ef2b02 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
@@ -1,5 +1,7 @@
package net.donnypz.displayentityutils.utils.DisplayEntities;
+import com.destroystokyo.paper.profile.PlayerProfile;
+import io.papermc.paper.datacomponent.item.ResolvableProfile;
import net.donnypz.displayentityutils.DisplayAPI;
import net.donnypz.displayentityutils.utils.Direction;
import net.donnypz.displayentityutils.utils.DisplayUtils;
@@ -8,11 +10,16 @@
import net.donnypz.displayentityutils.utils.packet.DisplayAttributeMap;
import net.donnypz.displayentityutils.utils.packet.PacketAttributeContainer;
import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttribute;
+import net.donnypz.displayentityutils.utils.version.VersionUtils;
import net.kyori.adventure.text.Component;
import org.bukkit.*;
+import org.bukkit.attribute.Attribute;
import org.bukkit.block.data.BlockData;
import org.bukkit.entity.*;
+import org.bukkit.inventory.EquipmentSlot;
import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.MainHand;
+import org.bukkit.inventory.meta.ItemMeta;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
import org.bukkit.util.Transformation;
@@ -21,63 +28,43 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
+import org.joml.Quaternionf;
import org.joml.Vector3f;
import java.util.*;
public final class SpawnedDisplayEntityPart extends ActivePart implements Spawned {
- private static final HashMap allParts = new HashMap<>();
private SpawnedDisplayEntityGroup group;
private Entity entity;
- private PartData partData;
private UUID entityUUID;
private final boolean isSingle;
- SpawnedDisplayEntityPart(SpawnedDisplayEntityGroup group, Display displayEntity, Random random){
- super(displayEntity.getEntityId(), true);
+ SpawnedDisplayEntityPart(SpawnedDisplayEntityGroup group, Entity entity, Random random){
+ super(entity.getEntityId(), false);
this.group = group;
- this.entity = displayEntity;
- if (displayEntity instanceof BlockDisplay){
- this.type = PartType.BLOCK_DISPLAY;
- }
- else if (displayEntity instanceof ItemDisplay){
- this.type = PartType.ITEM_DISPLAY;
- }
- else {
- this.type = PartType.TEXT_DISPLAY;
- }
+ this.entity = entity;
+ this.type = PartType.getType(entity);
applyData(random, entity);
if (isMaster()){
group.masterPart = this;
}
- partTags.addAll(DisplayUtils.getTags(displayEntity));
- isSingle = false;
- }
-
-
- SpawnedDisplayEntityPart(SpawnedDisplayEntityGroup group, Interaction interactionEntity, Random random){
- super(interactionEntity.getEntityId(), false);
- this.group = group;
- this.entity = interactionEntity;
- this.type = PartType.INTERACTION;
- applyData(random, interactionEntity);
- partTags.addAll(DisplayUtils.getTags(interactionEntity));
+ if (VersionUtils.IS_1_21_9 && entity instanceof Mannequin m){
+ DisplayUtils.prepareMannequin(m);
+ }
+ partTags.addAll(DisplayUtils.getTags(entity));
isSingle = false;
}
SpawnedDisplayEntityPart(Entity entity){
- super(entity.getEntityId(), false);
- switch (entity) {
- case BlockDisplay blockDisplay -> this.type = PartType.BLOCK_DISPLAY;
- case ItemDisplay itemDisplay -> this.type = PartType.ITEM_DISPLAY;
- case TextDisplay textDisplay -> this.type = PartType.TEXT_DISPLAY;
- default -> this.type = PartType.INTERACTION;
- }
+ super(entity.getEntityId(), true);
+ if (PartType.getType(entity) == null) throw new IllegalArgumentException("Entity is not a valid part type entity");
+ this.type = PartType.getType(entity);
this.entity = entity;
this.entityUUID = entity.getUniqueId();
- isSingle = true;
+ this.partUUID = UUID.randomUUID();
+ this.isSingle = true;
}
/**
@@ -86,59 +73,37 @@ else if (displayEntity instanceof ItemDisplay){
* If the entity is already included in a group, its respective part will be returned.
* @param uuid the entity uuid
* @return a {@link SpawnedDisplayEntityPart} or null if the entity uuid is not a display or interaction
+ * @throws IllegalArgumentException if the entity is not a valid entity
*/
- public static @Nullable SpawnedDisplayEntityPart create(@NotNull UUID uuid){
- Entity entity = Bukkit.getEntity(uuid);
- if (entity instanceof Interaction i){
- return create(i);
- }
- else if (entity instanceof Display d){
- return create(d);
- }
- return null;
+ public static @NotNull SpawnedDisplayEntityPart create(@NotNull UUID uuid){
+ return create(Bukkit.getEntity(uuid));
}
/**
* Create a {@link SpawnedDisplayEntityPart} that is not included in any group.
*
* If the entity is already included in a group, its respective part will be returned.
- * @param display the display entity
+ * @param entity the valid part entity
* @return a {@link SpawnedDisplayEntityPart}
+ * @throws IllegalArgumentException if the entity is not a valid entity
*/
- public static @NotNull SpawnedDisplayEntityPart create(@NotNull Display display){
- SpawnedDisplayEntityPart part = getPart(display);
- if (part != null) return part;
- return new SpawnedDisplayEntityPart(display);
- }
-
- /**
- * Create a {@link SpawnedDisplayEntityPart} that is not included in any group.
- *
- * If the entity is already included in a group, its respective part will be returned.
- * @param interaction the interaction entity
- * @return a {@link SpawnedDisplayEntityPart}
- */
- public static @NotNull SpawnedDisplayEntityPart create(@NotNull Interaction interaction){
- SpawnedDisplayEntityPart part = getPart(interaction);
- if (part != null) return part;
- return new SpawnedDisplayEntityPart(interaction);
+ public static @NotNull SpawnedDisplayEntityPart create(@NotNull Entity entity){
+ SpawnedDisplayEntityPart part = getPart(entity);
+ return part != null ? part : new SpawnedDisplayEntityPart(entity);
}
private void applyData(Random random, Entity entity){
adaptLegacyPartTags();
entity.getPersistentDataContainer().set(SpawnedDisplayEntityGroup.creationTimeKey, PersistentDataType.LONG, group.getCreationTime());
- if (entity instanceof Display display){
- removeFromPreviousGroup(display);
- }
- else{
- removeFromPreviousGroup((Interaction) entity);
- }
-
+ //Remove from previous group
+ SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(entity);
+ if (part != null && part.group != group){
+ part.remove(false);
+ }
+ partsById.put(getEntityId(), this);
- this.partData = new PartData(entity);
this.entityUUID = entity.getUniqueId();
- allParts.put(partData, this);
setPartUUID(random);
group.groupParts.put(partUUID, this);
@@ -196,18 +161,15 @@ private boolean groupContainsUUID(UUID partUUID){
return group.groupParts.containsKey(partUUID);
}
- private void removeFromPreviousGroup(Display display){
- SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(display);
- if (part != null){
- part.remove(false);
- }
- }
-
- private void removeFromPreviousGroup(Interaction i){
- SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(i);
- if (part != null){
- part.remove(false);
+ private void adaptLegacyPartTags(){ //Don't use getEntity()
+ List legacyTags = new ArrayList<>();
+ for (String s : new HashSet<>(entity.getScoreboardTags())){
+ if (s.contains(DisplayAPI.getLegacyPartTagPrefix())){
+ legacyTags.add(s.replace(DisplayAPI.getLegacyPartTagPrefix(), ""));
+ entity.removeScoreboardTag(s);
+ }
}
+ DisplayUtils.addTags(entity, legacyTags);
}
/**
@@ -221,7 +183,7 @@ private void removeFromPreviousGroup(Interaction i){
/**
* Get whether this part can be added to a group. This returns false if the part was
- * created through {@link #create(Display)} or {@link #create(Interaction)}
+ * created through {@link #create(Entity)}
* @return a boolean.
*/
@Override
@@ -229,6 +191,15 @@ public boolean hasGroup(){
return !isSingle;
}
+ @Override
+ public void teleport(@NotNull Location location) {
+ if (group != null && isDisplay() && !isMaster()){
+ return;
+ }
+ Entity e = getEntity();
+ if (e != null) e.teleport(location);
+ }
+
@Override
public float getPitch() {
return getEntity().getPitch();
@@ -292,19 +263,14 @@ public void refreshEntity(@NotNull Entity nonStaleEntity){
return getEntity().getLocation();
}
-
- PartData getPartData() {
- return partData;
- }
-
/**
- * Get the {@link SpawnedDisplayEntityPart} of an entity, during this play session. Use {@link #create(Display)} or similar methods if the part is not grouped.
- * @param entity the part entity (Display/Interaction)
- * @return The SpawnedDisplayEntityPart. Null if not created during play session or not associated with any group
+ * Get the {@link SpawnedDisplayEntityPart} of an entity, during this play session. Use {@link #create(Entity)} if the part is not grouped.
+ * @param entity the part entity
+ * @return a {@link SpawnedDisplayEntityPart} or null if not created during play session
*/
public static @Nullable SpawnedDisplayEntityPart getPart(@NotNull Entity entity){
- if (!(entity instanceof Interaction || entity instanceof Display)) return null;
- return allParts.get(new PartData(entity));
+ if (!DisplayUtils.isPartEntity(entity)) return null;
+ return (SpawnedDisplayEntityPart) getPart(entity.getEntityId());
}
@@ -350,18 +316,6 @@ public SpawnedDisplayEntityPart adaptScoreboardTags(boolean removeFromScoreboard
return this;
}
-
- private void adaptLegacyPartTags(){ //Don't use getEntity()
- List legacyTags = new ArrayList<>();
- for (String s : new HashSet<>(entity.getScoreboardTags())){
- if (s.contains(DisplayAPI.getLegacyPartTagPrefix())){
- legacyTags.add(s.replace(DisplayAPI.getLegacyPartTagPrefix(), ""));
- entity.removeScoreboardTag(s);
- }
- }
- DisplayUtils.addTags(entity, legacyTags);
- }
-
SpawnedDisplayEntityPart setMaster(){
group.masterPart = this;
getEntity().getPersistentDataContainer().set(DisplayAPI.getMasterKey(), PersistentDataType.BOOLEAN, true);
@@ -379,7 +333,7 @@ public SpawnedDisplayEntityPart setGroup(@NotNull SpawnedDisplayEntityGroup newG
}
this.group = newGroup;
- if (type != PartType.INTERACTION){
+ if (isDisplay()){
Display display = (Display) getEntity();
if (isMaster() && this != newGroup.masterPart){
newGroup.masterPart = this;
@@ -477,18 +431,13 @@ public boolean isInLoadedChunk(){
}
-
-
-
/**
* Adds the glow effect to this SpawnDisplayEntityPart. This does NOT apply to Interaction or Text Display entities. Use {@link #markInteraction(Player, long)} to show an outline of
* an interaction for a specific player.
*/
public void glow(){
+ if (!canGlow()) return;
Entity entity = getEntity();
- if (type == PartType.INTERACTION || type == PartType.TEXT_DISPLAY) {
- return;
- }
entity.setGlowing(true);
}
@@ -499,10 +448,9 @@ public void glow(){
*/
@Override
public void glow(long durationInTicks){
+ if (!canGlow()) return;
+
Entity entity = getEntity();
- if (type == PartType.INTERACTION || type == PartType.TEXT_DISPLAY) {
- return;
- }
entity.setGlowing(true);
DisplayAPI.getScheduler().entityRunLater(entity, () -> {
@@ -521,7 +469,7 @@ public boolean isGlowing() {
*/
@Override
public void unglow(){
- if (type != PartType.INTERACTION) {
+ if (canGlow()) {
getEntity().setGlowing(false);
}
}
@@ -532,7 +480,7 @@ public void unglow(){
*/
@Override
public void unglow(@NotNull Player player){
- if (type != PartType.INTERACTION) {
+ if (canGlow()) {
PacketUtils.setGlowing(player, getEntityId(), false);
}
}
@@ -545,15 +493,18 @@ public Collection getTrackingPlayers() {
/**
* Change the yaw of this part
* @param yaw The yaw to set for this part
- * @param pivotIfInteraction true if this part's type is {@link PartType#INTERACTION} and it should pivot around the group's location
+ * @param pivot whether the part should pivot around its group's location, if it has one, and if the part is an Interaction
*/
@Override
- public void setYaw(float yaw, boolean pivotIfInteraction){
+ public void setYaw(float yaw, boolean pivot){
Entity entity = getEntity();
- if (type == PartType.INTERACTION && pivotIfInteraction){
+ if (!isDisplay() && pivot){
pivot(yaw-entity.getYaw());
}
entity.setRotation(yaw, entity.getPitch());
+ if (entity instanceof LivingEntity le){
+ le.setBodyYaw(yaw);
+ }
}
/**
@@ -567,10 +518,8 @@ public void setPitch(float pitch){
}
@Override
- public boolean setXScale(float scale){
- if (type == PartType.INTERACTION){
- return false;
- }
+ public boolean setDisplayXScale(float scale){
+ if (!isDisplay()) return false;
Transformation t = getTransformation();
Vector3f v = t.getScale();
Transformation newT = new Transformation(t.getTranslation(), t.getLeftRotation(), new Vector3f(scale, v.y, v.z), t.getRightRotation());
@@ -580,8 +529,8 @@ public boolean setXScale(float scale){
}
@Override
- public boolean setYScale(float scale){
- if (type == PartType.INTERACTION) return false;
+ public boolean setDisplayYScale(float scale){
+ if (!isDisplay()) return false;
Transformation t = getTransformation();
Vector3f v = t.getScale();
Transformation newT = new Transformation(t.getTranslation(), t.getLeftRotation(), new Vector3f(v.x, scale, v.z), t.getRightRotation());
@@ -591,8 +540,8 @@ public boolean setYScale(float scale){
}
@Override
- public boolean setZScale(float scale){
- if (type == PartType.INTERACTION) return false;
+ public boolean setDisplayZScale(float scale){
+ if (!isDisplay()) return false;
Transformation t = getTransformation();
Vector3f v = t.getScale();
Transformation newT = new Transformation(t.getTranslation(), t.getLeftRotation(), new Vector3f(v.x, v.y, scale), t.getRightRotation());
@@ -602,8 +551,8 @@ public boolean setZScale(float scale){
}
@Override
- public boolean setScale(float x, float y, float z){
- if (type == PartType.INTERACTION) return false;
+ public boolean setDisplayScale(float x, float y, float z){
+ if (!isDisplay()) return false;
Transformation t = getTransformation();
Transformation newT = new Transformation(t.getTranslation(), t.getLeftRotation(), new Vector3f(x, y, z), t.getRightRotation());
Display entity = (Display) getEntity();
@@ -611,8 +560,25 @@ public boolean setScale(float x, float y, float z){
return true;
}
+ @Override
+ public void rotateDisplay(@NotNull Quaternionf rotation, boolean rotateTranslation) {
+ if (!isDisplay()) return;
+ Display display = (Display) getEntity();
+ if (display == null) return;
+ Transformation t = getTransformation();
+ Vector3f translation = t.getTranslation();
+ Quaternionf originalRot = t.getLeftRotation();
+ //World Space Rot
+ if (rotateTranslation){
+ translation.rotate(rotation);
+ }
+
+ rotation.mul(originalRot, originalRot);
+ Transformation newT = new Transformation(translation, originalRot, t.getScale(), t.getRightRotation());
+ display.setTransformation(newT);
+ }
/**
* Set the brightness of this part
@@ -621,11 +587,9 @@ public boolean setScale(float x, float y, float z){
@Override
public void setBrightness(@Nullable Display.Brightness brightness){
Entity entity = getEntity();
- if (entity instanceof Interaction){
- return;
+ if (entity instanceof Display display){
+ display.setBrightness(brightness);
}
- Display display = (Display) entity;
- display.setBrightness(brightness);
}
/**
@@ -635,11 +599,9 @@ public void setBrightness(@Nullable Display.Brightness brightness){
@Override
public void setBillboard(@NotNull Display.Billboard billboard){
Entity entity = getEntity();
- if (entity instanceof Interaction){
- return;
+ if (entity instanceof Display display){
+ display.setBillboard(billboard);
}
- Display display = (Display) entity;
- display.setBillboard(billboard);
}
/**
@@ -647,9 +609,10 @@ public void setBillboard(@NotNull Display.Billboard billboard){
*/
@Override
public void setTeleportDuration(int teleportDuration) {
- if (type == PartType.INTERACTION) return;
- Display display = (Display) getEntity();
- display.setTeleportDuration(teleportDuration);
+ Entity entity = getEntity();
+ if (entity instanceof Display display){
+ display.setTeleportDuration(teleportDuration);
+ }
}
/**
@@ -658,11 +621,10 @@ public void setTeleportDuration(int teleportDuration) {
*/
@Override
public void setInterpolationDuration(int interpolationDuration) {
- if (type == PartType.INTERACTION){
- return;
+ Entity entity = getEntity();
+ if (entity instanceof Display display){
+ display.setInterpolationDuration(interpolationDuration);
}
- Display display = (Display) getEntity();
- display.setInterpolationDuration(interpolationDuration);
}
/**
@@ -671,11 +633,10 @@ public void setInterpolationDuration(int interpolationDuration) {
*/
@Override
public void setInterpolationDelay(int interpolationDelay) {
- if (type == PartType.INTERACTION){
- return;
+ Entity entity = getEntity();
+ if (entity instanceof Display display){
+ display.setInterpolationDelay(interpolationDelay);
}
- Display display = (Display) getEntity();
- display.setInterpolationDelay(interpolationDelay);
}
/**
@@ -685,16 +646,15 @@ public void setInterpolationDelay(int interpolationDelay) {
@Override
public void setViewRange(float viewRangeMultiplier){
Entity entity = getEntity();
- if (entity instanceof Interaction){
- return;
+ if (entity instanceof Display display){
+ display.setViewRange(viewRangeMultiplier);
}
- Display display = (Display) entity;
- display.setViewRange(viewRangeMultiplier);
}
@Override
protected void cull(float width, float height){
+ if (!isDisplay()) return;
Entity entity = getEntity();
if (entity instanceof Display display){
display.setDisplayHeight(height);
@@ -710,20 +670,18 @@ protected void cull(float width, float height){
@Override
public void setGlowColor(@Nullable Color color){
Entity entity = getEntity();
- if (entity instanceof Interaction){
- return;
+ if (entity instanceof Display display){
+ display.setGlowColorOverride(color);
}
- Display display = (Display) entity;
- display.setGlowColorOverride(color);
}
@Override
public @Nullable Color getGlowColor(){
- if (type == PartType.INTERACTION){
- return null;
- }
Entity entity = getEntity();
- return ((Display) entity).getGlowColorOverride();
+ if (entity instanceof Display display){
+ return display.getGlowColorOverride();
+ }
+ return null;
}
/**
@@ -771,17 +729,15 @@ void translateForce(Direction direction, float distance, int durationInTicks, in
}
/**
- * Pivot an Interaction Entity around its group's master part
+ * Pivot a non-display entity around its group
* @param angleInDegrees the pivot angle
*/
@Override
public void pivot(float angleInDegrees){
- Entity entity = getEntity();
- if (type != PartType.INTERACTION || isSingle){
- return;
- }
- Interaction i = (Interaction) entity;
- DisplayUtils.pivot(i, group.getLocation(), angleInDegrees);
+ if (isDisplay() || isSingle || group == null) return;
+ Entity e = getEntity();
+ if (e == null) return;
+ DisplayUtils.pivot(e, group.getLocation(), angleInDegrees);
}
@@ -807,20 +763,20 @@ public Interaction spawnInteractionAtDisplay(){
i.setInteractionWidth(width);
i.setInteractionHeight(height);
});
- group.addInteractionEntity(interaction);
+ group.addEntity(interaction);
return interaction;
}
@Override
public void setTransformation(@NotNull Transformation transformation) {
- if (type == PartType.INTERACTION) return;
+ if (!isDisplay()) return;
((Display) getEntity()).setTransformation(transformation);
}
@Override
public void setTransformationMatrix(@NotNull Matrix4f matrix) {
- if (type == PartType.INTERACTION) return;
+ if (!isDisplay()) return;
((Display) getEntity()).setTransformationMatrix(matrix);
}
@@ -903,12 +859,19 @@ public void setItemDisplayTransform(ItemDisplay.@NotNull ItemDisplayTransform tr
public void setItemDisplayItemGlint(boolean hasGlint) {
ItemStack itemStack = getItemDisplayItem();
if (itemStack == null) return;
- itemStack.editMeta(meta -> {
- meta.setEnchantmentGlintOverride(hasGlint);
- });
+ ItemMeta meta = itemStack.getItemMeta();
+ meta.setEnchantmentGlintOverride(hasGlint);
+ itemStack.setItemMeta(meta);
((ItemDisplay) getEntity()).setItemStack(itemStack);
}
+ @Override
+ public boolean hasItemDisplayItemGlint() {
+ ItemStack itemStack = getItemDisplayItem();
+ if (itemStack == null) return false;
+ return itemStack.getItemMeta().getEnchantmentGlintOverride();
+ }
+
@Override
public @Nullable Component getTextDisplayText() {
if (type != PartType.TEXT_DISPLAY) return null;
@@ -938,7 +901,7 @@ public byte getTextDisplayTextOpacity() {
if (type != PartType.TEXT_DISPLAY) return -1;
TextDisplay td = (TextDisplay) getEntity();
if (td == null) return -1;
- return getTextDisplayTextOpacity();
+ return td.getTextOpacity();
}
@Override
@@ -989,6 +952,169 @@ public boolean isTextDisplayDefaultBackground() {
return display.getItemStack();
}
+ @Override
+ public void setMannequinPose(Pose pose) {
+ if (type != PartType.MANNEQUIN) return;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return;
+ if (pose == null) pose = Pose.STANDING;
+ mannequin.setPose(pose, true);
+ }
+
+ @Override
+ public @Nullable Pose getMannequinPose() {
+ if (type != PartType.MANNEQUIN) return null;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return null;
+ return mannequin.getPose();
+ }
+
+ @Override
+ public void setMannequinScale(double scale) {
+ if (type != PartType.MANNEQUIN) return;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return;
+ mannequin.getAttribute(Attribute.SCALE).setBaseValue(scale);
+ }
+
+ @Override
+ public double getMannequinScale() {
+ if (type != PartType.MANNEQUIN) return -1;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return -1;
+ return mannequin.getAttribute(Attribute.SCALE).getBaseValue();
+ }
+
+ @Override
+ public void setMannequinImmovable(boolean immovable) {
+ if (type != PartType.MANNEQUIN) return;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return;
+ mannequin.setImmovable(immovable);
+ }
+
+ @Override
+ public boolean isMannequinImmovable() {
+ if (type != PartType.MANNEQUIN) return false;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return false;
+ return mannequin.isImmovable();
+ }
+
+ @Override
+ public void setMannequinGravity(boolean gravity) {
+ if (type != PartType.MANNEQUIN) return;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return;
+ mannequin.setGravity(gravity);
+ }
+
+ @Override
+ public boolean hasMannequinGravity() {
+ if (type != PartType.MANNEQUIN) return false;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return false;
+ return mannequin.hasGravity();
+ }
+
+ @Override
+ public void setMannequinMainHand(@NotNull MainHand mainHand) {
+ if (type != PartType.MANNEQUIN) return;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return;
+ mannequin.setMainHand(mainHand);
+ }
+
+ @Override
+ public @Nullable MainHand getMannequinMainHand() {
+ if (type != PartType.MANNEQUIN) return null;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return null;
+ return mannequin.getMainHand();
+ }
+
+ @Override
+ public @Nullable ItemStack getMannequinEquipment(@NotNull EquipmentSlot equipmentSlot) {
+ if (type != PartType.MANNEQUIN) return null;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return null;
+ return mannequin.getEquipment().getItem(equipmentSlot);
+ }
+
+
+ @Override
+ public void setMannequinEquipment(@NotNull EquipmentSlot slot, @NotNull ItemStack itemStack) {
+ if (type != PartType.MANNEQUIN) return;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return;
+ mannequin.getEquipment().setItem(slot, itemStack);
+ }
+
+ @Override
+ public void setMannequinProfile(@NotNull PlayerProfile profile) {
+ setMannequinProfile(ResolvableProfile.resolvableProfile(profile));
+ }
+
+ @Override
+ public void setMannequinProfile(@NotNull ResolvableProfile profile) {
+ if (type != PartType.MANNEQUIN) return;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return;
+ mannequin.setProfile(profile);
+ }
+
+ @Override
+ public void setCustomName(@Nullable Component text) {
+ Entity e = getEntity();
+ if (e == null) return;
+ e.customName(text);
+ }
+
+ @Override
+ public void setCustomNameVisible(boolean visible) {
+ Entity e = getEntity();
+ if (e == null) return;
+ e.setCustomNameVisible(visible);
+ }
+
+ @Override
+ public @Nullable Component getCustomName() {
+ Entity e = getEntity();
+ if (e == null) return null;
+ return e.customName();
+ }
+
+ @Override
+ public boolean isCustomNameVisible() {
+ Entity e = getEntity();
+ if (e == null) return false;
+ return e.isCustomNameVisible();
+ }
+
+ @Override
+ public void setMannequinBelowName(@Nullable Component text) {
+ if (type != PartType.MANNEQUIN) return;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return;
+ mannequin.setDescription(text);
+ }
+
+ @Override
+ public ResolvableProfile getMannequinProfile() {
+ if (type != PartType.MANNEQUIN) return null;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return null;
+ return mannequin.getProfile();
+ }
+
+ @Override
+ public @Nullable Component getMannequinBelowName() {
+ if (type != PartType.MANNEQUIN) return null;
+ Mannequin mannequin = (Mannequin) getEntity();
+ if (mannequin == null) return null;
+ return mannequin.getDescription();
+ }
+
/**
* {@inheritDoc}
* The applied changes do not reflect the entity data server-side
@@ -1013,12 +1139,9 @@ public void setAttributes(@NotNull DisplayAttributeMap attributeMap) {
}
@Override
- public @Nullable Vector getInteractionTranslation() {
- if (type != PartType.INTERACTION) {
- return null;
- }
- if (group == null) return null;
- return DisplayUtils.getInteractionTranslation((Interaction) getEntity(), group.getLocation());
+ public @Nullable Vector getNonDisplayTranslation() {
+ if (isDisplay() || group == null) return null;
+ return DisplayUtils.getNonDisplayTranslation(getEntity(), group.getLocation());
}
@Override
@@ -1045,7 +1168,7 @@ public void setInteractionResponsive(boolean responsive) {
@Override
public @Nullable Transformation getTransformation() {
- if (type == PartType.INTERACTION) {
+ if (!isDisplay()) {
return null;
}
return ((Display) getEntity()).getTransformation();
@@ -1053,7 +1176,7 @@ public void setInteractionResponsive(boolean responsive) {
@Override
public @Nullable Display.Brightness getBrightness() {
- if (type == PartType.INTERACTION){
+ if (!isDisplay()){
return null;
}
return ((Display) getEntity()).getBrightness();
@@ -1061,7 +1184,7 @@ public void setInteractionResponsive(boolean responsive) {
@Override
public float getViewRange() {
- if (type == PartType.INTERACTION){
+ if (!isDisplay()){
return -1;
}
return ((Display) getEntity()).getViewRange();
@@ -1091,6 +1214,8 @@ public boolean isInteractionResponsive() {
return ((Interaction) getEntity()).isResponsive();
}
+
+
@Override
public void addInteractionCommand(@NotNull String command, boolean isLeftClick, boolean isConsole) {
if (type == PartType.INTERACTION) DisplayUtils.addInteractionCommand((Interaction) getEntity(), command, isLeftClick, isConsole);
@@ -1115,7 +1240,7 @@ public void removeInteractionCommand(@NotNull InteractionCommand command) {
@Override
public int getTeleportDuration() {
- if (type == PartType.INTERACTION){
+ if (!isDisplay()){
return -1;
}
return ((Display) getEntity()).getTeleportDuration();
@@ -1125,24 +1250,30 @@ public enum PartType{
BLOCK_DISPLAY,
ITEM_DISPLAY,
TEXT_DISPLAY,
- INTERACTION;
-
+ INTERACTION,
+ MANNEQUIN;
+
+ /**
+ * Get a type, respective of the given entity
+ * @param entity the entity
+ * @return a {@link PartType} or null if the entity does not have a type.
+ */
public static PartType getType(@NotNull Entity entity){
- switch (entity){
- case Interaction i -> {
- return INTERACTION;
- }
- case BlockDisplay d -> {
- return BLOCK_DISPLAY;
- }
- case ItemDisplay d -> {
- return ITEM_DISPLAY;
- }
- case TextDisplay d -> {
- return TEXT_DISPLAY;
- }
- default -> throw new IllegalStateException("Unexpected value: " + entity);
- }
+ if (entity instanceof BlockDisplay) return BLOCK_DISPLAY;
+ if (entity instanceof ItemDisplay) return ITEM_DISPLAY;
+ if (entity instanceof TextDisplay) return TEXT_DISPLAY;
+ if (entity instanceof Interaction) return INTERACTION;
+ if (VersionUtils.IS_1_21_9 && entity instanceof Mannequin) return MANNEQUIN;
+ return null;
+ }
+
+ public boolean isOfType(Entity e){
+ if (e instanceof BlockDisplay && this == BLOCK_DISPLAY) return true;
+ if (e instanceof ItemDisplay && this == ITEM_DISPLAY) return true;
+ if (e instanceof TextDisplay && this == TEXT_DISPLAY) return true;
+ if (e instanceof Interaction && this == INTERACTION) return true;
+ if (e instanceof Mannequin && this == MANNEQUIN) return true;
+ return false;
}
}
@@ -1164,7 +1295,6 @@ public Entity remove(boolean kill) {
}
}
this.entity = null;
- this.partData = null;
this.unregister();
return entity;
}
@@ -1176,7 +1306,6 @@ public Entity remove(boolean kill) {
*/
public void removeFromGroup() {
if (group != null){
- allParts.remove(partData);
group.groupParts.remove(partUUID);
for (SpawnedPartSelection selection : group.partSelections){
selection.removePart(this);
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 b8000b5c..0e450277 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/DisplayUtils.java
@@ -4,18 +4,12 @@
import net.donnypz.displayentityutils.DisplayConfig;
import net.donnypz.displayentityutils.events.PartTranslateEvent;
import net.donnypz.displayentityutils.managers.DisplayGroupManager;
-import net.donnypz.displayentityutils.utils.DisplayEntities.ActivePart;
-import net.donnypz.displayentityutils.utils.DisplayEntities.PacketDisplayEntityPart;
-import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityGroup;
-import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityPart;
+import net.donnypz.displayentityutils.utils.DisplayEntities.*;
import net.donnypz.displayentityutils.utils.version.folia.FoliaUtils;
import net.donnypz.displayentityutils.utils.version.folia.Scheduler;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
-import org.bukkit.entity.BlockDisplay;
-import org.bukkit.entity.Display;
-import org.bukkit.entity.Entity;
-import org.bukkit.entity.Interaction;
+import org.bukkit.entity.*;
import org.bukkit.persistence.ListPersistentDataType;
import org.bukkit.persistence.PersistentDataContainer;
import org.bukkit.persistence.PersistentDataType;
@@ -43,11 +37,15 @@ public final class DisplayUtils {
private static final ListPersistentDataType tagPDCType = PersistentDataType.LIST.strings();
private DisplayUtils(){}
+ public static boolean isPartEntity(Entity entity){ // don't add notnull annotation
+ return SpawnedDisplayEntityPart.PartType.getType(entity) != null;
+ }
+
public static @NotNull List getUngroupedPartEntities(@NotNull Location location, double distance){
List parts = new ArrayList<>();
for (Entity e : location.getNearbyEntities(distance, distance, distance)) {
- if (!(e instanceof Display) && !(e instanceof Interaction)) continue;
+ if (!isPartEntity(e)) continue;
if (e instanceof Display){
if (e.getVehicle() instanceof BlockDisplay) continue;
}
@@ -169,13 +167,13 @@ public static Vector pivotPitchAndYaw(@NotNull Vector vector, float pitchChange,
}
/**
- * Get the location where a {@link PacketDisplayEntityPart} display entity is translated based off of its {@link Transformation}'s translation alone.
+ * Get the location where an {@link PacketDisplayEntityPart} display entity is translated based off of its {@link Transformation}'s translation alone.
* This may not be a perfect representation of where the model's location actually is, due to the shape of models varying (e.g.: Stone Block vs Stone Pressure Plate)
* @param part The entity to get the location from
* @return the location where the part is translated at. Null if the part is an interaction entity or if the transformation/location of the entity is unset
*/
public static @Nullable Location getFixedModelLocation(@NotNull PacketDisplayEntityPart part){
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!part.isDisplay()){
return null;
}
@@ -188,13 +186,13 @@ public static Vector pivotPitchAndYaw(@NotNull Vector vector, float pitchChange,
}
/**
- * Get the location where a {@link PacketDisplayEntityPart} display entity is translated based off of its {@link Transformation} and pitch and yaw.
+ * Get the location where an {@link ActivePart} display entity is translated based off of its {@link Transformation} and pitch and yaw.
* This may not be a perfect representation of where the model's location actually is, due to the shape of models varying (e.g.: Stone Block vs Stone Pressure Plate)
* @param part The entity to get the location from
- * @return the location where the part is translated at. Null if the part is an interaction entity
+ * @return the location where the part is translated at. Null if the part is not a display
*/
public static @Nullable Location getModelLocation(@NotNull ActivePart part){
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
+ if (!part.isDisplay()){
return null;
}
@@ -221,7 +219,7 @@ public static Vector pivotPitchAndYaw(@NotNull Vector vector, float pitchChange,
/**
* Calculate and get the culling values that would be applied to a {@link Display}
* @param display the display
- * @return a float array containing the width and height, respectively
+ * @return a float array containing the width and height, respectively. null if the part is not a display
*/
public static float[] getAutoCullValues(@NotNull Display display){
return getAutoCullValues(display, DisplayConfig.widthCullingAdder(), DisplayConfig.heightCullingAdder());
@@ -232,7 +230,7 @@ public static float[] getAutoCullValues(@NotNull Display display){
* @param display the display
* @param widthAdder the fixed value to increase the calculated width by
* @param heightAdder the fixed value to increase the calculated height by
- * @return a float array containing the width and height, respectively
+ * @return a float array containing the width and height, respectively. null if the part is not a display
*/
public static float[] getAutoCullValues(@NotNull Display display, float widthAdder, float heightAdder){
SpawnedDisplayEntityPart.PartType type = display instanceof BlockDisplay ? SpawnedDisplayEntityPart.PartType.BLOCK_DISPLAY : null;
@@ -243,7 +241,7 @@ public static float[] getAutoCullValues(@NotNull Display display, float widthAdd
/**
* Calculate and get the culling values that would be applied to an {@link ActivePart}
* @param part the part
- * @return a float array containing the width and height, respectively
+ * @return a float array containing the width and height, respectively. null if the part is not a display
*/
public static float[] getAutoCullValues(@NotNull ActivePart part){
return getAutoCullValues(part, DisplayConfig.widthCullingAdder(), DisplayConfig.heightCullingAdder());
@@ -254,9 +252,10 @@ public static float[] getAutoCullValues(@NotNull ActivePart part){
* @param part the part
* @param widthAdder the fixed value to increase the calculated width by
* @param heightAdder the fixed value to increase the calculated height by
- * @return a float array containing the width and height, respectively
+ * @return a float array containing the width and height, respectively. null if the part is not a display
*/
public static float[] getAutoCullValues(@NotNull ActivePart part, float widthAdder, float heightAdder){
+ if (!part.isDisplay()) return null;
Transformation t = part.getTransformation();
return getAutoCullValues(part.getType(), t.getTranslation(), t.getScale(), t.getLeftRotation(), widthAdder, heightAdder);
}
@@ -317,33 +316,26 @@ public static Location getInteractionCenter(@NotNull Interaction interaction){
/**
- * Get the translation vector from the group's master part to the interaction's location
- * @param interaction the interaction
- * @return a vector or null if the Interaction entity is not in a group
+ * Get the translation vector from the entity's group's master part to the entity's location
+ * @param entity the interaction
+ * @return a vector or null if the entity is not in a group
*/
- public static Vector getInteractionTranslation(@NotNull Interaction interaction){
- SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(interaction);
+ public static @Nullable Vector getNonDisplayTranslation(@NotNull Entity entity){
+ SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(entity);
if (part == null){
return null;
}
- return getInteractionTranslation(interaction, part.getGroup().getLocation());
- /*return part
- .getGroup()
- .getMasterPart()
- .getEntity()
- .getLocation()
- .toVector()
- .subtract(interaction.getLocation().toVector());*/
+ return getNonDisplayTranslation(entity, part.getGroup().getLocation());
}
/**
- * Get the translation vector from a location to the interaction's location
- * @param interaction the interaction
+ * Get the translation vector from a location to the entity's location
+ * @param entity the entity
* @param referenceLocation the reference location
* @return a vector
*/
- public static Vector getInteractionTranslation(@NotNull Interaction interaction, @NotNull Location referenceLocation){
- return referenceLocation.toVector().subtract(interaction.getLocation().toVector());
+ public static @NotNull Vector getNonDisplayTranslation(@NotNull Entity entity, @NotNull Location referenceLocation){
+ return referenceLocation.toVector().subtract(entity.getLocation().toVector());
}
/**
@@ -472,24 +464,24 @@ public static void translate(@NotNull Display display, @NotNull Direction direct
}
/**
- * Attempts to change the translation of an interaction entity similar
+ * Attempts to change the translation of an entity similar
* to a Display Entity, through smooth teleportation.
- * Doing multiple translations on an Interaction entity at the same time may have unexpected results
- * @param direction The direction to translate the interaction entity
- * @param interaction Interaction Entity to translate
- * @param distance How far the interaction entity should be translated
+ * Doing multiple translations on an entity at the same time may have unexpected results
+ * @param direction The direction to translate the entity
+ * @param entity entity to translate
+ * @param distance How far the entity should be translated
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translate(@NotNull Interaction interaction, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
+ public static void translate(@NotNull Entity entity, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
if (distance == 0) return;
- Location destination = interaction.getLocation().clone().add(direction.clone().normalize().multiply(distance));
- if (!new PartTranslateEvent(interaction, destination, null,null).callEvent()){
+ Location destination = entity.getLocation().clone().add(direction.clone().normalize().multiply(distance));
+ if (!new PartTranslateEvent(entity, destination, null,null).callEvent()){
return;
}
if (durationInTicks <= 0 && delayInTicks <= 0){
- FoliaUtils.teleport(interaction, destination);
+ FoliaUtils.teleport(entity, destination);
return;
}
@@ -499,25 +491,25 @@ public static void translate(@NotNull Interaction interaction, @NotNull Vector d
.normalize()
.multiply(movementIncrement);
- DisplayAPI.getScheduler().entityRunTimer(interaction, new Scheduler.SchedulerRunnable() {
+ DisplayAPI.getScheduler().entityRunTimer(entity, new Scheduler.SchedulerRunnable() {
double currentDistance = 0;
- float lastYaw = interaction.getYaw();
+ float lastYaw = entity.getYaw();
@Override
public void run() {
- float newYaw = interaction.getYaw();
+ float newYaw = entity.getYaw();
if (newYaw != lastYaw){
incrementVector.rotateAroundY(Math.toRadians(lastYaw-newYaw));
lastYaw = newYaw;
}
currentDistance+=Math.abs(movementIncrement);
- Location tpLoc = interaction.getLocation().clone().add(incrementVector);
+ Location tpLoc = entity.getLocation().clone().add(incrementVector);
if (currentDistance >= distance){
- FoliaUtils.teleport(interaction, destination);
+ FoliaUtils.teleport(entity, destination);
cancel();
}
else{
- FoliaUtils.teleport(interaction, tpLoc);
+ FoliaUtils.teleport(entity, tpLoc);
}
}
}, delayInTicks, 1);
@@ -525,17 +517,17 @@ public void run() {
/**
- * Attempts to change the translation of an interaction entity similar
+ * Attempts to change the translation of an entity similar
* to a Display Entity, through smooth teleportation.
- * Doing multiple translations on an Interaction entity at the same time may have unexpected results
- * @param interaction Interaction Entity to translate
- * @param direction The direction to translate the interaction entity
- * @param distance How far the interaction entity should be translated
+ * Doing multiple translations on an entity at the same time may have unexpected results
+ * @param entity entity to translate
+ * @param direction The direction to translate the entity
+ * @param distance How far the entity should be translated
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translate(@NotNull Interaction interaction, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
- translate(interaction, direction.getVector(interaction, true), distance, durationInTicks, delayInTicks);
+ public static void translate(@NotNull Entity entity, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
+ translate(entity, direction.getVector(entity, true), distance, durationInTicks, delayInTicks);
}
/**
@@ -549,8 +541,8 @@ public static void translate(@NotNull Interaction interaction, @NotNull Directio
* @param delayInTicks How long before the translation should begin
*/
public static void translate(@NotNull SpawnedDisplayEntityPart part, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
- translate((Interaction) part.getEntity(), direction, distance, durationInTicks, delayInTicks);
+ if (!part.isDisplay()){
+ translate(part.getEntity(), direction, distance, durationInTicks, delayInTicks);
return;
}
translate((Display) part.getEntity(), direction, distance, durationInTicks, delayInTicks);
@@ -567,9 +559,9 @@ public static void translate(@NotNull SpawnedDisplayEntityPart part, @NotNull Ve
* @param delayInTicks How long before the translation should begin
*/
public static void translate(@NotNull SpawnedDisplayEntityPart part, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
- Interaction interaction = (Interaction) part.getEntity();
- translate(interaction, direction.getVector(interaction, true), distance, durationInTicks, delayInTicks);
+ if (!part.isDisplay()){
+ Entity entity = part.getEntity();
+ translate(entity, direction.getVector(entity, true), distance, durationInTicks, delayInTicks);
return;
}
Display display = (Display) part.getEntity();
@@ -578,22 +570,22 @@ public static void translate(@NotNull SpawnedDisplayEntityPart part, @NotNull Di
/**
- * Pivot an Interaction entity around a location
- * @param interaction the interaction
- * @param center the location the interaction should pivot around
+ * Pivot an entity around a location
+ * @param entity the entity
+ * @param center the location the entity should pivot around
* @param angleInDegrees the pivot angle in degrees
*/
- public static void pivot(@NotNull Interaction interaction, @NotNull Location center, double angleInDegrees){
- Vector3f translationVector = DisplayUtils.getInteractionTranslation(interaction, center).toVector3f();
+ public static void pivot(@NotNull Entity entity, @NotNull Location center, double angleInDegrees){
+ Vector3f translationVector = DisplayUtils.getNonDisplayTranslation(entity, center).toVector3f();
new Quaternionf()
.rotateY((float) Math.toRadians(-angleInDegrees))
.transform(translationVector);
Location newLoc = center.clone().subtract(Vector.fromJOML(translationVector));
- FoliaUtils.teleport(interaction, newLoc);
+ FoliaUtils.teleport(entity, newLoc);
}
/**
- * Get the location an interaction entity would be pivoted to after using {@link DisplayUtils#pivot(Interaction, Location, double)}
+ * Get the location an interaction entity would be pivoted to after using {@link DisplayUtils#pivot(Entity, Location, double)}
* @param offsetLocation the interaction entity's location
* @param origin the location the interaction should pivot around
* @param angleInDegrees the pivot angle in degrees
@@ -605,7 +597,7 @@ public static void pivot(@NotNull Interaction interaction, @NotNull Location cen
}
/**
- * Get the location an interaction entity would be pivoted to after using {@link DisplayUtils#pivot(Interaction, Location, double)}
+ * Get the location an interaction entity would be pivoted to after using {@link DisplayUtils#pivot(Entity, Location, double)}
* @param translationVector the translation offset for an interaction entity from a center location
* @param origin the location the interaction should pivot around
* @param angleInDegrees the pivot angle in degrees
@@ -616,7 +608,7 @@ public static void pivot(@NotNull Interaction interaction, @NotNull Location cen
}
/**
- * Get the location an interaction entity would be pivoted to after using {@link DisplayUtils#pivot(Interaction, Location, double)}
+ * Get the location an interaction entity would be pivoted to after using {@link DisplayUtils#pivot(Entity, Location, double)}
* @param translationVector the translation offset for an interaction entity from a center location
* @param origin the location the interaction should pivot around
* @param angleInDegrees the pivot angle in degrees
@@ -636,7 +628,7 @@ public static void pivot(@NotNull Interaction interaction, @NotNull Location cen
* @param durationInTicks how long the scaling should take
* @param delayInTicks how long before the scaling should start
*/
- public static void scaleInteraction(Interaction interaction, float newHeight, float newWidth, int durationInTicks, int delayInTicks){
+ public static void scaleInteraction(@NotNull Interaction interaction, float newHeight, float newWidth, int durationInTicks, int delayInTicks){
if (durationInTicks <= 0 && delayInTicks <= 0){
interaction.setInteractionHeight(newHeight);
interaction.setInteractionWidth(newWidth);
@@ -662,49 +654,24 @@ public void run() {
}
/**
- * Gets the group tag of a Display Entity
- * @param display Display Entity to retrieve the tag from
- * @return Group tag of the entity. Null if the entity did not have a group tag.
+ * Gets the group tag of a valid part entity
+ * @param entity entity to retrieve the tag from
+ * @return a string, null if the entity did not have a group tag.
*/
- public static @Nullable String getGroupTag(Display display){
- return getPDCGroupTag(display);
- }
-
- /**
- * Gets the group tag of an Interaction Entity
- * @param interaction Interaction Entity to retrieve the tag from
- * @return Group tag of the entity. Null if the entity did not have a group tag.
- */
- public static @Nullable String getGroupTag(Interaction interaction){
- return getPDCGroupTag(interaction);
- }
-
- private static String getPDCGroupTag(Entity entity){
+ public static @Nullable String getGroupTag(Entity entity){
+ if (entity == null) return null;
PersistentDataContainer pdc = entity.getPersistentDataContainer();
return pdc.get(DisplayAPI.getGroupTagKey(), PersistentDataType.STRING);
}
/**
- * Gets the part uuid of a Display Entity
- * @param display Display Entity to retrieve the uuid from
- * @return Part UUID of the entity. Null if the entity is not part of a display entity group. Will still return a value if the entity
+ * Gets the part uuid of a valid part entity
+ * @param entity entity to retrieve the uuid from
+ * @return a UUID or null if the entity is not part of a group. Will still return a value if the entity
* was previously part of a group, but later removed.
*/
- public static @Nullable UUID getPartUUID(Display display){
- return getPDCPartUUID(display);
- }
-
- /**
- * Gets the part uuid of an Interaction Entity
- * @param interaction Interaction Entity to retrieve the uuid from
- * @return Part UUID of the entity. Null if the entity is not part of a display entity group. Will still return a value if the entity
- * was previously part of a group, but later removed.
- */
- public static @Nullable UUID getPartUUID(Interaction interaction){
- return getPDCPartUUID(interaction);
- }
-
- private static UUID getPDCPartUUID(Entity entity){
+ public static @Nullable UUID getPartUUID(Entity entity){
+ if (entity == null) return null;
PersistentDataContainer pdc = entity.getPersistentDataContainer();
String value = pdc.get(DisplayAPI.getPartUUIDKey(), PersistentDataType.STRING);
if (value != null){
@@ -719,7 +686,6 @@ private static UUID getPDCPartUUID(Entity entity){
* @return List of commands stored on this interaction entity
*/
public static @NotNull List getInteractionCommands(@NotNull Interaction interaction){
-
List commands = new ArrayList<>();
commands.addAll(getInteractionLeftConsoleCommands(interaction));
commands.addAll(getInteractionLeftPlayerCommands(interaction));
@@ -951,27 +917,14 @@ public static boolean hasPartTag(@NotNull Entity entity, @NotNull String tag){
/**
- * Checks if this display entity has the specified group tag
- * @param display Display Entity to check for a group tag
- * @param tag The tag to check for
- * @return boolean whether this display entity has the group tag
- */
- public static boolean isGroupTag(Display display, @NotNull String tag){
- String value = display.getPersistentDataContainer().get(DisplayAPI.getGroupTagKey(), PersistentDataType.STRING);
- if (value == null){
- return false;
- }
- return tag.equals(value);
- }
-
- /**
- * Checks if this interaction entity has the specified group tag
- * @param interaction Interaction Entity to check for a group tag
- * @param tag The tag to check for
- * @return boolean whether this interaction entity has the group tag
+ * Checks if an entity has the specified group tag
+ * @param entity entity to check for a group tag
+ * @param tag the group tag
+ * @return a boolean
*/
- public static boolean isGroupTag(Interaction interaction, @NotNull String tag){
- String value = interaction.getPersistentDataContainer().get(DisplayAPI.getGroupTagKey(), PersistentDataType.STRING);
+ public static boolean isGroupTag(Entity entity, @NotNull String tag){
+ if (entity == null) return false;
+ String value = entity.getPersistentDataContainer().get(DisplayAPI.getGroupTagKey(), PersistentDataType.STRING);
if (value == null){
return false;
}
@@ -984,31 +937,8 @@ public static boolean isGroupTag(Interaction interaction, @NotNull String tag){
* @return a boolean
*/
public static boolean isInGroup(Entity entity){
- if (entity instanceof Display display){
- return SpawnedDisplayEntityPart.getPart(display) != null;
- }
- else if (entity instanceof Interaction interaction){
- return SpawnedDisplayEntityPart.getPart(interaction) != null;
- }
- return false;
- }
-
- /**
- * Get the creation time of a Display Entity
- * @param display The Display Entity to check for a creation time
- * @return The Display Entity's Group's Creation time. -1 if this display is not part of a group
- */
- public static long getCreationTime(Display display){
- return getCreationTime((Entity) display);
- }
-
- /**
- * Get the creation time of an Interaction Entity
- * @param interaction The Interaction to check for a creation time
- * @return The Interaction's Group's Creation time. -1 if this interaction is not part of a group
- */
- public static long getCreationTime(Interaction interaction){
- return getCreationTime((Entity) interaction);
+ SpawnedDisplayEntityPart part = SpawnedDisplayEntityPart.getPart(entity);
+ return part != null && part.hasGroup();
}
/**
@@ -1033,6 +963,10 @@ public static boolean isMaster(Display display){
return container.has(DisplayAPI.getMasterKey(), PersistentDataType.BOOLEAN);
}
-
-
+ @ApiStatus.Internal
+ public static void prepareMannequin(Mannequin mannequin){
+ mannequin.setInvulnerable(true);
+ mannequin.setImmovable(true);
+ mannequin.setGravity(false);
+ }
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/PacketUtils.java b/api/src/main/java/net/donnypz/displayentityutils/utils/PacketUtils.java
index 00540419..3cfdb74a 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/PacketUtils.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/PacketUtils.java
@@ -6,7 +6,6 @@
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityTeleport;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import net.donnypz.displayentityutils.DisplayAPI;
-import net.donnypz.displayentityutils.managers.DEUUser;
import net.donnypz.displayentityutils.utils.DisplayEntities.ActivePart;
import net.donnypz.displayentityutils.utils.DisplayEntities.PacketDisplayEntityGroup;
import net.donnypz.displayentityutils.utils.DisplayEntities.PacketDisplayEntityPart;
@@ -16,18 +15,15 @@
import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttribute;
import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttributes;
import net.donnypz.displayentityutils.utils.version.folia.Scheduler;
-import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.entity.Interaction;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;
-import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.joml.Vector3f;
import java.util.Collection;
import java.util.List;
-import java.util.SequencedCollection;
public final class PacketUtils {
@@ -279,7 +275,7 @@ private static void sendInteractionPacket(int entityId, float newHeight, float n
/**
* Change the translation of a {@link PacketDisplayEntityPart} if it's not an {@link SpawnedDisplayEntityPart.PartType#INTERACTION}.
- * If it is an interaction, {@link #translateInteraction(PacketDisplayEntityPart, Direction, double, int, int)} will be called instead.
+ * If it is an interaction, {@link #translateNonDisplay(PacketDisplayEntityPart, Direction, double, int, int)} will be called instead.
* @param part the part to translate
* @param direction the direction to translate the display entity
* @param distance translation distance
@@ -288,8 +284,8 @@ private static void sendInteractionPacket(int entityId, float newHeight, float n
*/
public static void translate(@NotNull PacketDisplayEntityPart part, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
if (distance == 0) return;
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
- translateInteraction(part, direction, distance, durationInTicks, delayInTicks);
+ if (part.isDisplay()){
+ translateNonDisplay(part, direction, distance, durationInTicks, delayInTicks);
return;
}
translate(part, direction.getVector(part, true), distance, durationInTicks, delayInTicks);
@@ -297,7 +293,7 @@ public static void translate(@NotNull PacketDisplayEntityPart part, @NotNull Dir
/**
* Change the translation of a {@link PacketDisplayEntityPart} if it's not an {@link SpawnedDisplayEntityPart.PartType#INTERACTION}.
- * If it is an interaction, {@link #translateInteraction(PacketDisplayEntityPart, Vector, double, int, int)} will be called instead.
+ * If it is an interaction, {@link #translateNonDisplay(PacketDisplayEntityPart, Vector, double, int, int)} will be called instead.
* @param part the part to translate
* @param direction the direction to translate the display entity
* @param distance translation distance
@@ -306,8 +302,8 @@ public static void translate(@NotNull PacketDisplayEntityPart part, @NotNull Dir
*/
public static void translate(@NotNull PacketDisplayEntityPart part, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
if (distance == 0) return;
- if (part.getType() == SpawnedDisplayEntityPart.PartType.INTERACTION){
- translateInteraction(part, direction, distance, durationInTicks, delayInTicks);
+ if (!part.isDisplay()){
+ translateNonDisplay(part, direction, distance, durationInTicks, delayInTicks);
return;
}
if (delayInTicks < 0){
@@ -333,8 +329,8 @@ public static void translate(@NotNull PacketDisplayEntityPart part, @NotNull Vec
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull PacketDisplayEntityPart part, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
- translateInteraction(part, direction.getVector(part, true), distance, durationInTicks, delayInTicks);
+ public static void translateNonDisplay(@NotNull PacketDisplayEntityPart part, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
+ translateNonDisplay(part, direction.getVector(part, true), distance, durationInTicks, delayInTicks);
}
@@ -348,8 +344,8 @@ public static void translateInteraction(@NotNull PacketDisplayEntityPart part, @
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull PacketDisplayEntityPart part, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
- if (part.getType() != SpawnedDisplayEntityPart.PartType.INTERACTION) return;
+ public static void translateNonDisplay(@NotNull PacketDisplayEntityPart part, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
+ if (part.isDisplay()) return;
Location destination = part.getLocation().clone().add(direction.clone().normalize().multiply(distance));
if (durationInTicks <= 0 && delayInTicks <= 0){
@@ -398,8 +394,8 @@ public void run() {
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull Player player, @NotNull Interaction interaction, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
- translateInteraction(player, interaction, direction.getVector(interaction, true), distance, durationInTicks, delayInTicks);
+ public static void translateNonDisplay(@NotNull Player player, @NotNull Interaction interaction, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
+ translateNonDisplay(player, interaction, direction.getVector(interaction, true), distance, durationInTicks, delayInTicks);
}
/**
@@ -413,8 +409,8 @@ public static void translateInteraction(@NotNull Player player, @NotNull Interac
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull Player player, @NotNull Interaction interaction, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
- translateInteraction(List.of(player), interaction, direction, distance, durationInTicks, delayInTicks);
+ public static void translateNonDisplay(@NotNull Player player, @NotNull Interaction interaction, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
+ translateNonDisplay(List.of(player), interaction, direction, distance, durationInTicks, delayInTicks);
}
/**
@@ -428,8 +424,8 @@ public static void translateInteraction(@NotNull Player player, @NotNull Interac
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull Player player, @NotNull ActivePart part, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
- translateInteraction(player, part, direction.getVector(part, true), distance, durationInTicks, delayInTicks);
+ public static void translateNonDisplay(@NotNull Player player, @NotNull ActivePart part, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
+ translateNonDisplay(player, part, direction.getVector(part, true), distance, durationInTicks, delayInTicks);
}
/**
@@ -443,8 +439,8 @@ public static void translateInteraction(@NotNull Player player, @NotNull ActiveP
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull Player player, @NotNull ActivePart part, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
- translateInteraction(List.of(player), part, direction, distance, durationInTicks, delayInTicks);
+ public static void translateNonDisplay(@NotNull Player player, @NotNull ActivePart part, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
+ translateNonDisplay(List.of(player), part, direction, distance, durationInTicks, delayInTicks);
}
/**
@@ -458,8 +454,8 @@ public static void translateInteraction(@NotNull Player player, @NotNull ActiveP
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull Collection players, @NotNull Interaction interaction, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
- translateInteraction(players, interaction, direction.getVector(interaction, true), distance, durationInTicks, delayInTicks);
+ public static void translateNonDisplay(@NotNull Collection players, @NotNull Interaction interaction, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
+ translateNonDisplay(players, interaction, direction.getVector(interaction, true), distance, durationInTicks, delayInTicks);
}
/**
@@ -473,9 +469,9 @@ public static void translateInteraction(@NotNull Collection players, @No
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull Collection players, @NotNull Interaction interaction, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
+ public static void translateNonDisplay(@NotNull Collection players, @NotNull Interaction interaction, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
Location l = interaction.getLocation();
- translateInteraction(players, interaction.getEntityId(), l, l.getYaw(), direction, distance, durationInTicks, delayInTicks);
+ translateNonDisplay(players, interaction.getEntityId(), l, l.getYaw(), direction, distance, durationInTicks, delayInTicks);
}
/**
@@ -489,8 +485,8 @@ public static void translateInteraction(@NotNull Collection players, @No
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull Collection players, @NotNull ActivePart part, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
- translateInteraction(players, part, direction.getVector(part, true), distance, durationInTicks, delayInTicks);
+ public static void translateNonDisplay(@NotNull Collection players, @NotNull ActivePart part, @NotNull Direction direction, double distance, int durationInTicks, int delayInTicks){
+ translateNonDisplay(players, part, direction.getVector(part, true), distance, durationInTicks, delayInTicks);
}
/**
@@ -504,17 +500,17 @@ public static void translateInteraction(@NotNull Collection players, @No
* @param durationInTicks How long it should take for the translation to complete
* @param delayInTicks How long before the translation should begin
*/
- public static void translateInteraction(@NotNull Collection players, @NotNull ActivePart part, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
- if (part.getType() != SpawnedDisplayEntityPart.PartType.INTERACTION) return;
+ public static void translateNonDisplay(@NotNull Collection players, @NotNull ActivePart part, @NotNull Vector direction, double distance, int durationInTicks, int delayInTicks){
+ if (part.isDisplay()) return;
Location l = part.getLocation();
if (l == null) return;
- translateInteraction(players, part.getEntityId(), l, l.getYaw(), direction, distance, durationInTicks, delayInTicks);
+ translateNonDisplay(players, part.getEntityId(), l, l.getYaw(), direction, distance, durationInTicks, delayInTicks);
}
- private static void translateInteraction(Collection players, int entityId, Location location, float lastYaw, Vector direction, double distance, int durationInTicks, int delayInTicks){
+ private static void translateNonDisplay(Collection players, int entityId, Location location, float lastYaw, Vector direction, double distance, int durationInTicks, int delayInTicks){
Location destination = location.clone().add(direction.clone().normalize().multiply(distance));
if (durationInTicks <= 0 && delayInTicks <= 0){
- sendInteractionTeleportPacket(entityId, destination, players);
+ sendTranslateTeleportPacket(entityId, destination, players);
return;
}
@@ -539,17 +535,17 @@ public void run() {
tpLoc.add(incrementVector);
if (currentDistance >= distance){
- sendInteractionTeleportPacket(entityId, destination, players);
+ sendTranslateTeleportPacket(entityId, destination, players);
cancel();
}
else{
- sendInteractionTeleportPacket(entityId, tpLoc, players);
+ sendTranslateTeleportPacket(entityId, tpLoc, players);
}
}
}, delayInTicks, 1);
}
- private static void sendInteractionTeleportPacket(int entityId, Location location, Collection players){
+ private static void sendTranslateTeleportPacket(int entityId, Location location, Collection players){
for (Player p : players){
PacketEvents.getAPI().getPlayerManager().sendPacket(p, new WrapperPlayServerEntityTeleport(entityId,
SpigotConversionUtil.fromBukkitLocation(location),
@@ -622,27 +618,4 @@ public static void setGlowing(@NotNull Player player, int entityId, long duratio
setGlowing(player, entityId, false);
}, durationInTicks);
}
-
- @ApiStatus.Internal
- public static int[] getTrackedIntersection(DEUUser user, SequencedCollection parts){
- Player p = Bukkit.getPlayer(user.getUserUUID());
- return parts.stream()
- .filter(part -> {
- if (part.isTrackedBy(user.getUserUUID())){
- part.hideFromPlayer(p);
- return true;
- }
- return false;
- })
- .mapToInt(PacketDisplayEntityPart::getEntityId)
- .toArray();
-
- /*int[] ids = new int[parts.size()];
- int i = 0;
- for (PacketDisplayEntityPart part : parts){
- ids[i] = part.getEntityId();
- i++;
- }
- return ids;*/
- }
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/file/BDEItemDisplay.java b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/file/BDEItemDisplay.java
index 1eb24bd8..30bf14d5 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/file/BDEItemDisplay.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/bdengine/convert/file/BDEItemDisplay.java
@@ -40,11 +40,11 @@ void apply(ItemDisplay display) {
//For Player Heads
if (playerHeadTexture != null && !playerHeadTexture.isBlank()){
- item.editMeta(SkullMeta.class, meta -> {
- PlayerProfile profile = Bukkit.createProfile(UUID.randomUUID());
- profile.setProperty(new ProfileProperty("textures", playerHeadTexture));
- meta.setPlayerProfile(profile);
- });
+ SkullMeta meta = (SkullMeta) item.getItemMeta();
+ PlayerProfile profile = Bukkit.createProfile(UUID.randomUUID());
+ profile.setProperty(new ProfileProperty("textures", playerHeadTexture));
+ meta.setPlayerProfile(profile);
+ item.setItemMeta(meta);
}
display.setItemStack(item);
}
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/controller/DisplayController.java b/api/src/main/java/net/donnypz/displayentityutils/utils/controller/DisplayController.java
index 8346e0cd..817934e3 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/controller/DisplayController.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/controller/DisplayController.java
@@ -91,7 +91,7 @@ boolean isMarkedNull(){
return grouplessControllers.containsKey(this);
}
- @ApiStatus.Internal
+
/**
* Set the {@link DisplayEntityGroup} this controller should use
* @param group
diff --git a/api/src/main/java/net/donnypz/displayentityutils/utils/packet/PacketAttributeContainer.java b/api/src/main/java/net/donnypz/displayentityutils/utils/packet/PacketAttributeContainer.java
index 0135456f..c0f9f62f 100644
--- a/api/src/main/java/net/donnypz/displayentityutils/utils/packet/PacketAttributeContainer.java
+++ b/api/src/main/java/net/donnypz/displayentityutils/utils/packet/PacketAttributeContainer.java
@@ -5,19 +5,26 @@
import com.github.retrooper.packetevents.protocol.entity.data.EntityDataType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityType;
import com.github.retrooper.packetevents.protocol.entity.type.EntityTypes;
+import com.github.retrooper.packetevents.protocol.player.Equipment;
+import com.github.retrooper.packetevents.protocol.player.EquipmentSlot;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityEquipment;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerEntityMetadata;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerSpawnEntity;
+import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateAttributes;
import io.github.retrooper.packetevents.util.SpigotConversionUtil;
import io.github.retrooper.packetevents.util.SpigotReflectionUtil;
import net.donnypz.displayentityutils.utils.DisplayEntities.PacketDisplayEntityPart;
import net.donnypz.displayentityutils.utils.DisplayEntities.SpawnedDisplayEntityPart;
import net.donnypz.displayentityutils.utils.DisplayUtils;
+import net.donnypz.displayentityutils.utils.packet.attributes.AttributeDisplayAttribute;
import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttribute;
import net.donnypz.displayentityutils.utils.packet.attributes.DisplayAttributes;
+import net.donnypz.displayentityutils.utils.packet.attributes.EquipmentAttribute;
import org.bukkit.Bukkit;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
import org.bukkit.util.Transformation;
import org.jetbrains.annotations.NotNull;
import org.joml.Matrix4f;
@@ -138,7 +145,7 @@ public PacketAttributeContainer setAttributes(@NotNull DisplayAttributeMap attri
*/
public PacketAttributeContainer setAttributeAndSend(@NotNull DisplayAttribute attribute, T value, int entityId, @NotNull Player player){
this.attributes.put(attribute, value);
- sendAttributes(player, entityId, getMetadataList(Map.of(attribute, value)));
+ new PacketResult(entityId, attribute, value).send(player);
return this;
}
@@ -151,7 +158,7 @@ public PacketAttributeContainer setAttributeAndSend(@NotNull DisplayAttri
*/
public void setAttributeAndSend(@NotNull DisplayAttribute attribute, T value, int entityId, @NotNull Collection playerUUIDs){
this.attributes.put(attribute, value);
- sendAttributesToUUIDs(playerUUIDs, entityId, getMetadataList(new DisplayAttributeMap().add(attribute, value).attributes));
+ new PacketResult(entityId, attribute, value).sendUUIDs(playerUUIDs);
}
/**
@@ -164,7 +171,7 @@ public void setAttributeAndSend(@NotNull DisplayAttribute attribute
*/
public PacketAttributeContainer setAttributesAndSend(@NotNull DisplayAttributeMap attributeMap, int entityId, @NotNull Player player){
this.attributes.putAll(attributeMap.attributes);
- sendAttributes(player, entityId, getMetadataList(attributeMap.attributes));
+ new PacketResult(entityId, attributeMap).send(player);
return this;
}
@@ -178,7 +185,7 @@ public PacketAttributeContainer setAttributesAndSend(@NotNull DisplayAttributeMa
*/
public PacketAttributeContainer setAttributesAndSend(@NotNull DisplayAttributeMap attributeMap, int entityId, @NotNull Collection playerUUIDs){
this.attributes.putAll(attributeMap.attributes);
- sendAttributesToUUIDs(playerUUIDs, entityId, getMetadataList(attributeMap.attributes));
+ new PacketResult(entityId, attributeMap).sendUUIDs(playerUUIDs);
return this;
}
@@ -358,7 +365,7 @@ public int sendEntityUsingUUIDs(@NotNull SpawnedDisplayEntityPart.PartType partT
* @return this
*/
private PacketAttributeContainer sendAttributes(SpawnedDisplayEntityPart.PartType partType, Player player, int entityId){
- sendAttributes(player, entityId, getMetadataList(attributes, partType));
+ new PacketResult(entityId).send(player);
return this;
}
@@ -370,7 +377,7 @@ private PacketAttributeContainer sendAttributes(SpawnedDisplayEntityPart.PartTyp
* @return this
*/
public PacketAttributeContainer sendAttributes(@NotNull Player player, int entityId){
- sendAttributes(player, entityId, getMetadataList(attributes));
+ new PacketResult(entityId).send(player);
return this;
}
@@ -381,7 +388,7 @@ public PacketAttributeContainer sendAttributes(@NotNull Player player, int entit
* @return this
*/
public PacketAttributeContainer sendAttributesUsingUUIDs(@NotNull Collection playerUUIDs, int entityId){
- sendAttributesToUUIDs(playerUUIDs, entityId, getMetadataList(attributes));
+ new PacketResult(entityId).sendUUIDs(playerUUIDs);
return this;
}
@@ -392,26 +399,10 @@ public PacketAttributeContainer sendAttributesUsingUUIDs(@NotNull Collection players, int entityId){
- sendAttributesToPlayers(players, entityId, getMetadataList(attributes));
+ new PacketResult(entityId).send(players);
return this;
}
- private void sendAttributes(@NotNull Player player, int entityId, List> data){
- PacketEvents.getAPI().getPlayerManager().sendPacket(player, new WrapperPlayServerEntityMetadata(entityId, data));
- }
-
- private void sendAttributesToPlayers(@NotNull Collection players, int entityId, List> data){
- for (Player player : players){
- PacketEvents.getAPI().getPlayerManager().sendPacket(player, new WrapperPlayServerEntityMetadata(entityId, data));
- }
- }
-
- private void sendAttributesToUUIDs(@NotNull Collection playerUUIDs, int entityId, List> data){
- for (UUID uuid : playerUUIDs){
- PacketEvents.getAPI().getPlayerManager().sendPacket(Bukkit.getPlayer(uuid), new WrapperPlayServerEntityMetadata(entityId, data));
- }
- }
-
private WrapperPlayServerSpawnEntity createEntityPacket(int entityId, SpawnedDisplayEntityPart.PartType partType, Location location){
return new WrapperPlayServerSpawnEntity(
entityId,
@@ -419,86 +410,11 @@ private WrapperPlayServerSpawnEntity createEntityPacket(int entityId, SpawnedDis
getEntityType(partType),
//SpigotConversionUtil.fromBukkitLocation(getTrueLocation(partType, location)),
SpigotConversionUtil.fromBukkitLocation(location),
- 0,
+ location.getYaw(),
0,
null);
}
- private WrapperPlayServerEntityMetadata createFullMetadataPacket(int entityId){
- return new WrapperPlayServerEntityMetadata(entityId, getMetadataList());
- }
-
-
-
- private List> getMetadataList() {
- return getMetadataList(attributes);
- }
-
- private List> getMetadataList(Map, Object> attributes){
- List> metadata = new ArrayList<>();
- for (Map.Entry, Object> entry : attributes.entrySet()) {
- DisplayAttribute, ?> attr = entry.getKey();
- Object val = entry.getValue();
-
-
- DisplayAttribute