net/minecraft/world/entity/item

This commit is contained in:
Shane Freeder
2024-12-14 17:57:17 +00:00
parent 822b963d3f
commit 1a214aed6a
4 changed files with 306 additions and 326 deletions

View File

@@ -0,0 +1,165 @@
--- a/net/minecraft/world/entity/item/FallingBlockEntity.java
+++ b/net/minecraft/world/entity/item/FallingBlockEntity.java
@@ -47,8 +_,14 @@
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
+import org.bukkit.event.entity.CreatureSpawnEvent;
import org.slf4j.Logger;
+// CraftBukkit start;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.entity.EntityRemoveEvent;
+// CraftBukkit end
+
public class FallingBlockEntity extends Entity {
private static final Logger LOGGER = LogUtils.getLogger();
public BlockState blockState = Blocks.SAND.defaultBlockState();
@@ -62,6 +_,7 @@
public CompoundTag blockData;
public boolean forceTickAfterTeleportToDuplicate;
protected static final EntityDataAccessor<BlockPos> DATA_START_POS = SynchedEntityData.defineId(FallingBlockEntity.class, EntityDataSerializers.BLOCK_POS);
+ public boolean autoExpire = true; // Paper - Expand FallingBlock API
public FallingBlockEntity(EntityType<? extends FallingBlockEntity> entityType, Level level) {
super(entityType, level);
@@ -80,6 +_,10 @@
}
public static FallingBlockEntity fall(Level level, BlockPos pos, BlockState blockState) {
+ // CraftBukkit start
+ return FallingBlockEntity.fall(level, pos, blockState, CreatureSpawnEvent.SpawnReason.DEFAULT);
+ }
+ public static FallingBlockEntity fall(Level level, BlockPos pos, BlockState blockState, org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason) {
FallingBlockEntity fallingBlockEntity = new FallingBlockEntity(
level,
pos.getX() + 0.5,
@@ -89,6 +_,7 @@
? blockState.setValue(BlockStateProperties.WATERLOGGED, Boolean.valueOf(false))
: blockState
);
+ if (!CraftEventFactory.callEntityChangeBlockEvent(fallingBlockEntity, pos, blockState.getFluidState().createLegacyBlock())) return fallingBlockEntity; // CraftBukkit
level.setBlock(pos, blockState.getFluidState().createLegacyBlock(), 3);
level.addFreshEntity(fallingBlockEntity);
return fallingBlockEntity;
@@ -139,13 +_,22 @@
@Override
public void tick() {
if (this.blockState.isAir()) {
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
} else {
Block block = this.blockState.getBlock();
this.time++;
this.applyGravity();
this.move(MoverType.SELF, this.getDeltaMovement());
this.applyEffectsFromBlocks();
+ // Paper start - Configurable falling blocks height nerf
+ if (this.level().paperConfig().fixes.fallingBlockHeightNerf.test(v -> this.getY() > v)) {
+ if (this.dropItem && this.level() instanceof final ServerLevel serverLevel && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
+ this.spawnAtLocation(serverLevel, block);
+ }
+ this.discard(EntityRemoveEvent.Cause.OUT_OF_WORLD);
+ return;
+ }
+ // Paper end - Configurable falling blocks height nerf
this.handlePortal();
if (this.level() instanceof ServerLevel serverLevel && (this.isAlive() || this.forceTickAfterTeleportToDuplicate)) {
BlockPos blockPos = this.blockPosition();
@@ -166,12 +_,12 @@
}
if (!this.onGround() && !flag1) {
- if (this.time > 100 && (blockPos.getY() <= this.level().getMinY() || blockPos.getY() > this.level().getMaxY()) || this.time > 600) {
+ if ((this.time > 100 && autoExpire) && (blockPos.getY() <= this.level().getMinY() || blockPos.getY() > this.level().getMaxY()) || (this.time > 600 && autoExpire)) { // Paper - Expand FallingBlock API
if (this.dropItem && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
this.spawnAtLocation(serverLevel, block);
}
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause
}
} else {
BlockState blockState = this.level().getBlockState(blockPos);
@@ -189,12 +_,18 @@
this.blockState = this.blockState.setValue(BlockStateProperties.WATERLOGGED, Boolean.valueOf(true));
}
+ // CraftBukkit start
+ if (!CraftEventFactory.callEntityChangeBlockEvent(this, blockPos, this.blockState)) {
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // SPIGOT-6586 called before the event in previous versions
+ return;
+ }
+ // CraftBukkit end
if (this.level().setBlock(blockPos, this.blockState, 3)) {
((ServerLevel)this.level())
.getChunkSource()
.chunkMap
.broadcast(this, new ClientboundBlockUpdatePacket(blockPos, this.level().getBlockState(blockPos)));
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
if (block instanceof Fallable) {
((Fallable)block).onLand(this.level(), blockPos, this.blockState, blockState, this);
}
@@ -218,19 +_,19 @@
}
}
} else if (this.dropItem && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause
this.callOnBrokenAfterFall(block, blockPos);
this.spawnAtLocation(serverLevel, block);
}
} else {
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DROP); // CraftBukkit - add Bukkit remove cause
if (this.dropItem && serverLevel.getGameRules().getBoolean(GameRules.RULE_DOENTITYDROPS)) {
this.callOnBrokenAfterFall(block, blockPos);
this.spawnAtLocation(serverLevel, block);
}
}
} else {
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
this.callOnBrokenAfterFall(block, blockPos);
}
}
@@ -290,6 +_,7 @@
}
compound.putBoolean("CancelDrop", this.cancelDrop);
+ if (!autoExpire) {compound.putBoolean("Paper.AutoExpire", false);} // Paper - Expand FallingBlock API
}
@Override
@@ -308,7 +_,7 @@
this.dropItem = compound.getBoolean("DropItem");
}
- if (compound.contains("TileEntityData", 10)) {
+ if (compound.contains("TileEntityData", 10) && !(this.level().paperConfig().entities.spawning.filterBadTileEntityNbtFromFallingBlocks && this.blockState.getBlock() instanceof net.minecraft.world.level.block.GameMasterBlock)) { // Paper - Filter bad block entity nbt data from falling blocks
this.blockData = compound.getCompound("TileEntityData").copy();
}
@@ -316,6 +_,12 @@
if (this.blockState.isAir()) {
this.blockState = Blocks.SAND.defaultBlockState();
}
+
+ // Paper start - Expand FallingBlock API
+ if (compound.contains("Paper.AutoExpire")) {
+ this.autoExpire = compound.getBoolean("Paper.AutoExpire");
+ }
+ // Paper end - Expand FallingBlock API
}
public void setHurtsEntities(float fallDamagePerDistance, int fallDamageMax) {
@@ -372,7 +_,7 @@
ResourceKey<Level> resourceKey1 = this.level().dimension();
boolean flag = (resourceKey1 == Level.END || resourceKey == Level.END) && resourceKey1 != resourceKey;
Entity entity = super.teleport(teleportTransition);
- this.forceTickAfterTeleportToDuplicate = entity != null && flag;
+ this.forceTickAfterTeleportToDuplicate = entity != null && flag && io.papermc.paper.configuration.GlobalConfiguration.get().unsupportedSettings.allowUnsafeEndPortalTeleportation; // Paper
return entity;
}
}

View File

@@ -0,0 +1,358 @@
--- a/net/minecraft/world/entity/item/ItemEntity.java
+++ b/net/minecraft/world/entity/item/ItemEntity.java
@@ -3,18 +_,6 @@
import java.util.Objects;
import java.util.UUID;
import javax.annotation.Nullable;
-import net.minecraft.core.BlockPos;
-import net.minecraft.nbt.CompoundTag;
-import net.minecraft.network.chat.Component;
-import net.minecraft.network.syncher.EntityDataAccessor;
-import net.minecraft.network.syncher.EntityDataSerializers;
-import net.minecraft.network.syncher.SynchedEntityData;
-import net.minecraft.server.level.ServerLevel;
-import net.minecraft.sounds.SoundSource;
-import net.minecraft.stats.Stats;
-import net.minecraft.tags.FluidTags;
-import net.minecraft.tags.ItemTags;
-import net.minecraft.util.Mth;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
@@ -22,7 +_,6 @@
import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.SlotAccess;
import net.minecraft.world.entity.TraceableEntity;
-import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Explosion;
@@ -31,6 +_,27 @@
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.portal.TeleportTransition;
import net.minecraft.world.phys.Vec3;
+import net.minecraft.core.BlockPos;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.chat.Component;
+import net.minecraft.network.syncher.EntityDataAccessor;
+import net.minecraft.network.syncher.EntityDataSerializers;
+import net.minecraft.network.syncher.SynchedEntityData;
+// CraftBukkit start
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ServerLevel;
+import net.minecraft.sounds.SoundSource;
+import net.minecraft.stats.Stats;
+import net.minecraft.tags.FluidTags;
+import net.minecraft.tags.ItemTags;
+import net.minecraft.util.Mth;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.entity.Player;
+import org.bukkit.event.entity.EntityPickupItemEvent;
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.player.PlayerPickupItemEvent;
+// CraftBukkit end
+import org.bukkit.event.player.PlayerAttemptPickupItemEvent; // Paper
public class ItemEntity extends Entity implements TraceableEntity {
private static final EntityDataAccessor<ItemStack> DATA_ITEM = SynchedEntityData.defineId(ItemEntity.class, EntityDataSerializers.ITEM_STACK);
@@ -49,6 +_,9 @@
@Nullable
public UUID target;
public final float bobOffs;
+ public boolean canMobPickup = true; // Paper - Item#canEntityPickup
+ private int despawnRate = -1; // Paper - Alternative item-despawn-rate
+ public net.kyori.adventure.util.TriState frictionState = net.kyori.adventure.util.TriState.NOT_SET; // Paper - Friction API
public ItemEntity(EntityType<? extends ItemEntity> entityType, Level level) {
super(entityType, level);
@@ -57,7 +_,12 @@
}
public ItemEntity(Level level, double posX, double posY, double posZ, ItemStack itemStack) {
- this(level, posX, posY, posZ, itemStack, level.random.nextDouble() * 0.2 - 0.1, 0.2, level.random.nextDouble() * 0.2 - 0.1);
+ // Paper start - Don't use level random in entity constructors (to make them thread-safe)
+ this(EntityType.ITEM, level);
+ this.setPos(posX, posY, posZ);
+ this.setDeltaMovement(this.random.nextDouble() * 0.2D - 0.1D, 0.2D, this.random.nextDouble() * 0.2D - 0.1D);
+ this.setItem(itemStack);
+ // Paper end - Don't use level random in entity constructors
}
public ItemEntity(Level level, double posX, double posY, double posZ, ItemStack itemStack, double deltaX, double deltaY, double deltaZ) {
@@ -119,7 +_,7 @@
@Override
public void tick() {
if (this.getItem().isEmpty()) {
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
} else {
super.tick();
if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
@@ -147,11 +_,15 @@
}
}
- if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > 1.0E-5F || (this.tickCount + this.getId()) % 4 == 0) {
+ if (!this.onGround() || this.getDeltaMovement().horizontalDistanceSqr() > 1.0E-5F || (this.tickCount + this.getId()) % 4 == 0) { // Paper - Diff on change; ActivationRange immunity
this.move(MoverType.SELF, this.getDeltaMovement());
this.applyEffectsFromBlocks();
float f = 0.98F;
- if (this.onGround()) {
+ // Paper start - Friction API
+ if (frictionState == net.kyori.adventure.util.TriState.FALSE) {
+ f = 1F;
+ } else if (this.onGround()) {
+ // Paper end - Friction API
f = this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getFriction() * 0.98F;
}
@@ -184,11 +_,42 @@
}
}
- if (!this.level().isClientSide && this.age >= 6000) {
- this.discard();
- }
- }
- }
+ if (!this.level().isClientSide && this.age >= this.despawnRate) { // Spigot // Paper - Alternative item-despawn-rate
+ // CraftBukkit start - fire ItemDespawnEvent
+ if (CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
+ this.age = 0;
+ return;
+ }
+ // CraftBukkit end
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
+ }
+ }
+ }
+
+ // Spigot start - copied from above
+ @Override
+ public void inactiveTick() {
+ // Paper start - remove anti tick skipping measures / wall time - copied from above
+ if (this.pickupDelay > 0 && this.pickupDelay != 32767) {
+ --this.pickupDelay;
+ }
+ if (this.age != -32768) {
+ ++this.age;
+ }
+ // Paper end - remove anti tick skipping measures / wall time - copied from above
+
+ if (!this.level().isClientSide && this.age >= this.despawnRate) { // Spigot // Paper - Alternative item-despawn-rate
+ // CraftBukkit start - fire ItemDespawnEvent
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) {
+ this.age = 0;
+ return;
+ }
+ // CraftBukkit end
+ this.discard(EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause
+ }
+ }
+ // Spigot end
+
@Override
public BlockPos getBlockPosBelowThatAffectsMyMovement() {
@@ -210,9 +_,18 @@
private void mergeWithNeighbours() {
if (this.isMergable()) {
+ double radius = this.level().spigotConfig.itemMerge; // Spigot
for (ItemEntity itemEntity : this.level()
- .getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(0.5, 0.0, 0.5), neighbour -> neighbour != this && neighbour.isMergable())) {
+ .getEntitiesOfClass(ItemEntity.class, this.getBoundingBox().inflate(radius, this.level().paperConfig().entities.behavior.onlyMergeItemsHorizontally ? 0 : radius - 0.5D, radius), neighbour -> neighbour != this && neighbour.isMergable())) { // Spigot // Paper - configuration to only merge items horizontally
if (itemEntity.isMergable()) {
+ // Paper start - Fix items merging through walls
+ if (this.level().paperConfig().fixes.fixItemsMergingThroughWalls) {
+ if (this.level().clipDirect(this.position(), itemEntity.position(),
+ net.minecraft.world.phys.shapes.CollisionContext.of(this)) == net.minecraft.world.phys.HitResult.Type.BLOCK) {
+ continue;
+ }
+ }
+ // Paper end - Fix items merging through walls
this.tryToMerge(itemEntity);
if (this.isRemoved()) {
break;
@@ -224,14 +_,14 @@
private boolean isMergable() {
ItemStack item = this.getItem();
- return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < 6000 && item.getCount() < item.getMaxStackSize();
+ return this.isAlive() && this.pickupDelay != 32767 && this.age != -32768 && this.age < this.despawnRate && item.getCount() < item.getMaxStackSize(); // Paper - Alternative item-despawn-rate
}
private void tryToMerge(ItemEntity itemEntity) {
ItemStack item = this.getItem();
ItemStack item1 = itemEntity.getItem();
if (Objects.equals(this.target, itemEntity.target) && areMergable(item, item1)) {
- if (item1.getCount() < item.getCount()) {
+ if (true || item1.getCount() < item.getCount()) { // Spigot
merge(this, item, itemEntity, item1);
} else {
merge(itemEntity, item1, this, item);
@@ -257,11 +_,16 @@
}
private static void merge(ItemEntity destinationEntity, ItemStack destinationStack, ItemEntity originEntity, ItemStack originStack) {
+ // CraftBukkit start
+ if (!CraftEventFactory.callItemMergeEvent(originEntity, destinationEntity)) {
+ return;
+ }
+ // CraftBukkit end
merge(destinationEntity, destinationStack, originStack);
destinationEntity.pickupDelay = Math.max(destinationEntity.pickupDelay, originEntity.pickupDelay);
destinationEntity.age = Math.min(destinationEntity.age, originEntity.age);
if (originStack.isEmpty()) {
- originEntity.discard();
+ originEntity.discard(EntityRemoveEvent.Cause.MERGE); // CraftBukkit - add Bukkit remove cause
}
}
@@ -289,12 +_,17 @@
} else if (!this.getItem().canBeHurtBy(damageSource)) {
return false;
} else {
+ // CraftBukkit start
+ if (CraftEventFactory.handleNonLivingEntityDamageEvent(this, damageSource, amount)) {
+ return false;
+ }
+ // CraftBukkit end
this.markHurt();
this.health = (int)(this.health - amount);
this.gameEvent(GameEvent.ENTITY_DAMAGE, damageSource.getEntity());
if (this.health <= 0) {
this.getItem().onDestroyed(this);
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause
}
return true;
@@ -308,6 +_,11 @@
@Override
public void addAdditionalSaveData(CompoundTag compound) {
+ // Paper start - Friction API
+ if (this.frictionState != net.kyori.adventure.util.TriState.NOT_SET) {
+ compound.putString("Paper.FrictionState", this.frictionState.toString());
+ }
+ // Paper end - Friction API
compound.putShort("Health", (short)this.health);
compound.putShort("Age", (short)this.age);
compound.putShort("PickupDelay", (short)this.pickupDelay);
@@ -347,22 +_,95 @@
} else {
this.setItem(ItemStack.EMPTY);
}
+ // Paper start - Friction API
+ if (compound.contains("Paper.FrictionState")) {
+ String fs = compound.getString("Paper.FrictionState");
+ try {
+ frictionState = net.kyori.adventure.util.TriState.valueOf(fs);
+ } catch (Exception ignored) {
+ com.mojang.logging.LogUtils.getLogger().error("Unknown friction state " + fs + " for " + this);
+ }
+ }
+ // Paper end - Friction API
if (this.getItem().isEmpty()) {
- this.discard();
+ this.discard(null); // CraftBukkit - add Bukkit remove cause
}
}
@Override
- public void playerTouch(Player entity) {
+ public void playerTouch(net.minecraft.world.entity.player.Player entity) {
if (!this.level().isClientSide) {
ItemStack item = this.getItem();
Item item1 = item.getItem();
int count = item.getCount();
+ // CraftBukkit start - fire PlayerPickupItemEvent
+ int canHold = entity.getInventory().canHold(item);
+ int remaining = count - canHold;
+ boolean flyAtPlayer = false; // Paper
+
+ // Paper start - PlayerAttemptPickupItemEvent
+ if (this.pickupDelay <= 0) {
+ PlayerAttemptPickupItemEvent attemptEvent = new PlayerAttemptPickupItemEvent((org.bukkit.entity.Player) entity.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
+ this.level().getCraftServer().getPluginManager().callEvent(attemptEvent);
+
+ flyAtPlayer = attemptEvent.getFlyAtPlayer();
+ if (attemptEvent.isCancelled()) {
+ if (flyAtPlayer) {
+ entity.take(this, count);
+ }
+
+ return;
+ }
+ }
+
+ if (this.pickupDelay <= 0 && canHold > 0) {
+ item.setCount(canHold);
+ // Call legacy event
+ PlayerPickupItemEvent playerEvent = new PlayerPickupItemEvent((Player) entity.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
+ playerEvent.setCancelled(!playerEvent.getPlayer().getCanPickupItems());
+ this.level().getCraftServer().getPluginManager().callEvent(playerEvent);
+ flyAtPlayer = playerEvent.getFlyAtPlayer(); // Paper
+ if (playerEvent.isCancelled()) {
+ item.setCount(count); // SPIGOT-5294 - restore count
+ // Paper start
+ if (flyAtPlayer) {
+ entity.take(this, count);
+ }
+ // Paper end
+ return;
+ }
+
+ // Call newer event afterwards
+ EntityPickupItemEvent entityEvent = new EntityPickupItemEvent((Player) entity.getBukkitEntity(), (org.bukkit.entity.Item) this.getBukkitEntity(), remaining);
+ entityEvent.setCancelled(!entityEvent.getEntity().getCanPickupItems());
+ this.level().getCraftServer().getPluginManager().callEvent(entityEvent);
+ if (entityEvent.isCancelled()) {
+ item.setCount(count); // SPIGOT-5294 - restore count
+ return;
+ }
+
+ // Update the ItemStack if it was changed in the event
+ ItemStack current = this.getItem();
+ if (!item.equals(current)) {
+ item = current;
+ } else {
+ item.setCount(canHold + remaining); // = i
+ }
+
+ // Possibly < 0; fix here so we do not have to modify code below
+ this.pickupDelay = 0;
+ } else if (this.pickupDelay == 0) {
+ // ensure that the code below isn't triggered if canHold says we can't pick the items up
+ this.pickupDelay = -1;
+ }
+ // CraftBukkit end
+ // Paper end - PlayerAttemptPickupItemEvent
if (this.pickupDelay == 0 && (this.target == null || this.target.equals(entity.getUUID())) && entity.getInventory().add(item)) {
+ if (flyAtPlayer) // Paper - PlayerPickupItemEvent
entity.take(this, count);
if (item.isEmpty()) {
- this.discard();
+ this.discard(EntityRemoveEvent.Cause.PICKUP); // CraftBukkit - add Bukkit remove cause
item.setCount(count);
}
@@ -400,6 +_,7 @@
public void setItem(ItemStack stack) {
this.getEntityData().set(DATA_ITEM, stack);
+ this.despawnRate = this.level().paperConfig().entities.spawning.altItemDespawnRate.enabled ? this.level().paperConfig().entities.spawning.altItemDespawnRate.items.getOrDefault(stack.getItem(), this.level().spigotConfig.itemDespawnRate) : this.level().spigotConfig.itemDespawnRate; // Paper - Alternative item-despawn-rate
}
@Override
@@ -453,7 +_,7 @@
public void makeFakeItem() {
this.setNeverPickUp();
- this.age = 5999;
+ this.age = this.despawnRate - 1; // Spigot // Paper - Alternative item-despawn-rate
}
public static float getSpin(float age, float bobOffset) {

View File

@@ -0,0 +1,124 @@
--- a/net/minecraft/world/entity/item/PrimedTnt.java
+++ b/net/minecraft/world/entity/item/PrimedTnt.java
@@ -27,6 +_,12 @@
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.portal.TeleportTransition;
+// CraftBukkit start;
+import org.bukkit.craftbukkit.event.CraftEventFactory;
+import org.bukkit.event.entity.EntityRemoveEvent;
+import org.bukkit.event.entity.ExplosionPrimeEvent;
+// CraftBukkit end
+
public class PrimedTnt extends Entity implements TraceableEntity {
private static final EntityDataAccessor<Integer> DATA_FUSE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.INT);
private static final EntityDataAccessor<BlockState> DATA_BLOCK_STATE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.BLOCK_STATE);
@@ -50,6 +_,7 @@
public LivingEntity owner;
private boolean usedPortal;
public float explosionPower = 4.0F;
+ public boolean isIncendiary = false; // CraftBukkit - add field
public PrimedTnt(EntityType<? extends PrimedTnt> entityType, Level level) {
super(entityType, level);
@@ -59,7 +_,7 @@
public PrimedTnt(Level level, double x, double y, double z, @Nullable LivingEntity owner) {
this(EntityType.TNT, level);
this.setPos(x, y, z);
- double d = level.random.nextDouble() * (float) (Math.PI * 2);
+ double d = this.random.nextDouble() * (float) (Math.PI * 2); // Paper - Don't use level random in entity constructors
this.setDeltaMovement(-Math.sin(d) * 0.02, 0.2F, -Math.cos(d) * 0.02);
this.setFuse(80);
this.xo = x;
@@ -91,10 +_,17 @@
@Override
public void tick() {
+ if (this.level().spigotConfig.maxTntTicksPerTick > 0 && ++this.level().spigotConfig.currentPrimedTnt > this.level().spigotConfig.maxTntTicksPerTick) { return; } // Spigot
this.handlePortal();
this.applyGravity();
this.move(MoverType.SELF, this.getDeltaMovement());
this.applyEffectsFromBlocks();
+ // Paper start - Configurable TNT height nerf
+ if (this.level().paperConfig().fixes.tntEntityHeightNerf.test(v -> this.getY() > v)) {
+ this.discard(EntityRemoveEvent.Cause.OUT_OF_WORLD);
+ return;
+ }
+ // Paper end - Configurable TNT height nerf
this.setDeltaMovement(this.getDeltaMovement().scale(0.98));
if (this.onGround()) {
this.setDeltaMovement(this.getDeltaMovement().multiply(0.7, -0.5, 0.7));
@@ -103,19 +_,49 @@
int i = this.getFuse() - 1;
this.setFuse(i);
if (i <= 0) {
- this.discard();
+ // CraftBukkit start - Need to reverse the order of the explosion and the entity death so we have a location for the event
+ //this.discard();
if (!this.level().isClientSide) {
this.explode();
}
+ this.discard(EntityRemoveEvent.Cause.EXPLODE); // CraftBukkit - add Bukkit remove cause
+ // CraftBukkit end
} else {
this.updateInWaterStateAndDoFluidPushing();
if (this.level().isClientSide) {
this.level().addParticle(ParticleTypes.SMOKE, this.getX(), this.getY() + 0.5, this.getZ(), 0.0, 0.0, 0.0);
}
}
+ // Paper start - Option to prevent TNT from moving in water
+ if (!this.isRemoved() && this.wasTouchingWater && this.level().paperConfig().fixes.preventTntFromMovingInWater) {
+ /*
+ * Author: Jedediah Smith <jedediah@silencegreys.com>
+ */
+ // Send position and velocity updates to nearby players on every tick while the TNT is in water.
+ // This does pretty well at keeping their clients in sync with the server.
+ net.minecraft.server.level.ChunkMap.TrackedEntity ete = ((net.minecraft.server.level.ServerLevel) this.level()).getChunkSource().chunkMap.entityMap.get(this.getId());
+ if (ete != null) {
+ net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket velocityPacket = new net.minecraft.network.protocol.game.ClientboundSetEntityMotionPacket(this);
+ net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket positionPacket = net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket.teleport(this.getId(), net.minecraft.world.entity.PositionMoveRotation.of(this), java.util.Set.of(), this.onGround);
+
+ ete.seenBy.stream()
+ .filter(viewer -> (viewer.getPlayer().getX() - this.getX()) * (viewer.getPlayer().getY() - this.getY()) * (viewer.getPlayer().getZ() - this.getZ()) < 16 * 16)
+ .forEach(viewer -> {
+ viewer.send(velocityPacket);
+ viewer.send(positionPacket);
+ });
+ }
+ }
+ // Paper end - Option to prevent TNT from moving in water
}
private void explode() {
+ // CraftBukkit start
+ ExplosionPrimeEvent event = CraftEventFactory.callExplosionPrimeEvent((org.bukkit.entity.Explosive) this.getBukkitEntity());
+ if (event.isCancelled()) {
+ return;
+ }
+ // Craftbukkit end
this.level()
.explode(
this,
@@ -124,8 +_,8 @@
this.getX(),
this.getY(0.0625),
this.getZ(),
- this.explosionPower,
- false,
+ event.getRadius(), // CraftBukkit
+ event.getFire(), // CraftBukkit
Level.ExplosionInteraction.TNT
);
}
@@ -200,4 +_,11 @@
public final boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) {
return false;
}
+
+ // Paper start - Option to prevent TNT from moving in water
+ @Override
+ public boolean isPushedByFluid() {
+ return !level().paperConfig().fixes.preventTntFromMovingInWater && super.isPushedByFluid();
+ }
+ // Paper end - Option to prevent TNT from moving in water
}