== AT == public net.minecraft.server.level.ServerChunkCache mainThread public net.minecraft.server.level.ServerLevel chunkSource public org.bukkit.craftbukkit.inventory.CraftItemStack handle public net.minecraft.server.level.ChunkMap getVisibleChunkIfPresent(J)Lnet/minecraft/server/level/ChunkHolder; public net.minecraft.server.level.ServerChunkCache mainThreadProcessor public net.minecraft.server.level.ServerChunkCache$MainThreadExecutor public net.minecraft.world.level.chunk.LevelChunkSection states
267 lines
9.9 KiB
Java
267 lines
9.9 KiB
Java
package org.spigotmc;
|
|
|
|
import net.minecraft.server.MinecraftServer;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.ExperienceOrb;
|
|
import net.minecraft.world.entity.LightningBolt;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.PathfinderMob;
|
|
import net.minecraft.world.entity.ambient.AmbientCreature;
|
|
import net.minecraft.world.entity.animal.Animal;
|
|
import net.minecraft.world.entity.animal.Sheep;
|
|
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.Monster;
|
|
import net.minecraft.world.entity.monster.Slime;
|
|
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.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;
|
|
import org.bukkit.craftbukkit.SpigotTimings;
|
|
|
|
public class 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 );
|
|
}
|
|
|
|
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(Entity entity)
|
|
{
|
|
if ( entity instanceof Raider )
|
|
{
|
|
return ActivationType.RAIDER;
|
|
} else if ( entity instanceof Monster || entity instanceof Slime )
|
|
{
|
|
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(Entity entity, SpigotWorldConfig config)
|
|
{
|
|
if ( ( 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 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 EndCrystal
|
|
|| entity instanceof FireworkRocketEntity
|
|
|| entity instanceof ThrownTrident )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Find what entities are in range of the players in the world and set
|
|
* active if in range.
|
|
*
|
|
* @param world
|
|
*/
|
|
public static void activateEntities(Level world)
|
|
{
|
|
SpigotTimings.entityActivationCheckTimer.startTiming();
|
|
final int miscActivationRange = world.spigotConfig.miscActivationRange;
|
|
final int raiderActivationRange = world.spigotConfig.raiderActivationRange;
|
|
final int animalActivationRange = world.spigotConfig.animalActivationRange;
|
|
final int monsterActivationRange = world.spigotConfig.monsterActivationRange;
|
|
|
|
int maxRange = Math.max( monsterActivationRange, animalActivationRange );
|
|
maxRange = Math.max( maxRange, raiderActivationRange );
|
|
maxRange = Math.max( maxRange, miscActivationRange );
|
|
maxRange = Math.min( ( world.spigotConfig.simulationDistance << 4 ) - 8, maxRange );
|
|
|
|
for ( Player player : world.players() )
|
|
{
|
|
player.activatedTick = MinecraftServer.currentTick;
|
|
if ( world.spigotConfig.ignoreSpectatorActivation && player.isSpectator() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
ActivationRange.maxBB = player.getBoundingBox().inflate( maxRange, 256, maxRange );
|
|
ActivationType.MISC.boundingBox = player.getBoundingBox().inflate( miscActivationRange, 256, miscActivationRange );
|
|
ActivationType.RAIDER.boundingBox = player.getBoundingBox().inflate( raiderActivationRange, 256, raiderActivationRange );
|
|
ActivationType.ANIMAL.boundingBox = player.getBoundingBox().inflate( animalActivationRange, 256, animalActivationRange );
|
|
ActivationType.MONSTER.boundingBox = player.getBoundingBox().inflate( monsterActivationRange, 256, monsterActivationRange );
|
|
|
|
world.getEntities().get(ActivationRange.maxBB, ActivationRange::activateEntity);
|
|
}
|
|
SpigotTimings.entityActivationCheckTimer.stopTiming();
|
|
}
|
|
|
|
/**
|
|
* Checks for the activation state of all entities in this chunk.
|
|
*
|
|
* @param chunk
|
|
*/
|
|
private static void activateEntity(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 boolean checkEntityImmunities(Entity entity)
|
|
{
|
|
// quick checks.
|
|
if ( entity.wasTouchingWater || entity.getRemainingFireTicks() > 0 )
|
|
{
|
|
return true;
|
|
}
|
|
if ( !( entity instanceof AbstractArrow ) )
|
|
{
|
|
if ( !entity.onGround() || !entity.passengers.isEmpty() || entity.isPassenger() )
|
|
{
|
|
return true;
|
|
}
|
|
} else if ( !( (AbstractArrow) entity ).isInGround() )
|
|
{
|
|
return true;
|
|
}
|
|
// special cases.
|
|
if ( entity instanceof LivingEntity )
|
|
{
|
|
LivingEntity living = (LivingEntity) entity;
|
|
if ( /*TODO: Missed mapping? living.attackTicks > 0 || */ living.hurtTime > 0 || living.activeEffects.size() > 0 )
|
|
{
|
|
return true;
|
|
}
|
|
if ( entity instanceof PathfinderMob && ( (PathfinderMob) entity ).getTarget() != null )
|
|
{
|
|
return true;
|
|
}
|
|
if ( entity instanceof Villager && ( (Villager) entity ).canBreed() )
|
|
{
|
|
return true;
|
|
}
|
|
if ( entity instanceof Animal )
|
|
{
|
|
Animal animal = (Animal) entity;
|
|
if ( animal.isBaby() || animal.isInLove() )
|
|
{
|
|
return true;
|
|
}
|
|
if ( entity instanceof Sheep && ( (Sheep) entity ).isSheared() )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
if (entity instanceof Creeper && ((Creeper) entity).isIgnited()) { // isExplosive
|
|
return true;
|
|
}
|
|
}
|
|
// SPIGOT-6644: Otherwise the target refresh tick will be missed
|
|
if (entity instanceof ExperienceOrb) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Checks if the entity is active for this tick.
|
|
*
|
|
* @param entity
|
|
* @return
|
|
*/
|
|
public static boolean checkIfActive(Entity entity)
|
|
{
|
|
SpigotTimings.checkIfActiveTimer.startTiming();
|
|
// Never safe to skip fireworks or item gravity
|
|
if (entity instanceof FireworkRocketEntity || (entity instanceof ItemEntity && (entity.tickCount + entity.getId() + 1) % 4 == 0)) {
|
|
SpigotTimings.checkIfActiveTimer.stopTiming();
|
|
return true;
|
|
}
|
|
|
|
boolean isActive = entity.activatedTick >= MinecraftServer.currentTick || entity.defaultActivationState;
|
|
|
|
// Should this entity tick?
|
|
if ( !isActive )
|
|
{
|
|
if ( ( MinecraftServer.currentTick - entity.activatedTick - 1 ) % 20 == 0 )
|
|
{
|
|
// Check immunities every 20 ticks.
|
|
if ( ActivationRange.checkEntityImmunities( entity ) )
|
|
{
|
|
// Triggered some sort of immunity, give 20 full ticks before we check again.
|
|
entity.activatedTick = MinecraftServer.currentTick + 20;
|
|
}
|
|
isActive = true;
|
|
}
|
|
// Add a little performance juice to active entities. Skip 1/4 if not immune.
|
|
} else if ( !entity.defaultActivationState && entity.tickCount % 4 == 0 && !ActivationRange.checkEntityImmunities( entity ) )
|
|
{
|
|
isActive = false;
|
|
}
|
|
SpigotTimings.checkIfActiveTimer.stopTiming();
|
|
return isActive;
|
|
}
|
|
}
|