mirror of
https://github.com/PaperMC/Paper.git
synced 2025-10-26 05:04:31 +00:00
Co-authored-by: Lulu13022002 <41980282+Lulu13022002@users.noreply.github.com> This patch includes extensive changes to teleport logic in the API to better represent vanilla behavior. In particular, teleporting with passengers is now supported by default. This also allows for entities to be teleported across dimensions with passengers, except for players, which is still not supported. Entity state related teleportation flags have been deprecated, as that behavior occurs by default now. Additionally, relative teleportation flags are now supported on entities. Most importantly, we no longer hack onto the respawn logic when teleporting players between dimensions in the API. This is a good step towards working on no longer reusing player entities on dimension swap, which is something we are trying to no longer do as it causes a large amount of desync issues on the client/server. Entity teleportation via /tp now aligns with vanilla too, which now maintains velocity when teleporting. This is a breaking change.
1879 lines
87 KiB
Diff
1879 lines
87 KiB
Diff
--- a/net/minecraft/world/entity/Entity.java
|
|
+++ b/net/minecraft/world/entity/Entity.java
|
|
@@ -150,6 +_,105 @@
|
|
import org.slf4j.Logger;
|
|
|
|
public abstract class Entity implements SyncedDataHolder, DebugValueSource, Nameable, ItemOwner, EntityAccess, ScoreHolder, DataComponentGetter {
|
|
+ // CraftBukkit start
|
|
+ private static final int CURRENT_LEVEL = 2;
|
|
+ static boolean isLevelAtLeast(ValueInput input, int level) {
|
|
+ return input.getIntOr("Bukkit.updateLevel", CURRENT_LEVEL) >= 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 @Nullable org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason spawnReason; // Paper - Entity#getEntitySpawnReason
|
|
+
|
|
+ private volatile @Nullable org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity; // Paper - Folia schedulers - volatile
|
|
+
|
|
+ public org.bukkit.craftbukkit.entity.CraftEntity getBukkitEntity() {
|
|
+ if (this.bukkitEntity == null) {
|
|
+ // Paper start - Folia schedulers
|
|
+ synchronized (this) {
|
|
+ if (this.bukkitEntity == null) {
|
|
+ return this.bukkitEntity = org.bukkit.craftbukkit.entity.CraftEntity.getEntity(this.level.getCraftServer(), this);
|
|
+ }
|
|
+ }
|
|
+ // Paper end - Folia schedulers
|
|
+ }
|
|
+ return this.bukkitEntity;
|
|
+ }
|
|
+ // Paper start
|
|
+ public @Nullable org.bukkit.craftbukkit.entity.CraftEntity getBukkitEntityRaw() {
|
|
+ return this.bukkitEntity;
|
|
+ }
|
|
+ // Paper end
|
|
private static final Logger LOGGER = LogUtils.getLogger();
|
|
public static final String TAG_ID = "id";
|
|
public static final String TAG_UUID = "UUID";
|
|
@@ -227,7 +_,7 @@
|
|
public double yOld;
|
|
public double zOld;
|
|
public boolean noPhysics;
|
|
- public final RandomSource random = RandomSource.create();
|
|
+ public final RandomSource random = SHARED_RANDOM; // Paper - Share random for entities to make them more random
|
|
public int tickCount;
|
|
private int remainingFireTicks;
|
|
public boolean wasTouchingWater;
|
|
@@ -264,7 +_,7 @@
|
|
protected UUID uuid = Mth.createInsecureUUID(this.random);
|
|
protected String stringUUID = this.uuid.toString();
|
|
private boolean hasGlowingTag;
|
|
- private final Set<String> tags = Sets.newHashSet();
|
|
+ private final Set<String> tags = new io.papermc.paper.util.SizeLimitedSet<>(new it.unimi.dsi.fastutil.objects.ObjectOpenHashSet<>(), MAX_ENTITY_TAG_COUNT); // Paper - fully limit tag size - replace set impl
|
|
private final double[] pistonDeltas = new double[]{0.0, 0.0, 0.0};
|
|
private long pistonDeltasGameTime;
|
|
private EntityDimensions dimensions;
|
|
@@ -275,7 +_,7 @@
|
|
private boolean onGroundNoBlocks = false;
|
|
private float crystalSoundIntensity;
|
|
private int lastCrystalSoundPlayTick;
|
|
- public boolean hasVisualFire;
|
|
+ public net.kyori.adventure.util.TriState visualFire = net.kyori.adventure.util.TriState.NOT_SET; // Paper - improve visual fire API
|
|
@Nullable
|
|
private BlockState inBlockState = null;
|
|
public static final int MAX_MOVEMENTS_HANDELED_PER_TICK = 100;
|
|
@@ -284,6 +_,41 @@
|
|
private final LongSet visitedBlocks = new LongOpenHashSet();
|
|
private final InsideBlockEffectApplier.StepBasedCollector insideEffectCollector = new InsideBlockEffectApplier.StepBasedCollector();
|
|
private CustomData customData = CustomData.EMPTY;
|
|
+ // 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 @Nullable 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;
|
|
+ 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
|
|
+ public @Nullable Vec3 origin;
|
|
+ public @Nullable UUID originWorld;
|
|
+ // Paper end
|
|
+ public boolean freezeLocked = false; // Paper - Freeze Tick Lock API
|
|
+ public boolean fixedPose = false; // Paper - Expand Pose API
|
|
+ private final int despawnTime; // Paper - entity despawn time limit
|
|
+ public int totalEntityAge; // Paper - age-like counter for all entities
|
|
+ public final io.papermc.paper.entity.activation.ActivationType activationType = io.papermc.paper.entity.activation.ActivationType.activationTypeFor(this); // Paper - EAR 2/tracking ranges
|
|
+ // 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 level) {
|
|
this.type = type;
|
|
@@ -305,6 +_,7 @@
|
|
this.entityData = builder.build();
|
|
this.setPos(0.0, 0.0, 0.0);
|
|
this.eyeHeight = this.dimensions.eyeHeight();
|
|
+ this.despawnTime = level == null || type == EntityType.PLAYER ? -1 : level.paperConfig().entities.spawning.despawnTime.getOrDefault(type, io.papermc.paper.configuration.type.number.IntOr.Disabled.DISABLED).or(-1); // Paper - entity despawn time limit
|
|
}
|
|
|
|
public boolean isColliding(BlockPos pos, BlockState state) {
|
|
@@ -317,6 +_,12 @@
|
|
return team != null && team.getColor().getColor() != null ? team.getColor().getColor() : 16777215;
|
|
}
|
|
|
|
+ // CraftBukkit start - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
+ public int getDefaultMaxAirSupply() {
|
|
+ return Entity.TOTAL_AIR_SUPPLY;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public boolean isSpectator() {
|
|
return false;
|
|
}
|
|
@@ -369,7 +_,7 @@
|
|
}
|
|
|
|
public boolean addTag(String tag) {
|
|
- return this.tags.size() < 1024 && this.tags.add(tag);
|
|
+ return this.tags.add(tag); // Paper - fully limit tag size - replace set impl
|
|
}
|
|
|
|
public boolean removeTag(String tag) {
|
|
@@ -377,12 +_,18 @@
|
|
}
|
|
|
|
public void kill(ServerLevel level) {
|
|
- this.remove(Entity.RemovalReason.KILLED);
|
|
+ this.remove(Entity.RemovalReason.KILLED, org.bukkit.event.entity.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(@Nullable org.bukkit.event.entity.EntityRemoveEvent.Cause cause) {
|
|
+ this.remove(Entity.RemovalReason.DISCARDED, cause);
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
protected abstract void defineSynchedData(SynchedEntityData.Builder builder);
|
|
@@ -391,6 +_,48 @@
|
|
return this.entityData;
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ public void refreshEntityData(ServerPlayer to) {
|
|
+ List<SynchedEntityData.DataValue<?>> list = this.entityData.packAll(); // Paper - Update EVERYTHING not just not default
|
|
+
|
|
+ if (to.getBukkitEntity().canSee(this.getBukkitEntity())) { // Paper
|
|
+ to.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket(this.getId(), list));
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+ // Paper start
|
|
+ // This method should only be used if the data of an entity could have become desynced
|
|
+ // due to interactions on the client.
|
|
+ public void resendPossiblyDesyncedEntityData(net.minecraft.server.level.ServerPlayer player) {
|
|
+ if (player.getBukkitEntity().canSee(this.getBukkitEntity())) {
|
|
+ ServerLevel world = (net.minecraft.server.level.ServerLevel)this.level();
|
|
+ net.minecraft.server.level.ChunkMap.TrackedEntity tracker = world == null ? null : world.getChunkSource().chunkMap.entityMap.get(this.getId());
|
|
+ if (tracker == null) {
|
|
+ return;
|
|
+ }
|
|
+ final net.minecraft.server.level.ServerEntity serverEntity = tracker.serverEntity;
|
|
+ final List<net.minecraft.network.protocol.Packet<? super net.minecraft.network.protocol.game.ClientGamePacketListener>> list = new java.util.ArrayList<>();
|
|
+ serverEntity.sendPairingData(player, list::add);
|
|
+ player.connection.send(new net.minecraft.network.protocol.game.ClientboundBundlePacket(list));
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // This method allows you to specifically resend certain data accessor keys to the client
|
|
+ public void resendPossiblyDesyncedDataValues(List<EntityDataAccessor<?>> keys, ServerPlayer to) {
|
|
+ if (!to.getBukkitEntity().canSee(this.getBukkitEntity())) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ final List<SynchedEntityData.DataValue<?>> values = new java.util.ArrayList<>(keys.size());
|
|
+ for (final EntityDataAccessor<?> key : keys) {
|
|
+ final SynchedEntityData.DataItem<?> synchedValue = this.entityData.getItem(key);
|
|
+ values.add(synchedValue.value());
|
|
+ }
|
|
+
|
|
+ to.connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket(this.id, values));
|
|
+ }
|
|
+ // Paper end
|
|
+
|
|
@Override
|
|
public boolean equals(Object other) {
|
|
return other instanceof Entity && ((Entity)other).id == this.id;
|
|
@@ -402,7 +_,13 @@
|
|
}
|
|
|
|
public void remove(Entity.RemovalReason reason) {
|
|
- this.setRemoved(reason);
|
|
+ // CraftBukkit start - add Bukkit remove cause
|
|
+ this.remove(reason, null);
|
|
+ }
|
|
+
|
|
+ public void remove(Entity.RemovalReason reason, @Nullable org.bukkit.event.entity.EntityRemoveEvent.Cause eventCause) {
|
|
+ this.setRemoved(reason, eventCause);
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
public void onClientRemoval() {
|
|
@@ -412,6 +_,15 @@
|
|
}
|
|
|
|
public void setPose(Pose pose) {
|
|
+ if (this.fixedPose) return; // Paper - Expand Pose API
|
|
+ // CraftBukkit start
|
|
+ if (pose == this.getPose()) {
|
|
+ return;
|
|
+ }
|
|
+ if (!this.generation) {
|
|
+ this.level.getCraftServer().getPluginManager().callEvent(new org.bukkit.event.entity.EntityPoseChangeEvent(this.getBukkitEntity(), org.bukkit.entity.Pose.values()[pose.ordinal()]));
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.entityData.set(DATA_POSE, pose);
|
|
}
|
|
|
|
@@ -435,6 +_,32 @@
|
|
}
|
|
|
|
public void setRot(float yRot, float xRot) {
|
|
+ // CraftBukkit start - yaw was sometimes set to NaN, so we need to set it back to 0
|
|
+ if (Float.isNaN(yRot)) {
|
|
+ yRot = 0;
|
|
+ }
|
|
+
|
|
+ if (yRot == Float.POSITIVE_INFINITY || yRot == 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");
|
|
+ ((org.bukkit.craftbukkit.entity.CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite yaw (Hacking?)");
|
|
+ }
|
|
+ yRot = 0;
|
|
+ }
|
|
+
|
|
+ // pitch was sometimes set to NaN, so we need to set it back to 0
|
|
+ if (Float.isNaN(xRot)) {
|
|
+ xRot = 0;
|
|
+ }
|
|
+
|
|
+ if (xRot == Float.POSITIVE_INFINITY || xRot == 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");
|
|
+ ((org.bukkit.craftbukkit.entity.CraftPlayer) this.getBukkitEntity()).kickPlayer("Infinite pitch (Hacking?)");
|
|
+ }
|
|
+ xRot = 0;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.setYRot(yRot % 360.0F);
|
|
this.setXRot(xRot % 360.0F);
|
|
}
|
|
@@ -444,8 +_,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() {
|
|
@@ -475,12 +_,28 @@
|
|
}
|
|
|
|
public void tick() {
|
|
+ // Paper start - entity despawn time limit
|
|
+ if (this.despawnTime >= 0 && this.totalEntityAge >= this.despawnTime) {
|
|
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN);
|
|
+ return;
|
|
+ }
|
|
+ // Paper end - entity despawn time limit
|
|
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 profilerFiller = Profiler.get();
|
|
profilerFiller.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();
|
|
@@ -490,7 +_,7 @@
|
|
this.boardingCooldown--;
|
|
}
|
|
|
|
- this.handlePortal();
|
|
+ if (this instanceof ServerPlayer) this.handlePortal(); // CraftBukkit - Moved up to postTick
|
|
if (this.canSpawnSprintParticle()) {
|
|
this.spawnSprintParticle();
|
|
}
|
|
@@ -518,6 +_,10 @@
|
|
|
|
if (this.isInLava()) {
|
|
this.fallDistance *= 0.5;
|
|
+ // CraftBukkit start
|
|
+ } else {
|
|
+ this.lastLavaContact = null;
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
this.checkBelowWorld();
|
|
@@ -534,11 +_,16 @@
|
|
}
|
|
|
|
public void setSharedFlagOnFire(boolean isOnFire) {
|
|
- this.setSharedFlag(0, isOnFire || this.hasVisualFire);
|
|
+ this.setSharedFlag(0, this.visualFire.toBooleanOrElse(isOnFire)); // Paper - improve visual fire API
|
|
}
|
|
|
|
public void checkBelowWorld() {
|
|
- if (this.getY() < this.level().getMinY() - 64) {
|
|
+ if (!this.level.getWorld().isVoidDamageEnabled()) return; // Paper - check if void damage is enabled on the world
|
|
+ // Paper start - Configurable nether ceiling damage
|
|
+ if (this.getY() < (this.level.getMinY() + this.level.getWorld().getVoidDamageMinBuildHeightOffset()) || (this.level.getWorld().getEnvironment() == org.bukkit.World.Environment.NETHER // Paper - use configured min build height offset
|
|
+ && 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();
|
|
}
|
|
}
|
|
@@ -566,15 +_,41 @@
|
|
}
|
|
|
|
public void lavaIgnite() {
|
|
+ // Paper start - track lava contact
|
|
+ this.lavaIgnite(this.lastLavaContact); // fallback for minecarts if defined
|
|
+ }
|
|
+
|
|
+ public void lavaIgnite(@Nullable BlockPos pos) {
|
|
+ // Paper end - track lava contact
|
|
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 = pos == null ? null : org.bukkit.craftbukkit.block.CraftBlock.at(this.level, pos);
|
|
+ org.bukkit.entity.Entity damagee = this.getBukkitEntity();
|
|
+ org.bukkit.event.entity.EntityCombustEvent combustEvent = new org.bukkit.event.entity.EntityCombustByBlockEvent(damager, damagee, 15.0F);
|
|
+
|
|
+ if (combustEvent.callEvent()) {
|
|
+ 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
|
|
}
|
|
}
|
|
|
|
public void lavaHurt() {
|
|
+ // Paper start - track lava contact
|
|
+ this.lavaHurt(this.lastLavaContact); // fallback for minecarts if defined
|
|
+ }
|
|
+
|
|
+ public void lavaHurt(@Nullable BlockPos pos) {
|
|
+ // Paper end - track lava contact
|
|
if (!this.fireImmune()) {
|
|
if (this.level() instanceof ServerLevel serverLevel
|
|
- && this.hurtServer(serverLevel, this.damageSources().lava(), 4.0F)
|
|
+ && this.hurtServer(serverLevel, this.damageSources().lava().eventBlockDamager(this.level, pos), 4.0F) // CraftBukkit - we also don't throw an event unless the object in lava is living, to save on some event calls
|
|
&& this.shouldPlayLavaHurtSound()
|
|
&& !this.isSilent()) {
|
|
serverLevel.playSound(
|
|
@@ -589,6 +_,20 @@
|
|
}
|
|
|
|
public final void igniteForSeconds(float seconds) {
|
|
+ // CraftBukkit start
|
|
+ this.igniteForSeconds(seconds, true);
|
|
+ }
|
|
+
|
|
+ public final void igniteForSeconds(float seconds, boolean callEvent) {
|
|
+ if (callEvent) {
|
|
+ org.bukkit.event.entity.EntityCombustEvent event = new org.bukkit.event.entity.EntityCombustEvent(this.getBukkitEntity(), seconds);
|
|
+ if (!event.callEvent()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ seconds = event.getDuration();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.igniteForTicks(Mth.floor(seconds * 20.0F));
|
|
}
|
|
|
|
@@ -613,7 +_,7 @@
|
|
}
|
|
|
|
protected void onBelowWorld() {
|
|
- this.discard();
|
|
+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.OUT_OF_WORLD); // CraftBukkit - add Bukkit remove cause
|
|
}
|
|
|
|
public boolean isFree(double x, double y, double z) {
|
|
@@ -669,7 +_,28 @@
|
|
return this.onGround;
|
|
}
|
|
|
|
+ // Paper start - detailed watchdog information
|
|
+ public final Object posLock = new Object(); // Paper - log detailed entity tick information
|
|
+
|
|
+ @Nullable
|
|
+ private Vec3 moveVector;
|
|
+ private double moveStartX;
|
|
+ private double moveStartY;
|
|
+ private double moveStartZ;
|
|
+ // Paper end - detailed watchdog information
|
|
+
|
|
public void move(MoverType type, Vec3 movement) {
|
|
+ final Vec3 originalMovement = movement; // Paper - Expose pre-collision velocity
|
|
+ // Paper start - detailed watchdog information
|
|
+ ca.spottedleaf.moonrise.common.util.TickThread.ensureTickThread("Cannot move an entity off-main");
|
|
+ synchronized (this.posLock) {
|
|
+ this.moveStartX = this.getX();
|
|
+ this.moveStartY = this.getY();
|
|
+ this.moveStartZ = this.getZ();
|
|
+ this.moveVector = movement;
|
|
+ }
|
|
+ try {
|
|
+ // Paper end - detailed watchdog information
|
|
if (this.noPhysics) {
|
|
this.setPos(this.getX() + movement.x, this.getY() + movement.y, this.getZ() + movement.z);
|
|
this.horizontalCollision = false;
|
|
@@ -752,6 +_,27 @@
|
|
block.updateEntityMovementAfterFallOn(this.level(), this);
|
|
}
|
|
}
|
|
+ // CraftBukkit start
|
|
+ if (this.horizontalCollision && this.getBukkitEntity() instanceof org.bukkit.entity.Vehicle) {
|
|
+ org.bukkit.entity.Vehicle vehicle = (org.bukkit.entity.Vehicle) this.getBukkitEntity();
|
|
+ org.bukkit.block.Block block = this.level.getWorld().getBlockAt(Mth.floor(this.getX()), Mth.floor(this.getY()), Mth.floor(this.getZ()));
|
|
+
|
|
+ if (movement.x > vec3.x) {
|
|
+ block = block.getRelative(org.bukkit.block.BlockFace.EAST);
|
|
+ } else if (movement.x < vec3.x) {
|
|
+ block = block.getRelative(org.bukkit.block.BlockFace.WEST);
|
|
+ } else if (movement.z > vec3.z) {
|
|
+ block = block.getRelative(org.bukkit.block.BlockFace.SOUTH);
|
|
+ } else if (movement.z < vec3.z) {
|
|
+ block = block.getRelative(org.bukkit.block.BlockFace.NORTH);
|
|
+ }
|
|
+
|
|
+ if (!block.getType().isAir()) {
|
|
+ org.bukkit.event.vehicle.VehicleBlockCollisionEvent event = new org.bukkit.event.vehicle.VehicleBlockCollisionEvent(vehicle, block, org.bukkit.craftbukkit.util.CraftVector.toBukkit(originalMovement)); // Paper - Expose pre-collision velocity
|
|
+ event.callEvent();
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
|
|
if (!this.level().isClientSide() || this.isLocalInstanceAuthoritative()) {
|
|
Entity.MovementEmission movementEmission = this.getMovementEmission();
|
|
@@ -765,6 +_,13 @@
|
|
profilerFiller.pop();
|
|
}
|
|
}
|
|
+ // Paper start - detailed watchdog information
|
|
+ } finally {
|
|
+ synchronized (this.posLock) { // Paper
|
|
+ this.moveVector = null;
|
|
+ } // Paper
|
|
+ }
|
|
+ // Paper end - detailed watchdog information
|
|
}
|
|
|
|
private void applyMovementEmissionAndPlaySound(Entity.MovementEmission movementEmission, Vec3 movement, BlockPos pos, BlockState state) {
|
|
@@ -946,7 +_,7 @@
|
|
}
|
|
|
|
protected BlockPos getOnPos(float yOffset) {
|
|
- if (this.mainSupportingBlockPos.isPresent()) {
|
|
+ if (this.mainSupportingBlockPos.isPresent() && this.level().getChunkIfLoadedImmediately(this.mainSupportingBlockPos.get()) != null) { // Paper - ensure no loads
|
|
BlockPos blockPos = this.mainSupportingBlockPos.get();
|
|
if (!(yOffset > 1.0E-5F)) {
|
|
return blockPos;
|
|
@@ -1201,7 +_,7 @@
|
|
if (flag2) {
|
|
try {
|
|
boolean flag4 = flag || aabb.intersects(pos);
|
|
- stepBasedCollector.advanceStep(index);
|
|
+ stepBasedCollector.advanceStep(index, pos); // Paper - track position inside effect was triggered on
|
|
blockState.entityInside(this.level(), pos, this, stepBasedCollector, flag4);
|
|
this.onInsideBlock(blockState);
|
|
} catch (Throwable var20) {
|
|
@@ -1215,7 +_,7 @@
|
|
}
|
|
|
|
if (flag3) {
|
|
- stepBasedCollector.advanceStep(index);
|
|
+ stepBasedCollector.advanceStep(index, pos); // Paper - track position inside effect was triggered on
|
|
blockState.getFluidState().entityInside(this.level(), pos, this, stepBasedCollector);
|
|
}
|
|
|
|
@@ -1652,6 +_,7 @@
|
|
this.setXRot(Mth.clamp(xRot, -90.0F, 90.0F) % 360.0F);
|
|
this.yRotO = this.getYRot();
|
|
this.xRotO = this.getXRot();
|
|
+ this.setYHeadRot(yRot); // Paper - Update head rotation
|
|
}
|
|
|
|
public void absSnapTo(double x, double y, double z) {
|
|
@@ -1661,6 +_,7 @@
|
|
this.yo = y;
|
|
this.zo = d1;
|
|
this.setPos(d, y, d1);
|
|
+ if (this.valid) this.level.getChunk((int) Math.floor(this.getX()) >> 4, (int) Math.floor(this.getZ()) >> 4); // CraftBukkit
|
|
}
|
|
|
|
public void snapTo(Vec3 pos) {
|
|
@@ -1685,6 +_,7 @@
|
|
this.setXRot(xRot);
|
|
this.setOldPosAndRot();
|
|
this.reapplyPosition();
|
|
+ this.setYHeadRot(yRot); // Paper - Update head rotation
|
|
}
|
|
|
|
public final void setOldPosAndRot() {
|
|
@@ -1751,6 +_,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 d = entity.getX() - this.getX();
|
|
double d1 = entity.getZ() - this.getZ();
|
|
double max = Mth.absMax(d, d1);
|
|
@@ -1784,7 +_,21 @@
|
|
}
|
|
|
|
public void push(double x, double y, double z) {
|
|
- this.setDeltaMovement(this.getDeltaMovement().add(x, y, z));
|
|
+ // Paper start - Add EntityKnockbackByEntityEvent and EntityPushedByEntityAttackEvent
|
|
+ this.push(x, y, z, null);
|
|
+ }
|
|
+
|
|
+ public void push(double x, double y, double z, @Nullable Entity pushingEntity) {
|
|
+ org.bukkit.util.Vector delta = new org.bukkit.util.Vector(x, y, z);
|
|
+ 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;
|
|
}
|
|
|
|
@@ -1891,8 +_,20 @@
|
|
}
|
|
|
|
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 entity, DamageSource damageSource) {
|
|
if (entity instanceof ServerPlayer) {
|
|
@@ -1919,15 +_,23 @@
|
|
}
|
|
|
|
public boolean saveAsPassenger(ValueOutput output) {
|
|
- if (this.removalReason != null && !this.removalReason.shouldSave()) {
|
|
+ // CraftBukkit start - allow excluding certain data when saving
|
|
+ // Paper start - Raw entity serialization API
|
|
+ return this.saveAsPassenger(output, true, false, false);
|
|
+ }
|
|
+
|
|
+ public boolean saveAsPassenger(ValueOutput output, boolean includeAll, boolean includeNonSaveable, boolean forceSerialization) {
|
|
+ // Paper end - Raw entity serialization API
|
|
+ // CraftBukkit end
|
|
+ if (this.removalReason != null && !this.removalReason.shouldSave() && !forceSerialization) { // Paper - Raw entity serialization API
|
|
return false;
|
|
} else {
|
|
- String encodeId = this.getEncodeId();
|
|
- if (encodeId == null) {
|
|
+ String encodeId = this.getEncodeId(includeNonSaveable); // Paper - Raw entity serialization API
|
|
+ if ((!this.persist && !forceSerialization) || encodeId == null) { // CraftBukkit - persist flag // Paper - Raw entity serialization API
|
|
return false;
|
|
} else {
|
|
output.putString("id", encodeId);
|
|
- this.saveWithoutId(output);
|
|
+ this.saveWithoutId(output , includeAll, includeNonSaveable, forceSerialization); // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API
|
|
return true;
|
|
}
|
|
}
|
|
@@ -1938,14 +_,35 @@
|
|
}
|
|
|
|
public void saveWithoutId(ValueOutput output) {
|
|
+ // CraftBukkit start - allow excluding certain data when saving
|
|
+ // Paper start - Raw entity serialization API
|
|
+ this.saveWithoutId(output, true, false, false);
|
|
+ }
|
|
+
|
|
+ public void saveWithoutId(ValueOutput output, boolean includeAll, boolean includeNonSaveable, boolean forceSerialization) {
|
|
+ // Paper end - Raw entity serialization API
|
|
+ // CraftBukkit end
|
|
try {
|
|
+ if (includeAll) { // CraftBukkit - selectively save position
|
|
if (this.vehicle != null) {
|
|
output.store("Pos", Vec3.CODEC, new Vec3(this.vehicle.getX(), this.getY(), this.vehicle.getZ()));
|
|
} else {
|
|
output.store("Pos", Vec3.CODEC, this.position());
|
|
}
|
|
+ } // CraftBukkit
|
|
|
|
+ this.setDeltaMovement(io.papermc.paper.util.MCUtil.sanitizeNanInf(this.deltaMovement, 0D)); // Paper - remove NaN values before usage in saving
|
|
output.store("Motion", Vec3.CODEC, this.getDeltaMovement());
|
|
+ // 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
|
|
output.store("Rotation", Vec2.CODEC, new Vec2(this.getYRot(), this.getXRot()));
|
|
output.putDouble("fall_distance", this.fallDistance);
|
|
output.putShort("Fire", (short)this.remainingFireTicks);
|
|
@@ -1953,7 +_,29 @@
|
|
output.putBoolean("OnGround", this.onGround());
|
|
output.putBoolean("Invulnerable", this.invulnerable);
|
|
output.putInt("PortalCooldown", this.portalCooldown);
|
|
+ // CraftBukkit start - selectively save uuid and world
|
|
+ if (includeAll) {
|
|
output.store("UUID", UUIDUtil.CODEC, this.getUUID());
|
|
+ // PAIL: Check above UUID reads 1.8 properly, ie: UUIDMost / UUIDLeast
|
|
+ output.putLong("WorldUUIDLeast", this.level.getWorld().getUID().getLeastSignificantBits());
|
|
+ output.putLong("WorldUUIDMost", this.level.getWorld().getUID().getMostSignificantBits());
|
|
+ }
|
|
+ output.putInt("Bukkit.updateLevel", Entity.CURRENT_LEVEL);
|
|
+ if (!this.persist) {
|
|
+ output.putBoolean("Bukkit.persist", this.persist);
|
|
+ }
|
|
+ if (!this.visibleByDefault) {
|
|
+ output.putBoolean("Bukkit.visibleByDefault", this.visibleByDefault);
|
|
+ }
|
|
+ if (this.persistentInvisibility) {
|
|
+ output.putBoolean("Bukkit.invisible", this.persistentInvisibility);
|
|
+ }
|
|
+ // SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
+ if (this.maxAirTicks != this.getDefaultMaxAirSupply()) {
|
|
+ output.putInt("Bukkit.MaxAirSupply", this.getMaxAirSupply());
|
|
+ }
|
|
+ output.putInt("Spigot.ticksLived", this.totalEntityAge); // Paper
|
|
+ // CraftBukkit end
|
|
output.storeNullable("CustomName", ComponentSerialization.CODEC, this.getCustomName());
|
|
if (this.isCustomNameVisible()) {
|
|
output.putBoolean("CustomNameVisible", this.isCustomNameVisible());
|
|
@@ -1976,9 +_,12 @@
|
|
output.putInt("TicksFrozen", this.getTicksFrozen());
|
|
}
|
|
|
|
- if (this.hasVisualFire) {
|
|
- output.putBoolean("HasVisualFire", this.hasVisualFire);
|
|
+ // Paper start - improve visual fire API
|
|
+ if (this.visualFire.equals(net.kyori.adventure.util.TriState.TRUE)) {
|
|
+ output.putBoolean("HasVisualFire", true);
|
|
}
|
|
+ output.putString("Paper.FireOverride", visualFire.name());
|
|
+ // Paper end
|
|
|
|
if (!this.tags.isEmpty()) {
|
|
output.store("Tags", TAG_LIST_CODEC, List.copyOf(this.tags));
|
|
@@ -1988,13 +_,13 @@
|
|
output.store("data", CustomData.CODEC, this.customData);
|
|
}
|
|
|
|
- this.addAdditionalSaveData(output);
|
|
+ this.addAdditionalSaveData(output, includeAll); // CraftBukkit - pass on includeAll
|
|
if (this.isVehicle()) {
|
|
ValueOutput.ValueOutputList valueOutputList = output.childrenList("Passengers");
|
|
|
|
for (Entity entity : this.getPassengers()) {
|
|
ValueOutput valueOutput = valueOutputList.addChild();
|
|
- if (!entity.saveAsPassenger(valueOutput)) {
|
|
+ if (!entity.saveAsPassenger(valueOutput, includeAll, includeNonSaveable, forceSerialization)) { // CraftBukkit - pass on includeAll // Paper - Raw entity serialization API
|
|
valueOutputList.discardLast();
|
|
}
|
|
}
|
|
@@ -2003,6 +_,34 @@
|
|
output.discard("Passengers");
|
|
}
|
|
}
|
|
+
|
|
+ // CraftBukkit start - stores eventually existing bukkit values
|
|
+ if (this.bukkitEntity != null) {
|
|
+ this.bukkitEntity.storeBukkitValues(output);
|
|
+ }
|
|
+ // 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) {
|
|
+ output.store("Paper.OriginWorld", UUIDUtil.CODEC, originWorld);
|
|
+ }
|
|
+ output.store("Paper.Origin", Vec3.CODEC, this.origin);
|
|
+ }
|
|
+ if (this.spawnReason != null) {
|
|
+ output.putString("Paper.SpawnReason", this.spawnReason.name());
|
|
+ }
|
|
+ // Save entity's from mob spawner status
|
|
+ if (this.spawnedViaMobSpawner) {
|
|
+ output.putBoolean("Paper.FromMobSpawner", true);
|
|
+ }
|
|
+ if (this.fromNetherPortal) {
|
|
+ output.putBoolean("Paper.FromNetherPortal", true);
|
|
+ }
|
|
+ if (this.freezeLocked) {
|
|
+ output.putBoolean("Paper.FreezeLock", true);
|
|
+ }
|
|
+ // Paper end
|
|
} catch (Throwable var7) {
|
|
CrashReport crashReport = CrashReport.forThrowable(var7, "Saving entity NBT");
|
|
CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being saved");
|
|
@@ -2014,7 +_,7 @@
|
|
public void load(ValueInput input) {
|
|
try {
|
|
Vec3 vec3 = input.read("Pos", Vec3.CODEC).orElse(Vec3.ZERO);
|
|
- Vec3 vec31 = input.read("Motion", Vec3.CODEC).orElse(Vec3.ZERO);
|
|
+ Vec3 vec31 = input.read("Motion", Vec3.CODEC).orElse(Vec3.ZERO); vec31 = io.papermc.paper.util.MCUtil.sanitizeNanInf(vec31, 0D); // Paper - avoid setting NaN values
|
|
Vec2 vec2 = input.read("Rotation", Vec2.CODEC).orElse(Vec2.ZERO);
|
|
this.setDeltaMovement(Math.abs(vec31.x) > 10.0 ? 0.0 : vec31.x, Math.abs(vec31.y) > 10.0 ? 0.0 : vec31.y, Math.abs(vec31.z) > 10.0 ? 0.0 : vec31.z);
|
|
this.hasImpulse = true;
|
|
@@ -2046,7 +_,20 @@
|
|
this.setNoGravity(input.getBooleanOr("NoGravity", false));
|
|
this.setGlowingTag(input.getBooleanOr("Glowing", false));
|
|
this.setTicksFrozen(input.getIntOr("TicksFrozen", 0));
|
|
- this.hasVisualFire = input.getBooleanOr("HasVisualFire", false);
|
|
+ // Paper start - improve visual fire API
|
|
+ input.getString("Paper.FireOverride").ifPresentOrElse(
|
|
+ override -> {
|
|
+ try {
|
|
+ this.visualFire = net.kyori.adventure.util.TriState.valueOf(override);
|
|
+ } catch (final Exception ignored) {
|
|
+ LOGGER.error("Unknown fire override {} for {}", override, this);
|
|
+ }
|
|
+ },
|
|
+ () -> this.visualFire = input.read("HasVisualFire", Codec.BOOL)
|
|
+ .map(net.kyori.adventure.util.TriState::byBoolean)
|
|
+ .orElse(net.kyori.adventure.util.TriState.NOT_SET)
|
|
+ );
|
|
+ // Paper end
|
|
this.customData = input.read("data", CustomData.CODEC).orElse(CustomData.EMPTY);
|
|
this.tags.clear();
|
|
input.read("Tags", TAG_LIST_CODEC).ifPresent(this.tags::addAll);
|
|
@@ -2057,6 +_,59 @@
|
|
} else {
|
|
throw new IllegalStateException("Entity has invalid rotation");
|
|
}
|
|
+
|
|
+ // CraftBukkit start
|
|
+ // Spigot start
|
|
+ if (this instanceof net.minecraft.world.entity.LivingEntity) {
|
|
+ this.totalEntityAge = input.getIntOr("Spigot.ticksLived", 0); // Paper
|
|
+ }
|
|
+ // Spigot end
|
|
+ this.persist = input.getBooleanOr("Bukkit.persist", true);
|
|
+ this.visibleByDefault = input.getBooleanOr("Bukkit.visibleByDefault", true);
|
|
+ // SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
+ this.maxAirTicks = input.getIntOr("Bukkit.MaxAirSupply",this.maxAirTicks);
|
|
+ // CraftBukkit end
|
|
+
|
|
+ // CraftBukkit start
|
|
+ // Paper - move world parsing/loading to PlayerList#placeNewPlayer
|
|
+ this.getBukkitEntity().readBukkitValues(input);
|
|
+ input.read("Bukkit.invisible", Codec.BOOL).ifPresent(bukkitInvisible -> {
|
|
+ this.setInvisible(bukkitInvisible);
|
|
+ this.persistentInvisibility = bukkitInvisible;
|
|
+ });
|
|
+ // CraftBukkit end
|
|
+
|
|
+ // Paper start
|
|
+ Optional<Vec3> originVec = input.read("Paper.Origin", Vec3.CODEC);
|
|
+ if (originVec.isPresent()) {
|
|
+ this.originWorld = input.read("Paper.OriginWorld", UUIDUtil.CODEC)
|
|
+ .orElse(this.level != null ? this.level.getWorld().getUID() : null);
|
|
+ this.origin = originVec.get();
|
|
+ }
|
|
+
|
|
+ spawnedViaMobSpawner = input.getBooleanOr("Paper.FromMobSpawner", false); // Restore entity's from mob spawner status
|
|
+ fromNetherPortal = input.getBooleanOr("Paper.FromNetherPortal", false);
|
|
+ input.getString("Paper.SpawnReason").ifPresent(spawnReasonName -> {
|
|
+ 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 (!input.getBooleanOr("PersistenceRequired", false)) {
|
|
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.NATURAL;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ if (spawnReason == null) {
|
|
+ spawnReason = org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason.DEFAULT;
|
|
+ }
|
|
+ freezeLocked = input.getBooleanOr("Paper.FreezeLock", false);
|
|
+ // Paper end
|
|
} catch (Throwable var7) {
|
|
CrashReport crashReport = CrashReport.forThrowable(var7, "Loading entity NBT");
|
|
CrashReportCategory crashReportCategory = crashReport.addCategory("Entity being loaded");
|
|
@@ -2071,13 +_,24 @@
|
|
|
|
@Nullable
|
|
public final String getEncodeId() {
|
|
+ // Paper start - Raw entity serialization API
|
|
+ return getEncodeId(false);
|
|
+ }
|
|
+ public final @Nullable String getEncodeId(boolean includeNonSaveable) {
|
|
+ // Paper end - Raw entity serialization API
|
|
EntityType<?> type = this.getType();
|
|
ResourceLocation key = EntityType.getKey(type);
|
|
- return !type.canSerialize() ? null : key.toString();
|
|
+ return (type.canSerialize() || includeNonSaveable) ? key.toString() : null; // Paper - Raw entity serialization API
|
|
}
|
|
|
|
protected abstract void readAdditionalSaveData(ValueInput input);
|
|
|
|
+ // CraftBukkit start - allow excluding certain data when saving
|
|
+ protected void addAdditionalSaveData(ValueOutput output, boolean includeAll) {
|
|
+ this.addAdditionalSaveData(output);
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
protected abstract void addAdditionalSaveData(ValueOutput output);
|
|
|
|
@Nullable
|
|
@@ -2092,11 +_,62 @@
|
|
|
|
@Nullable
|
|
public ItemEntity spawnAtLocation(ServerLevel level, ItemStack stack, Vec3 offset) {
|
|
+ // Paper start - Restore vanilla drops behavior
|
|
+ return this.spawnAtLocation(level, stack, offset, 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 level, ItemStack stack, Vec3 offset, @Nullable java.util.function.Consumer<? super ItemEntity> delayedAddConsumer) {
|
|
+ // Paper end - Restore vanilla drops behavior
|
|
if (stack.isEmpty()) {
|
|
return null;
|
|
} else {
|
|
- ItemEntity itemEntity = new ItemEntity(level, this.getX() + offset.x, this.getY() + offset.y, this.getZ() + offset.z, stack);
|
|
- itemEntity.setDefaultPickUpDelay();
|
|
+ // CraftBukkit start - Capture drops for death event
|
|
+ if (this instanceof 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() + offset.x, this.getY() + offset.y, this.getZ() + offset.z, 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 itemEntity = new ItemEntity(level, this.getX() + offset.x, this.getY() + offset.y, this.getZ() + offset.z, 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
|
|
+
|
|
+ itemEntity.setDefaultPickUpDelay(); // Paper - diff on change (in dropConsumer)
|
|
+ // Paper start - Call EntityDropItemEvent
|
|
+ return this.spawnAtLocation(level, itemEntity);
|
|
+ }
|
|
+ }
|
|
+ @Nullable
|
|
+ public ItemEntity spawnAtLocation(ServerLevel level, ItemEntity itemEntity) {
|
|
+ {
|
|
+ // Paper end - Call EntityDropItemEvent
|
|
+ // CraftBukkit start
|
|
+ org.bukkit.event.entity.EntityDropItemEvent event = new org.bukkit.event.entity.EntityDropItemEvent(this.getBukkitEntity(), (org.bukkit.entity.Item) itemEntity.getBukkitEntity());
|
|
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
|
|
+ if (event.isCancelled()) {
|
|
+ return null;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
level.addFreshEntity(itemEntity);
|
|
return itemEntity;
|
|
}
|
|
@@ -2142,6 +_,15 @@
|
|
|
|
for (Leashable leashable1 : list) {
|
|
if (leashable1.canHaveALeashAttachedTo(this)) {
|
|
+ // Paper start - PlayerLeashEvent
|
|
+ final org.bukkit.event.entity.PlayerLeashEntityEvent event = org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerLeashEntityEvent(
|
|
+ leashable1,
|
|
+ this,
|
|
+ player,
|
|
+ hand
|
|
+ );
|
|
+ if (event != null && event.isCancelled()) continue; // If the event was called and cancelled, skip this.
|
|
+ // Paper end - PlayerLeashEvent
|
|
leashable1.setLeashedTo(this, true);
|
|
flag = true;
|
|
}
|
|
@@ -2156,7 +_,7 @@
|
|
}
|
|
|
|
ItemStack itemInHand = player.getItemInHand(hand);
|
|
- if (itemInHand.is(Items.SHEARS) && this.shearOffAllLeashConnections(player)) {
|
|
+ if (itemInHand.is(Items.SHEARS) && this.shearOffAllLeashConnections(player, hand)) { // Paper - PlayerUnleashEntityEvent - pass used hand
|
|
itemInHand.hurtAndBreak(1, player, hand);
|
|
return InteractionResult.SUCCESS;
|
|
} else if (this instanceof Mob mob
|
|
@@ -2169,11 +_,13 @@
|
|
if (this.isAlive() && this instanceof Leashable leashable2) {
|
|
if (leashable2.getLeashHolder() == player) {
|
|
if (!this.level().isClientSide()) {
|
|
- if (player.hasInfiniteMaterials()) {
|
|
- leashable2.removeLeash();
|
|
- } else {
|
|
- leashable2.dropLeash();
|
|
+ // Paper start - EntityUnleashEvent
|
|
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent(
|
|
+ leashable2, player, hand, !player.hasInfiniteMaterials(), true
|
|
+ )) {
|
|
+ return InteractionResult.PASS;
|
|
}
|
|
+ // Paper end - EntityUnleashEvent
|
|
|
|
this.gameEvent(GameEvent.ENTITY_INTERACT, player);
|
|
this.playSound(SoundEvents.LEAD_UNTIED);
|
|
@@ -2186,9 +_,22 @@
|
|
if (itemInHand1.is(Items.LEAD) && !(leashable2.getLeashHolder() instanceof Player)) {
|
|
if (!this.level().isClientSide() && leashable2.canHaveALeashAttachedTo(player)) {
|
|
if (leashable2.isLeashed()) {
|
|
- leashable2.dropLeash();
|
|
+ // Paper start - EntityUnleashEvent
|
|
+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent(
|
|
+ leashable2, player, hand, true, true
|
|
+ )) {
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ // Paper end - EntityUnleashEvent
|
|
+ // leashable2.dropLeash(); // Paper - EntityUnleashEvent - moved into handlePlayerUnleashEntityEvent
|
|
}
|
|
|
|
+ // Paper start - EntityLeashEvent
|
|
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callPlayerLeashEntityEvent(this, player, player, hand).isCancelled()) {
|
|
+ ((ServerPlayer) player).connection.send(new net.minecraft.network.protocol.game.ClientboundSetEntityLinkPacket(this, leashable2.getLeashHolder()));
|
|
+ return InteractionResult.PASS;
|
|
+ }
|
|
+ // Paper end - EntityLeashEvent
|
|
leashable2.setLeashedTo(player, true);
|
|
this.playSound(SoundEvents.LEAD_TIED);
|
|
itemInHand1.shrink(1);
|
|
@@ -2203,7 +_,12 @@
|
|
}
|
|
|
|
public boolean shearOffAllLeashConnections(@Nullable Player player) {
|
|
- boolean flag = this.dropAllLeashConnections(player);
|
|
+ // Paper start - EntityUnleashEvent - overload
|
|
+ return this.shearOffAllLeashConnections(player, null);
|
|
+ }
|
|
+ public boolean shearOffAllLeashConnections(@Nullable Player player, @Nullable InteractionHand interactionHand) {
|
|
+ // Paper end - EntityUnleashEvent - overload
|
|
+ boolean flag = this.dropAllLeashConnections(player, interactionHand); // Paper - EntityUnleashEvent - overload
|
|
if (flag && this.level() instanceof ServerLevel serverLevel) {
|
|
serverLevel.playSound(null, this.blockPosition(), SoundEvents.SHEARS_SNIP, player != null ? player.getSoundSource() : this.getSoundSource());
|
|
}
|
|
@@ -2212,15 +_,38 @@
|
|
}
|
|
|
|
public boolean dropAllLeashConnections(@Nullable Player player) {
|
|
+ // Paper start - EntityUnleashEvent - overload
|
|
+ return dropAllLeashConnections(player, null);
|
|
+ }
|
|
+ public boolean dropAllLeashConnections(@Nullable Player player, @Nullable InteractionHand interactionHand) {
|
|
+ // Paper start - EntityUnleashEvent - overload
|
|
List<Leashable> list = Leashable.leashableLeashedTo(this);
|
|
- boolean flag = !list.isEmpty();
|
|
+ boolean flag = false; // Paper - EntityUnleashEvent - compute flag later, events might prevent unleashing all connected leashables.
|
|
if (this instanceof Leashable leashable && leashable.isLeashed()) {
|
|
- leashable.dropLeash();
|
|
- flag = true;
|
|
+ // Paper start - EntityUnleashEvent
|
|
+ flag |= org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent(
|
|
+ this,
|
|
+ player,
|
|
+ interactionHand,
|
|
+ true,
|
|
+ true
|
|
+ );
|
|
+ // Paper end - EntityUnleashEvent
|
|
+ // leashable.dropLeash(); // Paper - EntityUnleashEvent - moved into handlePlayerUnleashEntityEvent
|
|
+ // flag = true; // Paper - EntityUnleashEvent - moved above
|
|
}
|
|
|
|
for (Leashable leashable1 : list) {
|
|
- leashable1.dropLeash();
|
|
+ // Paper start - EntityUnleashEvent
|
|
+ flag |= org.bukkit.craftbukkit.event.CraftEventFactory.handlePlayerUnleashEntityEvent( // Update flag here, if any entity was unleashed, set to true.
|
|
+ leashable1,
|
|
+ player,
|
|
+ interactionHand,
|
|
+ true,
|
|
+ true
|
|
+ );
|
|
+ // leashable1.dropLeash(); // Paper - EntityUnleashEvent - moved into handlePlayerUnleashEntityEvent
|
|
+ // Paper end - EntityUnleashEvent
|
|
}
|
|
|
|
if (flag) {
|
|
@@ -2244,7 +_,9 @@
|
|
this.gameEvent(GameEvent.SHEAR, player);
|
|
this.playSound(equippable.shearingSound().value());
|
|
if (this.level() instanceof ServerLevel serverLevel) {
|
|
+ this.forceDrops = true; // Paper
|
|
this.spawnAtLocation(serverLevel, itemBySlot, average);
|
|
+ this.forceDrops = false; // Paper
|
|
CriteriaTriggers.PLAYER_SHEARED_EQUIPMENT.trigger((ServerPlayer)player, itemBySlot, mob);
|
|
}
|
|
|
|
@@ -2317,11 +_,11 @@
|
|
}
|
|
|
|
public boolean startRiding(Entity entity, boolean force, boolean triggerEvents) {
|
|
- 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) {
|
|
@@ -2331,6 +_,27 @@
|
|
}
|
|
|
|
if (force || this.canRide(entity) && entity.canAddPassenger(this)) {
|
|
+ // CraftBukkit start
|
|
+ if (entity.getBukkitEntity() instanceof org.bukkit.entity.Vehicle && this.getBukkitEntity() instanceof org.bukkit.entity.LivingEntity) {
|
|
+ org.bukkit.event.vehicle.VehicleEnterEvent event = new org.bukkit.event.vehicle.VehicleEnterEvent((org.bukkit.entity.Vehicle) entity.getBukkitEntity(), this.getBukkitEntity());
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ if (event.isCancelled()) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ org.bukkit.event.entity.EntityMountEvent event = new org.bukkit.event.entity.EntityMountEvent(this.getBukkitEntity(), entity.getBukkitEntity());
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ if (event.isCancelled()) {
|
|
+ return false;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
if (this.isPassenger()) {
|
|
this.stopRiding();
|
|
}
|
|
@@ -2363,10 +_,16 @@
|
|
}
|
|
|
|
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
|
|
Entity.RemovalReason removalReason = this.getRemovalReason();
|
|
if (removalReason == null || removalReason.shouldDestroy()) {
|
|
this.level().gameEvent(this, GameEvent.ENTITY_DISMOUNT, entity.position);
|
|
@@ -2375,7 +_,13 @@
|
|
}
|
|
|
|
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) {
|
|
@@ -2397,10 +_,43 @@
|
|
}
|
|
}
|
|
|
|
- protected void removePassenger(Entity passenger) {
|
|
+ // Paper start - Force entity dismount during teleportation
|
|
+ protected boolean removePassenger(Entity passenger) {
|
|
+ return removePassenger(passenger, false);
|
|
+ }
|
|
+ protected boolean removePassenger(Entity passenger, boolean suppressCancellation) { // CraftBukkit
|
|
+ // Paper end - Force entity dismount during teleportation
|
|
if (passenger.getVehicle() == this) {
|
|
throw new IllegalStateException("Use x.stopRiding(y), not y.removePassenger(x)");
|
|
} else {
|
|
+ // CraftBukkit start
|
|
+ org.bukkit.craftbukkit.entity.CraftEntity craft = (org.bukkit.craftbukkit.entity.CraftEntity) passenger.getBukkitEntity().getVehicle();
|
|
+ Entity orig = craft == null ? null : craft.getHandle();
|
|
+ if (this.getBukkitEntity() instanceof org.bukkit.entity.Vehicle && passenger.getBukkitEntity() instanceof org.bukkit.entity.LivingEntity) {
|
|
+ org.bukkit.event.vehicle.VehicleExitEvent event = new org.bukkit.event.vehicle.VehicleExitEvent(
|
|
+ (org.bukkit.entity.Vehicle) this.getBukkitEntity(),
|
|
+ (org.bukkit.entity.LivingEntity) passenger.getBukkitEntity(), !suppressCancellation // Paper - Force entity dismount during teleportation
|
|
+ );
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ org.bukkit.craftbukkit.entity.CraftEntity craftn = (org.bukkit.craftbukkit.entity.CraftEntity) passenger.getBukkitEntity().getVehicle();
|
|
+ Entity n = craftn == null ? null : craftn.getHandle();
|
|
+ if (event.isCancelled() || n != orig) {
|
|
+ return false;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ org.bukkit.event.entity.EntityDismountEvent event = new org.bukkit.event.entity.EntityDismountEvent(passenger.getBukkitEntity(), this.getBukkitEntity(), !suppressCancellation); // Paper - Force entity dismount during teleportation
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ org.bukkit.Bukkit.getPluginManager().callEvent(event);
|
|
+ }
|
|
+ if (event.isCancelled()) {
|
|
+ return false;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
if (this.passengers.size() == 1 && this.passengers.get(0) == passenger) {
|
|
this.passengers = ImmutableList.of();
|
|
} else {
|
|
@@ -2409,6 +_,7 @@
|
|
|
|
passenger.boardingCooldown = 60;
|
|
}
|
|
+ return true; // CraftBukkit
|
|
}
|
|
|
|
protected boolean canAddPassenger(Entity passenger) {
|
|
@@ -2505,8 +_,8 @@
|
|
TeleportTransition portalDestination = this.portalProcess.getPortalDestination(serverLevel, this);
|
|
if (portalDestination != null) {
|
|
ServerLevel level = portalDestination.newLevel();
|
|
- if (serverLevel.getServer().isAllowedToEnterPortal(level)
|
|
- && (level.dimension() == serverLevel.dimension() || this.canTeleport(serverLevel, level))) {
|
|
+ if (this instanceof ServerPlayer // CraftBukkit - always call event for players
|
|
+ || (level != null && (level.dimension() == serverLevel.dimension() || this.canTeleport(serverLevel, level)))) { // CraftBukkit
|
|
this.teleport(portalDestination);
|
|
}
|
|
}
|
|
@@ -2587,7 +_,7 @@
|
|
}
|
|
|
|
public boolean isCrouching() {
|
|
- return this.hasPose(Pose.CROUCHING);
|
|
+ return this.hasPose(net.minecraft.world.entity.Pose.CROUCHING);
|
|
}
|
|
|
|
public boolean isSprinting() {
|
|
@@ -2603,7 +_,7 @@
|
|
}
|
|
|
|
public boolean isVisuallySwimming() {
|
|
- return this.hasPose(Pose.SWIMMING);
|
|
+ return this.hasPose(net.minecraft.world.entity.Pose.SWIMMING);
|
|
}
|
|
|
|
public boolean isVisuallyCrawling() {
|
|
@@ -2611,6 +_,13 @@
|
|
}
|
|
|
|
public void setSwimming(boolean swimming) {
|
|
+ // CraftBukkit start
|
|
+ if (this.valid && this.isSwimming() != swimming && this instanceof net.minecraft.world.entity.LivingEntity) {
|
|
+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callToggleSwimEvent((net.minecraft.world.entity.LivingEntity) this, swimming).isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+ // CraftBukkit end
|
|
this.setSharedFlag(4, swimming);
|
|
}
|
|
|
|
@@ -2649,6 +_,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());
|
|
}
|
|
|
|
@@ -2665,7 +_,11 @@
|
|
}
|
|
|
|
public void setInvisible(boolean invisible) {
|
|
- this.setSharedFlag(5, invisible);
|
|
+ // CraftBukkit - start
|
|
+ if (!this.persistentInvisibility) { // Prevent Minecraft from removing our invisibility flag
|
|
+ this.setSharedFlag(5, invisible);
|
|
+ }
|
|
+ // CraftBukkit - end
|
|
}
|
|
|
|
public boolean getSharedFlag(int flag) {
|
|
@@ -2682,7 +_,7 @@
|
|
}
|
|
|
|
public int getMaxAirSupply() {
|
|
- return 300;
|
|
+ return this.maxAirTicks; // CraftBukkit - SPIGOT-6907: re-implement LivingEntity#setMaximumAir()
|
|
}
|
|
|
|
public int getAirSupply() {
|
|
@@ -2690,10 +_,22 @@
|
|
}
|
|
|
|
public void setAirSupply(int airSupply) {
|
|
- this.entityData.set(DATA_AIR_SUPPLY_ID, airSupply);
|
|
+ // CraftBukkit start
|
|
+ org.bukkit.event.entity.EntityAirChangeEvent event = new org.bukkit.event.entity.EntityAirChangeEvent(this.getBukkitEntity(), airSupply);
|
|
+ // Suppress during worldgen
|
|
+ if (this.valid) {
|
|
+ event.getEntity().getServer().getPluginManager().callEvent(event);
|
|
+ }
|
|
+ if (event.isCancelled() && this.getAirSupply() != airSupply) {
|
|
+ this.entityData.markDirty(DATA_AIR_SUPPLY_ID);
|
|
+ return;
|
|
+ }
|
|
+ this.entityData.set(DATA_AIR_SUPPLY_ID, event.getAmount());
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
public void clearFreeze() {
|
|
+ if (this.freezeLocked) return; // Paper - Freeze Tick Lock API
|
|
this.setTicksFrozen(0);
|
|
}
|
|
|
|
@@ -2720,11 +_,43 @@
|
|
|
|
public void thunderHit(ServerLevel level, 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 org.bukkit.plugin.PluginManager pluginManager = org.bukkit.Bukkit.getPluginManager();
|
|
+ // CraftBukkit end
|
|
if (this.remainingFireTicks == 0) {
|
|
- this.igniteForSeconds(8.0F);
|
|
- }
|
|
-
|
|
- this.hurtServer(level, this.damageSources().lightningBolt(), 5.0F);
|
|
+ // CraftBukkit start - Call a combust event when lightning strikes
|
|
+ org.bukkit.event.entity.EntityCombustByEntityEvent entityCombustEvent = new org.bukkit.event.entity.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
|
|
+ }
|
|
+
|
|
+ // CraftBukkit start
|
|
+ if (thisBukkitEntity instanceof org.bukkit.entity.Hanging) {
|
|
+ org.bukkit.event.hanging.HangingBreakByEntityEvent hangingEvent = new org.bukkit.event.hanging.HangingBreakByEntityEvent((org.bukkit.entity.Hanging) thisBukkitEntity, stormBukkitEntity);
|
|
+ pluginManager.callEvent(hangingEvent);
|
|
+
|
|
+ if (hangingEvent.isCancelled()) {
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ if (this.fireImmune()) {
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ if (!this.hurtServer(level, this.damageSources().lightningBolt().eventEntityDamager(lightning), 5.0F)) { // Paper - fix DamageSource API
|
|
+ return;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
}
|
|
|
|
public void onAboveBubbleColumn(boolean downwards, BlockPos pos) {
|
|
@@ -2880,26 +_,30 @@
|
|
return this.removalReason != null
|
|
? String.format(
|
|
Locale.ROOT,
|
|
- "%s['%s'/%d, l='%s', x=%.2f, y=%.2f, z=%.2f, removed=%s]",
|
|
+ "%s['%s'/%d, uuid='%s', l='%s', x=%.2f, y=%.2f, z=%.2f, cpos=%s, tl=%d, v=%b, removed=%s]", // Paper - add more info
|
|
this.getClass().getSimpleName(),
|
|
this.getPlainTextName(),
|
|
this.id,
|
|
+ this.uuid, // Paper - add more info
|
|
string,
|
|
this.getX(),
|
|
this.getY(),
|
|
this.getZ(),
|
|
+ this.chunkPosition(), this.tickCount, this.valid, // Paper - add more info
|
|
this.removalReason
|
|
)
|
|
: String.format(
|
|
Locale.ROOT,
|
|
- "%s['%s'/%d, l='%s', x=%.2f, y=%.2f, z=%.2f]",
|
|
+ "%s['%s'/%d, uuid='%s', l='%s', x=%.2f, y=%.2f, z=%.2f, cpos=%s, tl=%d, v=%b]", // Paper - add more info
|
|
this.getClass().getSimpleName(),
|
|
this.getPlainTextName(),
|
|
this.id,
|
|
+ this.uuid, // Paper - add more info
|
|
string,
|
|
this.getX(),
|
|
this.getY(),
|
|
- this.getZ()
|
|
+ this.getZ(),
|
|
+ this.chunkPosition(), this.tickCount, this.valid // Paper - add more info
|
|
);
|
|
}
|
|
|
|
@@ -2923,6 +_,13 @@
|
|
}
|
|
|
|
public void restoreFrom(Entity entity) {
|
|
+ // Paper start - Forward CraftEntity in teleport command
|
|
+ org.bukkit.craftbukkit.entity.CraftEntity bukkitEntity = entity.bukkitEntity;
|
|
+ if (bukkitEntity != null) {
|
|
+ bukkitEntity.setHandle(this);
|
|
+ this.bukkitEntity = bukkitEntity;
|
|
+ }
|
|
+ // Paper end - Forward CraftEntity in teleport command
|
|
try (ProblemReporter.ScopedCollector scopedCollector = new ProblemReporter.ScopedCollector(this.problemPath(), LOGGER)) {
|
|
TagValueOutput tagValueOutput = TagValueOutput.createWithContext(scopedCollector, entity.registryAccess());
|
|
entity.saveWithoutId(tagValueOutput);
|
|
@@ -2935,7 +_,65 @@
|
|
|
|
@Nullable
|
|
public Entity teleport(TeleportTransition teleportTransition) {
|
|
+ // Paper start - Fix item duplication and teleport issues
|
|
+ if ((!this.isAlive() || !this.valid) && (teleportTransition.newLevel() != this.level)) {
|
|
+ LOGGER.warn("Illegal Entity Teleport {} to {}:{}", this, teleportTransition.newLevel(), teleportTransition.position(), new Throwable());
|
|
+ return null;
|
|
+ }
|
|
+ // Paper end - Fix item duplication and teleport issues
|
|
if (this.level() instanceof ServerLevel serverLevel && !this.isRemoved()) {
|
|
+ // CraftBukkit start
|
|
+ PositionMoveRotation absolutePosition = PositionMoveRotation.calculateAbsolute(PositionMoveRotation.of(this), PositionMoveRotation.of(teleportTransition), teleportTransition.relatives());
|
|
+ Vec3 velocity = absolutePosition.deltaMovement(); // Paper
|
|
+ org.bukkit.Location to = org.bukkit.craftbukkit.util.CraftLocation.toBukkit(absolutePosition.position(), teleportTransition.newLevel(), absolutePosition.yRot(), absolutePosition.xRot());
|
|
+ // Paper start - gateway-specific teleport event
|
|
+ final org.bukkit.event.entity.EntityTeleportEvent teleEvent;
|
|
+ if (this.portalProcess != null && this.portalProcess.isSamePortal(((net.minecraft.world.level.block.EndGatewayBlock) 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 = org.bukkit.craftbukkit.event.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();
|
|
+ teleportTransition = new TeleportTransition(((org.bukkit.craftbukkit.CraftWorld) to.getWorld()).getHandle(), org.bukkit.craftbukkit.util.CraftLocation.toVec3(to), Vec3.ZERO, to.getYaw(), to.getPitch(), teleportTransition.missingRespawnBlock(), teleportTransition.asPassenger(), Set.of(), teleportTransition.postTeleportTransition(), teleportTransition.cause());
|
|
+ // Paper start - Call EntityPortalExitEvent
|
|
+ velocity = Vec3.ZERO;
|
|
+ }
|
|
+ if (this.portalProcess != null) { // if in a portal
|
|
+ org.bukkit.entity.Entity bukkitEntity = this.getBukkitEntity();
|
|
+ org.bukkit.util.Vector before = bukkitEntity.getVelocity();
|
|
+ org.bukkit.util.Vector after = org.bukkit.craftbukkit.util.CraftVector.toBukkit(velocity);
|
|
+ org.bukkit.event.entity.EntityPortalExitEvent event = new org.bukkit.event.entity.EntityPortalExitEvent(
|
|
+ bukkitEntity,
|
|
+ bukkitEntity.getLocation(), to.clone(),
|
|
+ before, after
|
|
+ );
|
|
+ event.callEvent();
|
|
+
|
|
+ // Only change the target if actually needed, since we reset relative flags
|
|
+ if (event.isCancelled() || !to.equals(event.getTo()) || !after.equals(event.getAfter())) {
|
|
+ if (event.isCancelled() || event.getTo() == null) {
|
|
+ org.bukkit.World toWorld = to.getWorld();
|
|
+ to = event.getFrom().clone();
|
|
+ to.setWorld(toWorld); // cancelling doesn't cancel the teleport just the position/velocity (old quirk)
|
|
+ velocity = org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getBefore());
|
|
+ } else {
|
|
+ to = event.getTo().clone();
|
|
+ velocity = org.bukkit.craftbukkit.util.CraftVector.toVec3(event.getAfter());
|
|
+ }
|
|
+ teleportTransition = new TeleportTransition(((org.bukkit.craftbukkit.CraftWorld) to.getWorld()).getHandle(), org.bukkit.craftbukkit.util.CraftLocation.toVec3(to), velocity, to.getYaw(), to.getPitch(), teleportTransition.missingRespawnBlock(), teleportTransition.asPassenger(), Set.of(), teleportTransition.postTeleportTransition(), teleportTransition.cause());
|
|
+ }
|
|
+ }
|
|
+ if (this.isRemoved()) {
|
|
+ return null;
|
|
+ }
|
|
+ // Paper end - Call EntityPortalExitEvent
|
|
+ // CraftBukkit end
|
|
ServerLevel level = teleportTransition.newLevel();
|
|
boolean flag = level.dimension() != serverLevel.dimension();
|
|
if (!teleportTransition.asPassenger()) {
|
|
@@ -2985,10 +_,15 @@
|
|
profilerFiller.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
|
|
entityx.restoreFrom(this);
|
|
this.removeAfterChangingDimensions();
|
|
entityx.teleportSetPosition(PositionMoveRotation.of(this), PositionMoveRotation.of(teleportTransition), teleportTransition.relatives());
|
|
- newLevel.addDuringTeleport(entityx);
|
|
+ if (this.inWorld) newLevel.addDuringTeleport(entityx); // CraftBukkit - Don't spawn the new entity if the current entity isn't spawned
|
|
|
|
for (Entity entity2 : list) {
|
|
entity2.startRiding(entityx, true, false);
|
|
@@ -3078,9 +_,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 org.bukkit.event.entity.EntityUnleashEvent event = new org.bukkit.event.entity.EntityUnleashEvent(this.getBukkitEntity(), org.bukkit.event.entity.EntityUnleashEvent.UnleashReason.UNKNOWN, false); // CraftBukkit
|
|
+ event.callEvent();
|
|
+ if (!event.isDropLeash()) {
|
|
+ leashable.removeLeash();
|
|
+ } else {
|
|
+ leashable.dropLeash();
|
|
+ }
|
|
+ // Paper end - Expand EntityUnleashEvent
|
|
}
|
|
|
|
if (this instanceof WaypointTransmitter waypointTransmitter && this.level instanceof ServerLevel serverLevel) {
|
|
@@ -3097,6 +_,7 @@
|
|
}
|
|
|
|
public boolean canTeleport(Level fromLevel, Level toLevel) {
|
|
+ if (!this.isAlive() || !this.valid) return false; // Paper - Fix item duplication and teleport issues
|
|
if (fromLevel.dimension() == Level.END && toLevel.dimension() == Level.OVERWORLD) {
|
|
for (Entity entity : this.getPassengers()) {
|
|
if (entity instanceof ServerPlayer serverPlayer && !serverPlayer.seenCredits) {
|
|
@@ -3204,13 +_,19 @@
|
|
return this.entityData.get(DATA_CUSTOM_NAME_VISIBLE);
|
|
}
|
|
|
|
- public boolean teleportTo(ServerLevel level, double x, double y, double z, Set<Relative> relativeMovements, float yaw, float pitch, boolean setCamera) {
|
|
- Entity entity = this.teleport(new TeleportTransition(level, new Vec3(x, y, z), Vec3.ZERO, yaw, pitch, relativeMovements, TeleportTransition.DO_NOTHING));
|
|
+ // CraftBukkit start
|
|
+ public final boolean teleportTo(ServerLevel level, double x, double y, double z, Set<Relative> relativeMovements, float yaw, float pitch, boolean setCamera) {
|
|
+ return this.teleportTo(level, x, y, z, relativeMovements, yaw, pitch, setCamera, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause.UNKNOWN);
|
|
+ }
|
|
+
|
|
+ public boolean teleportTo(ServerLevel level, double x, double y, double z, Set<Relative> relativeMovements, float yaw, float pitch, boolean setCamera, org.bukkit.event.player.PlayerTeleportEvent.TeleportCause cause) {
|
|
+ // CraftBukkit end
|
|
+ Entity entity = this.teleport(new TeleportTransition(level, new Vec3(x, y, z), Vec3.ZERO, yaw, pitch, relativeMovements, TeleportTransition.DO_NOTHING, cause)); // CraftBukkit
|
|
return entity != null;
|
|
}
|
|
|
|
public void dismountTo(double x, double y, double z) {
|
|
- this.teleportTo(x, y, z);
|
|
+ this.teleportTo(x, y, z); // Paper - diff on change for override
|
|
}
|
|
|
|
public void teleportTo(double x, double y, double z) {
|
|
@@ -3319,7 +_,26 @@
|
|
}
|
|
|
|
public final void setBoundingBox(AABB bb) {
|
|
- this.bb = bb;
|
|
+ // CraftBukkit start - block invalid bounding boxes
|
|
+ double minX = bb.minX,
|
|
+ minY = bb.minY,
|
|
+ minZ = bb.minZ,
|
|
+ maxX = bb.maxX,
|
|
+ maxY = bb.maxY,
|
|
+ maxZ = bb.maxZ;
|
|
+ double len = bb.maxX - bb.minX;
|
|
+ if (len < 0) maxX = minX;
|
|
+ if (len > 64) maxX = minX + 64.0;
|
|
+
|
|
+ len = bb.maxY - bb.minY;
|
|
+ if (len < 0) maxY = minY;
|
|
+ if (len > 64) maxY = minY + 64.0;
|
|
+
|
|
+ len = bb.maxZ - bb.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) {
|
|
@@ -3346,6 +_,12 @@
|
|
}
|
|
|
|
public void stopSeenByPlayer(ServerPlayer player) {
|
|
+ // Paper start - entity tracking events
|
|
+ // 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 transformRotation) {
|
|
@@ -3374,7 +_,7 @@
|
|
}
|
|
|
|
@Nullable
|
|
- public LivingEntity getControllingPassenger() {
|
|
+ public net.minecraft.world.entity.LivingEntity getControllingPassenger() {
|
|
return null;
|
|
}
|
|
|
|
@@ -3406,21 +_,32 @@
|
|
}
|
|
|
|
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() {
|
|
- return () -> this.getIndirectPassengersStream().iterator();
|
|
+ // 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();
|
|
+ // Paper end - Optimize indirect passenger iteration
|
|
}
|
|
|
|
public int countPlayerPassengers() {
|
|
@@ -3428,6 +_,7 @@
|
|
}
|
|
|
|
public boolean hasExactlyOnePlayerPassenger() {
|
|
+ if (this.passengers.isEmpty()) { return false; } // Paper - Optimize indirect passenger iteration
|
|
return this.countPlayerPassengers() == 1;
|
|
}
|
|
|
|
@@ -3510,9 +_,38 @@
|
|
return 0;
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ private final CommandSource commandSource = new CommandSource() {
|
|
+
|
|
+ @Override
|
|
+ public void sendSystemMessage(Component message) {
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public org.bukkit.command.CommandSender getBukkitSender(CommandSourceStack wrapper) {
|
|
+ return Entity.this.getBukkitEntity();
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean acceptsSuccess() {
|
|
+ return ((ServerLevel) Entity.this.level()).getGameRules().getBoolean(net.minecraft.world.level.GameRules.RULE_SENDCOMMANDFEEDBACK);
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean acceptsFailure() {
|
|
+ return true;
|
|
+ }
|
|
+
|
|
+ @Override
|
|
+ public boolean shouldInformAdmins() {
|
|
+ return true;
|
|
+ }
|
|
+ };
|
|
+ // CraftBukkit end
|
|
+
|
|
public CommandSourceStack createCommandSourceStackForNameResolution(ServerLevel level) {
|
|
return new CommandSourceStack(
|
|
- CommandSource.NULL, this.position(), this.getRotationVector(), level, 0, this.getPlainTextName(), this.getDisplayName(), level.getServer(), this
|
|
+ this.commandSource, this.position(), this.getRotationVector(), level, 0, this.getPlainTextName(), this.getDisplayName(), level.getServer(), this // CraftBukkit
|
|
);
|
|
}
|
|
|
|
@@ -3570,6 +_,11 @@
|
|
vec3 = vec3.add(flow);
|
|
i++;
|
|
}
|
|
+ // CraftBukkit start - store last lava contact location
|
|
+ if (fluidTag == FluidTags.LAVA) {
|
|
+ this.lastLavaContact = mutableBlockPos.immutable();
|
|
+ }
|
|
+ // CraftBukkit end
|
|
}
|
|
}
|
|
}
|
|
@@ -3668,7 +_,9 @@
|
|
}
|
|
|
|
public void setDeltaMovement(Vec3 deltaMovement) {
|
|
+ synchronized (this.posLock) { // Paper - detailed watchdog information
|
|
this.deltaMovement = deltaMovement;
|
|
+ } // Paper - detailed watchdog information
|
|
}
|
|
|
|
public void addDeltaMovement(Vec3 addend) {
|
|
@@ -3731,9 +_,35 @@
|
|
return this.getZ((2.0 * this.random.nextDouble() - 1.0) * scale);
|
|
}
|
|
|
|
+ // 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
|
|
if (this.position.x != x || this.position.y != y || this.position.z != z) {
|
|
+ synchronized (this.posLock) { // Paper - detailed watchdog information
|
|
this.position = new Vec3(x, y, z);
|
|
+ } // Paper - detailed watchdog information
|
|
int floor = Mth.floor(x);
|
|
int floor1 = Mth.floor(y);
|
|
int floor2 = Mth.floor(z);
|
|
@@ -3755,7 +_,18 @@
|
|
serverLevel.getWaypointManager().updatePlayer(serverPlayer);
|
|
}
|
|
}
|
|
- }
|
|
+ // Paper start - Fix MC-44654
|
|
+ if (this.getType().updateInterval() == Integer.MAX_VALUE) {
|
|
+ this.hasImpulse = true;
|
|
+ }
|
|
+ // Paper end - Fix MC-44654
|
|
+ }
|
|
+ // 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() {
|
|
@@ -3808,6 +_,12 @@
|
|
return this.getTicksFrozen() > 0;
|
|
}
|
|
|
|
+ // CraftBukkit start
|
|
+ public float getBukkitYaw() {
|
|
+ return this.yRot;
|
|
+ }
|
|
+ // CraftBukkit end
|
|
+
|
|
public float getYRot() {
|
|
return this.yRot;
|
|
}
|
|
@@ -3859,7 +_,9 @@
|
|
}
|
|
|
|
@Override
|
|
- public final void setRemoved(Entity.RemovalReason removalReason) {
|
|
+ public final void setRemoved(Entity.RemovalReason removalReason, @Nullable org.bukkit.event.entity.EntityRemoveEvent.Cause cause) { // CraftBukkit - add Bukkit remove cause
|
|
+ org.bukkit.craftbukkit.event.CraftEventFactory.callEntityRemoveEvent(this, cause); // CraftBukkit
|
|
+ final boolean alreadyRemoved = this.removalReason != null; // Paper - Folia schedulers
|
|
if (this.removalReason == null) {
|
|
this.removalReason = removalReason;
|
|
}
|
|
@@ -3871,12 +_,28 @@
|
|
this.getPassengers().forEach(Entity::stopRiding);
|
|
this.levelCallback.onRemove(removalReason);
|
|
this.onRemoval(removalReason);
|
|
+ // Paper start - Folia schedulers
|
|
+ if (!(this instanceof ServerPlayer) && 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 levelCallback) {
|
|
this.levelCallback = levelCallback;
|
|
@@ -4073,4 +_,14 @@
|
|
return this.save;
|
|
}
|
|
}
|
|
+
|
|
+ // Paper start - Expose entity id counter
|
|
+ public static int nextEntityId() {
|
|
+ return ENTITY_COUNTER.incrementAndGet();
|
|
+ }
|
|
+
|
|
+ public boolean isTicking() {
|
|
+ return ((ServerLevel) this.level()).isPositionEntityTicking(this.blockPosition());
|
|
+ }
|
|
+ // Paper end - Expose entity id counter
|
|
}
|