mirror of
https://github.com/Minestom/Minestom.git
synced 2024-09-27 14:13:24 +02:00
Fix deadlock with ItemEntity + added ItemEntity#getMergeUpdateOption to mitigate CPU usage increase
This commit is contained in:
parent
ea034701f8
commit
7e20278dd9
@ -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
|
||||
|
@ -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();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -18,7 +18,7 @@ public class ExperienceOrb extends Entity {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
public void update(long time) {
|
||||
// TODO slide toward nearest player
|
||||
}
|
||||
|
||||
|
@ -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<Entity> entities = instance.getChunkEntities(chunk);
|
||||
for (Entity entity : entities) {
|
||||
@ -48,18 +82,19 @@ 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;
|
||||
|
||||
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;
|
||||
@ -70,8 +105,6 @@ public class ItemEntity extends ObjectEntity {
|
||||
setItemStack(result);
|
||||
itemEntity.remove();
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -19,7 +19,7 @@ public abstract class ObjectEntity extends Entity {
|
||||
public abstract int getObjectData();
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
public void update(long time) {
|
||||
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -22,16 +22,6 @@ public class EntityBoat extends ObjectEntity {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Consumer<PacketWriter> getMetadataConsumer() {
|
||||
return packet -> {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user