Entity Activation Range
This feature gives 3 new configurable ranges that if an entity of the matching type is outside of this radius of any player, will tick at 5% of its normal rate. This will drastically cut down on tick timings for entities that are not in range of a user to actually be "used". This change can have dramatic impact on gameplay if configured too low. Balance according to your servers desired gameplay. By: Aikar <aikar@aikar.co>
This commit is contained in:
@@ -39,6 +39,9 @@ public class SpigotTimings {
|
||||
|
||||
public static final CustomTimingsHandler playerCommandTimer = new CustomTimingsHandler("** playerCommand");
|
||||
|
||||
public static final CustomTimingsHandler entityActivationCheckTimer = new CustomTimingsHandler("entityActivationCheck");
|
||||
public static final CustomTimingsHandler checkIfActiveTimer = new CustomTimingsHandler("** checkIfActive");
|
||||
|
||||
public static final HashMap<String, CustomTimingsHandler> entityTypeTimingMap = new HashMap<String, CustomTimingsHandler>();
|
||||
public static final HashMap<String, CustomTimingsHandler> tileEntityTypeTimingMap = new HashMap<String, CustomTimingsHandler>();
|
||||
public static final HashMap<String, CustomTimingsHandler> pluginTaskTimingMap = new HashMap<String, CustomTimingsHandler>();
|
||||
|
||||
263
paper-server/src/main/java/org/spigotmc/ActivationRange.java
Normal file
263
paper-server/src/main/java/org/spigotmc/ActivationRange.java
Normal file
@@ -0,0 +1,263 @@
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -194,4 +194,21 @@ public class SpigotWorldConfig
|
||||
this.itemDespawnRate = this.getInt( "item-despawn-rate", 6000 );
|
||||
this.log( "Item Despawn Rate: " + this.itemDespawnRate );
|
||||
}
|
||||
|
||||
public int animalActivationRange = 32;
|
||||
public int monsterActivationRange = 32;
|
||||
public int raiderActivationRange = 48;
|
||||
public int miscActivationRange = 16;
|
||||
public boolean tickInactiveVillagers = true;
|
||||
public boolean ignoreSpectatorActivation = false;
|
||||
private void activationRange()
|
||||
{
|
||||
this.animalActivationRange = this.getInt( "entity-activation-range.animals", this.animalActivationRange );
|
||||
this.monsterActivationRange = this.getInt( "entity-activation-range.monsters", this.monsterActivationRange );
|
||||
this.raiderActivationRange = this.getInt( "entity-activation-range.raiders", this.raiderActivationRange );
|
||||
this.miscActivationRange = this.getInt( "entity-activation-range.misc", this.miscActivationRange );
|
||||
this.tickInactiveVillagers = this.getBoolean( "entity-activation-range.tick-inactive-villagers", this.tickInactiveVillagers );
|
||||
this.ignoreSpectatorActivation = this.getBoolean( "entity-activation-range.ignore-spectators", this.ignoreSpectatorActivation );
|
||||
this.log( "Entity Activation Range: An " + this.animalActivationRange + " / Mo " + this.monsterActivationRange + " / Ra " + this.raiderActivationRange + " / Mi " + this.miscActivationRange + " / Tiv " + this.tickInactiveVillagers + " / Isa " + this.ignoreSpectatorActivation );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user