Moved switchEntityType from EntityCreature to Entity

This commit is contained in:
Konstantin Shandurenko 2021-02-25 10:37:02 +03:00
parent ca6546eb7d
commit 18da3e0168
5 changed files with 184 additions and 179 deletions

View File

@ -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.
* <p>
* Works by changing the internal entity type field and by calling {@link #removeViewer(Player)}
* followed by {@link #addViewer(Player)} to all current viewers.
* <p>
* 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<Player> viewers = new HashSet<>(getViewers());
getViewers().forEach(this::removeViewer);
viewers.forEach(this::addViewer);
}
}
@Override
public Data getData() {
return data;

View File

@ -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.
* <p>
* Works by changing the internal entity type field and by calling {@link #removeViewer(Player)}
* followed by {@link #addViewer(Player)} to all current viewers.
* <p>
* 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<Player> 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.
* <p>
@ -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();
}
}

View File

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

View File

@ -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;
}
/**

View File

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