package org.spigotmc;
import java.util.List;
import co.aikar.timings.MinecraftTimings;
import net.minecraft.server.AxisAlignedBB;
import net.minecraft.server.Chunk;
import net.minecraft.server.Entity;
import net.minecraft.server.EntityAmbient;
import net.minecraft.server.EntityAnimal;
import net.minecraft.server.EntityArrow;
import net.minecraft.server.EntityComplexPart;
import net.minecraft.server.EntityCreature;
import net.minecraft.server.EntityCreeper;
import net.minecraft.server.EntityEnderCrystal;
import net.minecraft.server.EntityEnderDragon;
import net.minecraft.server.EntityFallingBlock;
import net.minecraft.server.EntityFireball;
import net.minecraft.server.EntityFireworks;
import net.minecraft.server.EntityHuman;
import net.minecraft.server.EntityLiving;
import net.minecraft.server.EntityLlama;
import net.minecraft.server.EntityMonster;
import net.minecraft.server.EntityProjectile;
import net.minecraft.server.EntitySheep;
import net.minecraft.server.EntitySlime;
import net.minecraft.server.EntityTNTPrimed;
import net.minecraft.server.EntityVillager;
import net.minecraft.server.EntityWeather;
import net.minecraft.server.EntityWither;
import net.minecraft.server.MCUtil;
import net.minecraft.server.MathHelper;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.World;
public class ActivationRange
{
static AxisAlignedBB maxBB = new AxisAlignedBB( 0, 0, 0, 0, 0, 0 );
static AxisAlignedBB miscBB = new AxisAlignedBB( 0, 0, 0, 0, 0, 0 );
static AxisAlignedBB animalBB = new AxisAlignedBB( 0, 0, 0, 0, 0, 0 );
static AxisAlignedBB monsterBB = new AxisAlignedBB( 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 byte initializeEntityActivationType(Entity entity)
{
if ( entity instanceof EntityMonster || entity instanceof EntitySlime )
{
return 1; // Monster
} else if ( entity instanceof EntityCreature || entity instanceof EntityAmbient )
{
return 2; // Animal
} else
{
return 3; // Misc
}
}
/**
* These entities are excluded from Activation range checks.
*
* @param entity Entity to initialize
* @param config Spigot config to determine ranges
* @return boolean If it should always tick.
*/
public static boolean initializeEntityActivationState(Entity entity, SpigotWorldConfig config)
{
if ( ( entity.activationType == 3 && config.miscActivationRange == 0 )
|| ( entity.activationType == 2 && config.animalActivationRange == 0 )
|| ( entity.activationType == 1 && config.monsterActivationRange == 0 )
|| entity instanceof EntityHuman
|| entity instanceof EntityProjectile
|| entity instanceof EntityEnderDragon
|| entity instanceof EntityComplexPart
|| entity instanceof EntityWither
|| entity instanceof EntityFireball
|| entity instanceof EntityWeather
|| entity instanceof EntityTNTPrimed
|| entity instanceof EntityFallingBlock // Paper - Always tick falling blocks
|| entity instanceof EntityEnderCrystal
|| entity instanceof EntityFireworks )
{
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(World world)
{
MinecraftTimings.entityActivationCheckTimer.startTiming();
final int miscActivationRange = world.spigotConfig.miscActivationRange;
final int animalActivationRange = world.spigotConfig.animalActivationRange;
final int monsterActivationRange = world.spigotConfig.monsterActivationRange;
int maxRange = Math.max( monsterActivationRange, animalActivationRange );
maxRange = Math.max( maxRange, miscActivationRange );
maxRange = Math.min( ( world.spigotConfig.viewDistance << 4 ) - 8, maxRange );
Chunk chunk; // Paper
for ( EntityHuman player : world.players )
{
player.activatedTick = MinecraftServer.currentTick;
maxBB = player.getBoundingBox().grow( maxRange, 256, maxRange );
miscBB = player.getBoundingBox().grow( miscActivationRange, 256, miscActivationRange );
animalBB = player.getBoundingBox().grow( animalActivationRange, 256, animalActivationRange );
monsterBB = player.getBoundingBox().grow( monsterActivationRange, 256, monsterActivationRange );
int i = MathHelper.floor( maxBB.a / 16.0D );
int j = MathHelper.floor( maxBB.d / 16.0D );
int k = MathHelper.floor( maxBB.c / 16.0D );
int l = MathHelper.floor( maxBB.f / 16.0D );
for ( int i1 = i; i1 <= j; ++i1 )
{
for ( int j1 = k; j1 <= l; ++j1 )
{
if ( (chunk = MCUtil.getLoadedChunkWithoutMarkingActive(world, i1, j1 )) != null ) // Paper
{
activateChunkEntities( chunk ); // Paper
}
}
}
}
MinecraftTimings.entityActivationCheckTimer.stopTiming();
}
/**
* Checks for the activation state of all entities in this chunk.
*
* @param chunk
*/
private static void activateChunkEntities(Chunk chunk)
{
for ( List<Entity> slice : chunk.entitySlices )
{
for ( Entity entity : slice )
{
if ( MinecraftServer.currentTick > entity.activatedTick )
{
if ( entity.defaultActivationState )
{
entity.activatedTick = MinecraftServer.currentTick;
continue;
}
switch ( entity.activationType )
{
case 1:
if ( monsterBB.c( entity.getBoundingBox() ) )
{
entity.activatedTick = MinecraftServer.currentTick;
}
break;
case 2:
if ( animalBB.c( entity.getBoundingBox() ) )
{
entity.activatedTick = MinecraftServer.currentTick;
}
break;
case 3:
default:
if ( miscBB.c( 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.inWater || entity.fireTicks > 0 )
{
return true;
}
if ( !( entity instanceof EntityArrow ) )
{
if ( !entity.onGround || !entity.passengers.isEmpty() || entity.isPassenger() )
{
return true;
}
} else if ( !( (EntityArrow) entity ).inGround )
{
return true;
}
// special cases.
if ( entity instanceof EntityLiving )
{
EntityLiving living = (EntityLiving) entity;
if ( living.lastDamageByPlayerTime > 0 || living.hurtTicks > 0 || living.effects.size() > 0 ) // Paper
{
return true;
}
if ( entity instanceof EntityCreature )
{
// Paper start
EntityCreature creature = (EntityCreature) entity;
if (creature.getGoalTarget() != null || creature.getMovingTarget() != null) {
return true;
}
// Paper end
}
if ( entity instanceof EntityVillager && ( (EntityVillager) entity ).isMating() ) // Paper
{
return true;
}
// Paper start
if ( entity instanceof EntityLlama && ( (EntityLlama ) entity ).inCaravan() )
{
return true;
}
// Paper end
if ( entity instanceof EntityAnimal )
{
EntityAnimal animal = (EntityAnimal) entity;
if ( animal.isBaby() || animal.isInLove() )
{
return true;
}
if ( entity instanceof EntitySheep && ( (EntitySheep) entity ).isSheared() )
{
return true;
}
}
if (entity instanceof EntityCreeper && ((EntityCreeper) entity).isIgnited()) { // isExplosive
return true;
}
}
return false;
}
/**
* Checks if the entity is active for this tick.
*
* @param entity
* @return
*/
public static boolean checkIfActive(Entity entity)
{
// Never safe to skip fireworks or entities not yet added to chunk
// PAIL: inChunk - boolean under datawatchers
if ( !entity.aa || entity instanceof EntityFireworks ) {
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 ( 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.ticksLived % 4 == 0 && !checkEntityImmunities( entity ) )
{
isActive = false;
}
int x = MathHelper.floor( entity.locX );
int z = MathHelper.floor( entity.locZ );
// Make sure not on edge of unloaded chunk
Chunk chunk = entity.world.getChunkIfLoaded( x >> 4, z >> 4 );
if ( isActive && !( chunk != null && chunk.areNeighborsLoaded( 1 ) ) )
{
isActive = false;
}
return isActive;
}
}