diff --git a/demo/src/main/java/net/minestom/demo/PlayerInit.java b/demo/src/main/java/net/minestom/demo/PlayerInit.java index 0af45fc7d..458e9b72e 100644 --- a/demo/src/main/java/net/minestom/demo/PlayerInit.java +++ b/demo/src/main/java/net/minestom/demo/PlayerInit.java @@ -36,12 +36,15 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; import net.minestom.server.item.component.BlockPredicates; import net.minestom.server.item.component.ItemBlockState; +import net.minestom.server.item.component.PotionContents; import net.minestom.server.monitoring.BenchmarkManager; import net.minestom.server.monitoring.TickMonitor; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.packet.server.play.ExplosionPacket; import net.minestom.server.particle.Particle; import net.minestom.server.particle.data.BlockParticleData; +import net.minestom.server.potion.CustomPotionEffect; +import net.minestom.server.potion.PotionEffect; import net.minestom.server.sound.SoundEvent; import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.NamespaceID; @@ -135,6 +138,13 @@ public class PlayerInit { player.getInventory().addItemStack(ItemStack.builder(Material.BLACK_BANNER) .build()); + player.getInventory().addItemStack(ItemStack.builder(Material.POTION) + .set(ItemComponent.POTION_CONTENTS, new PotionContents(null, null, List.of( + new CustomPotionEffect(PotionEffect.JUMP_BOOST, new CustomPotionEffect.Settings((byte) 4, + 45 * 20, false, true, true, null)) + ))) + .build()); + if (event.isFirstSpawn()) { Notification notification = new Notification( Component.text("Welcome!"), @@ -240,7 +250,8 @@ public class PlayerInit { BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager(); MinecraftServer.getSchedulerManager().buildTask(() -> { - if (MinecraftServer.getConnectionManager().getOnlinePlayerCount() != 0) + if (LAST_TICK.get() == null) return; + if (MinecraftServer.getConnectionManager().getOnlinePlayerCount() == 0) return; long ramUsage = benchmarkManager.getUsedMemory(); @@ -254,6 +265,6 @@ public class PlayerInit { .append(Component.text("ACQ TIME: " + MathUtils.round(tickMonitor.getAcquisitionTime(), 2) + "ms")); final Component footer = benchmarkManager.getCpuMonitoringMessage(); Audiences.players().sendPlayerListHeaderAndFooter(header, footer); - }).repeat(10, TimeUnit.SERVER_TICK); //.schedule(); + }).repeat(10, TimeUnit.SERVER_TICK).schedule(); } } diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index fe1622b49..3484e55bb 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -20,10 +20,7 @@ import net.minestom.server.inventory.EquipmentHandler; import net.minestom.server.item.ItemStack; import net.minestom.server.network.ConnectionState; import net.minestom.server.network.packet.server.LazyPacket; -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.EntityAttributesPacket; -import net.minestom.server.network.packet.server.play.SoundEffectPacket; +import net.minestom.server.network.packet.server.play.*; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.scoreboard.Team; import net.minestom.server.sound.SoundEvent; @@ -339,6 +336,8 @@ public class LivingEntity extends Entity implements EquipmentHandler { sendPacketToViewersAndSelf(new EntityAnimationPacket(getEntityId(), EntityAnimationPacket.Animation.TAKE_DAMAGE)); } + sendPacketToViewersAndSelf(new DamageEventPacket(getEntityId(), damage.getType().id(), 0, 0, null)); + // Additional hearts support if (this instanceof Player player) { final float additionalHearts = player.getAdditionalHearts(); diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index ddb96b51b..9830617ec 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -194,7 +194,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable, private int food; private float foodSaturation; private long startEatingTime; - private long defaultEatingTime = 1000L; private long eatingTime; private Hand eatingHand; @@ -436,7 +435,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, // Eating animation if (isEating()) { - if (time - startEatingTime >= eatingTime) { + if (instance.getWorldAge() - startEatingTime >= eatingTime) { triggerStatus((byte) 9); // Mark item use as finished ItemUpdateStateEvent itemUpdateStateEvent = callItemUpdateStateEvent(eatingHand); @@ -448,8 +447,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable, final ItemStack foodItem = itemUpdateStateEvent.getItemStack(); final boolean isFood = foodItem.has(ItemComponent.FOOD); - - if (isFood) { + if (isFood || foodItem.material() == Material.POTION) { PlayerEatEvent playerEatEvent = new PlayerEatEvent(this, foodItem, eatingHand); EventDispatcher.call(playerEatEvent); } @@ -1128,24 +1126,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable, return eatingHand; } - /** - * Gets the player default eating time. - * - * @return the player default eating time - */ - public long getDefaultEatingTime() { - return defaultEatingTime; - } - - /** - * Used to change the default eating time animation. - * - * @param defaultEatingTime the default eating time in milliseconds - */ - public void setDefaultEatingTime(long defaultEatingTime) { - this.defaultEatingTime = defaultEatingTime; - } - @Override public double getEyeHeight() { return switch (getPose()) { @@ -2172,18 +2152,18 @@ public class Player extends LivingEntity implements CommandSender, Localizable, refreshEating(null); } - public void refreshEating(@Nullable Hand eatingHand, long eatingTime) { + public void refreshEating(@Nullable Hand eatingHand, long eatingTimeTicks) { this.eatingHand = eatingHand; if (eatingHand != null) { - this.startEatingTime = System.currentTimeMillis(); - this.eatingTime = eatingTime; + this.startEatingTime = instance.getWorldAge(); + this.eatingTime = eatingTimeTicks; } else { this.startEatingTime = 0; } } public void refreshEating(@Nullable Hand eatingHand) { - refreshEating(eatingHand, defaultEatingTime); + refreshEating(eatingHand, 0); } /** diff --git a/src/main/java/net/minestom/server/entity/metadata/display/TextDisplayMeta.java b/src/main/java/net/minestom/server/entity/metadata/display/TextDisplayMeta.java index 10e09204c..b0d22c366 100644 --- a/src/main/java/net/minestom/server/entity/metadata/display/TextDisplayMeta.java +++ b/src/main/java/net/minestom/server/entity/metadata/display/TextDisplayMeta.java @@ -43,8 +43,8 @@ public class TextDisplayMeta extends AbstractDisplayMeta { super.metadata.setIndex(OFFSET + 2, Metadata.VarInt(value)); } - public int getTextOpacity() { - return super.metadata.getIndex(OFFSET + 3, -1); + public byte getTextOpacity() { + return super.metadata.getIndex(OFFSET + 3, (byte) -1); } public void setTextOpacity(byte value) { diff --git a/src/main/java/net/minestom/server/event/player/PlayerPreEatEvent.java b/src/main/java/net/minestom/server/event/player/PlayerPreEatEvent.java index 27de8eee6..abf6b6336 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerPreEatEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerPreEatEvent.java @@ -44,9 +44,7 @@ public class PlayerPreEatEvent implements ItemEvent, PlayerInstanceEvent, Cancel } /** - * Gets the food eating time. - *

- * This is by default {@link Player#getDefaultEatingTime()}. + * Gets the food eating time in ticks. * * @return the eating time */ @@ -57,7 +55,7 @@ public class PlayerPreEatEvent implements ItemEvent, PlayerInstanceEvent, Cancel /** * Changes the food eating time. * - * @param eatingTime the new eating time + * @param eatingTime the new eating time in ticks */ public void setEatingTime(long eatingTime) { this.eatingTime = eatingTime; diff --git a/src/main/java/net/minestom/server/item/component/PotionContents.java b/src/main/java/net/minestom/server/item/component/PotionContents.java index 36e04df96..4d8435b79 100644 --- a/src/main/java/net/minestom/server/item/component/PotionContents.java +++ b/src/main/java/net/minestom/server/item/component/PotionContents.java @@ -4,7 +4,7 @@ import net.kyori.adventure.nbt.*; import net.minestom.server.color.Color; import net.minestom.server.network.NetworkBuffer; import net.minestom.server.potion.CustomPotionEffect; -import net.minestom.server.potion.PotionEffect; +import net.minestom.server.potion.PotionType; import net.minestom.server.utils.nbt.BinaryTagSerializer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -13,24 +13,27 @@ import java.util.ArrayList; import java.util.List; public record PotionContents( - @Nullable PotionEffect potion, + @Nullable PotionType potion, @Nullable Color customColor, @NotNull List customEffects ) { + public static final int POTION_DRINK_TIME = 32; // 32 ticks, in ms public static final PotionContents EMPTY = new PotionContents(null, null, List.of()); public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { @Override public void write(@NotNull NetworkBuffer buffer, PotionContents value) { - buffer.writeOptional(PotionEffect.NETWORK_TYPE, value.potion); + Integer typeId = value.potion == null ? null : value.potion.id(); + buffer.writeOptional(NetworkBuffer.VAR_INT, typeId); buffer.writeOptional(NetworkBuffer.COLOR, value.customColor); buffer.writeCollection(CustomPotionEffect.NETWORK_TYPE, value.customEffects); } @Override public PotionContents read(@NotNull NetworkBuffer buffer) { + Integer typeId = buffer.readOptional(NetworkBuffer.VAR_INT); return new PotionContents( - buffer.readOptional(PotionEffect.NETWORK_TYPE), + typeId == null ? null : PotionType.fromId(typeId), buffer.readOptional(NetworkBuffer.COLOR), buffer.readCollection(CustomPotionEffect.NETWORK_TYPE, Short.MAX_VALUE) ); @@ -40,14 +43,14 @@ public record PotionContents( public static final BinaryTagSerializer NBT_TYPE = new BinaryTagSerializer<>() { @Override public @NotNull BinaryTag write(@NotNull PotionContents value) { - return null; + throw new UnsupportedOperationException(); } @Override public @NotNull PotionContents read(@NotNull BinaryTag tag) { // Can be a string with just a potion effect id if (tag instanceof StringBinaryTag string) { - return new PotionContents(PotionEffect.fromNamespaceId(string.value()), null, List.of()); + return new PotionContents(PotionType.fromNamespaceId(string.value()), null, List.of()); } // Otherwise must be a compound @@ -55,9 +58,9 @@ public record PotionContents( return EMPTY; } - PotionEffect potion = null; + PotionType potion = null; if (compound.get("potion") instanceof StringBinaryTag potionTag) - potion = PotionEffect.fromNamespaceId(potionTag.value()); + potion = PotionType.fromNamespaceId(potionTag.value()); Color customColor = null; if (compound.get("custom_color") instanceof IntBinaryTag colorTag) { diff --git a/src/main/java/net/minestom/server/listener/UseItemListener.java b/src/main/java/net/minestom/server/listener/UseItemListener.java index c35efed6f..51ee43f60 100644 --- a/src/main/java/net/minestom/server/listener/UseItemListener.java +++ b/src/main/java/net/minestom/server/listener/UseItemListener.java @@ -10,6 +10,8 @@ import net.minestom.server.inventory.PlayerInventory; import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemStack; import net.minestom.server.item.Material; +import net.minestom.server.item.component.Food; +import net.minestom.server.item.component.PotionContents; import net.minestom.server.network.packet.client.play.ClientUseItemPacket; public class UseItemListener { @@ -52,11 +54,13 @@ public class UseItemListener { itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.SHIELD; } else if (material == Material.TRIDENT) { itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.TRIDENT; - } else if (itemStack.has(ItemComponent.FOOD)) { + } else if (itemStack.has(ItemComponent.FOOD) || itemStack.material() == Material.POTION) { itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.EAT; // Eating code, contains the eating time customisation - PlayerPreEatEvent playerPreEatEvent = new PlayerPreEatEvent(player, itemStack, hand, player.getDefaultEatingTime()); + final Food food = itemStack.get(ItemComponent.FOOD); + int defaultEatingTime = food != null ? food.eatDurationTicks() : PotionContents.POTION_DRINK_TIME; + PlayerPreEatEvent playerPreEatEvent = new PlayerPreEatEvent(player, itemStack, hand, defaultEatingTime); EventDispatcher.callCancellable(playerPreEatEvent, () -> player.refreshEating(hand, playerPreEatEvent.getEatingTime())); if (playerPreEatEvent.isCancelled()) { diff --git a/src/main/java/net/minestom/server/potion/CustomPotionEffect.java b/src/main/java/net/minestom/server/potion/CustomPotionEffect.java index 82a15f851..2ac3495cf 100644 --- a/src/main/java/net/minestom/server/potion/CustomPotionEffect.java +++ b/src/main/java/net/minestom/server/potion/CustomPotionEffect.java @@ -14,17 +14,17 @@ public record CustomPotionEffect(@NotNull PotionEffect id, @NotNull Settings set public static final NetworkBuffer.Type NETWORK_TYPE = new NetworkBuffer.Type<>() { @Override public void write(@NotNull NetworkBuffer buffer, CustomPotionEffect value) { - buffer.write(PotionEffect.NETWORK_TYPE, value.id); + buffer.write(NetworkBuffer.VAR_INT, value.id.id()); buffer.write(Settings.NETWORK_TYPE, value.settings); } @Override public CustomPotionEffect read(@NotNull NetworkBuffer buffer) { - return new CustomPotionEffect(buffer.read(PotionEffect.NETWORK_TYPE), buffer.read(Settings.NETWORK_TYPE)); + return new CustomPotionEffect(PotionEffect.fromId(buffer.read(NetworkBuffer.VAR_INT)), buffer.read(Settings.NETWORK_TYPE)); } }; - public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.COMPOUND.map( + public static final BinaryTagSerializer NBT_TYPE = BinaryTagSerializer.lazy(() -> BinaryTagSerializer.COMPOUND.map( tag -> new CustomPotionEffect( PotionEffect.fromNamespaceId(tag.getString("id")), Settings.NBT_TYPE.read(tag)), @@ -32,7 +32,7 @@ public record CustomPotionEffect(@NotNull PotionEffect id, @NotNull Settings set .putString("id", value.id.name()) .put((CompoundBinaryTag) Settings.NBT_TYPE.write(value.settings)) .build() - ); + )); public CustomPotionEffect(@NotNull PotionEffect id, byte amplifier, int duration, boolean isAmbient, boolean showParticles, boolean showIcon) { this(id, new Settings(amplifier, duration, isAmbient, showParticles, showIcon, null));