All LivingEntity can now have equipments + cleanup

This commit is contained in:
Felix Cravic 2020-05-06 22:35:32 +02:00
parent 201df39685
commit 59214b3b96
9 changed files with 292 additions and 81 deletions

View File

@ -1,7 +1,7 @@
package fr.themode.demo;
import fr.themode.demo.blocks.StoneBlock;
import fr.themode.demo.blocks.BurningTorchBlock;
import fr.themode.demo.blocks.StoneBlock;
import fr.themode.demo.blocks.UpdatableBlockDemo;
import fr.themode.demo.commands.GamemodeCommand;
import fr.themode.demo.commands.HealthCommand;
@ -54,7 +54,7 @@ public class Main {
recipeManager.addRecipe(shapelessRecipe);
StorageManager storageManager = MinecraftServer.getStorageManager();
storageManager.defineStorageSystem(FileStorageSystem::new);
storageManager.defineDefaultStorageSystem(FileStorageSystem::new);
MinecraftServer.getBenchmarkManager().enable(new UpdateOption(10 * 1000, TimeUnit.MILLISECOND));

View File

@ -5,18 +5,17 @@ import net.minestom.server.entity.EntityCreature;
import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.Player;
import net.minestom.server.entity.vehicle.PlayerVehicleInformation;
import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material;
import net.minestom.server.utils.Position;
import net.minestom.server.utils.Vector;
public class ChickenCreature extends EntityCreature {
public ChickenCreature(Position defaultPosition) {
super(EntityType.CHICKEN, defaultPosition);
super(EntityType.SKELETON, defaultPosition);
setBoundingBox(0.4f, 0.7f, 0.4f);
}
public ChickenCreature() {
this(new Position());
setHelmet(new ItemStack(Material.DIAMOND_HELMET, (byte) 1));
}
@Override

View File

@ -193,6 +193,7 @@ public abstract class Entity implements Viewable, DataContainer {
PlayerConnection playerConnection = player.getPlayerConnection();
playerConnection.sendPacket(getVelocityPacket());
playerConnection.sendPacket(getPassengersPacket());
playerConnection.sendPacket(getMetadataPacket());
}
@Override

View File

@ -3,6 +3,7 @@ package net.minestom.server.entity;
import net.minestom.server.collision.CollisionUtils;
import net.minestom.server.entity.pathfinding.EntityPathFinder;
import net.minestom.server.entity.property.Attribute;
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.utils.BlockPosition;
@ -19,6 +20,16 @@ public abstract class EntityCreature extends LivingEntity {
private LinkedList<BlockPosition> blockPositions;
private Position targetPosition;
// Equipments
private ItemStack mainHandItem;
private ItemStack offHandItem;
private ItemStack helmet;
private ItemStack chestplate;
private ItemStack leggings;
private ItemStack boots;
public EntityCreature(EntityType entityType, Position spawnPosition) {
super(entityType.getId(), spawnPosition);
}
@ -130,6 +141,72 @@ public abstract class EntityCreature extends LivingEntity {
playerConnection.sendPacket(getMetadataPacket());
}
@Override
public ItemStack getItemInMainHand() {
return mainHandItem;
}
@Override
public void setItemInMainHand(ItemStack itemStack) {
this.mainHandItem = itemStack;
syncEquipment(EntityEquipmentPacket.Slot.MAIN_HAND);
}
@Override
public ItemStack getItemInOffHand() {
return offHandItem;
}
@Override
public void setItemInOffHand(ItemStack itemStack) {
this.offHandItem = itemStack;
syncEquipment(EntityEquipmentPacket.Slot.OFF_HAND);
}
@Override
public ItemStack getHelmet() {
return helmet;
}
@Override
public void setHelmet(ItemStack itemStack) {
this.helmet = itemStack;
syncEquipment(EntityEquipmentPacket.Slot.HELMET);
}
@Override
public ItemStack getChestplate() {
return chestplate;
}
@Override
public void setChestplate(ItemStack itemStack) {
this.chestplate = itemStack;
syncEquipment(EntityEquipmentPacket.Slot.CHESTPLATE);
}
@Override
public ItemStack getLeggings() {
return leggings;
}
@Override
public void setLeggings(ItemStack itemStack) {
this.leggings = itemStack;
syncEquipment(EntityEquipmentPacket.Slot.LEGGINGS);
}
@Override
public ItemStack getBoots() {
return boots;
}
@Override
public void setBoots(ItemStack itemStack) {
this.boots = itemStack;
syncEquipment(EntityEquipmentPacket.Slot.BOOTS);
}
public void jump(float height) {
// FIXME magic value
Vector velocity = new Vector(0, height * 10, 0);

View File

@ -8,12 +8,10 @@ import net.minestom.server.event.EntityDamageEvent;
import net.minestom.server.event.EntityFireEvent;
import net.minestom.server.event.PickupItemEvent;
import net.minestom.server.instance.Chunk;
import net.minestom.server.inventory.EquipmentHandler;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.PacketWriter;
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.sound.Sound;
import net.minestom.server.sound.SoundCategory;
import net.minestom.server.utils.Position;
@ -22,7 +20,7 @@ import net.minestom.server.utils.time.TimeUnit;
import java.util.Set;
import java.util.function.Consumer;
public abstract class LivingEntity extends Entity {
public abstract class LivingEntity extends Entity implements EquipmentHandler {
protected boolean canPickupItem;
protected boolean isDead;
@ -77,7 +75,7 @@ public abstract class LivingEntity extends Entity {
}
// Items picking
if (canPickupItem) {
if (canPickupItem()) {
Chunk chunk = instance.getChunkAt(getPosition()); // TODO check surrounding chunks
Set<Entity> entities = instance.getChunkEntities(chunk);
BoundingBox livingBoundingBox = expandedBoundingBox;
@ -128,6 +126,14 @@ public abstract class LivingEntity extends Entity {
};
}
@Override
public void addViewer(Player player) {
super.addViewer(player);
// Equipments synchronization
syncEquipments();
}
public void kill() {
refreshIsDead(true); // So the entity isn't killed over and over again
triggerStatus((byte) 3); // Start death animation status
@ -238,6 +244,33 @@ public abstract class LivingEntity extends Entity {
return this.attributeValues[attribute.ordinal()];
}
// Equipments
public void syncEquipments() {
for (EntityEquipmentPacket.Slot slot : EntityEquipmentPacket.Slot.values()) {
syncEquipment(slot);
}
}
public void syncEquipment(EntityEquipmentPacket.Slot slot) {
EntityEquipmentPacket entityEquipmentPacket = getEquipmentPacket(slot);
if (entityEquipmentPacket == null)
return;
sendPacketToViewers(entityEquipmentPacket);
}
protected EntityEquipmentPacket getEquipmentPacket(EntityEquipmentPacket.Slot slot) {
ItemStack itemStack = getEquipment(slot);
if (itemStack == null)
return null;
EntityEquipmentPacket equipmentPacket = new EntityEquipmentPacket();
equipmentPacket.entityId = getEntityId();
equipmentPacket.slot = slot;
equipmentPacket.itemStack = itemStack;
return equipmentPacket;
}
public boolean isDead() {
return isDead;
}

View File

@ -313,11 +313,6 @@ public class Player extends LivingEntity {
viewerConnection.sendPacket(pInfoPacket);
viewerConnection.sendPacket(spawnPlayerPacket);
viewerConnection.sendPacket(getMetadataPacket());
for (EntityEquipmentPacket.Slot slot : EntityEquipmentPacket.Slot.values()) {
viewerConnection.sendPacket(getEquipmentPacket(slot));
}
// Team
if (team != null)
@ -414,9 +409,9 @@ public class Player extends LivingEntity {
SoundEffectPacket soundEffectPacket = new SoundEffectPacket();
soundEffectPacket.soundId = sound.getId();
soundEffectPacket.soundCategory = soundCategory;
soundEffectPacket.x = x*8;
soundEffectPacket.y = y*8;
soundEffectPacket.z = z*8;
soundEffectPacket.x = x * 8;
soundEffectPacket.y = y * 8;
soundEffectPacket.z = z * 8;
soundEffectPacket.volume = volume;
soundEffectPacket.pitch = pitch;
playerConnection.sendPacket(soundEffectPacket);
@ -869,24 +864,6 @@ public class Player extends LivingEntity {
this.bossBars.forEach(bossBar -> bossBar.removeViewer(this));
}
public void syncEquipment(EntityEquipmentPacket.Slot slot) {
sendPacketToViewers(getEquipmentPacket(slot));
}
public void syncEquipments() {
for (EntityEquipmentPacket.Slot slot : EntityEquipmentPacket.Slot.values()) {
syncEquipment(slot);
}
}
protected EntityEquipmentPacket getEquipmentPacket(EntityEquipmentPacket.Slot slot) {
EntityEquipmentPacket equipmentPacket = new EntityEquipmentPacket();
equipmentPacket.entityId = getEntityId();
equipmentPacket.slot = slot;
equipmentPacket.itemStack = inventory.getEquipment(slot);
return equipmentPacket;
}
public void updateViewPosition(Chunk chunk) {
UpdateViewPositionPacket updateViewPositionPacket = new UpdateViewPositionPacket(chunk);
playerConnection.sendPacket(updateViewPositionPacket);
@ -1055,6 +1032,66 @@ public class Player extends LivingEntity {
return lastKeepAlive;
}
@Override
public ItemStack getItemInMainHand() {
return inventory.getItemInMainHand();
}
@Override
public void setItemInMainHand(ItemStack itemStack) {
inventory.setItemInMainHand(itemStack);
}
@Override
public ItemStack getItemInOffHand() {
return inventory.getItemInOffHand();
}
@Override
public void setItemInOffHand(ItemStack itemStack) {
inventory.setItemInOffHand(itemStack);
}
@Override
public ItemStack getHelmet() {
return inventory.getHelmet();
}
@Override
public void setHelmet(ItemStack itemStack) {
inventory.setHelmet(itemStack);
}
@Override
public ItemStack getChestplate() {
return inventory.getChestplate();
}
@Override
public void setChestplate(ItemStack itemStack) {
inventory.setChestplate(itemStack);
}
@Override
public ItemStack getLeggings() {
return inventory.getLeggings();
}
@Override
public void setLeggings(ItemStack itemStack) {
inventory.setLeggings(itemStack);
}
@Override
public ItemStack getBoots() {
return inventory.getBoots();
}
@Override
public void setBoots(ItemStack itemStack) {
inventory.setBoots(itemStack);
}
public enum Hand {
MAIN,
OFF

View File

@ -1,6 +1,7 @@
package net.minestom.server.event;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
public class ArmorEquipEvent extends Event {
@ -28,6 +29,21 @@ public class ArmorEquipEvent extends Event {
HELMET,
CHESTPLATE,
LEGGINGS,
BOOTS
BOOTS;
public EntityEquipmentPacket.Slot toEquipmentPacketSlot() {
switch (this) {
case HELMET:
return EntityEquipmentPacket.Slot.HELMET;
case CHESTPLATE:
return EntityEquipmentPacket.Slot.CHESTPLATE;
case LEGGINGS:
return EntityEquipmentPacket.Slot.LEGGINGS;
case BOOTS:
return EntityEquipmentPacket.Slot.BOOTS;
default:
return null;
}
}
}
}

View File

@ -0,0 +1,68 @@
package net.minestom.server.inventory;
import net.minestom.server.event.ArmorEquipEvent;
import net.minestom.server.item.ItemStack;
import net.minestom.server.network.packet.server.play.EntityEquipmentPacket;
import static net.minestom.server.utils.inventory.PlayerInventoryUtils.*;
public interface EquipmentHandler {
ItemStack getItemInMainHand();
void setItemInMainHand(ItemStack itemStack);
ItemStack getItemInOffHand();
void setItemInOffHand(ItemStack itemStack);
ItemStack getHelmet();
void setHelmet(ItemStack itemStack);
ItemStack getChestplate();
void setChestplate(ItemStack itemStack);
ItemStack getLeggings();
void setLeggings(ItemStack itemStack);
ItemStack getBoots();
void setBoots(ItemStack itemStack);
default ItemStack getEquipment(EntityEquipmentPacket.Slot slot) {
switch (slot) {
case MAIN_HAND:
return getItemInMainHand();
case OFF_HAND:
return getItemInOffHand();
case HELMET:
return getHelmet();
case CHESTPLATE:
return getChestplate();
case LEGGINGS:
return getLeggings();
case BOOTS:
return getBoots();
default:
throw new NullPointerException("Equipment slot cannot be null");
}
}
default ArmorEquipEvent getArmorEquipEventPacket(int slot, ItemStack itemStack) {
if (slot == HELMET_SLOT) {
return new ArmorEquipEvent(itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
} else if (slot == CHESTPLATE_SLOT) {
return new ArmorEquipEvent(itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
} else if (slot == LEGGINGS_SLOT) {
return new ArmorEquipEvent(itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
} else if (slot == BOOTS_SLOT) {
return new ArmorEquipEvent(itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
}
return null;
}
}

View File

@ -20,7 +20,7 @@ import java.util.concurrent.CopyOnWriteArrayList;
import static net.minestom.server.utils.inventory.PlayerInventoryUtils.*;
public class PlayerInventory implements InventoryModifier, InventoryClickHandler {
public class PlayerInventory implements InventoryModifier, InventoryClickHandler, EquipmentHandler {
public static final int INVENTORY_SIZE = 46;
@ -101,50 +101,62 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
return false;
}
@Override
public ItemStack getItemInMainHand() {
return getItemStack(player.getHeldSlot());
}
@Override
public void setItemInMainHand(ItemStack itemStack) {
safeItemInsert(player.getHeldSlot(), itemStack);
}
@Override
public ItemStack getItemInOffHand() {
return getItemStack(OFFHAND_SLOT);
}
@Override
public void setItemInOffHand(ItemStack itemStack) {
safeItemInsert(OFFHAND_SLOT, itemStack);
}
@Override
public ItemStack getHelmet() {
return getItemStack(HELMET_SLOT);
}
@Override
public void setHelmet(ItemStack itemStack) {
safeItemInsert(HELMET_SLOT, itemStack);
}
@Override
public ItemStack getChestplate() {
return getItemStack(CHESTPLATE_SLOT);
}
@Override
public void setChestplate(ItemStack itemStack) {
safeItemInsert(CHESTPLATE_SLOT, itemStack);
}
@Override
public ItemStack getLeggings() {
return getItemStack(LEGGINGS_SLOT);
}
@Override
public void setLeggings(ItemStack itemStack) {
safeItemInsert(LEGGINGS_SLOT, itemStack);
}
@Override
public ItemStack getBoots() {
return getItemStack(BOOTS_SLOT);
}
@Override
public void setBoots(ItemStack itemStack) {
safeItemInsert(BOOTS_SLOT, itemStack);
}
@ -166,25 +178,6 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
this.cursorItem = cursorItem;
}
public ItemStack getEquipment(EntityEquipmentPacket.Slot slot) {
switch (slot) {
case MAIN_HAND:
return getItemInMainHand();
case OFF_HAND:
return getItemInOffHand();
case HELMET:
return getHelmet();
case CHESTPLATE:
return getChestplate();
case LEGGINGS:
return getLeggings();
case BOOTS:
return getBoots();
default:
throw new NullPointerException("Equipment slot cannot be null");
}
}
private void safeItemInsert(int slot, ItemStack itemStack) {
synchronized (this) {
itemStack = itemStack == null ? ItemStack.getAirItem() : itemStack;
@ -195,28 +188,15 @@ public class PlayerInventory implements InventoryModifier, InventoryClickHandler
equipmentSlot = EntityEquipmentPacket.Slot.MAIN_HAND;
} else if (slot == OFFHAND_SLOT) {
equipmentSlot = EntityEquipmentPacket.Slot.OFF_HAND;
} else if (slot == HELMET_SLOT) {
equipmentSlot = EntityEquipmentPacket.Slot.HELMET;
ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(itemStack, ArmorEquipEvent.ArmorSlot.HELMET);
player.callEvent(ArmorEquipEvent.class, armorEquipEvent);
itemStack = armorEquipEvent.getArmorItem();
} else if (slot == CHESTPLATE_SLOT) {
equipmentSlot = EntityEquipmentPacket.Slot.CHESTPLATE;
ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(itemStack, ArmorEquipEvent.ArmorSlot.CHESTPLATE);
player.callEvent(ArmorEquipEvent.class, armorEquipEvent);
itemStack = armorEquipEvent.getArmorItem();
} else if (slot == LEGGINGS_SLOT) {
equipmentSlot = EntityEquipmentPacket.Slot.LEGGINGS;
ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(itemStack, ArmorEquipEvent.ArmorSlot.LEGGINGS);
player.callEvent(ArmorEquipEvent.class, armorEquipEvent);
itemStack = armorEquipEvent.getArmorItem();
} else if (slot == BOOTS_SLOT) {
equipmentSlot = EntityEquipmentPacket.Slot.BOOTS;
ArmorEquipEvent armorEquipEvent = new ArmorEquipEvent(itemStack, ArmorEquipEvent.ArmorSlot.BOOTS);
player.callEvent(ArmorEquipEvent.class, armorEquipEvent);
itemStack = armorEquipEvent.getArmorItem();
} else {
equipmentSlot = null;
ArmorEquipEvent armorEquipEvent = getArmorEquipEventPacket(slot, itemStack);
if (armorEquipEvent != null) {
equipmentSlot = armorEquipEvent.getArmorSlot().toEquipmentPacketSlot();
player.callEvent(ArmorEquipEvent.class, armorEquipEvent);
itemStack = armorEquipEvent.getArmorItem();
} else {
equipmentSlot = null;
}
}
if (itemStack != null) {