diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 478974090..7dd8b13da 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -13,11 +13,15 @@ import net.minestom.server.event.Event; import net.minestom.server.event.EventCallback; import net.minestom.server.event.entity.*; import net.minestom.server.event.handler.EventHandler; +import net.minestom.server.event.item.ArmorEquipEvent; import net.minestom.server.instance.Chunk; import net.minestom.server.instance.Instance; import net.minestom.server.instance.InstanceManager; import net.minestom.server.instance.block.CustomBlock; +import net.minestom.server.inventory.EquipmentHandler; +import net.minestom.server.item.ItemStack; import net.minestom.server.network.packet.server.play.*; +import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.permission.Permission; import net.minestom.server.permission.PermissionHandler; import net.minestom.server.potion.Potion; @@ -114,6 +118,11 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P private long ticks; private final EntityTickEvent tickEvent = new EntityTickEvent(this); + /** + * Lock used to support #switchEntityType + */ + private final Object entityTypeLock = new Object(); + public Entity(@NotNull EntityType entityType, @NotNull UUID uuid, @NotNull Position spawnPosition) { this.id = generateId(); this.entityType = entityType; @@ -313,18 +322,41 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P } @Override - public boolean addViewer(@NotNull Player player) { - boolean result = this.viewers.add(player); - if (!result) + public final boolean addViewer(@NotNull Player player) { + synchronized (this.entityTypeLock) { + return addViewer0(player); + } + } + + public boolean addViewer0(@NotNull Player player) { + if (!this.viewers.add(player)) { return false; + } player.viewableEntities.add(this); + + PlayerConnection playerConnection = player.getPlayerConnection(); + playerConnection.sendPacket(getEntityType().getSpawnType().getSpawnPacket(this)); + playerConnection.sendPacket(getVelocityPacket()); + playerConnection.sendPacket(getMetadataPacket()); + + if (hasPassenger()) { + playerConnection.sendPacket(getPassengersPacket()); + } + return true; } @Override - public boolean removeViewer(@NotNull Player player) { - if (!viewers.remove(player)) + public final boolean removeViewer(@NotNull Player player) { + synchronized (this.entityTypeLock) { + return removeViewer0(player); + } + } + + public boolean removeViewer0(@NotNull Player player) { + if (!viewers.remove(player)) { return false; + } DestroyEntitiesPacket destroyEntitiesPacket = new DestroyEntitiesPacket(); destroyEntitiesPacket.entityIds = new int[]{getEntityId()}; @@ -339,6 +371,29 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer, P return unmodifiableViewers; } + /** + * Changes the entity type of this entity. + *
+ * Works by changing the internal entity type field and by calling {@link #removeViewer(Player)} + * followed by {@link #addViewer(Player)} to all current viewers. + *
+ * Be aware that this only change the visual of the entity, the {@link net.minestom.server.collision.BoundingBox}
+ * will not be modified.
+ *
+ * @param entityType the new entity type
+ */
+ public final void switchEntityType(@NotNull EntityType entityType) {
+ synchronized (entityTypeLock) {
+ this.entityType = entityType;
+ this.metadata = new Metadata(this);
+ this.entityMeta = entityType.getMetaConstructor().apply(this, this.metadata);
+
+ Set
- * Works by changing the internal entity type field and by calling {@link #removeViewer(Player)}
- * followed by {@link #addViewer(Player)} to all current viewers.
- *
- * Be aware that this only change the visual of the entity, the {@link net.minestom.server.collision.BoundingBox}
- * will not be modified.
- *
- * @param entityType the new entity type
- */
- public void switchEntityType(@NotNull EntityType entityType) {
- synchronized (entityTypeLock) {
- this.entityType = entityType;
-
- Set
@@ -221,78 +142,6 @@ public class EntityCreature extends LivingEntity implements NavigableEntity, Ent
this.target = target;
}
- @NotNull
- @Override
- public ItemStack getItemInMainHand() {
- return mainHandItem;
- }
-
- @Override
- public void setItemInMainHand(@NotNull ItemStack itemStack) {
- this.mainHandItem = itemStack;
- syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
- }
-
- @NotNull
- @Override
- public ItemStack getItemInOffHand() {
- return offHandItem;
- }
-
- @Override
- public void setItemInOffHand(@NotNull ItemStack itemStack) {
- this.offHandItem = itemStack;
- syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
- }
-
- @NotNull
- @Override
- public ItemStack getHelmet() {
- return helmet;
- }
-
- @Override
- public void setHelmet(@NotNull ItemStack itemStack) {
- this.helmet = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
- syncEquipment(EntityEquipmentPacket.Slot.HELMET);
- }
-
- @NotNull
- @Override
- public ItemStack getChestplate() {
- return chestplate;
- }
-
- @Override
- public void setChestplate(@NotNull ItemStack itemStack) {
- this.chestplate = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
- syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
- }
-
- @NotNull
- @Override
- public ItemStack getLeggings() {
- return leggings;
- }
-
- @Override
- public void setLeggings(@NotNull ItemStack itemStack) {
- this.leggings = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
- syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
- }
-
- @NotNull
- @Override
- public ItemStack getBoots() {
- return boots;
- }
-
- @Override
- public void setBoots(@NotNull ItemStack itemStack) {
- this.boots = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
- syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
- }
-
@NotNull
@Override
public Navigator getNavigator() {
@@ -323,9 +172,4 @@ public class EntityCreature extends LivingEntity implements NavigableEntity, Ent
attack(target, false);
}
- private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull ArmorEquipEvent.ArmorSlot armorSlot) {
- ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(this, itemStack, armorSlot);
- callEvent(ArmorEquipEvent.class, armorEquipEvent);
- return armorEquipEvent.getArmorItem();
- }
}
diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java
index f69a2995b..771d32f23 100644
--- a/src/main/java/net/minestom/server/entity/LivingEntity.java
+++ b/src/main/java/net/minestom/server/entity/LivingEntity.java
@@ -9,15 +9,13 @@ import net.minestom.server.entity.metadata.LivingEntityMeta;
import net.minestom.server.event.entity.EntityDamageEvent;
import net.minestom.server.event.entity.EntityDeathEvent;
import net.minestom.server.event.entity.EntityFireEvent;
+import net.minestom.server.event.item.ArmorEquipEvent;
import net.minestom.server.event.item.PickupItemEvent;
import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.block.Block;
import net.minestom.server.inventory.EquipmentHandler;
import net.minestom.server.item.ItemStack;
-import net.minestom.server.network.packet.server.play.CollectItemPacket;
-import net.minestom.server.network.packet.server.play.EntityAnimationPacket;
-import net.minestom.server.network.packet.server.play.EntityPropertiesPacket;
-import net.minestom.server.network.packet.server.play.SoundEffectPacket;
+import net.minestom.server.network.packet.server.play.*;
import net.minestom.server.scoreboard.Team;
import net.minestom.server.sound.Sound;
import net.minestom.server.sound.SoundCategory;
@@ -75,10 +73,20 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
private int arrowCount;
private float health = 1F;
+ // Equipments
+ private ItemStack mainHandItem;
+ private ItemStack offHandItem;
+
+ private ItemStack helmet;
+ private ItemStack chestplate;
+ private ItemStack leggings;
+ private ItemStack boots;
+
public LivingEntity(@NotNull EntityType entityType, @NotNull Position spawnPosition) {
super(entityType, spawnPosition);
setupAttributes();
setGravity(0.02f, 0.08f, 3.92f);
+ initEquipments();
}
/**
@@ -88,6 +96,7 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
super(entityType, uuid, spawnPosition);
setupAttributes();
setGravity(0.02f, 0.08f, 3.92f);
+ initEquipments();
}
public LivingEntity(@NotNull EntityType entityType) {
@@ -101,6 +110,94 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
this(entityType, uuid, new Position());
}
+ private void initEquipments() {
+ this.mainHandItem = ItemStack.getAirItem();
+ this.offHandItem = ItemStack.getAirItem();
+
+ this.helmet = ItemStack.getAirItem();
+ this.chestplate = ItemStack.getAirItem();
+ this.leggings = ItemStack.getAirItem();
+ this.boots = ItemStack.getAirItem();
+ }
+
+ @NotNull
+ @Override
+ public ItemStack getItemInMainHand() {
+ return mainHandItem;
+ }
+
+ @Override
+ public void setItemInMainHand(@NotNull ItemStack itemStack) {
+ this.mainHandItem = itemStack;
+ syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
+ }
+
+ @NotNull
+ @Override
+ public ItemStack getItemInOffHand() {
+ return offHandItem;
+ }
+
+ @Override
+ public void setItemInOffHand(@NotNull ItemStack itemStack) {
+ this.offHandItem = itemStack;
+ syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
+ }
+
+ @NotNull
+ @Override
+ public ItemStack getHelmet() {
+ return helmet;
+ }
+
+ @Override
+ public void setHelmet(@NotNull ItemStack itemStack) {
+ this.helmet = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
+ syncEquipment(EntityEquipmentPacket.Slot.HELMET);
+ }
+
+ @NotNull
+ @Override
+ public ItemStack getChestplate() {
+ return chestplate;
+ }
+
+ @Override
+ public void setChestplate(@NotNull ItemStack itemStack) {
+ this.chestplate = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
+ syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
+ }
+
+ @NotNull
+ @Override
+ public ItemStack getLeggings() {
+ return leggings;
+ }
+
+ @Override
+ public void setLeggings(@NotNull ItemStack itemStack) {
+ this.leggings = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
+ syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
+ }
+
+ @NotNull
+ @Override
+ public ItemStack getBoots() {
+ return boots;
+ }
+
+ @Override
+ public void setBoots(@NotNull ItemStack itemStack) {
+ this.boots = getEquipmentItem(itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
+ syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
+ }
+
+ private ItemStack getEquipmentItem(@NotNull ItemStack itemStack, @NotNull ArmorEquipEvent.ArmorSlot armorSlot) {
+ ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(this, itemStack, armorSlot);
+ callEvent(ArmorEquipEvent.class, armorEquipEvent);
+ return armorEquipEvent.getArmorItem();
+ }
+
@Override
public void update(long time) {
if (isOnFire()) {
@@ -429,6 +526,15 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler {
this.canPickupItem = canPickupItem;
}
+ @Override
+ public boolean addViewer0(@NotNull Player player) {
+ if (!super.addViewer0(player)) {
+ return false;
+ }
+ syncEquipments(player.getPlayerConnection());
+ return true;
+ }
+
@Override
public void setBoundingBox(double x, double y, double z) {
super.setBoundingBox(x, y, z);
diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java
index 780a092ec..e41392295 100644
--- a/src/main/java/net/minestom/server/entity/Player.java
+++ b/src/main/java/net/minestom/server/entity/Player.java
@@ -626,13 +626,10 @@ public class Player extends LivingEntity implements CommandSender {
}
@Override
- public boolean addViewer(@NotNull Player player) {
- if (player == this)
- return false;
-
- final boolean result = super.addViewer(player);
- if (!result)
+ public boolean addViewer0(@NotNull Player player) {
+ if (player == this || !super.addViewer0(player)) {
return false;
+ }
PlayerConnection viewerConnection = player.getPlayerConnection();
showPlayer(viewerConnection);
@@ -640,18 +637,19 @@ public class Player extends LivingEntity implements CommandSender {
}
@Override
- public boolean removeViewer(@NotNull Player player) {
- if (player == this)
+ public boolean removeViewer0(@NotNull Player player) {
+ if (player == this || !super.removeViewer0(player)) {
return false;
+ }
- boolean result = super.removeViewer(player);
PlayerConnection viewerConnection = player.getPlayerConnection();
viewerConnection.sendPacket(getRemovePlayerToList());
// Team
- if (this.getTeam() != null && this.getTeam().getMembers().size() == 1) // If team only contains "this" player
+ if (this.getTeam() != null && this.getTeam().getMembers().size() == 1) {// If team only contains "this" player
viewerConnection.sendPacket(this.getTeam().createTeamDestructionPacket());
- return result;
+ }
+ return true;
}
/**
diff --git a/src/main/java/net/minestom/server/entity/type/decoration/EntityArmorStand.java b/src/main/java/net/minestom/server/entity/type/decoration/EntityArmorStand.java
index e245ccd57..d293f79b8 100644
--- a/src/main/java/net/minestom/server/entity/type/decoration/EntityArmorStand.java
+++ b/src/main/java/net/minestom/server/entity/type/decoration/EntityArmorStand.java
@@ -52,10 +52,12 @@ public class EntityArmorStand extends ObjectEntity implements EquipmentHandler {
}
@Override
- public boolean addViewer(@NotNull Player player) {
- final boolean result = super.addViewer(player);
+ public boolean addViewer0(@NotNull Player player) {
+ if (!super.addViewer0(player)) {
+ return false;
+ }
syncEquipments(player.getPlayerConnection());
- return result;
+ return true;
}
@Override