fix: correctly handle eating potions, time eating using ticks, default to correct drink/eat time from component, send damage event on living entity damage

This commit is contained in:
mworzala 2024-04-27 19:11:44 -04:00
parent 95c5f6675f
commit d18e06188f
No known key found for this signature in database
GPG Key ID: B148F922E64797C7
8 changed files with 47 additions and 52 deletions

View File

@ -36,12 +36,15 @@ import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material; import net.minestom.server.item.Material;
import net.minestom.server.item.component.BlockPredicates; import net.minestom.server.item.component.BlockPredicates;
import net.minestom.server.item.component.ItemBlockState; 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.BenchmarkManager;
import net.minestom.server.monitoring.TickMonitor; import net.minestom.server.monitoring.TickMonitor;
import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.network.packet.server.play.ExplosionPacket; import net.minestom.server.network.packet.server.play.ExplosionPacket;
import net.minestom.server.particle.Particle; import net.minestom.server.particle.Particle;
import net.minestom.server.particle.data.BlockParticleData; 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.sound.SoundEvent;
import net.minestom.server.utils.MathUtils; import net.minestom.server.utils.MathUtils;
import net.minestom.server.utils.NamespaceID; import net.minestom.server.utils.NamespaceID;
@ -135,6 +138,13 @@ public class PlayerInit {
player.getInventory().addItemStack(ItemStack.builder(Material.BLACK_BANNER) player.getInventory().addItemStack(ItemStack.builder(Material.BLACK_BANNER)
.build()); .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()) { if (event.isFirstSpawn()) {
Notification notification = new Notification( Notification notification = new Notification(
Component.text("Welcome!"), Component.text("Welcome!"),
@ -240,7 +250,8 @@ public class PlayerInit {
BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager(); BenchmarkManager benchmarkManager = MinecraftServer.getBenchmarkManager();
MinecraftServer.getSchedulerManager().buildTask(() -> { MinecraftServer.getSchedulerManager().buildTask(() -> {
if (MinecraftServer.getConnectionManager().getOnlinePlayerCount() != 0) if (LAST_TICK.get() == null) return;
if (MinecraftServer.getConnectionManager().getOnlinePlayerCount() == 0)
return; return;
long ramUsage = benchmarkManager.getUsedMemory(); long ramUsage = benchmarkManager.getUsedMemory();
@ -254,6 +265,6 @@ public class PlayerInit {
.append(Component.text("ACQ TIME: " + MathUtils.round(tickMonitor.getAcquisitionTime(), 2) + "ms")); .append(Component.text("ACQ TIME: " + MathUtils.round(tickMonitor.getAcquisitionTime(), 2) + "ms"));
final Component footer = benchmarkManager.getCpuMonitoringMessage(); final Component footer = benchmarkManager.getCpuMonitoringMessage();
Audiences.players().sendPlayerListHeaderAndFooter(header, footer); Audiences.players().sendPlayerListHeaderAndFooter(header, footer);
}).repeat(10, TimeUnit.SERVER_TICK); //.schedule(); }).repeat(10, TimeUnit.SERVER_TICK).schedule();
} }
} }

View File

@ -20,10 +20,7 @@ import net.minestom.server.inventory.EquipmentHandler;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
import net.minestom.server.network.ConnectionState; import net.minestom.server.network.ConnectionState;
import net.minestom.server.network.packet.server.LazyPacket; 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.*;
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.player.PlayerConnection; import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.scoreboard.Team; import net.minestom.server.scoreboard.Team;
import net.minestom.server.sound.SoundEvent; 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 EntityAnimationPacket(getEntityId(), EntityAnimationPacket.Animation.TAKE_DAMAGE));
} }
sendPacketToViewersAndSelf(new DamageEventPacket(getEntityId(), damage.getType().id(), 0, 0, null));
// Additional hearts support // Additional hearts support
if (this instanceof Player player) { if (this instanceof Player player) {
final float additionalHearts = player.getAdditionalHearts(); final float additionalHearts = player.getAdditionalHearts();

View File

@ -194,7 +194,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
private int food; private int food;
private float foodSaturation; private float foodSaturation;
private long startEatingTime; private long startEatingTime;
private long defaultEatingTime = 1000L;
private long eatingTime; private long eatingTime;
private Hand eatingHand; private Hand eatingHand;
@ -436,7 +435,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
// Eating animation // Eating animation
if (isEating()) { if (isEating()) {
if (time - startEatingTime >= eatingTime) { if (instance.getWorldAge() - startEatingTime >= eatingTime) {
triggerStatus((byte) 9); // Mark item use as finished triggerStatus((byte) 9); // Mark item use as finished
ItemUpdateStateEvent itemUpdateStateEvent = callItemUpdateStateEvent(eatingHand); ItemUpdateStateEvent itemUpdateStateEvent = callItemUpdateStateEvent(eatingHand);
@ -448,8 +447,7 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
final ItemStack foodItem = itemUpdateStateEvent.getItemStack(); final ItemStack foodItem = itemUpdateStateEvent.getItemStack();
final boolean isFood = foodItem.has(ItemComponent.FOOD); final boolean isFood = foodItem.has(ItemComponent.FOOD);
if (isFood || foodItem.material() == Material.POTION) {
if (isFood) {
PlayerEatEvent playerEatEvent = new PlayerEatEvent(this, foodItem, eatingHand); PlayerEatEvent playerEatEvent = new PlayerEatEvent(this, foodItem, eatingHand);
EventDispatcher.call(playerEatEvent); EventDispatcher.call(playerEatEvent);
} }
@ -1128,24 +1126,6 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
return eatingHand; 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 @Override
public double getEyeHeight() { public double getEyeHeight() {
return switch (getPose()) { return switch (getPose()) {
@ -2172,18 +2152,18 @@ public class Player extends LivingEntity implements CommandSender, Localizable,
refreshEating(null); refreshEating(null);
} }
public void refreshEating(@Nullable Hand eatingHand, long eatingTime) { public void refreshEating(@Nullable Hand eatingHand, long eatingTimeTicks) {
this.eatingHand = eatingHand; this.eatingHand = eatingHand;
if (eatingHand != null) { if (eatingHand != null) {
this.startEatingTime = System.currentTimeMillis(); this.startEatingTime = instance.getWorldAge();
this.eatingTime = eatingTime; this.eatingTime = eatingTimeTicks;
} else { } else {
this.startEatingTime = 0; this.startEatingTime = 0;
} }
} }
public void refreshEating(@Nullable Hand eatingHand) { public void refreshEating(@Nullable Hand eatingHand) {
refreshEating(eatingHand, defaultEatingTime); refreshEating(eatingHand, 0);
} }
/** /**

View File

@ -43,8 +43,8 @@ public class TextDisplayMeta extends AbstractDisplayMeta {
super.metadata.setIndex(OFFSET + 2, Metadata.VarInt(value)); super.metadata.setIndex(OFFSET + 2, Metadata.VarInt(value));
} }
public int getTextOpacity() { public byte getTextOpacity() {
return super.metadata.getIndex(OFFSET + 3, -1); return super.metadata.getIndex(OFFSET + 3, (byte) -1);
} }
public void setTextOpacity(byte value) { public void setTextOpacity(byte value) {

View File

@ -44,9 +44,7 @@ public class PlayerPreEatEvent implements ItemEvent, PlayerInstanceEvent, Cancel
} }
/** /**
* Gets the food eating time. * Gets the food eating time in ticks.
* <p>
* This is by default {@link Player#getDefaultEatingTime()}.
* *
* @return the eating time * @return the eating time
*/ */
@ -57,7 +55,7 @@ public class PlayerPreEatEvent implements ItemEvent, PlayerInstanceEvent, Cancel
/** /**
* Changes the food eating time. * Changes the food eating time.
* *
* @param eatingTime the new eating time * @param eatingTime the new eating time in ticks
*/ */
public void setEatingTime(long eatingTime) { public void setEatingTime(long eatingTime) {
this.eatingTime = eatingTime; this.eatingTime = eatingTime;

View File

@ -4,7 +4,7 @@ import net.kyori.adventure.nbt.*;
import net.minestom.server.color.Color; import net.minestom.server.color.Color;
import net.minestom.server.network.NetworkBuffer; import net.minestom.server.network.NetworkBuffer;
import net.minestom.server.potion.CustomPotionEffect; 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 net.minestom.server.utils.nbt.BinaryTagSerializer;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@ -13,24 +13,27 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
public record PotionContents( public record PotionContents(
@Nullable PotionEffect potion, @Nullable PotionType potion,
@Nullable Color customColor, @Nullable Color customColor,
@NotNull List<CustomPotionEffect> customEffects @NotNull List<CustomPotionEffect> 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 PotionContents EMPTY = new PotionContents(null, null, List.of());
public static final NetworkBuffer.Type<PotionContents> NETWORK_TYPE = new NetworkBuffer.Type<>() { public static final NetworkBuffer.Type<PotionContents> NETWORK_TYPE = new NetworkBuffer.Type<>() {
@Override @Override
public void write(@NotNull NetworkBuffer buffer, PotionContents value) { 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.writeOptional(NetworkBuffer.COLOR, value.customColor);
buffer.writeCollection(CustomPotionEffect.NETWORK_TYPE, value.customEffects); buffer.writeCollection(CustomPotionEffect.NETWORK_TYPE, value.customEffects);
} }
@Override @Override
public PotionContents read(@NotNull NetworkBuffer buffer) { public PotionContents read(@NotNull NetworkBuffer buffer) {
Integer typeId = buffer.readOptional(NetworkBuffer.VAR_INT);
return new PotionContents( return new PotionContents(
buffer.readOptional(PotionEffect.NETWORK_TYPE), typeId == null ? null : PotionType.fromId(typeId),
buffer.readOptional(NetworkBuffer.COLOR), buffer.readOptional(NetworkBuffer.COLOR),
buffer.readCollection(CustomPotionEffect.NETWORK_TYPE, Short.MAX_VALUE) buffer.readCollection(CustomPotionEffect.NETWORK_TYPE, Short.MAX_VALUE)
); );
@ -40,14 +43,14 @@ public record PotionContents(
public static final BinaryTagSerializer<PotionContents> NBT_TYPE = new BinaryTagSerializer<>() { public static final BinaryTagSerializer<PotionContents> NBT_TYPE = new BinaryTagSerializer<>() {
@Override @Override
public @NotNull BinaryTag write(@NotNull PotionContents value) { public @NotNull BinaryTag write(@NotNull PotionContents value) {
return null; throw new UnsupportedOperationException();
} }
@Override @Override
public @NotNull PotionContents read(@NotNull BinaryTag tag) { public @NotNull PotionContents read(@NotNull BinaryTag tag) {
// Can be a string with just a potion effect id // Can be a string with just a potion effect id
if (tag instanceof StringBinaryTag string) { 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 // Otherwise must be a compound
@ -55,9 +58,9 @@ public record PotionContents(
return EMPTY; return EMPTY;
} }
PotionEffect potion = null; PotionType potion = null;
if (compound.get("potion") instanceof StringBinaryTag potionTag) if (compound.get("potion") instanceof StringBinaryTag potionTag)
potion = PotionEffect.fromNamespaceId(potionTag.value()); potion = PotionType.fromNamespaceId(potionTag.value());
Color customColor = null; Color customColor = null;
if (compound.get("custom_color") instanceof IntBinaryTag colorTag) { if (compound.get("custom_color") instanceof IntBinaryTag colorTag) {

View File

@ -10,6 +10,8 @@ import net.minestom.server.inventory.PlayerInventory;
import net.minestom.server.item.ItemComponent; import net.minestom.server.item.ItemComponent;
import net.minestom.server.item.ItemStack; import net.minestom.server.item.ItemStack;
import net.minestom.server.item.Material; 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; import net.minestom.server.network.packet.client.play.ClientUseItemPacket;
public class UseItemListener { public class UseItemListener {
@ -52,11 +54,13 @@ public class UseItemListener {
itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.SHIELD; itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.SHIELD;
} else if (material == Material.TRIDENT) { } else if (material == Material.TRIDENT) {
itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.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; itemAnimationType = PlayerItemAnimationEvent.ItemAnimationType.EAT;
// Eating code, contains the eating time customisation // 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())); EventDispatcher.callCancellable(playerPreEatEvent, () -> player.refreshEating(hand, playerPreEatEvent.getEatingTime()));
if (playerPreEatEvent.isCancelled()) { if (playerPreEatEvent.isCancelled()) {

View File

@ -14,17 +14,17 @@ public record CustomPotionEffect(@NotNull PotionEffect id, @NotNull Settings set
public static final NetworkBuffer.Type<CustomPotionEffect> NETWORK_TYPE = new NetworkBuffer.Type<>() { public static final NetworkBuffer.Type<CustomPotionEffect> NETWORK_TYPE = new NetworkBuffer.Type<>() {
@Override @Override
public void write(@NotNull NetworkBuffer buffer, CustomPotionEffect value) { 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); buffer.write(Settings.NETWORK_TYPE, value.settings);
} }
@Override @Override
public CustomPotionEffect read(@NotNull NetworkBuffer buffer) { 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<CustomPotionEffect> NBT_TYPE = BinaryTagSerializer.COMPOUND.map( public static final BinaryTagSerializer<CustomPotionEffect> NBT_TYPE = BinaryTagSerializer.lazy(() -> BinaryTagSerializer.COMPOUND.map(
tag -> new CustomPotionEffect( tag -> new CustomPotionEffect(
PotionEffect.fromNamespaceId(tag.getString("id")), PotionEffect.fromNamespaceId(tag.getString("id")),
Settings.NBT_TYPE.read(tag)), Settings.NBT_TYPE.read(tag)),
@ -32,7 +32,7 @@ public record CustomPotionEffect(@NotNull PotionEffect id, @NotNull Settings set
.putString("id", value.id.name()) .putString("id", value.id.name())
.put((CompoundBinaryTag) Settings.NBT_TYPE.write(value.settings)) .put((CompoundBinaryTag) Settings.NBT_TYPE.write(value.settings))
.build() .build()
); ));
public CustomPotionEffect(@NotNull PotionEffect id, byte amplifier, int duration, boolean isAmbient, boolean showParticles, boolean showIcon) { 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)); this(id, new Settings(amplifier, duration, isAmbient, showParticles, showIcon, null));