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 viewers = new HashSet<>(getViewers()); + getViewers().forEach(this::removeViewer); + viewers.forEach(this::addViewer); + } + } + @Override public Data getData() { return data; diff --git a/src/main/java/net/minestom/server/entity/EntityCreature.java b/src/main/java/net/minestom/server/entity/EntityCreature.java index 23a3277b7..090dd0f0a 100644 --- a/src/main/java/net/minestom/server/entity/EntityCreature.java +++ b/src/main/java/net/minestom/server/entity/EntityCreature.java @@ -8,20 +8,14 @@ import net.minestom.server.entity.ai.TargetSelector; import net.minestom.server.entity.pathfinding.NavigableEntity; import net.minestom.server.entity.pathfinding.Navigator; import net.minestom.server.event.entity.EntityAttackEvent; -import net.minestom.server.event.item.ArmorEquipEvent; import net.minestom.server.instance.Instance; -import net.minestom.server.item.ItemStack; -import net.minestom.server.network.packet.server.play.EntityEquipmentPacket; -import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.utils.Position; import net.minestom.server.utils.time.TimeUnit; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; -import java.util.Set; public class EntityCreature extends LivingEntity implements NavigableEntity, EntityAI { @@ -35,31 +29,9 @@ public class EntityCreature extends LivingEntity implements NavigableEntity, Ent private Entity target; - /** - * Lock used to support #switchEntityType - */ - private final Object entityTypeLock = new Object(); - - // Equipments - private ItemStack mainHandItem; - private ItemStack offHandItem; - - private ItemStack helmet; - private ItemStack chestplate; - private ItemStack leggings; - private ItemStack boots; - public EntityCreature(@NotNull EntityType entityType, @NotNull Position spawnPosition) { super(entityType, spawnPosition); - this.mainHandItem = ItemStack.getAirItem(); - this.offHandItem = ItemStack.getAirItem(); - - this.helmet = ItemStack.getAirItem(); - this.chestplate = ItemStack.getAirItem(); - this.leggings = ItemStack.getAirItem(); - this.boots = ItemStack.getAirItem(); - heal(); } @@ -108,56 +80,6 @@ public class EntityCreature extends LivingEntity implements NavigableEntity, Ent } } - @Override - public boolean addViewer(@NotNull Player player) { - synchronized (entityTypeLock) { - final boolean result = super.addViewer(player); - - final PlayerConnection playerConnection = player.getPlayerConnection(); - - playerConnection.sendPacket(getEntityType().getSpawnType().getSpawnPacket(this)); - playerConnection.sendPacket(getVelocityPacket()); - playerConnection.sendPacket(getMetadataPacket()); - - // Equipments synchronization - syncEquipments(playerConnection); - - if (hasPassenger()) { - playerConnection.sendPacket(getPassengersPacket()); - } - - return result; - } - } - - @Override - public boolean removeViewer(@NotNull Player player) { - synchronized (entityTypeLock) { - return super.removeViewer(player); - } - } - - /** - * 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 void switchEntityType(@NotNull EntityType entityType) { - synchronized (entityTypeLock) { - this.entityType = entityType; - - Set viewers = new HashSet<>(getViewers()); - getViewers().forEach(this::removeViewer); - viewers.forEach(this::addViewer); - } - } - /** * Gets the kill animation delay before vanishing the entity. * @@ -167,7 +89,6 @@ public class EntityCreature extends LivingEntity implements NavigableEntity, Ent return removalAnimationDelay; } - /** * Changes the removal animation delay of the entity. *

@@ -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