mirror of
https://github.com/PaperMC/Paper.git
synced 2025-01-06 00:07:56 +01:00
fff5d44a12
Fixes EntityPotionEffectEvent Fixes EntityPoseChangeEvent Asynchronous chunk generation provides an opportunity for things to happen async that previously fired synchronous-only events. This patch is for mitigating those issues by various methods. Also fixes correctly marking/clearing the entity generation flag. This patch sets the generation flag anytime an entity is created via StructureTemplate before loading from NBT to catch uses of the flag during the loading logic. This patch clears the generation flag from an entity when added to a ServerLevel for the situation where generation happened directly to a ServerLevel and the entity still has the flag set.
1931 lines
86 KiB
Diff
1931 lines
86 KiB
Diff
--- a/net/minecraft/world/entity/Entity.java
|
|
+++ b/net/minecraft/world/entity/Entity.java
|
|
@@ -59,6 +59,8 @@
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
|
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
|
+import net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket;
|
|
+import net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket;
|
|
import net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket;
|
|
import net.minecraft.network.protocol.game.VecDeltaCodec;
|
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
@@ -101,8 +103,6 @@
|
|
import net.minecraft.world.level.ChunkPos;
|
|
import net.minecraft.world.level.ClipContext;
|
|
import net.minecraft.world.level.Explosion;
|
|
-import net.minecraft.world.level.ItemLike;
|
|
-import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.Block;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.FenceGateBlock;
|
|
@@ -138,9 +138,153 @@
|
|
import net.minecraft.world.scores.ScoreHolder;
|
|
import net.minecraft.world.scores.Team;
|
|
import org.slf4j.Logger;
|
|
+import net.minecraft.world.level.GameRules;
|
|
+import net.minecraft.world.level.ItemLike;
|
|
+import net.minecraft.world.level.Level;
|
|
+import org.bukkit.Bukkit;
|
|
+import org.bukkit.Location;
|
|
+import org.bukkit.Server;
|
|
+import org.bukkit.block.BlockFace;
|
|
+import org.bukkit.command.CommandSender;
|
|
+import org.bukkit.entity.Hanging;
|
|
+import org.bukkit.entity.LivingEntity;
|
|
+import org.bukkit.entity.Vehicle;
|
|
+import org.bukkit.event.entity.EntityCombustByEntityEvent;
|
|
+import org.bukkit.event.hanging.HangingBreakByEntityEvent;
|
|
+import org.bukkit.event.vehicle.VehicleBlockCollisionEvent;
|
|
+import org.bukkit.event.vehicle.VehicleEnterEvent;
|
|
+import org.bukkit.event.vehicle.VehicleExitEvent;
|
|
+import org.bukkit.craftbukkit.CraftWorld;
|
|
+import org.bukkit.craftbukkit.entity.CraftEntity;
|
|
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
|
+import org.bukkit.craftbukkit.event.CraftEventFactory;
|
|
+import org.bukkit.craftbukkit.event.CraftPortalEvent;
|
|
+import org.bukkit.craftbukkit.util.CraftLocation;
|
|
+import org.bukkit.entity.Pose;
|
|
+import org.bukkit.event.entity.EntityAirChangeEvent;
|
|
+import org.bukkit.event.entity.EntityCombustEvent;
|
|
+import org.bukkit.event.entity.EntityDismountEvent;
|
|
+import org.bukkit.event.entity.EntityDropItemEvent;
|
|
+import org.bukkit.event.entity.EntityMountEvent;
|
|
+import org.bukkit.event.entity.EntityPortalEvent;
|
|
+import org.bukkit.event.entity.EntityPoseChangeEvent;
|
|
+import org.bukkit.event.entity.EntityRemoveEvent;
|
|
+import org.bukkit.event.entity.EntityTeleportEvent;
|
|
+import org.bukkit.event.entity.EntityUnleashEvent;
|
|
+import org.bukkit.event.entity.EntityUnleashEvent.UnleashReason;
|
|
+import org.bukkit.event.player.PlayerTeleportEvent;
|
|
+import org.bukkit.plugin.PluginManager;
|
|
+// CraftBukkit end
|
|
|
|
public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess, ScoreHolder {
|
|
+
|
|
+ // CraftBukkit start
|
|
+ private static final int CURRENT_LEVEL = 2;
|
|
+ public boolean preserveMotion = true; // Paper - Fix Entity Teleportation and cancel velocity if teleported; keep initial motion on first setPositionRotation
|
|
+ static boolean isLevelAtLeast(CompoundTag tag, int level) {
|
|
+ return tag.contains("Bukkit.updateLevel") && tag.getInt("Bukkit.updateLevel") >= level;
|
|
+ }
|
|
+
|
|
+ // Paper start - Share random for entities to make them more random
|
|
+ public static RandomSource SHARED_RANDOM = new RandomRandomSource();
|
|
+ private static final class RandomRandomSource extends java.util.Random implements net.minecraft.world.level.levelgen.BitRandomSource {
|
|
+ private boolean locked = false;
|
|
+
|
|
+ @Override
|
|
+ public synchronized void setSeed(long seed) {
|
|
+ if (locked) {
|
|
+ LOGGER.error("Ignoring setSeed on Entity.SHARED_RANDOM", new Throwable());
|
|
+ } else {
|
|
+ super.setSeed(seed);
|
|
+ locked = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public RandomSource fork() {
|
|
+ return new net.minecraft.world.level.levelgen.LegacyRandomSource(this.nextLong());
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public net.minecraft.world.level.levelgen.PositionalRandomFactory forkPositional() {
|
|
+ return new net.minecraft.world.level.levelgen.LegacyRandomSource.LegacyPositionalRandomFactory(this.nextLong());
|
|
+ }
|
|
+
|
|
+ // these below are added to fix reobf issues that I don't wanna deal with right now
|
|
+ @Override
|
|
+ public int next(int bits) {
|
|
+ return super.next(bits);
|
|
+ }
|
|
|
|
+ @Override
|
|
+ public int nextInt(int origin, int bound) {
|
|
+ return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(origin, bound);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public long nextLong() {
|
|
+ return net.minecraft.world.level.levelgen.BitRandomSource.super.nextLong();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int nextInt() {
|
|
+ return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public int nextInt(int bound) {
|
|
+ return net.minecraft.world.level.levelgen.BitRandomSource.super.nextInt(bound);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean nextBoolean() {
|
|
+ return net.minecraft.world.level.levelgen.BitRandomSource.super.nextBoolean();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public float nextFloat() {
|
|
+ return net.minecraft.world.level.levelgen.BitRandomSource.super.nextFloat();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double nextDouble() {
|
|
+ return net.minecraft.world.level.levelgen.BitRandomSource.super.nextDouble();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public double nextGaussian() {
|
|
+ return super.nextGaussian();
|
|
+ }
|
|
+ }
|
|
+ // Paper end - Share random for entities to make them more random
|
|
+ public org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
|
|
+
|
|
+ private CraftEntity bukkitEntity;
|
|
+
|
|
+ public CraftEntity getBukkitEntity() {
|
|
+ if (this.bukkitEntity == null) {
|
|
+ // Paper start - Folia schedulers
|
|
+ synchronized (this) {
|
|
+ if (this.bukkitEntity == null) {
|
|
+ return this.bukkitEntity = CraftEntity.getEntity(this.level.getCraftServer(), this);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - Folia schedulers
|
|
+ }
|
|
+ return this.bukkitEntity;
|
|
+ }
|
|
+ // Paper start
|
|
+ public CraftEntity getBukkitEntityRaw() {
|
|
+ return this.bukkitEntity;
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
+ // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
+ public int getDefaultMaxAirSupply() {
|
|
+ return Entity.TOTAL_AIR_SUPPLY;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final String ID_TAG = "id";
|
|
public static final String PASSENGERS_TAG = "Passengers";
|
|
@@ -224,7 +368,7 @@
|
|
private static final EntityDataAccessor<Boolean> DATA_CUSTOM_NAME_VISIBLE = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.BOOLEAN);
|
|
private static final EntityDataAccessor<Boolean> DATA_SILENT = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.BOOLEAN);
|
|
private static final EntityDataAccessor<Boolean> DATA_NO_GRAVITY = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.BOOLEAN);
|
|
- protected static final EntityDataAccessor<Pose> DATA_POSE = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.POSE);
|
|
+ protected static final EntityDataAccessor<net.minecraft.world.entity.Pose> DATA_POSE = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.POSE);
|
|
private static final EntityDataAccessor<Integer> DATA_TICKS_FROZEN = SynchedEntityData.defineId(Entity.class, EntityDataSerializers.INT);
|
|
private EntityInLevelCallback levelCallback;
|
|
private final VecDeltaCodec packetPositionCodec;
|
|
@@ -253,7 +397,68 @@
|
|
private final List<Entity.Movement> movementThisTick;
|
|
private final Set<BlockState> blocksInside;
|
|
private final LongSet visitedBlocks;
|
|
+ // CraftBukkit start
|
|
+ public boolean forceDrops;
|
|
+ public boolean persist = true;
|
|
+ public boolean visibleByDefault = true;
|
|
+ public boolean valid;
|
|
+ public boolean inWorld = false;
|
|
+ public boolean generation;
|
|
+ public int maxAirTicks = this.getDefaultMaxAirSupply(); // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
+ @Nullable // Paper - Refresh ProjectileSource for projectiles
|
|
+ public org.bukkit.projectiles.ProjectileSource projectileSource; // For projectiles only
|
|
+ public boolean lastDamageCancelled; // SPIGOT-5339, SPIGOT-6252, SPIGOT-6777: Keep track if the event was canceled
|
|
+ public boolean persistentInvisibility = false;
|
|
+ public BlockPos lastLavaContact;
|
|
+ // Marks an entity, that it was removed by a plugin via Entity#remove
|
|
+ // Main use case currently is for SPIGOT-7487, preventing dropping of leash when leash is removed
|
|
+ public boolean pluginRemoved = false;
|
|
+ // Spigot start
|
|
+ public final org.spigotmc.ActivationRange.ActivationType activationType = org.spigotmc.ActivationRange.initializeEntityActivationType(this);
|
|
+ public final boolean defaultActivationState;
|
|
+ public long activatedTick = Integer.MIN_VALUE;
|
|
+ public void inactiveTick() { }
|
|
+ // Spigot end
|
|
+ protected int numCollisions = 0; // Paper - Cap entity collisions
|
|
+ public boolean fromNetherPortal; // Paper - Add option to nerf pigmen from nether portals
|
|
+ public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one
|
|
+ // Paper start - Entity origin API
|
|
+ @javax.annotation.Nullable
|
|
+ private org.bukkit.util.Vector origin;
|
|
+ @javax.annotation.Nullable
|
|
+ private UUID originWorld;
|
|
+ public boolean freezeLocked = false; // Paper - Freeze Tick Lock API
|
|
+ public boolean fixedPose = false; // Paper - Expand Pose API
|
|
|
|
+ public void setOrigin(@javax.annotation.Nonnull Location location) {
|
|
+ this.origin = location.toVector();
|
|
+ this.originWorld = location.getWorld().getUID();
|
|
+ }
|
|
+
|
|
+ @javax.annotation.Nullable
|
|
+ public org.bukkit.util.Vector getOriginVector() {
|
|
+ return this.origin != null ? this.origin.clone() : null;
|
|
+ }
|
|
+
|
|
+ @javax.annotation.Nullable
|
|
+ public UUID getOriginWorld() {
|
|
+ return this.originWorld;
|
|
+ }
|
|
+ // Paper end - Entity origin API
|
|
+ public float getBukkitYaw() {
|
|
+ return this.yRot;
|
|
+ }
|
|
+
|
|
+ public boolean isChunkLoaded() {
|
|
+ return this.level.hasChunk((int) Math.floor(this.getX()) >> 4, (int) Math.floor(this.getZ()) >> 4);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ // Paper start
|
|
+ public final AABB getBoundingBoxAt(double x, double y, double z) {
|
|
+ return this.dimensions.makeBoundingBox(x, y, z);
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
public Entity(EntityType<?> type, Level world) {
|
|
this.id = Entity.ENTITY_COUNTER.incrementAndGet();
|
|
this.passengers = ImmutableList.of();
|
|
@@ -261,7 +466,7 @@
|
|
this.bb = Entity.INITIAL_AABB;
|
|
this.stuckSpeedMultiplier = Vec3.ZERO;
|
|
this.nextStep = 1.0F;
|
|
- this.random = RandomSource.create();
|
|
+ this.random = SHARED_RANDOM; // Paper - Share random for entities to make them more random
|
|
this.remainingFireTicks = -this.getFireImmuneTicks();
|
|
this.fluidHeight = new Object2DoubleArrayMap(2);
|
|
this.fluidOnEyes = new HashSet();
|
|
@@ -284,6 +489,13 @@
|
|
this.position = Vec3.ZERO;
|
|
this.blockPosition = BlockPos.ZERO;
|
|
this.chunkPosition = ChunkPos.ZERO;
|
|
+ // Spigot start
|
|
+ if (world != null) {
|
|
+ this.defaultActivationState = org.spigotmc.ActivationRange.initializeEntityActivationState(this, world.spigotConfig);
|
|
+ } else {
|
|
+ this.defaultActivationState = false;
|
|
+ }
|
|
+ // Spigot end
|
|
SynchedEntityData.Builder datawatcher_a = new SynchedEntityData.Builder(this);
|
|
|
|
datawatcher_a.define(Entity.DATA_SHARED_FLAGS_ID, (byte) 0);
|
|
@@ -292,7 +504,7 @@
|
|
datawatcher_a.define(Entity.DATA_CUSTOM_NAME, Optional.empty());
|
|
datawatcher_a.define(Entity.DATA_SILENT, false);
|
|
datawatcher_a.define(Entity.DATA_NO_GRAVITY, false);
|
|
- datawatcher_a.define(Entity.DATA_POSE, Pose.STANDING);
|
|
+ datawatcher_a.define(Entity.DATA_POSE, net.minecraft.world.entity.Pose.STANDING);
|
|
datawatcher_a.define(Entity.DATA_TICKS_FROZEN, 0);
|
|
this.defineSynchedData(datawatcher_a);
|
|
this.entityData = datawatcher_a.build();
|
|
@@ -362,20 +574,36 @@
|
|
}
|
|
|
|
public void kill(ServerLevel world) {
|
|
- this.remove(Entity.RemovalReason.KILLED);
|
|
+ this.remove(Entity.RemovalReason.KILLED, EntityRemoveEvent.Cause.DEATH); // CraftBukkit - add Bukkit remove cause
|
|
this.gameEvent(GameEvent.ENTITY_DIE);
|
|
}
|
|
|
|
public final void discard() {
|
|
- this.remove(Entity.RemovalReason.DISCARDED);
|
|
+ // CraftBukkit start - add Bukkit remove cause
|
|
+ this.discard(null);
|
|
}
|
|
|
|
+ public final void discard(EntityRemoveEvent.Cause cause) {
|
|
+ this.remove(Entity.RemovalReason.DISCARDED, cause);
|
|
+ // CraftBukkit end
|
|
+ }
|
|
+
|
|
protected abstract void defineSynchedData(SynchedEntityData.Builder builder);
|
|
|
|
public SynchedEntityData getEntityData() {
|
|
return this.entityData;
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ public void refreshEntityData(ServerPlayer to) {
|
|
+ List<SynchedEntityData.DataValue<?>> list = this.getEntityData().getNonDefaultValues();
|
|
+
|
|
+ if (list != null) {
|
|
+ to.connection.send(new ClientboundSetEntityDataPacket(this.getId(), list));
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public boolean equals(Object object) {
|
|
return object instanceof Entity ? ((Entity) object).id == this.id : false;
|
|
}
|
|
@@ -385,22 +613,39 @@
|
|
}
|
|
|
|
public void remove(Entity.RemovalReason reason) {
|
|
- this.setRemoved(reason);
|
|
+ // CraftBukkit start - add Bukkit remove cause
|
|
+ this.setRemoved(reason, null);
|
|
}
|
|
|
|
+ public void remove(Entity.RemovalReason entity_removalreason, EntityRemoveEvent.Cause cause) {
|
|
+ this.setRemoved(entity_removalreason, cause);
|
|
+ // CraftBukkit end
|
|
+ }
|
|
+
|
|
public void onClientRemoval() {}
|
|
|
|
public void onRemoval(Entity.RemovalReason reason) {}
|
|
|
|
- public void setPose(Pose pose) {
|
|
+ public void setPose(net.minecraft.world.entity.Pose pose) {
|
|
+ if (this.fixedPose) return; // Paper - Expand Pose API
|
|
+ // CraftBukkit start
|
|
+ if (pose == this.getPose()) {
|
|
+ return;
|
|
+ }
|
|
+ // Paper start - Don't fire sync event during generation
|
|
+ if (!this.generation) {
|
|
+ this.level.getCraftServer().getPluginManager().callEvent(new EntityPoseChangeEvent(this.getBukkitEntity(), Pose.values()[pose.ordinal()]));
|
|
+ }
|
|
+ // Paper end - Don't fire sync event during generation
|
|
+ // CraftBukkit end
|
|
this.entityData.set(Entity.DATA_POSE, pose);
|
|
}
|
|
|
|
- public Pose getPose() {
|
|
- return (Pose) this.entityData.get(Entity.DATA_POSE);
|
|
+ public net.minecraft.world.entity.Pose getPose() {
|
|
+ return (net.minecraft.world.entity.Pose) this.entityData.get(Entity.DATA_POSE);
|
|
}
|
|
|
|
- public boolean hasPose(Pose pose) {
|
|
+ public boolean hasPose(net.minecraft.world.entity.Pose pose) {
|
|
return this.getPose() == pose;
|
|
}
|
|
|
|
@@ -417,6 +662,33 @@
|
|
}
|
|
|
|
public void setRot(float yaw, float pitch) {
|
|
+ // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0
|
|
+ if (Float.isNaN(yaw)) {
|
|
+ yaw = 0;
|
|
+ }
|
|
+
|
|
+ if (yaw == Float.POSITIVE_INFINITY || yaw == Float.NEGATIVE_INFINITY) {
|
|
+ if (this instanceof ServerPlayer) {
|
|
+ this.level.getCraftServer().getLogger().warning(this.getScoreboardName() + " was caught trying to crash the server with an invalid yaw");
|
|
+ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite yaw (Hacking?)");
|
|
+ }
|
|
+ yaw = 0;
|
|
+ }
|
|
+
|
|
+ // pitch was sometimes set to NaN, so we need to set it back to 0
|
|
+ if (Float.isNaN(pitch)) {
|
|
+ pitch = 0;
|
|
+ }
|
|
+
|
|
+ if (pitch == Float.POSITIVE_INFINITY || pitch == Float.NEGATIVE_INFINITY) {
|
|
+ if (this instanceof ServerPlayer) {
|
|
+ this.level.getCraftServer().getLogger().warning(this.getScoreboardName() + " was caught trying to crash the server with an invalid pitch");
|
|
+ ((CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite pitch (Hacking?)");
|
|
+ }
|
|
+ pitch = 0;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
this.setYRot(yaw % 360.0F);
|
|
this.setXRot(pitch % 360.0F);
|
|
}
|
|
@@ -426,8 +698,8 @@
|
|
}
|
|
|
|
public void setPos(double x, double y, double z) {
|
|
- this.setPosRaw(x, y, z);
|
|
- this.setBoundingBox(this.makeBoundingBox());
|
|
+ this.setPosRaw(x, y, z, true); // Paper - Block invalid positions and bounding box; force update
|
|
+ // this.setBoundingBox(this.makeBoundingBox()); // Paper - Block invalid positions and bounding box; move into setPosRaw
|
|
}
|
|
|
|
protected final AABB makeBoundingBox() {
|
|
@@ -460,12 +732,22 @@
|
|
|
|
public void tick() {
|
|
this.baseTick();
|
|
+ }
|
|
+
|
|
+ // CraftBukkit start
|
|
+ public void postTick() {
|
|
+ // No clean way to break out of ticking once the entity has been copied to a new world, so instead we move the portalling later in the tick cycle
|
|
+ if (!(this instanceof ServerPlayer) && this.isAlive()) { // Paper - don't attempt to teleport dead entities
|
|
+ this.handlePortal();
|
|
+ }
|
|
}
|
|
+ // CraftBukkit end
|
|
|
|
public void baseTick() {
|
|
ProfilerFiller gameprofilerfiller = Profiler.get();
|
|
|
|
gameprofilerfiller.push("entityBaseTick");
|
|
+ if (firstTick && this instanceof net.minecraft.world.entity.NeutralMob neutralMob) neutralMob.tickInitialPersistentAnger(level); // Paper - Prevent entity loading causing async lookups
|
|
this.inBlockState = null;
|
|
if (this.isPassenger() && this.getVehicle().isRemoved()) {
|
|
this.stopRiding();
|
|
@@ -475,7 +757,7 @@
|
|
--this.boardingCooldown;
|
|
}
|
|
|
|
- this.handlePortal();
|
|
+ if (this instanceof ServerPlayer) this.handlePortal(); // CraftBukkit - // Moved up to postTick
|
|
if (this.canSpawnSprintParticle()) {
|
|
this.spawnSprintParticle();
|
|
}
|
|
@@ -502,7 +784,7 @@
|
|
this.setRemainingFireTicks(this.remainingFireTicks - 1);
|
|
}
|
|
|
|
- if (this.getTicksFrozen() > 0) {
|
|
+ if (this.getTicksFrozen() > 0 && !freezeLocked) { // Paper - Freeze Tick Lock API
|
|
this.setTicksFrozen(0);
|
|
this.level().levelEvent((Player) null, 1009, this.blockPosition, 1);
|
|
}
|
|
@@ -514,6 +796,10 @@
|
|
if (this.isInLava()) {
|
|
this.lavaHurt();
|
|
this.fallDistance *= 0.5F;
|
|
+ // CraftBukkit start
|
|
+ } else {
|
|
+ this.lastLavaContact = null;
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
this.checkBelowWorld();
|
|
@@ -525,7 +811,7 @@
|
|
world = this.level();
|
|
if (world instanceof ServerLevel worldserver) {
|
|
if (this instanceof Leashable) {
|
|
- Leashable.tickLeash(worldserver, (Entity) ((Leashable) this));
|
|
+ Leashable.tickLeash(worldserver, (Entity & Leashable) this); // CraftBukkit - decompile error
|
|
}
|
|
}
|
|
|
|
@@ -537,7 +823,11 @@
|
|
}
|
|
|
|
public void checkBelowWorld() {
|
|
- if (this.getY() < (double) (this.level().getMinY() - 64)) {
|
|
+ // Paper start - Configurable nether ceiling damage
|
|
+ if (this.getY() < (double) (this.level.getMinY() - 64) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER
|
|
+ && this.level.paperConfig().environment.netherCeilingVoidDamageHeight.test(v -> this.getY() >= v)
|
|
+ && (!(this instanceof Player player) || !player.getAbilities().invulnerable))) {
|
|
+ // Paper end - Configurable nether ceiling damage
|
|
this.onBelowWorld();
|
|
}
|
|
|
|
@@ -568,15 +858,32 @@
|
|
|
|
public void lavaHurt() {
|
|
if (!this.fireImmune()) {
|
|
- this.igniteForSeconds(15.0F);
|
|
+ // CraftBukkit start - Fallen in lava TODO: this event spams!
|
|
+ if (this instanceof net.minecraft.world.entity.LivingEntity && this.remainingFireTicks <= 0) {
|
|
+ // not on fire yet
|
|
+ org.bukkit.block.Block damager = (this.lastLavaContact == null) ? null : org.bukkit.craftbukkit.block.CraftBlock.at(this.level, this.lastLavaContact);
|
|
+ org.bukkit.entity.Entity damagee = this.getBukkitEntity();
|
|
+ EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15);
|
|
+ this.level.getCraftServer().getPluginManager().callEvent(combustEvent);
|
|
+
|
|
+ if (!combustEvent.isCancelled()) {
|
|
+ this.igniteForSeconds(combustEvent.getDuration(), false);
|
|
+ }
|
|
+ } else {
|
|
+ // This will be called every single tick the entity is in lava, so don't throw an event
|
|
+ this.igniteForSeconds(15.0F, false);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
Level world = this.level();
|
|
|
|
if (world instanceof ServerLevel) {
|
|
ServerLevel worldserver = (ServerLevel) world;
|
|
|
|
- if (this.hurtServer(worldserver, this.damageSources().lava(), 4.0F) && this.shouldPlayLavaHurtSound() && !this.isSilent()) {
|
|
+ // CraftBukkit start
|
|
+ if (this.hurtServer(worldserver, this.damageSources().lava().directBlock(this.level, this.lastLavaContact), 4.0F) && this.shouldPlayLavaHurtSound() && !this.isSilent()) {
|
|
worldserver.playSound((Player) null, this.getX(), this.getY(), this.getZ(), SoundEvents.GENERIC_BURN, this.getSoundSource(), 0.4F, 2.0F + this.random.nextFloat() * 0.4F);
|
|
}
|
|
+ // CraftBukkit end - we also don't throw an event unless the object in lava is living, to save on some event calls
|
|
}
|
|
|
|
}
|
|
@@ -587,9 +894,25 @@
|
|
}
|
|
|
|
public final void igniteForSeconds(float seconds) {
|
|
- this.igniteForTicks(Mth.floor(seconds * 20.0F));
|
|
+ // CraftBukkit start
|
|
+ this.igniteForSeconds(seconds, true);
|
|
}
|
|
|
|
+ public final void igniteForSeconds(float f, boolean callEvent) {
|
|
+ if (callEvent) {
|
|
+ EntityCombustEvent event = new EntityCombustEvent(this.getBukkitEntity(), f);
|
|
+ this.level.getCraftServer().getPluginManager().callEvent(event);
|
|
+
|
|
+ if (event.isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ f = event.getDuration();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ this.igniteForTicks(Mth.floor(f * 20.0F));
|
|
+ }
|
|
+
|
|
public void igniteForTicks(int ticks) {
|
|
if (this.remainingFireTicks < ticks) {
|
|
this.setRemainingFireTicks(ticks);
|
|
@@ -610,7 +933,7 @@
|
|
}
|
|
|
|
protected void onBelowWorld() {
|
|
- this.discard();
|
|
+ this.discard(EntityRemoveEvent.Cause.OUT_OF_WORLD); // CraftBukkit - add Bukkit remove cause
|
|
}
|
|
|
|
public boolean isFree(double offsetX, double offsetY, double offsetZ) {
|
|
@@ -672,6 +995,7 @@
|
|
}
|
|
|
|
public void move(MoverType type, Vec3 movement) {
|
|
+ final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity
|
|
if (this.noPhysics) {
|
|
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
|
|
} else {
|
|
@@ -747,8 +1071,30 @@
|
|
|
|
if (movement.y != vec3d1.y) {
|
|
block.updateEntityMovementAfterFallOn(this.level(), this);
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // CraftBukkit start
|
|
+ if (this.horizontalCollision && this.getBukkitEntity() instanceof Vehicle) {
|
|
+ Vehicle vehicle = (Vehicle) this.getBukkitEntity();
|
|
+ org.bukkit.block.Block bl = this.level.getWorld().getBlockAt(Mth.floor(this.getX()), Mth.floor(this.getY()), Mth.floor(this.getZ()));
|
|
+
|
|
+ if (movement.x > vec3d1.x) {
|
|
+ bl = bl.getRelative(BlockFace.EAST);
|
|
+ } else if (movement.x < vec3d1.x) {
|
|
+ bl = bl.getRelative(BlockFace.WEST);
|
|
+ } else if (movement.z > vec3d1.z) {
|
|
+ bl = bl.getRelative(BlockFace.SOUTH);
|
|
+ } else if (movement.z < vec3d1.z) {
|
|
+ bl = bl.getRelative(BlockFace.NORTH);
|
|
}
|
|
+
|
|
+ if (!bl.getType().isAir()) {
|
|
+ VehicleBlockCollisionEvent event = new VehicleBlockCollisionEvent(vehicle, bl, org.bukkit.craftbukkit.util.CraftVector.toBukkit(originalMovement)); // Paper - Expose pre-collision velocity
|
|
+ this.level.getCraftServer().getPluginManager().callEvent(event);
|
|
+ }
|
|
}
|
|
+ // CraftBukkit end
|
|
|
|
if (!this.level().isClientSide() || this.isControlledByLocalInstance()) {
|
|
Entity.MovementEmission entity_movementemission = this.getMovementEmission();
|
|
@@ -913,7 +1259,7 @@
|
|
}
|
|
|
|
protected BlockPos getOnPos(float offset) {
|
|
- if (this.mainSupportingBlockPos.isPresent()) {
|
|
+ if (this.mainSupportingBlockPos.isPresent() && this.level().getChunkIfLoadedImmediately(this.mainSupportingBlockPos.get()) != null) { // Paper - ensure no loads
|
|
BlockPos blockposition = (BlockPos) this.mainSupportingBlockPos.get();
|
|
|
|
if (offset <= 1.0E-5F) {
|
|
@@ -1133,6 +1479,20 @@
|
|
return SoundEvents.GENERIC_SPLASH;
|
|
}
|
|
|
|
+ // CraftBukkit start - Add delegate methods
|
|
+ public SoundEvent getSwimSound0() {
|
|
+ return this.getSwimSound();
|
|
+ }
|
|
+
|
|
+ public SoundEvent getSwimSplashSound0() {
|
|
+ return this.getSwimSplashSound();
|
|
+ }
|
|
+
|
|
+ public SoundEvent getSwimHighSpeedSplashSound0() {
|
|
+ return this.getSwimHighSpeedSplashSound();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public void recordMovementThroughBlocks(Vec3 oldPos, Vec3 newPos) {
|
|
this.movementThisTick.add(new Entity.Movement(oldPos, newPos));
|
|
}
|
|
@@ -1599,6 +1959,7 @@
|
|
this.setXRot(Mth.clamp(pitch, -90.0F, 90.0F) % 360.0F);
|
|
this.yRotO = this.getYRot();
|
|
this.xRotO = this.getXRot();
|
|
+ this.setYHeadRot(yaw); // Paper - Update head rotation
|
|
}
|
|
|
|
public void absMoveTo(double x, double y, double z) {
|
|
@@ -1609,6 +1970,7 @@
|
|
this.yo = y;
|
|
this.zo = d4;
|
|
this.setPos(d3, y, d4);
|
|
+ if (this.valid) this.level.getChunk((int) Math.floor(this.getX()) >> 4, (int) Math.floor(this.getZ()) >> 4); // CraftBukkit
|
|
}
|
|
|
|
public void moveTo(Vec3 pos) {
|
|
@@ -1628,11 +1990,19 @@
|
|
}
|
|
|
|
public void moveTo(double x, double y, double z, float yaw, float pitch) {
|
|
+ // Paper start - Fix Entity Teleportation and cancel velocity if teleported
|
|
+ if (!preserveMotion) {
|
|
+ this.deltaMovement = Vec3.ZERO;
|
|
+ } else {
|
|
+ this.preserveMotion = false;
|
|
+ }
|
|
+ // Paper end - Fix Entity Teleportation and cancel velocity if teleported
|
|
this.setPosRaw(x, y, z);
|
|
this.setYRot(yaw);
|
|
this.setXRot(pitch);
|
|
this.setOldPosAndRot();
|
|
this.reapplyPosition();
|
|
+ this.setYHeadRot(yaw); // Paper - Update head rotation
|
|
}
|
|
|
|
public final void setOldPosAndRot() {
|
|
@@ -1701,6 +2071,7 @@
|
|
public void push(Entity entity) {
|
|
if (!this.isPassengerOfSameVehicle(entity)) {
|
|
if (!entity.noPhysics && !this.noPhysics) {
|
|
+ if (this.level.paperConfig().collisions.onlyPlayersCollide && !(entity instanceof ServerPlayer || this instanceof ServerPlayer)) return; // Paper - Collision option for requiring a player participant
|
|
double d0 = entity.getX() - this.getX();
|
|
double d1 = entity.getZ() - this.getZ();
|
|
double d2 = Mth.absMax(d0, d1);
|
|
@@ -1737,7 +2108,21 @@
|
|
}
|
|
|
|
public void push(double deltaX, double deltaY, double deltaZ) {
|
|
- this.setDeltaMovement(this.getDeltaMovement().add(deltaX, deltaY, deltaZ));
|
|
+ // Paper start - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent
|
|
+ this.push(deltaX, deltaY, deltaZ, null);
|
|
+ }
|
|
+
|
|
+ public void push(double deltaX, double deltaY, double deltaZ, @Nullable Entity pushingEntity) {
|
|
+ org.bukkit.util.Vector delta = new org.bukkit.util.Vector(deltaX, deltaY, deltaZ);
|
|
+ if (pushingEntity != null) {
|
|
+ io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent event = new io.papermc.paper.event.entity.EntityPushedByEntityAttackEvent(this.getBukkitEntity(), io.papermc.paper.event.entity.EntityKnockbackEvent.Cause.PUSH, pushingEntity.getBukkitEntity(), delta);
|
|
+ if (!event.callEvent()) {
|
|
+ return;
|
|
+ }
|
|
+ delta = event.getKnockback();
|
|
+ }
|
|
+ this.setDeltaMovement(this.getDeltaMovement().add(delta.getX(), delta.getY(), delta.getZ()));
|
|
+ // Paper end - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent
|
|
this.hasImpulse = true;
|
|
}
|
|
|
|
@@ -1858,9 +2243,21 @@
|
|
}
|
|
|
|
public boolean isPushable() {
|
|
+ // Paper start - Climbing should not bypass cramming gamerule
|
|
+ return isCollidable(false);
|
|
+ }
|
|
+
|
|
+ public boolean isCollidable(boolean ignoreClimbing) {
|
|
+ // Paper end - Climbing should not bypass cramming gamerule
|
|
return false;
|
|
}
|
|
|
|
+ // CraftBukkit start - collidable API
|
|
+ public boolean canCollideWithBukkit(Entity entity) {
|
|
+ return this.isPushable();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public void awardKillScore(Entity entityKilled, DamageSource damageSource) {
|
|
if (entityKilled instanceof ServerPlayer) {
|
|
CriteriaTriggers.ENTITY_KILLED_PLAYER.trigger((ServerPlayer) entityKilled, this, damageSource);
|
|
@@ -1889,74 +2286,133 @@
|
|
}
|
|
|
|
public boolean saveAsPassenger(CompoundTag nbt) {
|
|
+ // CraftBukkit start - allow excluding certain data when saving
|
|
+ return this.saveAsPassenger(nbt, true);
|
|
+ }
|
|
+
|
|
+ public boolean saveAsPassenger(CompoundTag nbttagcompound, boolean includeAll) {
|
|
+ // CraftBukkit end
|
|
if (this.removalReason != null && !this.removalReason.shouldSave()) {
|
|
return false;
|
|
} else {
|
|
String s = this.getEncodeId();
|
|
|
|
- if (s == null) {
|
|
+ if (!this.persist || s == null) { // CraftBukkit - persist flag
|
|
return false;
|
|
} else {
|
|
- nbt.putString("id", s);
|
|
- this.saveWithoutId(nbt);
|
|
+ nbttagcompound.putString("id", s);
|
|
+ this.saveWithoutId(nbttagcompound, includeAll); // CraftBukkit - pass on includeAll
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
+ // Paper start - Entity serialization api
|
|
+ public boolean serializeEntity(CompoundTag compound) {
|
|
+ List<Entity> pass = new java.util.ArrayList<>(this.getPassengers());
|
|
+ this.passengers = ImmutableList.of();
|
|
+ boolean result = save(compound);
|
|
+ this.passengers = ImmutableList.copyOf(pass);
|
|
+ return result;
|
|
+ }
|
|
+ // Paper end - Entity serialization api
|
|
public boolean save(CompoundTag nbt) {
|
|
return this.isPassenger() ? false : this.saveAsPassenger(nbt);
|
|
}
|
|
|
|
public CompoundTag saveWithoutId(CompoundTag nbt) {
|
|
+ // CraftBukkit start - allow excluding certain data when saving
|
|
+ return this.saveWithoutId(nbt, true);
|
|
+ }
|
|
+
|
|
+ public CompoundTag saveWithoutId(CompoundTag nbttagcompound, boolean includeAll) {
|
|
+ // CraftBukkit end
|
|
try {
|
|
- if (this.vehicle != null) {
|
|
- nbt.put("Pos", this.newDoubleList(this.vehicle.getX(), this.getY(), this.vehicle.getZ()));
|
|
- } else {
|
|
- nbt.put("Pos", this.newDoubleList(this.getX(), this.getY(), this.getZ()));
|
|
+ // CraftBukkit start - selectively save position
|
|
+ if (includeAll) {
|
|
+ if (this.vehicle != null) {
|
|
+ nbttagcompound.put("Pos", this.newDoubleList(this.vehicle.getX(), this.getY(), this.vehicle.getZ()));
|
|
+ } else {
|
|
+ nbttagcompound.put("Pos", this.newDoubleList(this.getX(), this.getY(), this.getZ()));
|
|
+ }
|
|
}
|
|
+ // CraftBukkit end
|
|
|
|
Vec3 vec3d = this.getDeltaMovement();
|
|
|
|
- nbt.put("Motion", this.newDoubleList(vec3d.x, vec3d.y, vec3d.z));
|
|
- nbt.put("Rotation", this.newFloatList(this.getYRot(), this.getXRot()));
|
|
- nbt.putFloat("FallDistance", this.fallDistance);
|
|
- nbt.putShort("Fire", (short) this.remainingFireTicks);
|
|
- nbt.putShort("Air", (short) this.getAirSupply());
|
|
- nbt.putBoolean("OnGround", this.onGround());
|
|
- nbt.putBoolean("Invulnerable", this.invulnerable);
|
|
- nbt.putInt("PortalCooldown", this.portalCooldown);
|
|
- nbt.putUUID("UUID", this.getUUID());
|
|
+ nbttagcompound.put("Motion", this.newDoubleList(vec3d.x, vec3d.y, vec3d.z));
|
|
+
|
|
+ // CraftBukkit start - Checking for NaN pitch/yaw and resetting to zero
|
|
+ // TODO: make sure this is the best way to address this.
|
|
+ if (Float.isNaN(this.yRot)) {
|
|
+ this.yRot = 0;
|
|
+ }
|
|
+
|
|
+ if (Float.isNaN(this.xRot)) {
|
|
+ this.xRot = 0;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
+ nbttagcompound.put("Rotation", this.newFloatList(this.getYRot(), this.getXRot()));
|
|
+ nbttagcompound.putFloat("FallDistance", this.fallDistance);
|
|
+ nbttagcompound.putShort("Fire", (short) this.remainingFireTicks);
|
|
+ nbttagcompound.putShort("Air", (short) this.getAirSupply());
|
|
+ nbttagcompound.putBoolean("OnGround", this.onGround());
|
|
+ nbttagcompound.putBoolean("Invulnerable", this.invulnerable);
|
|
+ nbttagcompound.putInt("PortalCooldown", this.portalCooldown);
|
|
+ // CraftBukkit start - selectively save uuid and world
|
|
+ if (includeAll) {
|
|
+ nbttagcompound.putUUID("UUID", this.getUUID());
|
|
+ // PAIL: Check above UUID reads 1.8 properly, ie: UUIDMost / UUIDLeast
|
|
+ nbttagcompound.putLong("WorldUUIDLeast", ((ServerLevel) this.level).getWorld().getUID().getLeastSignificantBits());
|
|
+ nbttagcompound.putLong("WorldUUIDMost", ((ServerLevel) this.level).getWorld().getUID().getMostSignificantBits());
|
|
+ }
|
|
+ nbttagcompound.putInt("Bukkit.updateLevel", Entity.CURRENT_LEVEL);
|
|
+ if (!this.persist) {
|
|
+ nbttagcompound.putBoolean("Bukkit.persist", this.persist);
|
|
+ }
|
|
+ if (!this.visibleByDefault) {
|
|
+ nbttagcompound.putBoolean("Bukkit.visibleByDefault", this.visibleByDefault);
|
|
+ }
|
|
+ if (this.persistentInvisibility) {
|
|
+ nbttagcompound.putBoolean("Bukkit.invisible", this.persistentInvisibility);
|
|
+ }
|
|
+ // SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
+ if (this.maxAirTicks != this.getDefaultMaxAirSupply()) {
|
|
+ nbttagcompound.putInt("Bukkit.MaxAirSupply", this.getMaxAirSupply());
|
|
+ }
|
|
+ nbttagcompound.putInt("Spigot.ticksLived", this.tickCount);
|
|
+ // CraftBukkit end
|
|
Component ichatbasecomponent = this.getCustomName();
|
|
|
|
if (ichatbasecomponent != null) {
|
|
- nbt.putString("CustomName", Component.Serializer.toJson(ichatbasecomponent, this.registryAccess()));
|
|
+ nbttagcompound.putString("CustomName", Component.Serializer.toJson(ichatbasecomponent, this.registryAccess()));
|
|
}
|
|
|
|
if (this.isCustomNameVisible()) {
|
|
- nbt.putBoolean("CustomNameVisible", this.isCustomNameVisible());
|
|
+ nbttagcompound.putBoolean("CustomNameVisible", this.isCustomNameVisible());
|
|
}
|
|
|
|
if (this.isSilent()) {
|
|
- nbt.putBoolean("Silent", this.isSilent());
|
|
+ nbttagcompound.putBoolean("Silent", this.isSilent());
|
|
}
|
|
|
|
if (this.isNoGravity()) {
|
|
- nbt.putBoolean("NoGravity", this.isNoGravity());
|
|
+ nbttagcompound.putBoolean("NoGravity", this.isNoGravity());
|
|
}
|
|
|
|
if (this.hasGlowingTag) {
|
|
- nbt.putBoolean("Glowing", true);
|
|
+ nbttagcompound.putBoolean("Glowing", true);
|
|
}
|
|
|
|
int i = this.getTicksFrozen();
|
|
|
|
if (i > 0) {
|
|
- nbt.putInt("TicksFrozen", this.getTicksFrozen());
|
|
+ nbttagcompound.putInt("TicksFrozen", this.getTicksFrozen());
|
|
}
|
|
|
|
if (this.hasVisualFire) {
|
|
- nbt.putBoolean("HasVisualFire", this.hasVisualFire);
|
|
+ nbttagcompound.putBoolean("HasVisualFire", this.hasVisualFire);
|
|
}
|
|
|
|
ListTag nbttaglist;
|
|
@@ -1972,10 +2428,10 @@
|
|
nbttaglist.add(StringTag.valueOf(s));
|
|
}
|
|
|
|
- nbt.put("Tags", nbttaglist);
|
|
+ nbttagcompound.put("Tags", nbttaglist);
|
|
}
|
|
|
|
- this.addAdditionalSaveData(nbt);
|
|
+ this.addAdditionalSaveData(nbttagcompound, includeAll); // CraftBukkit - pass on includeAll
|
|
if (this.isVehicle()) {
|
|
nbttaglist = new ListTag();
|
|
iterator = this.getPassengers().iterator();
|
|
@@ -1984,17 +2440,44 @@
|
|
Entity entity = (Entity) iterator.next();
|
|
CompoundTag nbttagcompound1 = new CompoundTag();
|
|
|
|
- if (entity.saveAsPassenger(nbttagcompound1)) {
|
|
+ if (entity.saveAsPassenger(nbttagcompound1, includeAll)) { // CraftBukkit - pass on includeAll
|
|
nbttaglist.add(nbttagcompound1);
|
|
}
|
|
}
|
|
|
|
if (!nbttaglist.isEmpty()) {
|
|
- nbt.put("Passengers", nbttaglist);
|
|
+ nbttagcompound.put("Passengers", nbttaglist);
|
|
}
|
|
}
|
|
|
|
- return nbt;
|
|
+ // CraftBukkit start - stores eventually existing bukkit values
|
|
+ if (this.bukkitEntity != null) {
|
|
+ this.bukkitEntity.storeBukkitValues(nbttagcompound);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ // Paper start
|
|
+ if (this.origin != null) {
|
|
+ UUID originWorld = this.originWorld != null ? this.originWorld : this.level != null ? this.level.getWorld().getUID() : null;
|
|
+ if (originWorld != null) {
|
|
+ nbttagcompound.putUUID("Paper.OriginWorld", originWorld);
|
|
+ }
|
|
+ nbttagcompound.put("Paper.Origin", this.newDoubleList(origin.getX(), origin.getY(), origin.getZ()));
|
|
+ }
|
|
+ if (spawnReason != null) {
|
|
+ nbttagcompound.putString("Paper.SpawnReason", spawnReason.name());
|
|
+ }
|
|
+ // Save entity's from mob spawner status
|
|
+ if (spawnedViaMobSpawner) {
|
|
+ nbttagcompound.putBoolean("Paper.FromMobSpawner", true);
|
|
+ }
|
|
+ if (fromNetherPortal) {
|
|
+ nbttagcompound.putBoolean("Paper.FromNetherPortal", true);
|
|
+ }
|
|
+ if (freezeLocked) {
|
|
+ nbttagcompound.putBoolean("Paper.FreezeLock", true);
|
|
+ }
|
|
+ // Paper end
|
|
+ return nbttagcompound;
|
|
} catch (Throwable throwable) {
|
|
CrashReport crashreport = CrashReport.forThrowable(throwable, "Saving entity NBT");
|
|
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being saved");
|
|
@@ -2080,6 +2563,71 @@
|
|
} else {
|
|
throw new IllegalStateException("Entity has invalid position");
|
|
}
|
|
+
|
|
+ // CraftBukkit start
|
|
+ // Spigot start
|
|
+ if (this instanceof net.minecraft.world.entity.LivingEntity) {
|
|
+ this.tickCount = nbt.getInt("Spigot.ticksLived");
|
|
+ }
|
|
+ // Spigot end
|
|
+ this.persist = !nbt.contains("Bukkit.persist") || nbt.getBoolean("Bukkit.persist");
|
|
+ this.visibleByDefault = !nbt.contains("Bukkit.visibleByDefault") || nbt.getBoolean("Bukkit.visibleByDefault");
|
|
+ // SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
+ if (nbt.contains("Bukkit.MaxAirSupply")) {
|
|
+ this.maxAirTicks = nbt.getInt("Bukkit.MaxAirSupply");
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
+ // CraftBukkit start
|
|
+ // Paper - move world parsing/loading to PlayerList#placeNewPlayer
|
|
+ this.getBukkitEntity().readBukkitValues(nbt);
|
|
+ if (nbt.contains("Bukkit.invisible")) {
|
|
+ boolean bukkitInvisible = nbt.getBoolean("Bukkit.invisible");
|
|
+ this.setInvisible(bukkitInvisible);
|
|
+ this.persistentInvisibility = bukkitInvisible;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
+ // Paper start
|
|
+ ListTag originTag = nbt.getList("Paper.Origin", net.minecraft.nbt.Tag.TAG_DOUBLE);
|
|
+ if (!originTag.isEmpty()) {
|
|
+ UUID originWorld = null;
|
|
+ if (nbt.contains("Paper.OriginWorld")) {
|
|
+ originWorld = nbt.getUUID("Paper.OriginWorld");
|
|
+ } else if (this.level != null) {
|
|
+ originWorld = this.level.getWorld().getUID();
|
|
+ }
|
|
+ this.originWorld = originWorld;
|
|
+ origin = new org.bukkit.util.Vector(originTag.getDouble(0), originTag.getDouble(1), originTag.getDouble(2));
|
|
+ }
|
|
+
|
|
+ spawnedViaMobSpawner = nbt.getBoolean("Paper.FromMobSpawner"); // Restore entity's from mob spawner status
|
|
+ fromNetherPortal = nbt.getBoolean("Paper.FromNetherPortal");
|
|
+ if (nbt.contains("Paper.SpawnReason")) {
|
|
+ String spawnReasonName = nbt.getString("Paper.SpawnReason");
|
|
+ try {
|
|
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.valueOf(spawnReasonName);
|
|
+ } catch (Exception ignored) {
|
|
+ LOGGER.error("Unknown SpawnReason " + spawnReasonName + " for " + this);
|
|
+ }
|
|
+ }
|
|
+ if (spawnReason == null) {
|
|
+ if (spawnedViaMobSpawner) {
|
|
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.SPAWNER;
|
|
+ } else if (this instanceof Mob && (this instanceof net.minecraft.world.entity.animal.Animal || this instanceof net.minecraft.world.entity.animal.AbstractFish) && !((Mob) this).removeWhenFarAway(0.0)) {
|
|
+ if (!nbt.getBoolean("PersistenceRequired")) {
|
|
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (spawnReason == null) {
|
|
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT;
|
|
+ }
|
|
+ if (nbt.contains("Paper.FreezeLock")) {
|
|
+ freezeLocked = nbt.getBoolean("Paper.FreezeLock");
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
} catch (Throwable throwable) {
|
|
CrashReport crashreport = CrashReport.forThrowable(throwable, "Loading entity NBT");
|
|
CrashReportCategory crashreportsystemdetails = crashreport.addCategory("Entity being loaded");
|
|
@@ -2101,6 +2649,12 @@
|
|
return entitytypes.canSerialize() && minecraftkey != null ? minecraftkey.toString() : null;
|
|
}
|
|
|
|
+ // CraftBukkit start - allow excluding certain data when saving
|
|
+ protected void addAdditionalSaveData(CompoundTag nbttagcompound, boolean includeAll) {
|
|
+ this.addAdditionalSaveData(nbttagcompound);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
protected abstract void readAdditionalSaveData(CompoundTag nbt);
|
|
|
|
protected abstract void addAdditionalSaveData(CompoundTag nbt);
|
|
@@ -2150,12 +2704,60 @@
|
|
|
|
@Nullable
|
|
public ItemEntity spawnAtLocation(ServerLevel world, ItemStack stack, float yOffset) {
|
|
+ // Paper start - Restore vanilla drops behavior
|
|
+ return this.spawnAtLocation(world, stack, yOffset, null);
|
|
+ }
|
|
+ public record DefaultDrop(Item item, org.bukkit.inventory.ItemStack stack, @Nullable java.util.function.Consumer<ItemStack> dropConsumer) {
|
|
+ public DefaultDrop(final ItemStack stack, final java.util.function.Consumer<ItemStack> dropConsumer) {
|
|
+ this(stack.getItem(), org.bukkit.craftbukkit.inventory.CraftItemStack.asCraftMirror(stack), dropConsumer);
|
|
+ }
|
|
+
|
|
+ public void runConsumer(final java.util.function.Consumer<org.bukkit.inventory.ItemStack> fallback) {
|
|
+ if (this.dropConsumer == null || org.bukkit.craftbukkit.inventory.CraftItemType.bukkitToMinecraft(this.stack.getType()) != this.item) {
|
|
+ fallback.accept(this.stack);
|
|
+ } else {
|
|
+ this.dropConsumer.accept(org.bukkit.craftbukkit.inventory.CraftItemStack.asNMSCopy(this.stack));
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ @Nullable
|
|
+ public ItemEntity spawnAtLocation(ServerLevel world, ItemStack stack, float yOffset, @Nullable java.util.function.Consumer<? super ItemEntity> delayedAddConsumer) {
|
|
+ // Paper end - Restore vanilla drops behavior
|
|
if (stack.isEmpty()) {
|
|
return null;
|
|
} else {
|
|
- ItemEntity entityitem = new ItemEntity(world, this.getX(), this.getY() + (double) yOffset, this.getZ(), stack);
|
|
+ // CraftBukkit start - Capture drops for death event
|
|
+ if (this instanceof net.minecraft.world.entity.LivingEntity && !((net.minecraft.world.entity.LivingEntity) this).forceDrops) {
|
|
+ // Paper start - Restore vanilla drops behavior
|
|
+ ((net.minecraft.world.entity.LivingEntity) this).drops.add(new net.minecraft.world.entity.Entity.DefaultDrop(stack, itemStack -> {
|
|
+ ItemEntity itemEntity = new ItemEntity(this.level, this.getX(), this.getY() + (double) yOffset, this.getZ(), itemStack); // stack is copied before consumer
|
|
+ itemEntity.setDefaultPickUpDelay();
|
|
+ this.level.addFreshEntity(itemEntity);
|
|
+ if (delayedAddConsumer != null) delayedAddConsumer.accept(itemEntity);
|
|
+ }));
|
|
+ // Paper end - Restore vanilla drops behavior
|
|
+ return null;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ ItemEntity entityitem = new ItemEntity(world, this.getX(), this.getY() + (double) yOffset, this.getZ(), stack.copy()); // Paper - copy so we can destroy original
|
|
+ stack.setCount(0); // Paper - destroy this item - if this ever leaks due to game bugs, ensure it doesn't dupe
|
|
|
|
- entityitem.setDefaultPickUpDelay();
|
|
+ entityitem.setDefaultPickUpDelay(); // Paper - diff on change (in dropConsumer)
|
|
+ // Paper start - Call EntityDropItemEvent
|
|
+ return this.spawnAtLocation(world, entityitem);
|
|
+ }
|
|
+ }
|
|
+ @Nullable
|
|
+ public ItemEntity spawnAtLocation(ServerLevel world, ItemEntity entityitem) {
|
|
+ {
|
|
+ // Paper end - Call EntityDropItemEvent
|
|
+ // CraftBukkit start
|
|
+ EntityDropItemEvent event = new EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) entityitem.getBukkitEntity());
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+ if (event.isCancelled()) {
|
|
+ return null;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
world.addFreshEntity(entityitem);
|
|
return entityitem;
|
|
}
|
|
@@ -2184,7 +2786,16 @@
|
|
if (this.isAlive() && this instanceof Leashable leashable) {
|
|
if (leashable.getLeashHolder() == player) {
|
|
if (!this.level().isClientSide()) {
|
|
- if (player.hasInfiniteMaterials()) {
|
|
+ // CraftBukkit start - fire PlayerUnleashEntityEvent
|
|
+ // Paper start - Expand EntityUnleashEvent
|
|
+ org.bukkit.event.player.PlayerUnleashEntityEvent event = CraftEventFactory.callPlayerUnleashEntityEvent(this, player, hand, !player.hasInfiniteMaterials());
|
|
+ if (event.isCancelled()) {
|
|
+ // Paper end - Expand EntityUnleashEvent
|
|
+ ((ServerPlayer) player).connection.send(new ClientboundSetEntityLinkPacket(this, leashable.getLeashHolder()));
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ if (!event.isDropLeash()) { // Paper - Expand EntityUnleashEvent
|
|
leashable.removeLeash();
|
|
} else {
|
|
leashable.dropLeash();
|
|
@@ -2200,6 +2811,14 @@
|
|
|
|
if (itemstack.is(Items.LEAD) && leashable.canHaveALeashAttachedToIt()) {
|
|
if (!this.level().isClientSide()) {
|
|
+ // CraftBukkit start - fire PlayerLeashEntityEvent
|
|
+ if (CraftEventFactory.callPlayerLeashEntityEvent(this, player, player, hand).isCancelled()) {
|
|
+ // ((ServerPlayer) player).resendItemInHands(); // SPIGOT-7615: Resend to fix client desync with used item // Paper - Fix inventory desync
|
|
+ ((ServerPlayer) player).connection.send(new ClientboundSetEntityLinkPacket(this, leashable.getLeashHolder()));
|
|
+ player.containerMenu.sendAllDataToRemote(); // Paper - Fix inventory desync
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
leashable.setLeashedTo(player, true);
|
|
}
|
|
|
|
@@ -2265,15 +2884,15 @@
|
|
}
|
|
|
|
public boolean showVehicleHealth() {
|
|
- return this instanceof LivingEntity;
|
|
+ return this instanceof net.minecraft.world.entity.LivingEntity;
|
|
}
|
|
|
|
public boolean startRiding(Entity entity, boolean force) {
|
|
- if (entity == this.vehicle) {
|
|
+ if (entity == this.vehicle || entity.level != this.level) { // Paper - Ensure entity passenger world matches ridden entity (bad plugins)
|
|
return false;
|
|
} else if (!entity.couldAcceptPassenger()) {
|
|
return false;
|
|
- } else if (!this.level().isClientSide() && !entity.type.canSerialize()) {
|
|
+ } else if (!force && !this.level().isClientSide() && !entity.type.canSerialize()) { // SPIGOT-7947: Allow force riding all entities
|
|
return false;
|
|
} else {
|
|
for (Entity entity1 = entity; entity1.vehicle != null; entity1 = entity1.vehicle) {
|
|
@@ -2285,11 +2904,32 @@
|
|
if (!force && (!this.canRide(entity) || !entity.canAddPassenger(this))) {
|
|
return false;
|
|
} else {
|
|
+ // CraftBukkit start
|
|
+ if (entity.getBukkitEntity() instanceof Vehicle && this.getBukkitEntity() instanceof LivingEntity) {
|
|
+ VehicleEnterEvent event = new VehicleEnterEvent((Vehicle) entity.getBukkitEntity(), this.getBukkitEntity());
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ if (event.isCancelled()) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ EntityMountEvent event = new EntityMountEvent(this.getBukkitEntity(), entity.getBukkitEntity());
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ if (event.isCancelled()) {
|
|
+ return false;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
if (this.isPassenger()) {
|
|
this.stopRiding();
|
|
}
|
|
|
|
- this.setPose(Pose.STANDING);
|
|
+ this.setPose(net.minecraft.world.entity.Pose.STANDING);
|
|
this.vehicle = entity;
|
|
this.vehicle.addPassenger(this);
|
|
entity.getIndirectPassengersStream().filter((entity2) -> {
|
|
@@ -2314,19 +2954,30 @@
|
|
}
|
|
|
|
public void removeVehicle() {
|
|
+ // Paper start - Force entity dismount during teleportation
|
|
+ this.removeVehicle(false);
|
|
+ }
|
|
+ public void removeVehicle(boolean suppressCancellation) {
|
|
+ // Paper end - Force entity dismount during teleportation
|
|
if (this.vehicle != null) {
|
|
Entity entity = this.vehicle;
|
|
|
|
this.vehicle = null;
|
|
- entity.removePassenger(this);
|
|
+ if (!entity.removePassenger(this, suppressCancellation)) this.vehicle = entity; // CraftBukkit // Paper - Force entity dismount during teleportation
|
|
}
|
|
|
|
}
|
|
|
|
public void stopRiding() {
|
|
- this.removeVehicle();
|
|
+ // Paper start - Force entity dismount during teleportation
|
|
+ this.stopRiding(false);
|
|
}
|
|
|
|
+ public void stopRiding(boolean suppressCancellation) {
|
|
+ this.removeVehicle(suppressCancellation);
|
|
+ // Paper end - Force entity dismount during teleportation
|
|
+ }
|
|
+
|
|
protected void addPassenger(Entity passenger) {
|
|
if (passenger.getVehicle() != this) {
|
|
throw new IllegalStateException("Use x.startRiding(y), not y.addPassenger(x)");
|
|
@@ -2349,21 +3000,53 @@
|
|
}
|
|
}
|
|
|
|
- protected void removePassenger(Entity passenger) {
|
|
- if (passenger.getVehicle() == this) {
|
|
+ // Paper start - Force entity dismount during teleportation
|
|
+ protected boolean removePassenger(Entity entity) { return removePassenger(entity, false);}
|
|
+ protected boolean removePassenger(Entity entity, boolean suppressCancellation) { // CraftBukkit
|
|
+ // Paper end - Force entity dismount during teleportation
|
|
+ if (entity.getVehicle() == this) {
|
|
throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
|
|
} else {
|
|
- if (this.passengers.size() == 1 && this.passengers.get(0) == passenger) {
|
|
+ // CraftBukkit start
|
|
+ CraftEntity craft = (CraftEntity) entity.getBukkitEntity().getVehicle();
|
|
+ Entity orig = craft == null ? null : craft.getHandle();
|
|
+ if (this.getBukkitEntity() instanceof Vehicle && entity.getBukkitEntity() instanceof LivingEntity) {
|
|
+ VehicleExitEvent event = new VehicleExitEvent(
|
|
+ (Vehicle) this.getBukkitEntity(),
|
|
+ (LivingEntity) entity.getBukkitEntity(), !suppressCancellation // Paper - Force entity dismount during teleportation
|
|
+ );
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ CraftEntity craftn = (CraftEntity) entity.getBukkitEntity().getVehicle();
|
|
+ Entity n = craftn == null ? null : craftn.getHandle();
|
|
+ if (event.isCancelled() || n != orig) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ EntityDismountEvent event = new EntityDismountEvent(entity.getBukkitEntity(), this.getBukkitEntity(), !suppressCancellation); // Paper - Force entity dismount during teleportation
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ if (event.isCancelled()) {
|
|
+ return false;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ if (this.passengers.size() == 1 && this.passengers.get(0) == entity) {
|
|
this.passengers = ImmutableList.of();
|
|
} else {
|
|
this.passengers = (ImmutableList) this.passengers.stream().filter((entity1) -> {
|
|
- return entity1 != passenger;
|
|
+ return entity1 != entity;
|
|
}).collect(ImmutableList.toImmutableList());
|
|
}
|
|
|
|
- passenger.boardingCooldown = 60;
|
|
- this.gameEvent(GameEvent.ENTITY_DISMOUNT, passenger);
|
|
+ entity.boardingCooldown = 60;
|
|
+ this.gameEvent(GameEvent.ENTITY_DISMOUNT, entity);
|
|
}
|
|
+ return true; // CraftBukkit
|
|
}
|
|
|
|
protected boolean canAddPassenger(Entity passenger) {
|
|
@@ -2464,7 +3147,7 @@
|
|
if (teleporttransition != null) {
|
|
ServerLevel worldserver1 = teleporttransition.newLevel();
|
|
|
|
- if (worldserver.getServer().isLevelEnabled(worldserver1) && (worldserver1.dimension() == worldserver.dimension() || this.canTeleport(worldserver, worldserver1))) {
|
|
+ if (this instanceof ServerPlayer || (worldserver1 != null && (worldserver1.dimension() == worldserver.dimension() || this.canTeleport(worldserver, worldserver1)))) { // CraftBukkit - always call event for players
|
|
this.teleport(teleporttransition);
|
|
}
|
|
}
|
|
@@ -2547,7 +3230,7 @@
|
|
}
|
|
|
|
public boolean isCrouching() {
|
|
- return this.hasPose(Pose.CROUCHING);
|
|
+ return this.hasPose(net.minecraft.world.entity.Pose.CROUCHING);
|
|
}
|
|
|
|
public boolean isSprinting() {
|
|
@@ -2563,7 +3246,7 @@
|
|
}
|
|
|
|
public boolean isVisuallySwimming() {
|
|
- return this.hasPose(Pose.SWIMMING);
|
|
+ return this.hasPose(net.minecraft.world.entity.Pose.SWIMMING);
|
|
}
|
|
|
|
public boolean isVisuallyCrawling() {
|
|
@@ -2571,6 +3254,13 @@
|
|
}
|
|
|
|
public void setSwimming(boolean swimming) {
|
|
+ // CraftBukkit start
|
|
+ if (this.valid && this.isSwimming() != swimming && this instanceof net.minecraft.world.entity.LivingEntity) {
|
|
+ if (CraftEventFactory.callToggleSwimEvent((net.minecraft.world.entity.LivingEntity) this, swimming).isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.setSharedFlag(4, swimming);
|
|
}
|
|
|
|
@@ -2609,6 +3299,7 @@
|
|
|
|
@Nullable
|
|
public PlayerTeam getTeam() {
|
|
+ if (!this.level().paperConfig().scoreboards.allowNonPlayerEntitiesOnScoreboards && !(this instanceof Player)) { return null; } // Paper - Perf: Disable Scoreboards for non players by default
|
|
return this.level().getScoreboard().getPlayersTeam(this.getScoreboardName());
|
|
}
|
|
|
|
@@ -2624,8 +3315,12 @@
|
|
return this.getTeam() != null ? this.getTeam().isAlliedTo(team) : false;
|
|
}
|
|
|
|
+ // CraftBukkit - start
|
|
public void setInvisible(boolean invisible) {
|
|
- this.setSharedFlag(5, invisible);
|
|
+ if (!this.persistentInvisibility) { // Prevent Minecraft from removing our invisibility flag
|
|
+ this.setSharedFlag(5, invisible);
|
|
+ }
|
|
+ // CraftBukkit - end
|
|
}
|
|
|
|
public boolean getSharedFlag(int index) {
|
|
@@ -2644,7 +3339,7 @@
|
|
}
|
|
|
|
public int getMaxAirSupply() {
|
|
- return 300;
|
|
+ return this.maxAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
}
|
|
|
|
public int getAirSupply() {
|
|
@@ -2652,7 +3347,18 @@
|
|
}
|
|
|
|
public void setAirSupply(int air) {
|
|
- this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, air);
|
|
+ // CraftBukkit start
|
|
+ EntityAirChangeEvent event = new EntityAirChangeEvent(this.getBukkitEntity(), air);
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ event.getEntity().getServer().getPluginManager().callEvent(event);
|
|
+ }
|
|
+ if (event.isCancelled() && this.getAirSupply() != air) {
|
|
+ this.entityData.markDirty(Entity.DATA_AIR_SUPPLY_ID);
|
|
+ return;
|
|
+ }
|
|
+ this.entityData.set(Entity.DATA_AIR_SUPPLY_ID, event.getAmount());
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
public int getTicksFrozen() {
|
|
@@ -2679,11 +3385,44 @@
|
|
|
|
public void thunderHit(ServerLevel world, LightningBolt lightning) {
|
|
this.setRemainingFireTicks(this.remainingFireTicks + 1);
|
|
+ // CraftBukkit start
|
|
+ final org.bukkit.entity.Entity thisBukkitEntity = this.getBukkitEntity();
|
|
+ final org.bukkit.entity.Entity stormBukkitEntity = lightning.getBukkitEntity();
|
|
+ final PluginManager pluginManager = Bukkit.getPluginManager();
|
|
+ // CraftBukkit end
|
|
+
|
|
if (this.remainingFireTicks == 0) {
|
|
- this.igniteForSeconds(8.0F);
|
|
+ // CraftBukkit start - Call a combust event when lightning strikes
|
|
+ EntityCombustByEntityEvent entityCombustEvent = new EntityCombustByEntityEvent(stormBukkitEntity, thisBukkitEntity, 8.0F);
|
|
+ pluginManager.callEvent(entityCombustEvent);
|
|
+ if (!entityCombustEvent.isCancelled()) {
|
|
+ this.igniteForSeconds(entityCombustEvent.getDuration(), false);
|
|
+ // Paper start - fix EntityCombustEvent cancellation
|
|
+ } else {
|
|
+ this.setRemainingFireTicks(this.remainingFireTicks - 1);
|
|
+ // Paper end - fix EntityCombustEvent cancellation
|
|
+ }
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
- this.hurtServer(world, this.damageSources().lightningBolt(), 5.0F);
|
|
+ // CraftBukkit start
|
|
+ if (thisBukkitEntity instanceof Hanging) {
|
|
+ HangingBreakByEntityEvent hangingEvent = new HangingBreakByEntityEvent((Hanging) thisBukkitEntity, stormBukkitEntity);
|
|
+ pluginManager.callEvent(hangingEvent);
|
|
+
|
|
+ if (hangingEvent.isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (this.fireImmune()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!this.hurtServer(world, this.damageSources().lightningBolt().customEntityDamager(lightning), 5.0F)) {
|
|
+ return;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
public void onAboveBubbleCol(boolean drag) {
|
|
@@ -2713,7 +3452,7 @@
|
|
this.resetFallDistance();
|
|
}
|
|
|
|
- public boolean killedEntity(ServerLevel world, LivingEntity other) {
|
|
+ public boolean killedEntity(ServerLevel world, net.minecraft.world.entity.LivingEntity other) {
|
|
return true;
|
|
}
|
|
|
|
@@ -2818,7 +3557,7 @@
|
|
public String toString() {
|
|
String s = this.level() == null ? "~NULL~" : this.level().toString();
|
|
|
|
- return this.removalReason != null ? String.format(Locale.ROOT, "%s['%s'/%d, l='%s', x=%.2f, y=%.2f, z=%.2f, removed=%s]", this.getClass().getSimpleName(), this.getName().getString(), this.id, s, this.getX(), this.getY(), this.getZ(), this.removalReason) : String.format(Locale.ROOT, "%s['%s'/%d, l='%s', x=%.2f, y=%.2f, z=%.2f]", this.getClass().getSimpleName(), this.getName().getString(), this.id, s, this.getX(), this.getY(), this.getZ());
|
|
+ return this.removalReason != null ? String.format(Locale.ROOT, "%s['%s'/%d, uuid='%s', l='%s', x=%.2f, y=%.2f, z=%.2f, cpos=%s, tl=%d, v=%b, removed=%s]", this.getClass().getSimpleName(), this.getName().getString(), this.id, this.uuid, s, this.getX(), this.getY(), this.getZ(), this.chunkPosition(), this.tickCount, this.valid, this.removalReason) : String.format(Locale.ROOT, "%s['%s'/%d, uuid='%s', l='%s', x=%.2f, y=%.2f, z=%.2f, cpos=%s, tl=%d, v=%b]", this.getClass().getSimpleName(), this.getName().getString(), this.id, this.uuid, s, this.getX(), this.getY(), this.getZ(), this.chunkPosition(), this.tickCount, this.valid); // Paper - add more info
|
|
}
|
|
|
|
public final boolean isInvulnerableToBase(DamageSource damageSource) {
|
|
@@ -2838,6 +3577,13 @@
|
|
}
|
|
|
|
public void restoreFrom(Entity original) {
|
|
+ // Paper start - Forward CraftEntity in teleport command
|
|
+ CraftEntity bukkitEntity = original.bukkitEntity;
|
|
+ if (bukkitEntity != null) {
|
|
+ bukkitEntity.setHandle(this);
|
|
+ this.bukkitEntity = bukkitEntity;
|
|
+ }
|
|
+ // Paper end - Forward CraftEntity in teleport command
|
|
CompoundTag nbttagcompound = original.saveWithoutId(new CompoundTag());
|
|
|
|
nbttagcompound.remove("Dimension");
|
|
@@ -2850,8 +3596,57 @@
|
|
public Entity teleport(TeleportTransition teleportTarget) {
|
|
Level world = this.level();
|
|
|
|
+ // Paper start - Fix item duplication and teleport issues
|
|
+ if ((!this.isAlive() || !this.valid) && (teleportTarget.newLevel() != world)) {
|
|
+ LOGGER.warn("Illegal Entity Teleport " + this + " to " + teleportTarget.newLevel() + ":" + teleportTarget.position(), new Throwable());
|
|
+ return null;
|
|
+ }
|
|
+ // Paper end - Fix item duplication and teleport issues
|
|
if (world instanceof ServerLevel worldserver) {
|
|
if (!this.isRemoved()) {
|
|
+ // CraftBukkit start
|
|
+ PositionMoveRotation absolutePosition = PositionMoveRotation.calculateAbsolute(PositionMoveRotation.of(this), PositionMoveRotation.of(teleportTarget), teleportTarget.relatives());
|
|
+ Vec3 velocity = absolutePosition.deltaMovement(); // Paper
|
|
+ Location to = CraftLocation.toBukkit(absolutePosition.position(), teleportTarget.newLevel().getWorld(), absolutePosition.yRot(), absolutePosition.xRot());
|
|
+ // Paper start - gateway-specific teleport event
|
|
+ final EntityTeleportEvent teleEvent;
|
|
+ if (this.portalProcess != null && this.portalProcess.isSamePortal(((net.minecraft.world.level.block.EndGatewayBlock) net.minecraft.world.level.block.Blocks.END_GATEWAY)) && this.level.getBlockEntity(this.portalProcess.getEntryPosition()) instanceof net.minecraft.world.level.block.entity.TheEndGatewayBlockEntity theEndGatewayBlockEntity) {
|
|
+ teleEvent = new com.destroystokyo.paper.event.entity.EntityTeleportEndGatewayEvent(this.getBukkitEntity(), this.getBukkitEntity().getLocation(), to, new org.bukkit.craftbukkit.block.CraftEndGateway(to.getWorld(), theEndGatewayBlockEntity));
|
|
+ teleEvent.callEvent();
|
|
+ } else {
|
|
+ teleEvent = CraftEventFactory.callEntityTeleportEvent(this, to);
|
|
+ }
|
|
+ // Paper end - gateway-specific teleport event
|
|
+ if (teleEvent.isCancelled() || teleEvent.getTo() == null) {
|
|
+ return null;
|
|
+ }
|
|
+ if (!to.equals(teleEvent.getTo())) {
|
|
+ to = teleEvent.getTo();
|
|
+ teleportTarget = new TeleportTransition(((CraftWorld) to.getWorld()).getHandle(), CraftLocation.toVec3D(to), Vec3.ZERO, to.getYaw(), to.getPitch(), teleportTarget.missingRespawnBlock(), teleportTarget.asPassenger(), Set.of(), teleportTarget.postTeleportTransition(), teleportTarget.cause());
|
|
+ // Paper start - Call EntityPortalExitEvent
|
|
+ velocity = Vec3.ZERO;
|
|
+ }
|
|
+ if (this.portalProcess != null) { // if in a portal
|
|
+ CraftEntity bukkitEntity = this.getBukkitEntity();
|
|
+ org.bukkit.event.entity.EntityPortalExitEvent event = new org.bukkit.event.entity.EntityPortalExitEvent(
|
|
+ bukkitEntity,
|
|
+ bukkitEntity.getLocation(), to.clone(),
|
|
+ bukkitEntity.getVelocity(), org.bukkit.craftbukkit.util.CraftVector.toBukkit(velocity)
|
|
+ );
|
|
+ event.callEvent();
|
|
+
|
|
+ // Only change the target if actually needed, since we reset relative flags
|
|
+ if (!event.isCancelled() && event.getTo() != null && (!event.getTo().equals(event.getFrom()) || !event.getAfter().equals(event.getBefore()))) {
|
|
+ to = event.getTo().clone();
|
|
+ velocity = org.bukkit.craftbukkit.util.CraftVector.toNMS(event.getAfter());
|
|
+ teleportTarget = new TeleportTransition(((CraftWorld) to.getWorld()).getHandle(), CraftLocation.toVec3D(to), velocity, to.getYaw(), to.getPitch(), teleportTarget.missingRespawnBlock(), teleportTarget.asPassenger(), Set.of(), teleportTarget.postTeleportTransition(), teleportTarget.cause());
|
|
+ }
|
|
+ }
|
|
+ if (this.isRemoved()) {
|
|
+ return null;
|
|
+ }
|
|
+ // Paper end - Call EntityPortalExitEvent
|
|
+ // CraftBukkit end
|
|
ServerLevel worldserver1 = teleportTarget.newLevel();
|
|
boolean flag = worldserver1.dimension() != worldserver.dimension();
|
|
|
|
@@ -2918,10 +3713,19 @@
|
|
gameprofilerfiller.pop();
|
|
return null;
|
|
} else {
|
|
+ // Paper start - Fix item duplication and teleport issues
|
|
+ if (this instanceof Leashable leashable) {
|
|
+ leashable.dropLeash(); // Paper drop lead
|
|
+ }
|
|
+ // Paper end - Fix item duplication and teleport issues
|
|
entity.restoreFrom(this);
|
|
this.removeAfterChangingDimensions();
|
|
+ // CraftBukkit start - Forward the CraftEntity to the new entity
|
|
+ //this.getBukkitEntity().setHandle(entity);
|
|
+ //entity.bukkitEntity = this.getBukkitEntity(); // Paper - forward CraftEntity in teleport command; moved to Entity#restoreFrom
|
|
+ // CraftBukkit end
|
|
entity.teleportSetPosition(PositionMoveRotation.of(teleportTarget), teleportTarget.relatives());
|
|
- world.addDuringTeleport(entity);
|
|
+ if (this.inWorld) world.addDuringTeleport(entity); // CraftBukkit - Don't spawn the new entity if the current entity isn't spawned
|
|
Iterator iterator1 = list1.iterator();
|
|
|
|
while (iterator1.hasNext()) {
|
|
@@ -2947,7 +3751,7 @@
|
|
}
|
|
|
|
private void sendTeleportTransitionToRidingPlayers(TeleportTransition teleportTarget) {
|
|
- LivingEntity entityliving = this.getControllingPassenger();
|
|
+ net.minecraft.world.entity.LivingEntity entityliving = this.getControllingPassenger();
|
|
Iterator iterator = this.getIndirectPassengers().iterator();
|
|
|
|
while (iterator.hasNext()) {
|
|
@@ -2995,9 +3799,17 @@
|
|
}
|
|
|
|
protected void removeAfterChangingDimensions() {
|
|
- this.setRemoved(Entity.RemovalReason.CHANGED_DIMENSION);
|
|
- if (this instanceof Leashable leashable) {
|
|
- leashable.removeLeash();
|
|
+ this.setRemoved(Entity.RemovalReason.CHANGED_DIMENSION, null); // CraftBukkit - add Bukkit remove cause
|
|
+ if (this instanceof Leashable leashable && leashable.isLeashed()) { // Paper - only call if it is leashed
|
|
+ // Paper start - Expand EntityUnleashEvent
|
|
+ final EntityUnleashEvent event = new EntityUnleashEvent(this.getBukkitEntity(), UnleashReason.UNKNOWN, false); // CraftBukkit
|
|
+ event.callEvent();
|
|
+ if (!event.isDropLeash()) {
|
|
+ leashable.removeLeash();
|
|
+ } else {
|
|
+ leashable.dropLeash();
|
|
+ }
|
|
+ // Paper end - Expand EntityUnleashEvent
|
|
}
|
|
|
|
}
|
|
@@ -3006,11 +3818,34 @@
|
|
return PortalShape.getRelativePosition(portalRect, portalAxis, this.position(), this.getDimensions(this.getPose()));
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ public CraftPortalEvent callPortalEvent(Entity entity, Location exit, PlayerTeleportEvent.TeleportCause cause, int searchRadius, int creationRadius) {
|
|
+ org.bukkit.entity.Entity bukkitEntity = entity.getBukkitEntity();
|
|
+ Location enter = bukkitEntity.getLocation();
|
|
+
|
|
+ // Paper start
|
|
+ final org.bukkit.PortalType portalType = switch (cause) {
|
|
+ case END_PORTAL -> org.bukkit.PortalType.ENDER;
|
|
+ case NETHER_PORTAL -> org.bukkit.PortalType.NETHER;
|
|
+ case END_GATEWAY -> org.bukkit.PortalType.END_GATEWAY; // not actually used yet
|
|
+ default -> org.bukkit.PortalType.CUSTOM;
|
|
+ };
|
|
+ EntityPortalEvent event = new EntityPortalEvent(bukkitEntity, enter, exit, searchRadius, true, creationRadius, portalType);
|
|
+ // Paper end
|
|
+ event.getEntity().getServer().getPluginManager().callEvent(event);
|
|
+ if (event.isCancelled() || event.getTo() == null || event.getTo().getWorld() == null || !entity.isAlive()) {
|
|
+ return null;
|
|
+ }
|
|
+ return new CraftPortalEvent(event);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public boolean canUsePortal(boolean allowVehicles) {
|
|
return (allowVehicles || !this.isPassenger()) && this.isAlive();
|
|
}
|
|
|
|
public boolean canTeleport(Level from, Level to) {
|
|
+ if (!this.isAlive() || !this.valid) return false; // Paper - Fix item duplication and teleport issues
|
|
if (from.dimension() == Level.END && to.dimension() == Level.OVERWORLD) {
|
|
Iterator iterator = this.getPassengers().iterator();
|
|
|
|
@@ -3134,10 +3969,16 @@
|
|
return (Boolean) this.entityData.get(Entity.DATA_CUSTOM_NAME_VISIBLE);
|
|
}
|
|
|
|
- public boolean teleportTo(ServerLevel world, double destX, double destY, double destZ, Set<Relative> flags, float yaw, float pitch, boolean resetCamera) {
|
|
- float f2 = Mth.clamp(pitch, -90.0F, 90.0F);
|
|
- Entity entity = this.teleport(new TeleportTransition(world, new Vec3(destX, destY, destZ), Vec3.ZERO, yaw, f2, flags, TeleportTransition.DO_NOTHING));
|
|
+ // CraftBukkit start
|
|
+ public final boolean teleportTo(ServerLevel world, double destX, double destY, double destZ, Set<Relative> flags, float yaw, float pitch, boolean resetCamera) {
|
|
+ return this.teleportTo(world, destX, destY, destZ, flags, yaw, pitch, resetCamera, PlayerTeleportEvent.TeleportCause.UNKNOWN);
|
|
+ }
|
|
|
|
+ public boolean teleportTo(ServerLevel worldserver, double d0, double d1, double d2, Set<Relative> set, float f, float f1, boolean flag, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) {
|
|
+ float f2 = Mth.clamp(f1, -90.0F, 90.0F);
|
|
+ Entity entity = this.teleport(new TeleportTransition(worldserver, new Vec3(d0, d1, d2), Vec3.ZERO, f, f2, set, TeleportTransition.DO_NOTHING, cause));
|
|
+ // CraftBukkit end
|
|
+
|
|
return entity != null;
|
|
}
|
|
|
|
@@ -3187,7 +4028,7 @@
|
|
/** @deprecated */
|
|
@Deprecated
|
|
protected void fixupDimensions() {
|
|
- Pose entitypose = this.getPose();
|
|
+ net.minecraft.world.entity.Pose entitypose = this.getPose();
|
|
EntityDimensions entitysize = this.getDimensions(entitypose);
|
|
|
|
this.dimensions = entitysize;
|
|
@@ -3196,7 +4037,7 @@
|
|
|
|
public void refreshDimensions() {
|
|
EntityDimensions entitysize = this.dimensions;
|
|
- Pose entitypose = this.getPose();
|
|
+ net.minecraft.world.entity.Pose entitypose = this.getPose();
|
|
EntityDimensions entitysize1 = this.getDimensions(entitypose);
|
|
|
|
this.dimensions = entitysize1;
|
|
@@ -3258,10 +4099,29 @@
|
|
}
|
|
|
|
public final void setBoundingBox(AABB boundingBox) {
|
|
- this.bb = boundingBox;
|
|
+ // CraftBukkit start - block invalid bounding boxes
|
|
+ double minX = boundingBox.minX,
|
|
+ minY = boundingBox.minY,
|
|
+ minZ = boundingBox.minZ,
|
|
+ maxX = boundingBox.maxX,
|
|
+ maxY = boundingBox.maxY,
|
|
+ maxZ = boundingBox.maxZ;
|
|
+ double len = boundingBox.maxX - boundingBox.minX;
|
|
+ if (len < 0) maxX = minX;
|
|
+ if (len > 64) maxX = minX + 64.0;
|
|
+
|
|
+ len = boundingBox.maxY - boundingBox.minY;
|
|
+ if (len < 0) maxY = minY;
|
|
+ if (len > 64) maxY = minY + 64.0;
|
|
+
|
|
+ len = boundingBox.maxZ - boundingBox.minZ;
|
|
+ if (len < 0) maxZ = minZ;
|
|
+ if (len > 64) maxZ = minZ + 64.0;
|
|
+ this.bb = new AABB(minX, minY, minZ, maxX, maxY, maxZ);
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
- public final float getEyeHeight(Pose pose) {
|
|
+ public final float getEyeHeight(net.minecraft.world.entity.Pose pose) {
|
|
return this.getDimensions(pose).eyeHeight();
|
|
}
|
|
|
|
@@ -3300,7 +4160,14 @@
|
|
|
|
public void startSeenByPlayer(ServerPlayer player) {}
|
|
|
|
- public void stopSeenByPlayer(ServerPlayer player) {}
|
|
+ // Paper start - entity tracking events
|
|
+ public void stopSeenByPlayer(ServerPlayer player) {
|
|
+ // Since this event cannot be cancelled, we should call it here to catch all "un-tracks"
|
|
+ if (io.papermc.paper.event.player.PlayerUntrackEntityEvent.getHandlerList().getRegisteredListeners().length > 0) {
|
|
+ new io.papermc.paper.event.player.PlayerUntrackEntityEvent(player.getBukkitEntity(), this.getBukkitEntity()).callEvent();
|
|
+ }
|
|
+ }
|
|
+ // Paper end - entity tracking events
|
|
|
|
public float rotate(Rotation rotation) {
|
|
float f = Mth.wrapDegrees(this.getYRot());
|
|
@@ -3335,7 +4202,7 @@
|
|
}
|
|
|
|
@Nullable
|
|
- public LivingEntity getControllingPassenger() {
|
|
+ public net.minecraft.world.entity.LivingEntity getControllingPassenger() {
|
|
return null;
|
|
}
|
|
|
|
@@ -3373,20 +4240,34 @@
|
|
}
|
|
|
|
private Stream<Entity> getIndirectPassengersStream() {
|
|
+ if (this.passengers.isEmpty()) { return Stream.of(); } // Paper - Optimize indirect passenger iteration
|
|
return this.passengers.stream().flatMap(Entity::getSelfAndPassengers);
|
|
}
|
|
|
|
@Override
|
|
public Stream<Entity> getSelfAndPassengers() {
|
|
+ if (this.passengers.isEmpty()) { return Stream.of(this); } // Paper - Optimize indirect passenger iteration
|
|
return Stream.concat(Stream.of(this), this.getIndirectPassengersStream());
|
|
}
|
|
|
|
@Override
|
|
public Stream<Entity> getPassengersAndSelf() {
|
|
+ if (this.passengers.isEmpty()) { return Stream.of(this); } // Paper - Optimize indirect passenger iteration
|
|
return Stream.concat(this.passengers.stream().flatMap(Entity::getPassengersAndSelf), Stream.of(this));
|
|
}
|
|
|
|
public Iterable<Entity> getIndirectPassengers() {
|
|
+ // Paper start - Optimize indirect passenger iteration
|
|
+ if (this.passengers.isEmpty()) { return ImmutableList.of(); }
|
|
+ ImmutableList.Builder<Entity> indirectPassengers = ImmutableList.builder();
|
|
+ for (Entity passenger : this.passengers) {
|
|
+ indirectPassengers.add(passenger);
|
|
+ indirectPassengers.addAll(passenger.getIndirectPassengers());
|
|
+ }
|
|
+ return indirectPassengers.build();
|
|
+ }
|
|
+ private Iterable<Entity> getIndirectPassengers_old() {
|
|
+ // Paper end - Optimize indirect passenger iteration
|
|
return () -> {
|
|
return this.getIndirectPassengersStream().iterator();
|
|
};
|
|
@@ -3399,6 +4280,7 @@
|
|
}
|
|
|
|
public boolean hasExactlyOnePlayerPassenger() {
|
|
+ if (this.passengers.isEmpty()) { return false; } // Paper - Optimize indirect passenger iteration
|
|
return this.countPlayerPassengers() == 1;
|
|
}
|
|
|
|
@@ -3435,7 +4317,7 @@
|
|
}
|
|
|
|
public boolean isControlledByLocalInstance() {
|
|
- LivingEntity entityliving = this.getControllingPassenger();
|
|
+ net.minecraft.world.entity.LivingEntity entityliving = this.getControllingPassenger();
|
|
|
|
if (entityliving instanceof Player entityhuman) {
|
|
return entityhuman.isLocalPlayer();
|
|
@@ -3445,7 +4327,7 @@
|
|
}
|
|
|
|
public boolean isControlledByClient() {
|
|
- LivingEntity entityliving = this.getControllingPassenger();
|
|
+ net.minecraft.world.entity.LivingEntity entityliving = this.getControllingPassenger();
|
|
|
|
return entityliving != null && entityliving.isControlledByClient();
|
|
}
|
|
@@ -3463,7 +4345,7 @@
|
|
return new Vec3((double) f1 * d2 / (double) f3, 0.0D, (double) f2 * d2 / (double) f3);
|
|
}
|
|
|
|
- public Vec3 getDismountLocationForPassenger(LivingEntity passenger) {
|
|
+ public Vec3 getDismountLocationForPassenger(net.minecraft.world.entity.LivingEntity passenger) {
|
|
return new Vec3(this.getX(), this.getBoundingBox().maxY, this.getZ());
|
|
}
|
|
|
|
@@ -3489,8 +4371,37 @@
|
|
return 1;
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ private final CommandSource commandSource = new CommandSource() {
|
|
+
|
|
+ @Override
|
|
+ public void sendSystemMessage(Component message) {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public CommandSender getBukkitSender(CommandSourceStack wrapper) {
|
|
+ return Entity.this.getBukkitEntity();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean acceptsSuccess() {
|
|
+ return ((ServerLevel) Entity.this.level()).getGameRules().getBoolean(GameRules.RULE_SENDCOMMANDFEEDBACK);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean acceptsFailure() {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean shouldInformAdmins() {
|
|
+ return true;
|
|
+ }
|
|
+ };
|
|
+ // CraftBukkit end
|
|
+
|
|
public CommandSourceStack createCommandSourceStackForNameResolution(ServerLevel world) {
|
|
- return new CommandSourceStack(CommandSource.NULL, this.position(), this.getRotationVector(), world, 0, this.getName().getString(), this.getDisplayName(), world.getServer(), this);
|
|
+ return new CommandSourceStack(this.commandSource, this.position(), this.getRotationVector(), world, 0, this.getName().getString(), this.getDisplayName(), world.getServer(), this); // CraftBukkit
|
|
}
|
|
|
|
public void lookAt(EntityAnchorArgument.Anchor anchorPoint, Vec3 target) {
|
|
@@ -3551,6 +4462,11 @@
|
|
vec3d = vec3d.add(vec3d1);
|
|
++k1;
|
|
}
|
|
+ // CraftBukkit start - store last lava contact location
|
|
+ if (tag == FluidTags.LAVA) {
|
|
+ this.lastLavaContact = blockposition_mutableblockposition.immutable();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
}
|
|
}
|
|
}
|
|
@@ -3613,7 +4529,7 @@
|
|
return new ClientboundAddEntityPacket(this, entityTrackerEntry);
|
|
}
|
|
|
|
- public EntityDimensions getDimensions(Pose pose) {
|
|
+ public EntityDimensions getDimensions(net.minecraft.world.entity.Pose pose) {
|
|
return this.type.getDimensions();
|
|
}
|
|
|
|
@@ -3714,7 +4630,39 @@
|
|
return this.getZ((2.0D * this.random.nextDouble() - 1.0D) * widthScale);
|
|
}
|
|
|
|
+ // Paper start - Block invalid positions and bounding box
|
|
+ public static boolean checkPosition(Entity entity, double newX, double newY, double newZ) {
|
|
+ if (Double.isFinite(newX) && Double.isFinite(newY) && Double.isFinite(newZ)) {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ String entityInfo;
|
|
+ try {
|
|
+ entityInfo = entity.toString();
|
|
+ } catch (Exception ex) {
|
|
+ entityInfo = "[Entity info unavailable] ";
|
|
+ }
|
|
+ LOGGER.error("New entity position is invalid! Tried to set invalid position ({},{},{}) for entity {} located at {}, entity info: {}", newX, newY, newZ, entity.getClass().getName(), entity.position, entityInfo, new Throwable());
|
|
+ return false;
|
|
+ }
|
|
public final void setPosRaw(double x, double y, double z) {
|
|
+ this.setPosRaw(x, y, z, false);
|
|
+ }
|
|
+ public final void setPosRaw(double x, double y, double z, boolean forceBoundingBoxUpdate) {
|
|
+ if (!checkPosition(this, x, y, z)) {
|
|
+ return;
|
|
+ }
|
|
+ // Paper end - Block invalid positions and bounding box
|
|
+ // Paper start - Fix MC-4
|
|
+ if (this instanceof ItemEntity) {
|
|
+ if (io.papermc.paper.configuration.GlobalConfiguration.get().misc.fixEntityPositionDesync) {
|
|
+ // encode/decode from ClientboundMoveEntityPacket
|
|
+ x = Mth.lfloor(x * 4096.0) * (1 / 4096.0);
|
|
+ y = Mth.lfloor(y * 4096.0) * (1 / 4096.0);
|
|
+ z = Mth.lfloor(z * 4096.0) * (1 / 4096.0);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - Fix MC-4
|
|
if (this.position.x != x || this.position.y != y || this.position.z != z) {
|
|
this.position = new Vec3(x, y, z);
|
|
int i = Mth.floor(x);
|
|
@@ -3732,6 +4680,12 @@
|
|
this.levelCallback.onMove();
|
|
}
|
|
|
|
+ // Paper start - Block invalid positions and bounding box; don't allow desync of pos and AABB
|
|
+ // hanging has its own special logic
|
|
+ if (!(this instanceof net.minecraft.world.entity.decoration.HangingEntity) && (forceBoundingBoxUpdate || this.position.x != x || this.position.y != y || this.position.z != z)) {
|
|
+ this.setBoundingBox(this.makeBoundingBox());
|
|
+ }
|
|
+ // Paper end - Block invalid positions and bounding box
|
|
}
|
|
|
|
public void checkDespawn() {}
|
|
@@ -3818,8 +4772,17 @@
|
|
|
|
@Override
|
|
public final void setRemoved(Entity.RemovalReason reason) {
|
|
+ // CraftBukkit start - add Bukkit remove cause
|
|
+ this.setRemoved(reason, null);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public final void setRemoved(Entity.RemovalReason entity_removalreason, EntityRemoveEvent.Cause cause) {
|
|
+ CraftEventFactory.callEntityRemoveEvent(this, cause);
|
|
+ // CraftBukkit end
|
|
+ final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers
|
|
if (this.removalReason == null) {
|
|
- this.removalReason = reason;
|
|
+ this.removalReason = entity_removalreason;
|
|
}
|
|
|
|
if (this.removalReason.shouldDestroy()) {
|
|
@@ -3827,14 +4790,30 @@
|
|
}
|
|
|
|
this.getPassengers().forEach(Entity::stopRiding);
|
|
- this.levelCallback.onRemove(reason);
|
|
- this.onRemoval(reason);
|
|
+ this.levelCallback.onRemove(entity_removalreason);
|
|
+ this.onRemoval(entity_removalreason);
|
|
+ // Paper start - Folia schedulers
|
|
+ if (!(this instanceof ServerPlayer) && entity_removalreason != RemovalReason.CHANGED_DIMENSION && !alreadyRemoved) {
|
|
+ // Players need to be special cased, because they are regularly removed from the world
|
|
+ this.retireScheduler();
|
|
+ }
|
|
+ // Paper end - Folia schedulers
|
|
}
|
|
|
|
public void unsetRemoved() {
|
|
this.removalReason = null;
|
|
}
|
|
|
|
+ // Paper start - Folia schedulers
|
|
+ /**
|
|
+ * Invoked only when the entity is truly removed from the server, never to be added to any world.
|
|
+ */
|
|
+ public final void retireScheduler() {
|
|
+ // we need to force create the bukkit entity so that the scheduler can be retired...
|
|
+ this.getBukkitEntity().taskScheduler.retire();
|
|
+ }
|
|
+ // Paper end - Folia schedulers
|
|
+
|
|
@Override
|
|
public void setLevelCallback(EntityInLevelCallback changeListener) {
|
|
this.levelCallback = changeListener;
|
|
@@ -3887,7 +4866,7 @@
|
|
}
|
|
|
|
public Vec3 getKnownMovement() {
|
|
- LivingEntity entityliving = this.getControllingPassenger();
|
|
+ net.minecraft.world.entity.LivingEntity entityliving = this.getControllingPassenger();
|
|
|
|
if (entityliving instanceof Player entityhuman) {
|
|
if (this.isAlive()) {
|
|
@@ -3962,4 +4941,14 @@
|
|
|
|
void accept(Entity entity, double x, double y, double z);
|
|
}
|
|
+
|
|
+ // Paper start - Expose entity id counter
|
|
+ public static int nextEntityId() {
|
|
+ return ENTITY_COUNTER.incrementAndGet();
|
|
+ }
|
|
+
|
|
+ public boolean isTicking() {
|
|
+ return ((net.minecraft.server.level.ServerLevel) this.level).isPositionEntityTicking(this.blockPosition());
|
|
+ }
|
|
+ // Paper end - Expose entity id counter
|
|
}
|