diff --git a/feature-patches/1043-Entity-Activation-Range-2.0.patch b/feature-patches/1043-Entity-Activation-Range-2.0.patch index 2a9fee105b..3767bb01b0 100644 --- a/feature-patches/1043-Entity-Activation-Range-2.0.patch +++ b/feature-patches/1043-Entity-Activation-Range-2.0.patch @@ -13,38 +13,499 @@ Adds water Mobs to activation range config and nerfs fish Adds flying monsters to control ghast and phantoms Adds villagers as separate config - +diff --git a/io/papermc/paper/entity/activation/ActivationRange.java b/io/papermc/paper/entity/activation/ActivationRange.java +new file mode 100644 +index 0000000000000000000000000000000000000000..bd888ef719b9bfc93bace0b1d0fb771ac659f515 +--- /dev/null ++++ b/io/papermc/paper/entity/activation/ActivationRange.java +@@ -0,0 +1,318 @@ ++package io.papermc.paper.entity.activation; ++ ++import net.minecraft.core.BlockPos; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.entity.ExperienceOrb; ++import net.minecraft.world.entity.FlyingMob; ++import net.minecraft.world.entity.LightningBolt; ++import net.minecraft.world.entity.LivingEntity; ++import net.minecraft.world.entity.Mob; ++import net.minecraft.world.entity.ai.Brain; ++import net.minecraft.world.entity.animal.Animal; ++import net.minecraft.world.entity.animal.Bee; ++import net.minecraft.world.entity.animal.Sheep; ++import net.minecraft.world.entity.animal.horse.Llama; ++import net.minecraft.world.entity.boss.EnderDragonPart; ++import net.minecraft.world.entity.boss.enderdragon.EndCrystal; ++import net.minecraft.world.entity.boss.enderdragon.EnderDragon; ++import net.minecraft.world.entity.boss.wither.WitherBoss; ++import net.minecraft.world.entity.item.ItemEntity; ++import net.minecraft.world.entity.item.PrimedTnt; ++import net.minecraft.world.entity.monster.Creeper; ++import net.minecraft.world.entity.monster.Pillager; ++import net.minecraft.world.entity.npc.Villager; ++import net.minecraft.world.entity.player.Player; ++import net.minecraft.world.entity.projectile.AbstractArrow; ++import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; ++import net.minecraft.world.entity.projectile.EyeOfEnder; ++import net.minecraft.world.entity.projectile.FireworkRocketEntity; ++import net.minecraft.world.entity.projectile.ThrowableProjectile; ++import net.minecraft.world.entity.projectile.ThrownTrident; ++import net.minecraft.world.entity.schedule.Activity; ++import net.minecraft.world.level.Level; ++import net.minecraft.world.phys.AABB; ++import org.spigotmc.SpigotWorldConfig; ++ ++public final class ActivationRange { ++ ++ private ActivationRange() { ++ } ++ ++ static Activity[] VILLAGER_PANIC_IMMUNITIES = { ++ Activity.HIDE, ++ Activity.PRE_RAID, ++ Activity.RAID, ++ Activity.PANIC ++ }; ++ ++ private static int checkInactiveWakeup(final Entity entity) { ++ final Level world = entity.level(); ++ final SpigotWorldConfig config = world.spigotConfig; ++ final long inactiveFor = MinecraftServer.currentTick - entity.activatedTick; ++ if (entity.activationType == ActivationType.VILLAGER) { ++ if (inactiveFor > config.wakeUpInactiveVillagersEvery && world.wakeupInactiveRemainingVillagers > 0) { ++ world.wakeupInactiveRemainingVillagers--; ++ return config.wakeUpInactiveVillagersFor; ++ } ++ } else if (entity.activationType == ActivationType.ANIMAL) { ++ if (inactiveFor > config.wakeUpInactiveAnimalsEvery && world.wakeupInactiveRemainingAnimals > 0) { ++ world.wakeupInactiveRemainingAnimals--; ++ return config.wakeUpInactiveAnimalsFor; ++ } ++ } else if (entity.activationType == ActivationType.FLYING_MONSTER) { ++ if (inactiveFor > config.wakeUpInactiveFlyingEvery && world.wakeupInactiveRemainingFlying > 0) { ++ world.wakeupInactiveRemainingFlying--; ++ return config.wakeUpInactiveFlyingFor; ++ } ++ } else if (entity.activationType == ActivationType.MONSTER || entity.activationType == ActivationType.RAIDER) { ++ if (inactiveFor > config.wakeUpInactiveMonstersEvery && world.wakeupInactiveRemainingMonsters > 0) { ++ world.wakeupInactiveRemainingMonsters--; ++ return config.wakeUpInactiveMonstersFor; ++ } ++ } ++ return -1; ++ } ++ ++ static AABB maxBB = new AABB(0, 0, 0, 0, 0, 0); ++ ++ /** ++ * These entities are excluded from Activation range checks. ++ * ++ * @param entity ++ * @param config ++ * @return boolean If it should always tick. ++ */ ++ public static boolean initializeEntityActivationState(final Entity entity, final SpigotWorldConfig config) { ++ return (entity.activationType == ActivationType.MISC && config.miscActivationRange == 0) ++ || (entity.activationType == ActivationType.RAIDER && config.raiderActivationRange == 0) ++ || (entity.activationType == ActivationType.ANIMAL && config.animalActivationRange == 0) ++ || (entity.activationType == ActivationType.MONSTER && config.monsterActivationRange == 0) ++ || (entity.activationType == ActivationType.VILLAGER && config.villagerActivationRange <= 0) ++ || (entity.activationType == ActivationType.WATER && config.waterActivationRange <= 0) ++ || (entity.activationType == ActivationType.FLYING_MONSTER && config.flyingMonsterActivationRange <= 0) ++ || entity instanceof EyeOfEnder ++ || entity instanceof Player ++ || entity instanceof ThrowableProjectile ++ || entity instanceof EnderDragon ++ || entity instanceof EnderDragonPart ++ || entity instanceof WitherBoss ++ || entity instanceof AbstractHurtingProjectile ++ || entity instanceof LightningBolt ++ || entity instanceof PrimedTnt ++ || entity instanceof net.minecraft.world.entity.item.FallingBlockEntity ++ || entity instanceof net.minecraft.world.entity.vehicle.AbstractMinecart ++ || entity instanceof net.minecraft.world.entity.vehicle.AbstractBoat ++ || entity instanceof EndCrystal ++ || entity instanceof FireworkRocketEntity ++ || entity instanceof ThrownTrident; ++ } ++ ++ /** ++ * Find what entities are in range of the players in the world and set ++ * active if in range. ++ * ++ * @param world ++ */ ++ public static void activateEntities(final Level world) { ++ final int miscActivationRange = world.spigotConfig.miscActivationRange; ++ final int raiderActivationRange = world.spigotConfig.raiderActivationRange; ++ final int animalActivationRange = world.spigotConfig.animalActivationRange; ++ final int monsterActivationRange = world.spigotConfig.monsterActivationRange; ++ final int waterActivationRange = world.spigotConfig.waterActivationRange; ++ final int flyingActivationRange = world.spigotConfig.flyingMonsterActivationRange; ++ final int villagerActivationRange = world.spigotConfig.villagerActivationRange; ++ world.wakeupInactiveRemainingAnimals = Math.min(world.wakeupInactiveRemainingAnimals + 1, world.spigotConfig.wakeUpInactiveAnimals); ++ world.wakeupInactiveRemainingVillagers = Math.min(world.wakeupInactiveRemainingVillagers + 1, world.spigotConfig.wakeUpInactiveVillagers); ++ world.wakeupInactiveRemainingMonsters = Math.min(world.wakeupInactiveRemainingMonsters + 1, world.spigotConfig.wakeUpInactiveMonsters); ++ world.wakeupInactiveRemainingFlying = Math.min(world.wakeupInactiveRemainingFlying + 1, world.spigotConfig.wakeUpInactiveFlying); ++ ++ int maxRange = Math.max(monsterActivationRange, animalActivationRange); ++ maxRange = Math.max(maxRange, raiderActivationRange); ++ maxRange = Math.max(maxRange, miscActivationRange); ++ maxRange = Math.max(maxRange, flyingActivationRange); ++ maxRange = Math.max(maxRange, waterActivationRange); ++ maxRange = Math.max(maxRange, villagerActivationRange); ++ maxRange = Math.min((world.spigotConfig.simulationDistance << 4) - 8, maxRange); ++ ++ for (final Player player : world.players()) { ++ player.activatedTick = MinecraftServer.currentTick; ++ if (world.spigotConfig.ignoreSpectatorActivation && player.isSpectator()) { ++ continue; ++ } ++ ++ final int worldHeight = world.getHeight(); ++ ActivationRange.maxBB = player.getBoundingBox().inflate(maxRange, worldHeight, maxRange); ++ ActivationType.MISC.boundingBox = player.getBoundingBox().inflate(miscActivationRange, worldHeight, miscActivationRange); ++ ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate(raiderActivationRange, worldHeight, raiderActivationRange); ++ ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate(animalActivationRange, worldHeight, animalActivationRange); ++ ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate(monsterActivationRange, worldHeight, monsterActivationRange); ++ ActivationType.WATER.boundingBox = player.getBoundingBox().inflate(waterActivationRange, worldHeight, waterActivationRange); ++ ActivationType.FLYING_MONSTER.boundingBox = player.getBoundingBox().inflate(flyingActivationRange, worldHeight, flyingActivationRange); ++ ActivationType.VILLAGER.boundingBox = player.getBoundingBox().inflate(villagerActivationRange, worldHeight, villagerActivationRange); ++ ++ final java.util.List entities = world.getEntities((Entity) null, ActivationRange.maxBB, e -> true); ++ final boolean tickMarkers = world.paperConfig().entities.markers.tick; ++ for (final Entity entity : entities) { ++ if (!tickMarkers && entity instanceof net.minecraft.world.entity.Marker) { ++ continue; ++ } ++ ++ ActivationRange.activateEntity(entity); ++ } ++ } ++ } ++ ++ /** ++ * Tries to activate an entity. ++ * ++ * @param entity ++ */ ++ private static void activateEntity(final Entity entity) { ++ if (MinecraftServer.currentTick > entity.activatedTick) { ++ if (entity.defaultActivationState) { ++ entity.activatedTick = MinecraftServer.currentTick; ++ return; ++ } ++ if (entity.activationType.boundingBox.intersects(entity.getBoundingBox())) { ++ entity.activatedTick = MinecraftServer.currentTick; ++ } ++ } ++ } ++ ++ /** ++ * If an entity is not in range, do some more checks to see if we should ++ * give it a shot. ++ * ++ * @param entity ++ * @return ++ */ ++ public static int checkEntityImmunities(final Entity entity) { // return # of ticks to get immunity ++ final SpigotWorldConfig config = entity.level().spigotConfig; ++ final int inactiveWakeUpImmunity = checkInactiveWakeup(entity); ++ if (inactiveWakeUpImmunity > -1) { ++ return inactiveWakeUpImmunity; ++ } ++ if (entity.getRemainingFireTicks() > 0) { ++ return 2; ++ } ++ if (entity.activatedImmunityTick >= MinecraftServer.currentTick) { ++ return 1; ++ } ++ final long inactiveFor = MinecraftServer.currentTick - entity.activatedTick; ++ if ((entity.activationType != ActivationType.WATER && entity.isInWater() && entity.isPushedByFluid())) { ++ return 100; ++ } ++ if (!entity.onGround() || entity.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D) { ++ return 100; ++ } ++ if (!(entity instanceof final AbstractArrow arrow)) { ++ if ((!entity.onGround() && !(entity instanceof FlyingMob))) { ++ return 10; ++ } ++ } else if (!arrow.isInGround()) { ++ return 1; ++ } ++ // special cases. ++ if (entity instanceof final LivingEntity living) { ++ if (living.onClimbable() || living.jumping || living.hurtTime > 0 || !living.activeEffects.isEmpty() || living.isFreezing()) { ++ return 1; ++ } ++ if (entity instanceof final Mob mob && mob.getTarget() != null) { ++ return 20; ++ } ++ if (entity instanceof final Bee bee) { ++ final BlockPos movingTarget = bee.getMovingTarget(); ++ if (bee.isAngry() || ++ (bee.getHivePos() != null && bee.getHivePos().equals(movingTarget)) || ++ (bee.getSavedFlowerPos() != null && bee.getSavedFlowerPos().equals(movingTarget)) ++ ) { ++ return 20; ++ } ++ } ++ if (entity instanceof final Villager villager) { ++ final Brain behaviorController = villager.getBrain(); ++ ++ if (config.villagersActiveForPanic) { ++ for (final Activity activity : VILLAGER_PANIC_IMMUNITIES) { ++ if (behaviorController.isActive(activity)) { ++ return 20 * 5; ++ } ++ } ++ } ++ ++ if (config.villagersWorkImmunityAfter > 0 && inactiveFor >= config.villagersWorkImmunityAfter) { ++ if (behaviorController.isActive(Activity.WORK)) { ++ return config.villagersWorkImmunityFor; ++ } ++ } ++ } ++ if (entity instanceof final Llama llama && llama.inCaravan()) { ++ return 1; ++ } ++ if (entity instanceof final Animal animal) { ++ if (animal.isBaby() || animal.isInLove()) { ++ return 5; ++ } ++ if (entity instanceof final Sheep sheep && sheep.isSheared()) { ++ return 1; ++ } ++ } ++ if (entity instanceof final Creeper creeper && creeper.isIgnited()) { // isExplosive ++ return 20; ++ } ++ if (entity instanceof final Mob mob && mob.targetSelector.hasTasks()) { ++ return 0; ++ } ++ if (entity instanceof final Pillager pillager) { ++ // TODO:? ++ } ++ } ++ // SPIGOT-6644: Otherwise the target refresh tick will be missed ++ if (entity instanceof ExperienceOrb) { ++ return 20; ++ } ++ return -1; ++ } ++ ++ /** ++ * Checks if the entity is active for this tick. ++ * ++ * @param entity ++ * @return ++ */ ++ public static boolean checkIfActive(final Entity entity) { ++ // Never safe to skip fireworks or item gravity ++ if (entity instanceof FireworkRocketEntity || (entity instanceof ItemEntity && (entity.tickCount + entity.getId()) % 4 == 0)) { // Needed for item gravity, see ItemEntity tick ++ return true; ++ } ++ // special case always immunities ++ // immunize brand-new entities, dead entities, and portal scenarios ++ if (entity.defaultActivationState || entity.tickCount < 20 * 10 || !entity.isAlive() || (entity.portalProcess != null && !entity.portalProcess.hasExpired()) || entity.portalCooldown > 0) { ++ return true; ++ } ++ // immunize leashed entities ++ if (entity instanceof final Mob mob && mob.getLeashHolder() instanceof Player) { ++ return true; ++ } ++ ++ boolean isActive = entity.activatedTick >= MinecraftServer.currentTick; ++ entity.isTemporarilyActive = false; ++ ++ // Should this entity tick? ++ if (!isActive) { ++ if ((MinecraftServer.currentTick - entity.activatedTick - 1) % 20 == 0) { ++ // Check immunities every 20 ticks. ++ final int immunity = checkEntityImmunities(entity); ++ if (immunity >= 0) { ++ entity.activatedTick = MinecraftServer.currentTick + immunity; ++ } else { ++ entity.isTemporarilyActive = true; ++ } ++ isActive = true; ++ } ++ } ++ // removed the original's dumb tick skipping for active entities ++ return isActive; ++ } ++} +diff --git a/net/minecraft/server/level/ChunkMap.java b/net/minecraft/server/level/ChunkMap.java +index 9bbcafa8e70f95d5ab6318211a0265acbfc76b1c..f8f145cd9614f7e38d6aa72501fe31837340a8bb 100644 +--- a/net/minecraft/server/level/ChunkMap.java ++++ b/net/minecraft/server/level/ChunkMap.java +@@ -4,7 +4,6 @@ import com.google.common.collect.ImmutableList; + import com.google.common.collect.Iterables; + import com.google.common.collect.Lists; + import com.google.common.collect.Queues; +-import com.google.common.collect.Sets; + import com.google.common.collect.ImmutableList.Builder; + import com.mojang.datafixers.DataFixer; + import com.mojang.logging.LogUtils; +@@ -19,7 +18,6 @@ import it.unimi.dsi.fastutil.longs.LongIterator; + import it.unimi.dsi.fastutil.longs.LongLinkedOpenHashSet; + import it.unimi.dsi.fastutil.longs.LongOpenHashSet; + import it.unimi.dsi.fastutil.longs.LongSet; +-import it.unimi.dsi.fastutil.longs.Long2ObjectMap.Entry; + import java.io.IOException; + import java.io.Writer; + import java.nio.file.Path; +@@ -95,7 +93,6 @@ import net.minecraft.world.level.levelgen.structure.StructureStart; + import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager; + import net.minecraft.world.level.storage.DimensionDataStorage; + import net.minecraft.world.level.storage.LevelStorageSource; +-import net.minecraft.world.phys.Vec3; + import org.apache.commons.lang3.mutable.MutableBoolean; + import org.slf4j.Logger; + diff --git a/net/minecraft/server/level/ServerLevel.java b/net/minecraft/server/level/ServerLevel.java -index 1d7e9492a474c99dff372d6b57f1f195e42d5114..aa5eed48871b5ab67d18213e532271241aae9182 100644 +index 678b3027e8c53e6021ea49afa69cdbe5f60dcf25..c2d9f1e6c68f4945e3b9e5b95326e8180fa7a0fb 100644 --- a/net/minecraft/server/level/ServerLevel.java +++ b/net/minecraft/server/level/ServerLevel.java -@@ -963,10 +963,10 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe +@@ -551,6 +551,7 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + profilerFiller.pop(); + } + ++ io.papermc.paper.entity.activation.ActivationRange.activateEntities(this); // Paper - EAR + this.entityTickList + .forEach( + entity -> { +@@ -960,16 +961,19 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + entity.tickCount++; + profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()); profilerFiller.incrementCounter("tickNonPassenger"); - // Spigot start - final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity); // Paper - EAR 2 -- if (isActive) { ++ final boolean isActive = io.papermc.paper.entity.activation.ActivationRange.checkIfActive(entity); // Paper - EAR 2 + if (isActive) { // Paper - EAR 2 entity.tick(); entity.postTick(); // CraftBukkit -- } else {entity.inactiveTick();} // Spigot end + } else {entity.inactiveTick();} // Paper - EAR 2 profilerFiller.pop(); for (Entity entity1 : entity.getPassengers()) { +- this.tickPassenger(entity, entity1); ++ this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 + } + } + +- private void tickPassenger(Entity ridingEntity, Entity passengerEntity) { ++ private void tickPassenger(Entity ridingEntity, Entity passengerEntity, final boolean isActive) { // Paper - EAR 2 + if (passengerEntity.isRemoved() || passengerEntity.getVehicle() != ridingEntity) { + passengerEntity.stopRiding(); + } else if (passengerEntity instanceof Player || this.entityTickList.contains(passengerEntity)) { +@@ -978,12 +982,21 @@ public class ServerLevel extends Level implements ServerEntityGetter, WorldGenLe + ProfilerFiller profilerFiller = Profiler.get(); + profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString()); + profilerFiller.incrementCounter("tickPassenger"); ++ // Paper start - EAR 2 ++ if (isActive) { + passengerEntity.rideTick(); + passengerEntity.postTick(); // CraftBukkit ++ } else { ++ passengerEntity.setDeltaMovement(Vec3.ZERO); ++ passengerEntity.inactiveTick(); ++ // copied from inside of if (isPassenger()) of passengerTick, but that ifPassenger is unnecessary ++ ridingEntity.positionRider(passengerEntity); ++ } ++ // Paper end - EAR 2 + profilerFiller.pop(); + + for (Entity entity : passengerEntity.getPassengers()) { +- this.tickPassenger(passengerEntity, entity); ++ this.tickPassenger(passengerEntity, entity, isActive); // Paper - EAR 2 + } + } + } +diff --git a/net/minecraft/world/entity/AgeableMob.java b/net/minecraft/world/entity/AgeableMob.java +index a9f01e616ef6b0d74caf57cd68eb371a4fd30fd5..179f4e4b9b1eb57f78bbb2f9fa34b11ea79b7a88 100644 +--- a/net/minecraft/world/entity/AgeableMob.java ++++ b/net/minecraft/world/entity/AgeableMob.java +@@ -126,6 +126,23 @@ public abstract class AgeableMob extends PathfinderMob { + super.onSyncedDataUpdated(key); + } + ++ // Paper start - EAR 2 ++ @Override ++ public void inactiveTick() { ++ super.inactiveTick(); ++ if (this.level().isClientSide || this.ageLocked) { // CraftBukkit ++ this.refreshDimensions(); ++ } else { ++ int age = this.getAge(); ++ if (age < 0) { ++ this.setAge(++age); ++ } else if (age > 0) { ++ this.setAge(--age); ++ } ++ } ++ } ++ // Paper end - EAR 2 ++ + @Override + public void aiStep() { + super.aiStep(); +diff --git a/net/minecraft/world/entity/AreaEffectCloud.java b/net/minecraft/world/entity/AreaEffectCloud.java +index b4a1202a9f43525caf215d2f5c86ad92ea4f6de7..47db6ac3ef23fd0da127cfb5a4d3ba9ebd2ab54d 100644 +--- a/net/minecraft/world/entity/AreaEffectCloud.java ++++ b/net/minecraft/world/entity/AreaEffectCloud.java +@@ -128,6 +128,16 @@ public class AreaEffectCloud extends Entity implements TraceableEntity { + this.duration = duration; + } + ++ // Paper start - EAR 2 ++ @Override ++ public void inactiveTick() { ++ super.inactiveTick(); ++ if (this.tickCount >= this.waitTime + this.duration) { ++ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause ++ } ++ } ++ // Paper end - EAR 2 ++ + @Override + public void tick() { + super.tick(); diff --git a/net/minecraft/world/entity/Entity.java b/net/minecraft/world/entity/Entity.java -index 0aed1e455dec32c3d53d8fb2f1047e1bf177f171..8c2441a6c7abc3a80426923c1ea42000283ee167 100644 +index 020d05bb60abec10fa37e651c17f600c883af61d..2e5f1dc15ea6a20f8cbdef97ecbf00e5d56603cf 100644 --- a/net/minecraft/world/entity/Entity.java +++ b/net/minecraft/world/entity/Entity.java -@@ -383,6 +383,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess - // Spigot end - protected int numCollisions = 0; // Paper - Cap entity collisions - public boolean fromNetherPortal; // Paper - Add option to nerf pigmen from nether portals -+ public long activatedImmunityTick = Integer.MIN_VALUE; // Paper - EAR -+ public boolean isTemporarilyActive; // Paper - EAR - public boolean spawnedViaMobSpawner; // Paper - Yes this name is similar to above, upstream took the better one - // Paper start - Entity origin API - @javax.annotation.Nullable -@@ -959,6 +961,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -386,6 +386,15 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + public boolean fixedPose = false; // Paper - Expand Pose API + private final int despawnTime; // Paper - entity despawn time limit + public final io.papermc.paper.entity.activation.ActivationType activationType = io.papermc.paper.entity.activation.ActivationType.activationTypeFor(this); // Paper - EAR 2/tracking ranges ++ // Paper start - EAR 2 ++ public final boolean defaultActivationState; ++ public long activatedTick = Integer.MIN_VALUE; ++ public boolean isTemporarilyActive; ++ public long activatedImmunityTick = Integer.MIN_VALUE; ++ ++ public void inactiveTick() { ++ } ++ // Paper end - EAR 2 + + public void setOrigin(@javax.annotation.Nonnull org.bukkit.Location location) { + this.origin = location.toVector(); +@@ -423,6 +432,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess + this.position = Vec3.ZERO; + this.blockPosition = BlockPos.ZERO; + this.chunkPosition = ChunkPos.ZERO; ++ // Paper start - EAR 2 ++ if (level != null) { ++ this.defaultActivationState = io.papermc.paper.entity.activation.ActivationRange.initializeEntityActivationState(this, level.spigotConfig); ++ } else { ++ this.defaultActivationState = false; ++ } ++ // Paper end - EAR 2 + SynchedEntityData.Builder builder = new SynchedEntityData.Builder(this); + builder.define(DATA_SHARED_FLAGS_ID, (byte)0); + builder.define(DATA_AIR_SUPPLY_ID, this.getMaxAirSupply()); +@@ -946,6 +962,8 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess } else { this.wasOnFire = this.isOnFire(); if (type == MoverType.PISTON) { @@ -53,20 +514,39 @@ index 0aed1e455dec32c3d53d8fb2f1047e1bf177f171..8c2441a6c7abc3a80426923c1ea42000 movement = this.limitPistonMovement(movement); if (movement.equals(Vec3.ZERO)) { return; -@@ -972,6 +976,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess +@@ -959,6 +977,13 @@ public abstract class Entity implements SyncedDataHolder, Nameable, EntityAccess this.stuckSpeedMultiplier = Vec3.ZERO; this.setDeltaMovement(Vec3.ZERO); } + // Paper start - ignore movement changes while inactive. + if (isTemporarilyActive && !(this instanceof ItemEntity) && movement == getDeltaMovement() && type == MoverType.SELF) { + setDeltaMovement(Vec3.ZERO); -+ gameprofilerfiller.pop(); ++ profilerFiller.pop(); + return; + } + // Paper end movement = this.maybeBackOffFromEdge(movement, type); Vec3 vec3 = this.collide(movement); +diff --git a/net/minecraft/world/entity/LivingEntity.java b/net/minecraft/world/entity/LivingEntity.java +index 41ef8c24903e5efceead43796e647824a54193df..9de400977ec33e485e87cdf1cf145588527e1e10 100644 +--- a/net/minecraft/world/entity/LivingEntity.java ++++ b/net/minecraft/world/entity/LivingEntity.java +@@ -3089,6 +3089,14 @@ public abstract class LivingEntity extends Entity implements Attackable { + return false; + } + ++ // Paper start - EAR 2 ++ @Override ++ public void inactiveTick() { ++ super.inactiveTick(); ++ ++this.noActionTime; // Above all the floats ++ } ++ // Paper end - EAR 2 ++ + @Override + public void tick() { + super.tick(); diff --git a/net/minecraft/world/entity/Mob.java b/net/minecraft/world/entity/Mob.java index f7d69db61d1293510428ae275e8a50571dde5ddf..1ed07fd23985a6bf8cf8300f74c92b7531a79fc6 100644 --- a/net/minecraft/world/entity/Mob.java @@ -105,24 +585,18 @@ index 0caf50ec50f056b83a20bbc6a2fe0144593aef39..af59a700755654eb68d6bf57d0712c4a return this.getWalkTargetValue(pos, this.level()); } diff --git a/net/minecraft/world/entity/ai/goal/GoalSelector.java b/net/minecraft/world/entity/ai/goal/GoalSelector.java -index 88e7e245824b670652878e03a2142a13e97508fe..2730e76228b03b1ec52108f394c20fe003801b02 100644 +index 9338e63cc28413f5559bb0122ef5e04a84bd51d1..eeba224bd575451ba6023df65ef9d9b97f7f1c71 100644 --- a/net/minecraft/world/entity/ai/goal/GoalSelector.java +++ b/net/minecraft/world/entity/ai/goal/GoalSelector.java -@@ -22,12 +22,12 @@ public class GoalSelector { - return false; - } - }; -+ private int curRate; // Paper - EAR 2 +@@ -25,6 +25,7 @@ public class GoalSelector { private final Map lockedFlags = new EnumMap<>(Goal.Flag.class); private final Set availableGoals = new ObjectLinkedOpenHashSet<>(); - private static final Goal.Flag[] GOAL_FLAG_VALUES = Goal.Flag.values(); // Paper - remove streams from pathfindergoalselector - private final ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet goalTypes = new ca.spottedleaf.moonrise.common.set.OptimizedSmallEnumSet<>(Goal.Flag.class); // Paper - remove streams from pathfindergoalselector + private final EnumSet disabledFlags = EnumSet.noneOf(Goal.Flag.class); ++ private int curRate; // Paper - EAR 2 -- public void addGoal(int priority, Goal goal) { this.availableGoals.add(new WrappedGoal(priority, goal)); - } -@@ -37,6 +37,22 @@ public class GoalSelector { +@@ -35,6 +36,22 @@ public class GoalSelector { this.availableGoals.removeIf(wrappedGoal -> filter.test(wrappedGoal.getGoal())); } @@ -172,20 +646,55 @@ index 789fea258d70e60d38271ebb31270562dc7eb3ab..d0ab3db7bbd2942db19f473474371b20 return true; } } +diff --git a/net/minecraft/world/entity/item/ItemEntity.java b/net/minecraft/world/entity/item/ItemEntity.java +index 1c82a41acb8717b2c56498602fd1ecbe6aa58fe5..dcbd35d6bf81d7a0621020710114887b68a7dcc6 100644 +--- a/net/minecraft/world/entity/item/ItemEntity.java ++++ b/net/minecraft/world/entity/item/ItemEntity.java +@@ -124,6 +124,29 @@ public class ItemEntity extends Entity implements TraceableEntity { + return 0.04; + } + ++ // Paper start - EAR 2 ++ @Override ++ public void inactiveTick() { ++ super.inactiveTick(); ++ if (this.pickupDelay > 0 && this.pickupDelay != 32767) { ++ this.pickupDelay--; ++ } ++ if (this.age != -32768) { ++ this.age++; ++ } ++ ++ if (!this.level().isClientSide && this.age >= this.despawnRate) {// Paper - Alternative item-despawn-rate ++ // CraftBukkit start - fire ItemDespawnEvent ++ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) { ++ this.age = 0; ++ return; ++ } ++ // CraftBukkit end ++ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause ++ } ++ } ++ // Paper end - EAR 2 ++ + @Override + public void tick() { + if (this.getItem().isEmpty()) { diff --git a/net/minecraft/world/entity/npc/Villager.java b/net/minecraft/world/entity/npc/Villager.java -index 81a6d31d90dc139e4d99c2b8e609032e8b100b2d..0b73ad491eb6992e1d8fdd7e75264c9ce712a462 100644 +index 27568a1604d2dd5d46e836bbc25431929e218aa1..2b83262e4a13eae86df82913ce4f3121e3631a43 100644 --- a/net/minecraft/world/entity/npc/Villager.java +++ b/net/minecraft/world/entity/npc/Villager.java -@@ -269,18 +269,33 @@ public class Villager extends AbstractVillager implements ReputationEventHandler - @Override - public void inactiveTick() { - // SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :( -- if (this.level().spigotConfig.tickInactiveVillagers && this.isEffectiveAi()) { -- this.customServerAiStep((ServerLevel) this.level()); -+ // Paper start +@@ -265,11 +265,35 @@ public class Villager extends AbstractVillager implements ReputationEventHandler + return this.assignProfessionWhenSpawned; + } + ++ // Paper start - EAR 2 ++ @Override ++ public void inactiveTick() { ++ // SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :( + if (this.getUnhappyCounter() > 0) { + this.setUnhappyCounter(this.getUnhappyCounter() - 1); - } ++ } + if (this.isEffectiveAi()) { + if (this.level().spigotConfig.tickInactiveVillagers) { + this.customServerAiStep(this.level().getMinecraftWorld()); @@ -194,11 +703,10 @@ index 81a6d31d90dc139e4d99c2b8e609032e8b100b2d..0b73ad491eb6992e1d8fdd7e75264c9c + } + } + maybeDecayGossip(); -+ // Paper end - super.inactiveTick(); - } - // Spigot End - ++ super.inactiveTick(); ++ } ++ // Paper end - EAR 2 ++ @Override protected void customServerAiStep(ServerLevel level) { + // Paper start - EAR 2 @@ -213,7 +721,7 @@ index 81a6d31d90dc139e4d99c2b8e609032e8b100b2d..0b73ad491eb6992e1d8fdd7e75264c9c profilerFiller.pop(); if (this.assignProfessionWhenSpawned) { this.assignProfessionWhenSpawned = false; -@@ -304,7 +319,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -293,7 +317,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler this.lastTradedPlayer = null; } @@ -222,7 +730,7 @@ index 81a6d31d90dc139e4d99c2b8e609032e8b100b2d..0b73ad491eb6992e1d8fdd7e75264c9c Raid raidAt = level.getRaidAt(this.blockPosition()); if (raidAt != null && raidAt.isActive() && !raidAt.isOver()) { level.broadcastEntityEvent(this, (byte)42); -@@ -314,6 +329,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler +@@ -303,6 +327,7 @@ public class Villager extends AbstractVillager implements ReputationEventHandler if (this.getVillagerData().getProfession() == VillagerProfession.NONE && this.isTrading()) { this.stopTrading(); } @@ -230,6 +738,53 @@ index 81a6d31d90dc139e4d99c2b8e609032e8b100b2d..0b73ad491eb6992e1d8fdd7e75264c9c super.customServerAiStep(level); } +diff --git a/net/minecraft/world/entity/projectile/Arrow.java b/net/minecraft/world/entity/projectile/Arrow.java +index c1e09e701757a300183b62d343ded03033e63aa7..56574f8ef879159edc0114da09300143a2c79a35 100644 +--- a/net/minecraft/world/entity/projectile/Arrow.java ++++ b/net/minecraft/world/entity/projectile/Arrow.java +@@ -66,6 +66,16 @@ public class Arrow extends AbstractArrow { + builder.define(ID_EFFECT_COLOR, -1); + } + ++ // Paper start - EAR 2 ++ @Override ++ public void inactiveTick() { ++ if (this.isInGround()) { ++ this.life++; ++ } ++ super.inactiveTick(); ++ } ++ // Paper end ++ + @Override + public void tick() { + super.tick(); +diff --git a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java +index 7c0862c50b44555fb27ce7dc46f4ec95a3eb0022..774ca9e0b56fd175ae246051de762d0c4256ca58 100644 +--- a/net/minecraft/world/entity/projectile/FireworkRocketEntity.java ++++ b/net/minecraft/world/entity/projectile/FireworkRocketEntity.java +@@ -102,6 +102,21 @@ public class FireworkRocketEntity extends Projectile implements ItemSupplier { + return super.shouldRender(x, y, z) && !this.isAttachedToEntity(); + } + ++ // Paper start - EAR 2 ++ @Override ++ public void inactiveTick() { ++ this.life++; ++ if (this.life > this.lifetime && this.level() instanceof ServerLevel serverLevel) { ++ // CraftBukkit start ++ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) { ++ this.explode(serverLevel); ++ } ++ // CraftBukkit end ++ } ++ super.inactiveTick(); ++ } ++ // Paper end - EAR 2 ++ + @Override + public void tick() { + super.tick(); diff --git a/net/minecraft/world/entity/vehicle/MinecartHopper.java b/net/minecraft/world/entity/vehicle/MinecartHopper.java index c553cf0592dfa606dbbb1e6854d3377b9feb5efb..dec705ec57e4f63ef2ccaa87c5400c116aee9b35 100644 --- a/net/minecraft/world/entity/vehicle/MinecartHopper.java diff --git a/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch b/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch index daa7e73901..0eaf3ea736 100644 --- a/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch +++ b/paper-server/patches/sources/net/minecraft/server/level/ServerLevel.java.patch @@ -291,14 +291,6 @@ if (flag) { this.resetEmptyTime(); } -@@ -385,6 +_,7 @@ - profilerFiller.pop(); - } - -+ org.spigotmc.ActivationRange.activateEntities(this); // Spigot - this.entityTickList - .forEach( - entity -> { @@ -461,12 +_,12 @@ int minBlockZ = pos.getMinBlockZ(); ProfilerFiller profilerFiller = Profiler.get(); @@ -464,52 +456,22 @@ } public void resetEmptyTime() { -@@ -752,15 +_,20 @@ - entity.tickCount++; +@@ -753,6 +_,7 @@ profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(entity.getType()).toString()); profilerFiller.incrementCounter("tickNonPassenger"); -+ // Spigot start -+ final boolean isActive = org.spigotmc.ActivationRange.checkIfActive(entity); // Paper - EAR 2 -+ if (isActive) { entity.tick(); + entity.postTick(); // CraftBukkit -+ } else {entity.inactiveTick();} // Spigot end profilerFiller.pop(); for (Entity entity1 : entity.getPassengers()) { -- this.tickPassenger(entity, entity1); -+ this.tickPassenger(entity, entity1, isActive); // Paper - EAR 2 - } - } - -- private void tickPassenger(Entity ridingEntity, Entity passengerEntity) { -+ private void tickPassenger(Entity ridingEntity, Entity passengerEntity, boolean isActive) { // Paper - EAR 2 - if (passengerEntity.isRemoved() || passengerEntity.getVehicle() != ridingEntity) { - passengerEntity.stopRiding(); - } else if (passengerEntity instanceof Player || this.entityTickList.contains(passengerEntity)) { -@@ -769,11 +_,21 @@ - ProfilerFiller profilerFiller = Profiler.get(); +@@ -770,6 +_,7 @@ profilerFiller.push(() -> BuiltInRegistries.ENTITY_TYPE.getKey(passengerEntity.getType()).toString()); profilerFiller.incrementCounter("tickPassenger"); -+ // Paper start - EAR 2 -+ if (isActive) { passengerEntity.rideTick(); + passengerEntity.postTick(); // CraftBukkit -+ } else { -+ passengerEntity.setDeltaMovement(Vec3.ZERO); -+ passengerEntity.inactiveTick(); -+ // copied from inside of if (isPassenger()) of passengerTick, but that ifPassenger is unnecessary -+ ridingEntity.positionRider(passengerEntity); -+ // Paper end - EAR 2 -+ } profilerFiller.pop(); for (Entity entity : passengerEntity.getPassengers()) { -- this.tickPassenger(passengerEntity, entity); -+ this.tickPassenger(passengerEntity, entity, isActive); // Paper - EAR 2 - } - } - } @@ -786,6 +_,7 @@ public void save(@Nullable ProgressListener progress, boolean flush, boolean skipSave) { ServerChunkCache chunkSource = this.getChunkSource(); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/AgeableMob.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/AgeableMob.java.patch index dcf72a4df6..a0e42a63dd 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/AgeableMob.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/AgeableMob.java.patch @@ -1,6 +1,6 @@ --- a/net/minecraft/world/entity/AgeableMob.java +++ b/net/minecraft/world/entity/AgeableMob.java -@@ -20,11 +_,37 @@ +@@ -20,6 +_,7 @@ protected int age; protected int forcedAge; protected int forcedAgeTimer; @@ -8,36 +8,6 @@ protected AgeableMob(EntityType entityType, Level level) { super(entityType, level); - } - -+ // Spigot start -+ @Override -+ public void inactiveTick() -+ { -+ super.inactiveTick(); -+ if ( this.level().isClientSide || this.ageLocked ) -+ { // CraftBukkit -+ this.refreshDimensions(); -+ } else -+ { -+ int i = this.getAge(); -+ -+ if ( i < 0 ) -+ { -+ ++i; -+ this.setAge( i ); -+ } else if ( i > 0 ) -+ { -+ --i; -+ this.setAge( i ); -+ } -+ } -+ } -+ // Spigot end -+ - @Override - public SpawnGroupData finalizeSpawn( - ServerLevelAccessor level, DifficultyInstance difficulty, EntitySpawnReason spawnReason, @Nullable SpawnGroupData spawnGroupData @@ -66,6 +_,7 @@ } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/AreaEffectCloud.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/AreaEffectCloud.java.patch index 1f6b41bf6d..adddf98077 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/AreaEffectCloud.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/AreaEffectCloud.java.patch @@ -9,25 +9,6 @@ @Nullable public UUID ownerUUID; -@@ -128,6 +_,18 @@ - this.duration = duration; - } - -+ // Spigot start - copied from below -+ @Override -+ public void inactiveTick() { -+ super.inactiveTick(); -+ -+ if (this.tickCount >= this.waitTime + this.duration) { -+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause -+ return; -+ } -+ } -+ // Spigot end -+ - @Override - public void tick() { - super.tick(); @@ -177,7 +_,7 @@ private void serverTick(ServerLevel level) { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch index 7bd2ceef50..b77ca19e9f 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/Entity.java.patch @@ -133,7 +133,7 @@ private final double[] pistonDeltas = new double[]{0.0, 0.0, 0.0}; private long pistonDeltasGameTime; private EntityDimensions dimensions; -@@ -250,6 +_,68 @@ +@@ -250,6 +_,63 @@ private final List movementThisTick = new ArrayList<>(); private final Set blocksInside = new ReferenceArraySet<>(); private final LongSet visitedBlocks = new LongOpenHashSet(); @@ -153,12 +153,6 @@ + // 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 @@ -170,6 +164,7 @@ + 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 final io.papermc.paper.entity.activation.ActivationType activationType = io.papermc.paper.entity.activation.ActivationType.activationTypeFor(this); // Paper - EAR 2/tracking ranges + + public void setOrigin(@javax.annotation.Nonnull org.bukkit.Location location) { + this.origin = location.toVector(); @@ -202,20 +197,6 @@ public Entity(EntityType entityType, Level level) { this.type = entityType; -@@ -258,6 +_,13 @@ - this.position = Vec3.ZERO; - this.blockPosition = BlockPos.ZERO; - this.chunkPosition = ChunkPos.ZERO; -+ // Spigot start -+ if (level != null) { -+ this.defaultActivationState = org.spigotmc.ActivationRange.initializeEntityActivationState(this, level.spigotConfig); -+ } else { -+ this.defaultActivationState = false; -+ } -+ // Spigot end - SynchedEntityData.Builder builder = new SynchedEntityData.Builder(this); - builder.define(DATA_SHARED_FLAGS_ID, (byte)0); - builder.define(DATA_AIR_SUPPLY_ID, this.getMaxAirSupply()); @@ -271,6 +_,7 @@ this.entityData = builder.build(); this.setPos(0.0, 0.0, 0.0); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch index 6f3c797b1f..6a08fbc658 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/LivingEntity.java.patch @@ -39,7 +39,7 @@ public abstract class LivingEntity extends Entity implements Attackable { private static final Logger LOGGER = LogUtils.getLogger(); private static final String TAG_ACTIVE_EFFECTS = "active_effects"; -@@ -266,11 +_,36 @@ +@@ -266,11 +_,29 @@ EquipmentSlot.class ); protected float appliedScale = 1.0F; @@ -59,13 +59,6 @@ + return this.getYHeadRot(); + } + // CraftBukkit end -+ // Spigot start -+ public void inactiveTick() -+ { -+ super.inactiveTick(); -+ ++this.noActionTime; // Above all the floats -+ } -+ // Spigot end protected LivingEntity(EntityType entityType, Level level) { super(entityType, level); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch index 7e9f5a0f27..8700616d07 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/item/ItemEntity.java.patch @@ -51,15 +51,12 @@ f = this.level().getBlockState(this.getBlockPosBelowThatAffectsMyMovement()).getBlock().getFriction() * 0.98F; } -@@ -184,11 +_,40 @@ +@@ -184,8 +_,14 @@ } } - if (!this.level().isClientSide && this.age >= 6000) { - this.discard(); -- } -- } -- } + if (!this.level().isClientSide && this.age >= this.despawnRate) { // Spigot // Paper - Alternative item-despawn-rate + // CraftBukkit start - fire ItemDespawnEvent + if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) { @@ -68,35 +65,9 @@ + } + // CraftBukkit end + this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause -+ } -+ } -+ } -+ -+ // Spigot start - copied from above -+ @Override -+ public void inactiveTick() { -+ if (this.pickupDelay > 0 && this.pickupDelay != 32767) { -+ --this.pickupDelay; -+ } -+ if (this.age != -32768) { -+ ++this.age; -+ } -+ -+ if (!this.level().isClientSide && this.age >= this.despawnRate) { // Spigot // Paper - Alternative item-despawn-rate -+ // CraftBukkit start - fire ItemDespawnEvent -+ if (org.bukkit.craftbukkit.event.CraftEventFactory.callItemDespawnEvent(this).isCancelled()) { -+ this.age = 0; -+ return; -+ } -+ // CraftBukkit end -+ this.discard(org.bukkit.event.entity.EntityRemoveEvent.Cause.DESPAWN); // CraftBukkit - add Bukkit remove cause -+ } -+ } -+ // Spigot end -+ - - @Override - public BlockPos getBlockPosBelowThatAffectsMyMovement() { + } + } + } @@ -210,9 +_,18 @@ private void mergeWithNeighbours() { diff --git a/paper-server/patches/sources/net/minecraft/world/entity/npc/Villager.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/npc/Villager.java.patch index 7c58286a0c..a3b4672db7 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/npc/Villager.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/npc/Villager.java.patch @@ -15,24 +15,6 @@ public class Villager extends AbstractVillager implements ReputationEventHandler, VillagerDataHolder { private static final Logger LOGGER = LogUtils.getLogger(); private static final EntityDataAccessor DATA_VILLAGER_DATA = SynchedEntityData.defineId(Villager.class, EntityDataSerializers.VILLAGER_DATA); -@@ -257,6 +_,17 @@ - return this.assignProfessionWhenSpawned; - } - -+ // Spigot Start -+ @Override -+ public void inactiveTick() { -+ // SPIGOT-3874, SPIGOT-3894, SPIGOT-3846, SPIGOT-5286 :( -+ if (this.level().spigotConfig.tickInactiveVillagers && this.isEffectiveAi()) { -+ this.customServerAiStep((ServerLevel) this.level()); -+ } -+ super.inactiveTick(); -+ } -+ // Spigot End -+ - @Override - protected void customServerAiStep(ServerLevel level) { - ProfilerFiller profilerFiller = Profiler.get(); @@ -275,7 +_,7 @@ this.increaseProfessionLevelOnUpdate = false; } diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch index 23dc1ce9bf..cf61cc49d2 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/AbstractArrow.java.patch @@ -8,7 +8,7 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -@@ -63,16 +_,26 @@ +@@ -63,16 +_,16 @@ protected int inGroundTime; public AbstractArrow.Pickup pickup = AbstractArrow.Pickup.DISALLOWED; public int shakeTime; @@ -26,16 +26,6 @@ @Nullable - private ItemStack firedFromWeapon = null; + public ItemStack firedFromWeapon = null; // Paper - private -> public -+ -+ // Spigot Start -+ @Override -+ public void inactiveTick() { -+ if (this.isInGround()) { -+ this.life += 1; -+ } -+ super.inactiveTick(); -+ } -+ // Spigot End protected AbstractArrow(EntityType entityType, Level level) { super(entityType, level); diff --git a/paper-server/patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch b/paper-server/patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch index fb50fc5183..f0049b5ab4 100644 --- a/paper-server/patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch +++ b/paper-server/patches/sources/net/minecraft/world/entity/projectile/FireworkRocketEntity.java.patch @@ -8,33 +8,6 @@ public FireworkRocketEntity(EntityType entityType, Level level) { super(entityType, level); -@@ -84,6 +_,26 @@ - this.setOwner(shooter); - } - -+ // Spigot Start - copied from tick -+ @Override -+ public void inactiveTick() { -+ this.life += 1; -+ -+ if (this.life > this.lifetime) { -+ Level world = this.level(); -+ -+ if (world instanceof ServerLevel serverLevel) { -+ // CraftBukkit start -+ if (!org.bukkit.craftbukkit.event.CraftEventFactory.callFireworkExplodeEvent(this).isCancelled()) { -+ this.explode(serverLevel); -+ } -+ // CraftBukkit end -+ } -+ } -+ super.inactiveTick(); -+ } -+ // Spigot End -+ - @Override - protected void defineSynchedData(SynchedEntityData.Builder builder) { - builder.define(DATA_ID_FIREWORKS_ITEM, getDefaultItem()); @@ -158,7 +_,7 @@ } diff --git a/paper-server/src/main/java/io/papermc/paper/entity/activation/ActivationType.java b/paper-server/src/main/java/io/papermc/paper/entity/activation/ActivationType.java new file mode 100644 index 0000000000..cd43845a08 --- /dev/null +++ b/paper-server/src/main/java/io/papermc/paper/entity/activation/ActivationType.java @@ -0,0 +1,47 @@ +package io.papermc.paper.entity.activation; + +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.FlyingMob; +import net.minecraft.world.entity.PathfinderMob; +import net.minecraft.world.entity.ambient.AmbientCreature; +import net.minecraft.world.entity.animal.WaterAnimal; +import net.minecraft.world.entity.monster.Enemy; +import net.minecraft.world.entity.npc.Villager; +import net.minecraft.world.entity.raid.Raider; +import net.minecraft.world.phys.AABB; + +public enum ActivationType { + WATER, + FLYING_MONSTER, + VILLAGER, + MONSTER, + ANIMAL, + RAIDER, + MISC; + + AABB boundingBox = new AABB(0, 0, 0, 0, 0, 0); + + /** + * Returns the activation type for the given entity. + * + * @param entity entity to get the activation type for + * @return activation type + */ + public static ActivationType activationTypeFor(final Entity entity) { + if (entity instanceof WaterAnimal) { + return ActivationType.WATER; + } else if (entity instanceof Villager) { + return ActivationType.VILLAGER; + } else if (entity instanceof FlyingMob && entity instanceof Enemy) { + return ActivationType.FLYING_MONSTER; + } else if (entity instanceof Raider) { + return ActivationType.RAIDER; + } else if (entity instanceof Enemy) { + return ActivationType.MONSTER; + } else if (entity instanceof PathfinderMob || entity instanceof AmbientCreature) { + return ActivationType.ANIMAL; + } else { + return ActivationType.MISC; + } + } +} diff --git a/paper-server/src/main/java/org/spigotmc/ActivationRange.java b/paper-server/src/main/java/org/spigotmc/ActivationRange.java deleted file mode 100644 index 554d80ddbf..0000000000 --- a/paper-server/src/main/java/org/spigotmc/ActivationRange.java +++ /dev/null @@ -1,387 +0,0 @@ -package org.spigotmc; - -import net.minecraft.core.BlockPos; -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerChunkCache; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.ExperienceOrb; -import net.minecraft.world.entity.FlyingMob; -import net.minecraft.world.entity.LightningBolt; -import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.Mob; -import net.minecraft.world.entity.PathfinderMob; -import net.minecraft.world.entity.ai.Brain; -import net.minecraft.world.entity.ambient.AmbientCreature; -import net.minecraft.world.entity.animal.Animal; -import net.minecraft.world.entity.animal.Bee; -import net.minecraft.world.entity.animal.Sheep; -import net.minecraft.world.entity.animal.WaterAnimal; -import net.minecraft.world.entity.animal.horse.Llama; -import net.minecraft.world.entity.boss.EnderDragonPart; -import net.minecraft.world.entity.boss.enderdragon.EndCrystal; -import net.minecraft.world.entity.boss.enderdragon.EnderDragon; -import net.minecraft.world.entity.boss.wither.WitherBoss; -import net.minecraft.world.entity.item.ItemEntity; -import net.minecraft.world.entity.item.PrimedTnt; -import net.minecraft.world.entity.monster.Creeper; -import net.minecraft.world.entity.monster.Enemy; -import net.minecraft.world.entity.monster.Pillager; -import net.minecraft.world.entity.npc.Villager; -import net.minecraft.world.entity.player.Player; -import net.minecraft.world.entity.projectile.AbstractArrow; -import net.minecraft.world.entity.projectile.AbstractHurtingProjectile; -import net.minecraft.world.entity.projectile.EyeOfEnder; -import net.minecraft.world.entity.projectile.FireworkRocketEntity; -import net.minecraft.world.entity.projectile.ThrowableProjectile; -import net.minecraft.world.entity.projectile.ThrownTrident; -import net.minecraft.world.entity.raid.Raider; -import net.minecraft.world.level.Level; -import net.minecraft.world.phys.AABB; - -public final class ActivationRange { - - private ActivationRange() { - } - - public enum ActivationType { - WATER, // Paper - FLYING_MONSTER, // Paper - VILLAGER, // Paper - MONSTER, - ANIMAL, - RAIDER, - MISC; - - AABB boundingBox = new AABB(0, 0, 0, 0, 0, 0); - } - // Paper start - - static net.minecraft.world.entity.schedule.Activity[] VILLAGER_PANIC_IMMUNITIES = { - net.minecraft.world.entity.schedule.Activity.HIDE, - net.minecraft.world.entity.schedule.Activity.PRE_RAID, - net.minecraft.world.entity.schedule.Activity.RAID, - net.minecraft.world.entity.schedule.Activity.PANIC - }; - - private static int checkInactiveWakeup(final Entity entity) { - final Level world = entity.level(); - final SpigotWorldConfig config = world.spigotConfig; - final long inactiveFor = MinecraftServer.currentTick - entity.activatedTick; - if (entity.activationType == ActivationType.VILLAGER) { - if (inactiveFor > config.wakeUpInactiveVillagersEvery && world.wakeupInactiveRemainingVillagers > 0) { - world.wakeupInactiveRemainingVillagers--; - return config.wakeUpInactiveVillagersFor; - } - } else if (entity.activationType == ActivationType.ANIMAL) { - if (inactiveFor > config.wakeUpInactiveAnimalsEvery && world.wakeupInactiveRemainingAnimals > 0) { - world.wakeupInactiveRemainingAnimals--; - return config.wakeUpInactiveAnimalsFor; - } - } else if (entity.activationType == ActivationType.FLYING_MONSTER) { - if (inactiveFor > config.wakeUpInactiveFlyingEvery && world.wakeupInactiveRemainingFlying > 0) { - world.wakeupInactiveRemainingFlying--; - return config.wakeUpInactiveFlyingFor; - } - } else if (entity.activationType == ActivationType.MONSTER || entity.activationType == ActivationType.RAIDER) { - if (inactiveFor > config.wakeUpInactiveMonstersEvery && world.wakeupInactiveRemainingMonsters > 0) { - world.wakeupInactiveRemainingMonsters--; - return config.wakeUpInactiveMonstersFor; - } - } - return -1; - } - // Paper end - - static AABB maxBB = new AABB(0, 0, 0, 0, 0, 0); - - /** - * Initializes an entities type on construction to specify what group this - * entity is in for activation ranges. - * - * @param entity - * @return group id - */ - public static ActivationType initializeEntityActivationType(final Entity entity) { - if (entity instanceof WaterAnimal) { - return ActivationType.WATER; - } // Paper - else if (entity instanceof Villager) { - return ActivationType.VILLAGER; - } // Paper - else if (entity instanceof FlyingMob && entity instanceof Enemy) { - return ActivationType.FLYING_MONSTER; - } // Paper - doing & Monster incase Flying no longer includes monster in future - if (entity instanceof Raider) { - return ActivationType.RAIDER; - } else if (entity instanceof Enemy) { // Paper - correct monster check - return ActivationType.MONSTER; - } else if (entity instanceof PathfinderMob || entity instanceof AmbientCreature) { - return ActivationType.ANIMAL; - } else { - return ActivationType.MISC; - } - } - - /** - * These entities are excluded from Activation range checks. - * - * @param entity - * @param config - * @return boolean If it should always tick. - */ - public static boolean initializeEntityActivationState(final Entity entity, final SpigotWorldConfig config) { - return (entity.activationType == ActivationType.MISC && config.miscActivationRange == 0) - || (entity.activationType == ActivationType.RAIDER && config.raiderActivationRange == 0) - || (entity.activationType == ActivationType.ANIMAL && config.animalActivationRange == 0) - || (entity.activationType == ActivationType.MONSTER && config.monsterActivationRange == 0) - || (entity.activationType == ActivationType.VILLAGER && config.villagerActivationRange <= 0) // Paper - || (entity.activationType == ActivationType.WATER && config.waterActivationRange <= 0) // Paper - || (entity.activationType == ActivationType.FLYING_MONSTER && config.flyingMonsterActivationRange <= 0) // Paper - || entity instanceof EyeOfEnder // Paper - || entity instanceof Player - || entity instanceof ThrowableProjectile - || entity instanceof EnderDragon - || entity instanceof EnderDragonPart - || entity instanceof WitherBoss - || entity instanceof AbstractHurtingProjectile - || entity instanceof LightningBolt - || entity instanceof PrimedTnt - || entity instanceof net.minecraft.world.entity.item.FallingBlockEntity // Paper - Always tick falling blocks - || entity instanceof net.minecraft.world.entity.vehicle.AbstractMinecart // Paper - || entity instanceof net.minecraft.world.entity.vehicle.AbstractBoat // Paper - || entity instanceof EndCrystal - || entity instanceof FireworkRocketEntity - || entity instanceof ThrownTrident; - } - - /** - * Find what entities are in range of the players in the world and set - * active if in range. - * - * @param world - */ - public static void activateEntities(final Level world) { - final int miscActivationRange = world.spigotConfig.miscActivationRange; - final int raiderActivationRange = world.spigotConfig.raiderActivationRange; - final int animalActivationRange = world.spigotConfig.animalActivationRange; - final int monsterActivationRange = world.spigotConfig.monsterActivationRange; - // Paper start - final int waterActivationRange = world.spigotConfig.waterActivationRange; - final int flyingActivationRange = world.spigotConfig.flyingMonsterActivationRange; - final int villagerActivationRange = world.spigotConfig.villagerActivationRange; - world.wakeupInactiveRemainingAnimals = Math.min(world.wakeupInactiveRemainingAnimals + 1, world.spigotConfig.wakeUpInactiveAnimals); - world.wakeupInactiveRemainingVillagers = Math.min(world.wakeupInactiveRemainingVillagers + 1, world.spigotConfig.wakeUpInactiveVillagers); - world.wakeupInactiveRemainingMonsters = Math.min(world.wakeupInactiveRemainingMonsters + 1, world.spigotConfig.wakeUpInactiveMonsters); - world.wakeupInactiveRemainingFlying = Math.min(world.wakeupInactiveRemainingFlying + 1, world.spigotConfig.wakeUpInactiveFlying); - final ServerChunkCache chunkProvider = (ServerChunkCache) world.getChunkSource(); - // Paper end - - int maxRange = Math.max(monsterActivationRange, animalActivationRange); - maxRange = Math.max(maxRange, raiderActivationRange); - maxRange = Math.max(maxRange, miscActivationRange); - // Paper start - maxRange = Math.max(maxRange, flyingActivationRange); - maxRange = Math.max(maxRange, waterActivationRange); - maxRange = Math.max(maxRange, villagerActivationRange); - // Paper end - maxRange = Math.min((world.spigotConfig.simulationDistance << 4) - 8, maxRange); - - for (final Player player : world.players()) { - player.activatedTick = MinecraftServer.currentTick; - if (world.spigotConfig.ignoreSpectatorActivation && player.isSpectator()) { - continue; - } - - // Paper start - final int worldHeight = world.getHeight(); - ActivationRange.maxBB = player.getBoundingBox().inflate(maxRange, worldHeight, maxRange); - ActivationType.MISC.boundingBox = player.getBoundingBox().inflate(miscActivationRange, worldHeight, miscActivationRange); - ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate(raiderActivationRange, worldHeight, raiderActivationRange); - ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate(animalActivationRange, worldHeight, animalActivationRange); - ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate(monsterActivationRange, worldHeight, monsterActivationRange); - ActivationType.WATER.boundingBox = player.getBoundingBox().inflate(waterActivationRange, worldHeight, waterActivationRange); - ActivationType.FLYING_MONSTER.boundingBox = player.getBoundingBox().inflate(flyingActivationRange, worldHeight, flyingActivationRange); - ActivationType.VILLAGER.boundingBox = player.getBoundingBox().inflate(villagerActivationRange, worldHeight, villagerActivationRange); - // Paper end - - // Paper start - final java.util.List entities = world.getEntities((Entity) null, ActivationRange.maxBB, null); - final boolean tickMarkers = world.paperConfig().entities.markers.tick; // Paper - Configurable marker ticking - for (final Entity entity : entities) { - // Paper start - Configurable marker ticking - if (!tickMarkers && entity instanceof net.minecraft.world.entity.Marker) { - continue; - } - // Paper end - Configurable marker ticking - ActivationRange.activateEntity(entity); - } - // Paper end - } - } - - /** - * Tries to activate an entity. - * - * @param entity - */ - private static void activateEntity(final Entity entity) { - if (MinecraftServer.currentTick > entity.activatedTick) { - if (entity.defaultActivationState) { - entity.activatedTick = MinecraftServer.currentTick; - return; - } - if (entity.activationType.boundingBox.intersects(entity.getBoundingBox())) { - entity.activatedTick = MinecraftServer.currentTick; - } - } - } - - /** - * If an entity is not in range, do some more checks to see if we should - * give it a shot. - * - * @param entity - * @return - */ - public static int checkEntityImmunities(final Entity entity) { // Paper - return # of ticks to get immunity - // Paper start - final SpigotWorldConfig config = entity.level().spigotConfig; - final int inactiveWakeUpImmunity = checkInactiveWakeup(entity); - if (inactiveWakeUpImmunity > -1) { - return inactiveWakeUpImmunity; - } - if (entity.getRemainingFireTicks() > 0) { - return 2; - } - if (entity.activatedImmunityTick >= MinecraftServer.currentTick) { - return 1; - } - final long inactiveFor = MinecraftServer.currentTick - entity.activatedTick; - // Paper end - // quick checks. - if ((entity.activationType != ActivationType.WATER && entity.isInWater() && entity.isPushedByFluid())) // Paper - { - return 100; // Paper - } - // Paper start - if (!entity.onGround() || entity.getDeltaMovement().horizontalDistanceSqr() > 9.999999747378752E-6D) { - return 100; - } - // Paper end - if (!(entity instanceof final AbstractArrow arrow)) { - if ((!entity.onGround() && !(entity instanceof FlyingMob))) { // Paper - remove passengers logic - return 10; // Paper - } - } else if (!arrow.isInGround()) { - return 1; // Paper - } - // special cases. - if (entity instanceof final LivingEntity living) { - if (living.onClimbable() || living.jumping || living.hurtTime > 0 || !living.activeEffects.isEmpty() || living.isFreezing()) { // Paper - return 1; // Paper - } - if (entity instanceof final Mob mob && mob.getTarget() != null) { // Paper - return 20; // Paper - } - // Paper start - if (entity instanceof final Bee bee) { - final BlockPos movingTarget = bee.getMovingTarget(); - if (bee.isAngry() || - (bee.getHivePos() != null && bee.getHivePos().equals(movingTarget)) || - (bee.getSavedFlowerPos() != null && bee.getSavedFlowerPos().equals(movingTarget)) - ) { - return 20; - } - } - if (entity instanceof final Villager villager) { - final Brain behaviorController = villager.getBrain(); - - if (config.villagersActiveForPanic) { - for (final net.minecraft.world.entity.schedule.Activity activity : VILLAGER_PANIC_IMMUNITIES) { - if (behaviorController.isActive(activity)) { - return 20 * 5; - } - } - } - - if (config.villagersWorkImmunityAfter > 0 && inactiveFor >= config.villagersWorkImmunityAfter) { - if (behaviorController.isActive(net.minecraft.world.entity.schedule.Activity.WORK)) { - return config.villagersWorkImmunityFor; - } - } - } - if (entity instanceof final Llama llama && llama.inCaravan()) { - return 1; - } - // Paper end - if (entity instanceof final Animal animal) { - if (animal.isBaby() || animal.isInLove()) { - return 5; // Paper - } - if (entity instanceof final Sheep sheep && sheep.isSheared()) { - return 1; // Paper - } - } - if (entity instanceof final Creeper creeper && creeper.isIgnited()) { // isExplosive - return 20; // Paper - } - // Paper start - if (entity instanceof final Mob mob && mob.targetSelector.hasTasks()) { - return 0; - } - if (entity instanceof final Pillager pillager) { - // TODO:? - } - // Paper end - } - // SPIGOT-6644: Otherwise the target refresh tick will be missed - if (entity instanceof ExperienceOrb) { - return 20; // Paper - } - return -1; // Paper - } - - /** - * Checks if the entity is active for this tick. - * - * @param entity - * @return - */ - public static boolean checkIfActive(final Entity entity) { - // Never safe to skip fireworks or item gravity - if (entity instanceof FireworkRocketEntity || (entity instanceof ItemEntity && (entity.tickCount + entity.getId()) % 4 == 0)) { // Paper - Needed for item gravity, see ItemEntity tick - return true; - } - // Paper start - special case always immunities - // immunize brand new entities, dead entities, and portal scenarios - if (entity.defaultActivationState || entity.tickCount < 20 * 10 || !entity.isAlive() || (entity.portalProcess != null && !entity.portalProcess.hasExpired()) || entity.portalCooldown > 0) { - return true; - } - // immunize leashed entities - if (entity instanceof final Mob mob && mob.getLeashHolder() instanceof Player) { - return true; - } - // Paper end - - boolean isActive = entity.activatedTick >= MinecraftServer.currentTick; - entity.isTemporarilyActive = false; // Paper - - // Should this entity tick? - if (!isActive) { - if ((MinecraftServer.currentTick - entity.activatedTick - 1) % 20 == 0) { - // Check immunities every 20 ticks. - // Paper start - final int immunity = checkEntityImmunities(entity); - if (immunity >= 0) { - entity.activatedTick = MinecraftServer.currentTick + immunity; - } else { - entity.isTemporarilyActive = true; - } - // Paper end - isActive = true; - } - } - // Paper - remove dumb tick skipping for active entities - return isActive; - } -} diff --git a/paper-server/src/main/java/org/spigotmc/TrackingRange.java b/paper-server/src/main/java/org/spigotmc/TrackingRange.java index 039f8abeb5..4835431493 100644 --- a/paper-server/src/main/java/org/spigotmc/TrackingRange.java +++ b/paper-server/src/main/java/org/spigotmc/TrackingRange.java @@ -1,5 +1,6 @@ package org.spigotmc; +import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Display; import net.minecraft.world.entity.Entity; @@ -25,11 +26,12 @@ public final class TrackingRange { if (defaultRange == 0) { return defaultRange; } + final SpigotWorldConfig config = entity.level().spigotConfig; if (entity instanceof ServerPlayer) { return config.playerTrackingRange; - // Paper start - Simplify and set water mobs to animal tracking range } + switch (entity.activationType) { case RAIDER: case MONSTER: @@ -41,14 +43,15 @@ public final class TrackingRange { return config.animalTrackingRange; case MISC: } + if (entity instanceof ItemFrame || entity instanceof Painting || entity instanceof ItemEntity || entity instanceof ExperienceOrb) { - // Paper end return config.miscTrackingRange; } else if (entity instanceof Display) { return config.displayTrackingRange; } else { if (entity instanceof net.minecraft.world.entity.boss.enderdragon.EnderDragon) { - return ((net.minecraft.server.level.ServerLevel) (entity.getCommandSenderWorld())).getChunkSource().chunkMap.serverViewDistance; // Paper - enderdragon is exempt + // Exempt ender dragon + return ((ServerLevel) entity.getCommandSenderWorld()).getChunkSource().chunkMap.serverViewDistance; } return config.otherTrackingRange; }