package org.spigotmc; import java.util.ArrayList; import java.util.List; import net.minecraft.util.AxisAlignedBB; import net.minecraft.world.chunk.Chunk; import net.minecraft.entity.Entity; import net.minecraft.entity.passive.EntityAmbientCreature; import net.minecraft.entity.passive.EntityAnimal; import net.minecraft.entity.projectile.EntityArrow; import net.minecraft.entity.boss.EntityDragonPart; import net.minecraft.entity.EntityCreature; import net.minecraft.entity.item.EntityEnderCrystal; import net.minecraft.entity.boss.EntityDragon; import net.minecraft.entity.projectile.EntityFireball; import net.minecraft.entity.item.EntityFireworkRocket; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.EntityLiving; import net.minecraft.entity.monster.EntityMob; import net.minecraft.entity.projectile.EntityThrowable; import net.minecraft.entity.passive.EntitySheep; import net.minecraft.entity.monster.EntitySlime; import net.minecraft.entity.item.EntityTNTPrimed; import net.minecraft.entity.passive.EntityVillager; import net.minecraft.entity.effect.EntityWeatherEffect; import net.minecraft.entity.boss.EntityWither; import net.minecraft.util.MathHelper; import net.minecraft.server.MinecraftServer; import net.minecraft.world.World; import org.bukkit.craftbukkit.SpigotTimings; // Cauldron start import net.minecraft.entity.EnumCreatureType; import net.minecraftforge.common.DimensionManager; import net.minecraftforge.common.util.FakePlayer; // Cauldron end public class ActivationRange { static AxisAlignedBB maxBB = AxisAlignedBB.getBoundingBox( 0, 0, 0, 0, 0, 0 ); static AxisAlignedBB miscBB = AxisAlignedBB.getBoundingBox( 0, 0, 0, 0, 0, 0 ); static AxisAlignedBB animalBB = AxisAlignedBB.getBoundingBox( 0, 0, 0, 0, 0, 0 ); static AxisAlignedBB monsterBB = AxisAlignedBB.getBoundingBox( 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) { Chunk chunk = null; // Cauldron start - account for entities that dont extend EntityMob, EntityAmbientCreature, EntityCreature if ( entity instanceof EntityMob || entity instanceof EntitySlime || entity.isCreatureType(EnumCreatureType.monster, false)) // Cauldron - account for entities that dont extend EntityMob { return 1; // Monster } else if ( entity instanceof EntityCreature || entity instanceof EntityAmbientCreature || entity.isCreatureType(EnumCreatureType.creature, false) || entity.isCreatureType(EnumCreatureType.waterCreature, false) || entity.isCreatureType(EnumCreatureType.ambient, false)) { return 2; // Animal // Cauldron end } else { return 3; // Misc } } /** * These entities are excluded from Activation range checks. * * @param entity * @param world * @return boolean If it should always tick. */ public static boolean initializeEntityActivationState(Entity entity, SpigotWorldConfig config) { // Cauldron start - another fix for Proxy Worlds if (config == null && DimensionManager.getWorld(0) != null) { config = DimensionManager.getWorld(0).spigotConfig; } else { return true; } // Cauldron end if ( ( entity.activationType == 3 && config.miscActivationRange == 0 ) || ( entity.activationType == 2 && config.animalActivationRange == 0 ) || ( entity.activationType == 1 && config.monsterActivationRange == 0 ) || (entity instanceof EntityPlayer && !(entity instanceof FakePlayer)) // Cauldron || entity instanceof EntityThrowable || entity instanceof EntityDragon || entity instanceof EntityDragonPart || entity instanceof EntityWither || entity instanceof EntityFireball || entity instanceof EntityWeatherEffect || entity instanceof EntityTNTPrimed || entity instanceof EntityEnderCrystal || entity instanceof EntityFireworkRocket || entity instanceof EntityVillager // Cauldron start - force ticks for entities with superclass of Entity and not a creature/monster || (entity.getClass().getSuperclass() == Entity.class && !entity.isCreatureType(EnumCreatureType.creature, false) && !entity.isCreatureType(EnumCreatureType.ambient, false) && !entity.isCreatureType(EnumCreatureType.monster, false) && !entity.isCreatureType(EnumCreatureType.waterCreature, false))) { return true; } return false; } /** * Utility method to grow an AABB without creating a new AABB or touching * the pool, so we can re-use ones we have. * * @param target * @param source * @param x * @param y * @param z */ public static void growBB(AxisAlignedBB target, AxisAlignedBB source, int x, int y, int z) { target.minX = source.minX - x; target.minY = source.minY - y; target.minZ = source.minZ - z; target.maxX = source.maxX + x; target.maxY = source.maxY + y; target.maxZ = source.maxZ + z; } /** * 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) { SpigotTimings.entityActivationCheckTimer.startTiming(); // Cauldron start - proxy world support final int miscActivationRange = world.getSpigotConfig().miscActivationRange; final int animalActivationRange = world.getSpigotConfig().animalActivationRange; final int monsterActivationRange = world.getSpigotConfig().monsterActivationRange; // Cauldron end int maxRange = Math.max( monsterActivationRange, animalActivationRange ); maxRange = Math.max( maxRange, miscActivationRange ); maxRange = Math.min( ( world.getSpigotConfig().viewDistance << 4 ) - 8, maxRange ); // Cauldron for ( Entity player : new ArrayList( world.playerEntities ) ) { player.activatedTick = MinecraftServer.currentTick; growBB( maxBB, player.boundingBox, maxRange, 256, maxRange ); growBB( miscBB, player.boundingBox, miscActivationRange, 256, miscActivationRange ); growBB( animalBB, player.boundingBox, animalActivationRange, 256, animalActivationRange ); growBB( monsterBB, player.boundingBox, monsterActivationRange, 256, monsterActivationRange ); int i = MathHelper.floor_double( maxBB.minX / 16.0D ); int j = MathHelper.floor_double( maxBB.maxX / 16.0D ); int k = MathHelper.floor_double( maxBB.minZ / 16.0D ); int l = MathHelper.floor_double( maxBB.maxZ / 16.0D ); for ( int i1 = i; i1 <= j; ++i1 ) { for ( int j1 = k; j1 <= l; ++j1 ) { if ( world.getWorld().isChunkLoaded( i1, j1 ) ) { activateChunkEntities( world.getChunkFromChunkCoords( i1, j1 ) ); } } } } SpigotTimings.entityActivationCheckTimer.stopTiming(); } /** * Checks for the activation state of all entities in this chunk. * * @param chunk */ private static void activateChunkEntities(Chunk chunk) { for ( List slice : chunk.entityLists ) { for ( Entity entity : slice ) { if ( MinecraftServer.currentTick > entity.activatedTick ) { if ( entity.defaultActivationState ) { entity.activatedTick = MinecraftServer.currentTick; continue; } switch ( entity.activationType ) { case 1: if ( monsterBB.intersectsWith( entity.boundingBox ) ) { entity.activatedTick = MinecraftServer.currentTick; } break; case 2: if ( animalBB.intersectsWith( entity.boundingBox ) ) { entity.activatedTick = MinecraftServer.currentTick; } break; case 3: default: if ( miscBB.intersectsWith( entity.boundingBox ) ) { 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 /* isInWater */ || entity.fire > 0 ) { return true; } if ( !( entity instanceof EntityArrow ) ) { if ( !entity.onGround || entity.riddenByEntity != null || entity.ridingEntity != null ) { return true; } } else if ( !( (EntityArrow) entity ).inGround ) { return true; } // special cases. if ( entity instanceof EntityLiving ) { EntityLiving living = (EntityLiving) entity; if ( living.attackTime > 0 || living.hurtTime > 0 || living.activePotionsMap.size() > 0 ) { return true; } if ( entity instanceof EntityCreature && ( (EntityCreature) entity ).entityToAttack != null ) { return true; } if ( entity instanceof EntityVillager && ( (EntityVillager) entity ).isMating() /* Getter for first boolean */ ) { return true; } if ( entity instanceof EntityAnimal ) { EntityAnimal animal = (EntityAnimal) entity; if ( animal.isChild() || animal.isInLove() /*love*/ ) { return true; } if ( entity instanceof EntitySheep && ( (EntitySheep) entity ).getSheared() ) { 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(); 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.ticksExisted % 4 == 0 && !checkEntityImmunities( entity ) ) { isActive = false; } // Cauldron - we check for entities in forced chunks in World.updateEntityWithOptionalForce // Make sure not on edge of unloaded chunk int x = net.minecraft.util.MathHelper.floor_double( entity.posX ); int z = net.minecraft.util.MathHelper.floor_double( entity.posZ ); if ( isActive && !entity.worldObj.doChunksNearChunkExist( x, 0, z, 16 ) ) { isActive = false; } SpigotTimings.checkIfActiveTimer.stopTiming(); return isActive; } }