diff --git a/src/main/java/net/minestom/server/entity/Entity.java b/src/main/java/net/minestom/server/entity/Entity.java index 7a810bb3e..b381856de 100644 --- a/src/main/java/net/minestom/server/entity/Entity.java +++ b/src/main/java/net/minestom/server/entity/Entity.java @@ -141,8 +141,10 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer { /** * Called each tick + * + * @param time the time of update in milliseconds */ - public abstract void update(); + public abstract void update(long time); /** * Called when a new instance is set @@ -449,7 +451,7 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer { handleVoid(); // Call the abstract update method - update(); + update(time); ticks++; callEvent(EntityTickEvent.class, tickEvent); // reuse tickEvent to avoid recreating it each tick diff --git a/src/main/java/net/minestom/server/entity/EntityCreature.java b/src/main/java/net/minestom/server/entity/EntityCreature.java index 93cf83c02..1715c88aa 100644 --- a/src/main/java/net/minestom/server/entity/EntityCreature.java +++ b/src/main/java/net/minestom/server/entity/EntityCreature.java @@ -49,12 +49,11 @@ public abstract class EntityCreature extends LivingEntity { } @Override - public void update() { - super.update(); + public void update(long time) { + super.update(time); // Path finding pathProgress(); - } /** diff --git a/src/main/java/net/minestom/server/entity/ExperienceOrb.java b/src/main/java/net/minestom/server/entity/ExperienceOrb.java index d5321bcd4..e729b12b3 100644 --- a/src/main/java/net/minestom/server/entity/ExperienceOrb.java +++ b/src/main/java/net/minestom/server/entity/ExperienceOrb.java @@ -18,7 +18,7 @@ public class ExperienceOrb extends Entity { } @Override - public void update() { + public void update(long time) { // TODO slide toward nearest player } diff --git a/src/main/java/net/minestom/server/entity/ItemEntity.java b/src/main/java/net/minestom/server/entity/ItemEntity.java index 81adc3f02..4de93bd4d 100644 --- a/src/main/java/net/minestom/server/entity/ItemEntity.java +++ b/src/main/java/net/minestom/server/entity/ItemEntity.java @@ -6,13 +6,25 @@ import net.minestom.server.item.ItemStack; import net.minestom.server.item.StackingRule; import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.utils.Position; +import net.minestom.server.utils.time.CooldownUtils; import net.minestom.server.utils.time.TimeUnit; +import net.minestom.server.utils.time.UpdateOption; import java.util.Set; import java.util.function.Consumer; public class ItemEntity extends ObjectEntity { + /** + * Used to slow down the merge check delay + */ + private static UpdateOption mergeUpdateOption = new UpdateOption(10, TimeUnit.TICK); + + /** + * The last time that this item has checked his neighbors for merge + */ + private long lastMergeCheck; + private ItemStack itemStack; private boolean pickable = true; @@ -28,9 +40,31 @@ public class ItemEntity extends ObjectEntity { setBoundingBox(0.25f, 0.25f, 0.25f); } + /** + * Get the update option for the merging feature + * + * @return the merge update option + */ + public static UpdateOption getMergeUpdateOption() { + return mergeUpdateOption; + } + + /** + * Change the merge update option. + * Can be set to null to entirely remove the delay + * + * @param mergeUpdateOption the new merge update option + */ + public static void setMergeUpdateOption(UpdateOption mergeUpdateOption) { + ItemEntity.mergeUpdateOption = mergeUpdateOption; + } + @Override - public void update() { - if (isMergeable() && isPickable()) { + public void update(long time) { + if (isMergeable() && isPickable() && + (mergeUpdateOption != null && !CooldownUtils.hasCooldown(time, lastMergeCheck, mergeUpdateOption))) { + this.lastMergeCheck = time; + Chunk chunk = instance.getChunkAt(getPosition()); Set entities = instance.getChunkEntities(chunk); for (Entity entity : entities) { @@ -48,30 +82,29 @@ public class ItemEntity extends ObjectEntity { if (getDistance(itemEntity) > mergeRange) continue; - synchronized (this) { - synchronized (itemEntity) { - ItemStack itemStackEntity = itemEntity.getItemStack(); + // Use the class as a monitor to prevent deadlock + // Shouldn't happen too often to be an issue + synchronized (ItemEntity.class) { + final ItemStack itemStackEntity = itemEntity.getItemStack(); - StackingRule stackingRule = itemStack.getStackingRule(); - boolean canStack = stackingRule.canBeStacked(itemStack, itemStackEntity); + final StackingRule stackingRule = itemStack.getStackingRule(); + final boolean canStack = stackingRule.canBeStacked(itemStack, itemStackEntity); - if (!canStack) - continue; + if (!canStack) + continue; - int totalAmount = stackingRule.getAmount(itemStack) + stackingRule.getAmount(itemStackEntity); - boolean canApply = stackingRule.canApply(itemStack, totalAmount); + final int totalAmount = stackingRule.getAmount(itemStack) + stackingRule.getAmount(itemStackEntity); + final boolean canApply = stackingRule.canApply(itemStack, totalAmount); - if (!canApply) - continue; + if (!canApply) + continue; - EntityItemMergeEvent entityItemMergeEvent = new EntityItemMergeEvent(this, itemEntity); - callCancellableEvent(EntityItemMergeEvent.class, entityItemMergeEvent, () -> { - ItemStack result = stackingRule.apply(itemStack.clone(), totalAmount); - setItemStack(result); - itemEntity.remove(); - }); - - } + EntityItemMergeEvent entityItemMergeEvent = new EntityItemMergeEvent(this, itemEntity); + callCancellableEvent(EntityItemMergeEvent.class, entityItemMergeEvent, () -> { + ItemStack result = stackingRule.apply(itemStack.clone(), totalAmount); + setItemStack(result); + itemEntity.remove(); + }); } } diff --git a/src/main/java/net/minestom/server/entity/LivingEntity.java b/src/main/java/net/minestom/server/entity/LivingEntity.java index 30b7d6c69..733276e59 100644 --- a/src/main/java/net/minestom/server/entity/LivingEntity.java +++ b/src/main/java/net/minestom/server/entity/LivingEntity.java @@ -65,14 +65,14 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler { } @Override - public void update() { + public void update(long time) { if (isOnFire()) { - if (System.currentTimeMillis() > fireExtinguishTime) { + if (time > fireExtinguishTime) { setOnFire(false); } else { - if (System.currentTimeMillis() - lastFireDamageTime > fireDamagePeriod) { + if (time - lastFireDamageTime > fireDamagePeriod) { damage(DamageType.ON_FIRE, 1.0f); - lastFireDamageTime = System.currentTimeMillis(); + lastFireDamageTime = time; } } } @@ -92,6 +92,7 @@ public abstract class LivingEntity extends Entity implements EquipmentHandler { ItemEntity itemEntity = (ItemEntity) entity; if (!itemEntity.isPickable()) continue; + BoundingBox itemBoundingBox = itemEntity.getBoundingBox(); if (livingBoundingBox.intersect(itemBoundingBox)) { synchronized (itemEntity) { diff --git a/src/main/java/net/minestom/server/entity/ObjectEntity.java b/src/main/java/net/minestom/server/entity/ObjectEntity.java index f9d5543cb..9f88082cc 100644 --- a/src/main/java/net/minestom/server/entity/ObjectEntity.java +++ b/src/main/java/net/minestom/server/entity/ObjectEntity.java @@ -19,7 +19,7 @@ public abstract class ObjectEntity extends Entity { public abstract int getObjectData(); @Override - public void update() { + public void update(long time) { } diff --git a/src/main/java/net/minestom/server/entity/Player.java b/src/main/java/net/minestom/server/entity/Player.java index 5ddd456f7..cbf803f78 100644 --- a/src/main/java/net/minestom/server/entity/Player.java +++ b/src/main/java/net/minestom/server/entity/Player.java @@ -266,7 +266,7 @@ public class Player extends LivingEntity { } @Override - public void update() { + public void update(long time) { // Flush all pending packets playerConnection.flush(); @@ -276,12 +276,12 @@ public class Player extends LivingEntity { packet.process(this); } - super.update(); // Super update (item pickup/fire management) + super.update(time); // Super update (item pickup/fire management) // Target block stage if (targetCustomBlock != null) { final byte animationCount = 10; - long since = System.currentTimeMillis() - targetBlockTime; + long since = time - targetBlockTime; byte stage = (byte) (since / (blockBreakTime / animationCount)); stage = MathUtils.setBetween(stage, (byte) -1, animationCount); if (stage != targetLastStage) { @@ -317,7 +317,7 @@ public class Player extends LivingEntity { // Eating animation if (isEating()) { - if (System.currentTimeMillis() - startEatingTime >= eatingTime) { + if (time - startEatingTime >= eatingTime) { refreshEating(false); triggerStatus((byte) 9); // Mark item use as finished diff --git a/src/main/java/net/minestom/server/entity/type/EntityBoat.java b/src/main/java/net/minestom/server/entity/type/EntityBoat.java index 33ffcfb35..32b6fc2b9 100644 --- a/src/main/java/net/minestom/server/entity/type/EntityBoat.java +++ b/src/main/java/net/minestom/server/entity/type/EntityBoat.java @@ -22,16 +22,6 @@ public class EntityBoat extends ObjectEntity { return 0; } - @Override - public void update() { - - } - - @Override - public void spawn() { - - } - @Override public Consumer getMetadataConsumer() { return packet -> { diff --git a/src/main/java/net/minestom/server/event/player/PlayerAddItemStackEvent.java b/src/main/java/net/minestom/server/event/player/PlayerAddItemStackEvent.java index e06bc11ee..04e4a1ae7 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerAddItemStackEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerAddItemStackEvent.java @@ -3,6 +3,7 @@ package net.minestom.server.event.player; import net.minestom.server.entity.Player; import net.minestom.server.event.CancellableEvent; import net.minestom.server.item.ItemStack; +import net.minestom.server.utils.item.ItemStackUtils; /** * Called as a result of {@link net.minestom.server.inventory.PlayerInventory#addItemStack(ItemStack)} @@ -41,6 +42,6 @@ public class PlayerAddItemStackEvent extends CancellableEvent { * @param itemStack the new item stack */ public void setItemStack(ItemStack itemStack) { - this.itemStack = itemStack; + this.itemStack = ItemStackUtils.notNull(itemStack); } } diff --git a/src/main/java/net/minestom/server/event/player/PlayerSetItemStackEvent.java b/src/main/java/net/minestom/server/event/player/PlayerSetItemStackEvent.java index 5493d65f5..9aadfa62b 100644 --- a/src/main/java/net/minestom/server/event/player/PlayerSetItemStackEvent.java +++ b/src/main/java/net/minestom/server/event/player/PlayerSetItemStackEvent.java @@ -3,9 +3,11 @@ package net.minestom.server.event.player; import net.minestom.server.entity.Player; import net.minestom.server.event.CancellableEvent; import net.minestom.server.item.ItemStack; +import net.minestom.server.utils.item.ItemStackUtils; /** * Called as a result of {@link net.minestom.server.inventory.PlayerInventory#setItemStack(int, ItemStack)} + * and player click in his inventory */ public class PlayerSetItemStackEvent extends CancellableEvent { @@ -61,7 +63,7 @@ public class PlayerSetItemStackEvent extends CancellableEvent { * @param itemStack the new item stack */ public void setItemStack(ItemStack itemStack) { - this.itemStack = itemStack; + this.itemStack = ItemStackUtils.notNull(itemStack); } } diff --git a/src/main/java/net/minestom/server/utils/time/CooldownUtils.java b/src/main/java/net/minestom/server/utils/time/CooldownUtils.java index b4b7fe825..0a134bae3 100644 --- a/src/main/java/net/minestom/server/utils/time/CooldownUtils.java +++ b/src/main/java/net/minestom/server/utils/time/CooldownUtils.java @@ -7,6 +7,10 @@ public class CooldownUtils { return currentTime - lastUpdate < cooldownMs; } + public static boolean hasCooldown(long currentTime, long lastUpdate, UpdateOption updateOption) { + return hasCooldown(currentTime, lastUpdate, updateOption.getTimeUnit(), updateOption.getValue()); + } + public static boolean hasCooldown(long lastUpdate, TimeUnit timeUnit, int cooldown) { return hasCooldown(System.currentTimeMillis(), lastUpdate, timeUnit, cooldown); }