From f7e622680253de77248261ed9025946ceebb69e9 Mon Sep 17 00:00:00 2001 From: Aikar Date: Sat, 16 Feb 2013 19:45:09 +1100 Subject: [PATCH] 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. --- src/main/java/net/minecraft/server/Entity.java | 8 ++ .../java/net/minecraft/server/EntityArrow.java | 2 +- src/main/java/net/minecraft/server/World.java | 10 +- .../java/org/bukkit/craftbukkit/CraftWorld.java | 13 ++ src/main/java/org/bukkit/craftbukkit/Spigot.java | 149 +++++++++++++++++++++ .../java/org/bukkit/event/WorldTimingsHandler.java | 2 + src/main/resources/configurations/bukkit.yml | 3 + 7 files changed, 185 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java index d0a58f8..9da5035 100644 --- a/src/main/java/net/minecraft/server/Entity.java +++ b/src/main/java/net/minecraft/server/Entity.java @@ -111,6 +111,13 @@ public abstract class Entity { public UUID uniqueId = UUID.randomUUID(); // CraftBukkit public boolean valid = false; // CraftBukkit + // Spigot start + public boolean inWater = false; + public final byte activationType = org.bukkit.craftbukkit.Spigot.initializeEntityActivationType(this); + public final boolean defaultActivationState = org.bukkit.craftbukkit.Spigot.initializeEntityActivationState(this); + public boolean isActivated = defaultActivationState; + // Spigot end + public Entity(World world) { this.id = entityCount++; this.l = 1.0D; @@ -862,6 +869,7 @@ public abstract class Entity { this.ad = false; } + this.inWater = this.ad; // Spigot return this.ad; } diff --git a/src/main/java/net/minecraft/server/EntityArrow.java b/src/main/java/net/minecraft/server/EntityArrow.java index 916b9dc..bdd18f6 100644 --- a/src/main/java/net/minecraft/server/EntityArrow.java +++ b/src/main/java/net/minecraft/server/EntityArrow.java @@ -16,7 +16,7 @@ public class EntityArrow extends Entity implements IProjectile { private int f = -1; private int g = 0; private int h = 0; - private boolean inGround = false; + public boolean inGround = false; // Spigot - private -> public public int fromPlayer = 0; public int shake = 0; public Entity shooter; diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java index cd7ad64..53834a9 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -1241,6 +1241,7 @@ public abstract class World implements IBlockAccess { this.methodProfiler.c("regular"); timings.entityBaseTick.stopTiming(); // Spigot + org.bukkit.craftbukkit.Spigot.activateEntities(this); // Spigot timings.entityTick.startTiming(); // Spigot for (i = 0; i < this.entityList.size(); ++i) { entity = (Entity) this.entityList.get(i); @@ -1405,6 +1406,13 @@ public abstract class World implements IBlockAccess { if (entity == null) { return; } + if (!entity.isActivated && !org.bukkit.craftbukkit.Spigot.checkIfActive(entity)) { + if (entity.vehicle == null) { + entity.ticksLived++; + } + return; + } + entity.isActivated = entity.defaultActivationState; try { tickEntity(entity, flag); } catch (Exception e) { @@ -1434,7 +1442,7 @@ public abstract class World implements IBlockAccess { int j = MathHelper.floor(entity.locZ); byte b0 = 32; - if (!flag || this.d(i - b0, 0, j - b0, i + b0, 0, j + b0)) { + if (entity instanceof EntityFireworks || !flag || this.d(i - b0, 0, j - b0, i + b0, 0, j + b0)) { // Spigot - Not safe to skip a firework. entity.T = entity.locX; entity.U = entity.locY; entity.V = entity.locZ; diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java index 21bd64a..a083ae4 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java @@ -100,6 +100,10 @@ public class CraftWorld implements World { treeGrowthModifier = configuration.getInt("world-settings.default.tree-growth-modifier", treeGrowthModifier); mushroomGrowthModifier = configuration.getInt("world-settings.default.mushroom-growth-modifier", mushroomGrowthModifier); + miscEntityActivationRange = configuration.getInt("world-settings.default.entity-activation-range-misc", miscEntityActivationRange); + animalEntityActivationRange = configuration.getInt("world-settings.default.entity-activation-range-animals", animalEntityActivationRange); + monsterEntityActivationRange = configuration.getInt("world-settings.default.entity-activation-range-monsters", monsterEntityActivationRange); + //override defaults with world specific, if they exist growthPerTick = configuration.getInt("world-settings." + name + ".growth-chunks-per-tick", growthPerTick); itemMergeRadius = configuration.getDouble("world-settings." + name + ".item-merge-radius", itemMergeRadius); @@ -121,6 +125,10 @@ public class CraftWorld implements World { obfuscated = !world.getServer().orebfuscatorDisabledWorlds.contains(name); + miscEntityActivationRange = configuration.getInt("world-settings." + name + ".entity-activation-range-misc", miscEntityActivationRange); + animalEntityActivationRange = configuration.getInt("world-settings." + name + ".entity-activation-range-animals", animalEntityActivationRange); + monsterEntityActivationRange = configuration.getInt("world-settings." + name + ".entity-activation-range-monsters", monsterEntityActivationRange); + server.getLogger().info("-------------- Spigot ----------------"); server.getLogger().info("-------- World Settings For [" + name + "] --------"); server.getLogger().info("Growth Per Chunk: " + growthPerTick); @@ -138,6 +146,7 @@ public class CraftWorld implements World { server.getLogger().info("Mushroom Growth Modifier: " + mushroomGrowthModifier); server.getLogger().info("View distance: " + viewDistance); server.getLogger().info("Oreobfuscator: " + obfuscated); + server.getLogger().info("Entity Activation Range: An " + animalEntityActivationRange + " / Mo " + monsterEntityActivationRange + " / Mi " + miscEntityActivationRange); server.getLogger().info("-------------------------------------------------"); // Spigot end } @@ -158,6 +167,10 @@ public class CraftWorld implements World { public int sugarGrowthModifier = 100; public int treeGrowthModifier = 100; public int mushroomGrowthModifier = 100; + + public int miscEntityActivationRange = 0; + public int animalEntityActivationRange = 0; + public int monsterEntityActivationRange = 0; // Spigot end public Block getBlockAt(int x, int y, int z) { diff --git a/src/main/java/org/bukkit/craftbukkit/Spigot.java b/src/main/java/org/bukkit/craftbukkit/Spigot.java index 582dbab..75572f9 100644 --- a/src/main/java/org/bukkit/craftbukkit/Spigot.java +++ b/src/main/java/org/bukkit/craftbukkit/Spigot.java @@ -1,7 +1,10 @@ package org.bukkit.craftbukkit; +import java.util.ArrayList; +import net.minecraft.server.*; import org.bukkit.command.SimpleCommandMap; import org.bukkit.configuration.file.YamlConfiguration; +import java.util.List; public class Spigot { public static void initialize(CraftServer server, SimpleCommandMap commandMap, YamlConfiguration configuration) { @@ -37,5 +40,151 @@ public class Spigot { server.getLogger().severe("[Spigot] You should not disable chunk-gc. Resetting period-in-ticks to 600 ticks."); server.chunkGCPeriod = 600; } + + } + + /** + * 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 + * @return boolean If it should always tick. + */ + public static boolean initializeEntityActivationState(Entity entity) { + if (entity instanceof EntityHuman + || entity instanceof EntityArrow + || entity instanceof EntityProjectile + || entity instanceof EntityEnderDragon + || entity instanceof EntityComplexPart + || entity instanceof EntityWither + || entity instanceof EntityFireball + || entity instanceof EntityWeather + || entity instanceof EntityTNTPrimed + || entity instanceof EntityEnderCrystal + || entity instanceof EntityFireworks) { + 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.a = source.a - x; + target.b = source.b - y; + target.c = source.c - z; + target.d = source.d + x; + target.e = source.e + y; + target.f = source.f + 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) { + final int miscActivationRange = world.getWorld().miscEntityActivationRange; + final int animalActivationRange = world.getWorld().animalEntityActivationRange; + final int monsterActivationRange = world.getWorld().monsterEntityActivationRange; + + + world.timings.activationCheck.startTiming(); + int maxRange = Math.max(monsterActivationRange, animalActivationRange); + maxRange = Math.max(maxRange, miscActivationRange); + if (miscActivationRange == 0 || animalActivationRange == 0 || monsterActivationRange == 0) { + // One of them is disabled, set to view-distance + maxRange = world.getWorld().viewDistance << 4; + } else { + maxRange = Math.min(world.getWorld().viewDistance << 4, maxRange); // Do not tick on edge of unloaded chunks - vanilla behavior. + } + + AxisAlignedBB maxBB = AxisAlignedBB.a(0, 0, 0, 0, 0, 0); + AxisAlignedBB miscBB = AxisAlignedBB.a(0, 0, 0, 0, 0, 0); + AxisAlignedBB animalBB = AxisAlignedBB.a(0, 0, 0, 0, 0, 0); + AxisAlignedBB monsterBB = AxisAlignedBB.a(0, 0, 0, 0, 0, 0); + + for (Entity player : new ArrayList(world.players)) { + 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); + + final List list = world.getEntities(player, maxBB); + for (Entity entity : list) { + if (!entity.defaultActivationState) { + boolean isInRange = false; + switch (entity.activationType) { + case 1: + if (monsterActivationRange == 0 || monsterBB.a(entity.boundingBox)) { + isInRange = true; + } + break; + case 2: + if (animalActivationRange == 0 || animalBB.a(entity.boundingBox)) { + isInRange = true; + } + break; + case 3: + default: + if (miscActivationRange == 0 || miscBB.a(entity.boundingBox)) { + isInRange = true; + } + } + + entity.isActivated = isInRange; + } + } + } + world.timings.activationCheck.stopTiming(); + } + + /** + * 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 checkIfActive(Entity entity) { + // quick checks. + if (entity.ticksLived % 20 == 0 || !entity.onGround || entity.inWater || entity.passenger != null || entity.vehicle != null) { + return true; + } + // special cases. + if (entity instanceof EntityAnimal) { + EntityAnimal animal = (EntityAnimal) entity; + if (animal.isBaby() || animal.r() /*love*/) { + return true; + } + return (entity instanceof EntitySheep && ((EntitySheep) entity).isSheared()); + } + return (entity instanceof EntityArrow && !((EntityArrow) entity).inGround); + } } diff --git a/src/main/java/org/bukkit/event/WorldTimingsHandler.java b/src/main/java/org/bukkit/event/WorldTimingsHandler.java index bb0c191..6a4a05e 100644 --- a/src/main/java/org/bukkit/event/WorldTimingsHandler.java +++ b/src/main/java/org/bukkit/event/WorldTimingsHandler.java @@ -8,6 +8,7 @@ public class WorldTimingsHandler { public CustomTimingsHandler entityBaseTick; public CustomTimingsHandler entityTick; public CustomTimingsHandler tileEntityTick; + public CustomTimingsHandler activationCheck; public WorldTimingsHandler(World server) { String name = server.worldData.getName() +" - "; @@ -16,5 +17,6 @@ public class WorldTimingsHandler { entityBaseTick = new CustomTimingsHandler(name + "entityBaseTick"); entityTick = new CustomTimingsHandler(name + "entityTick"); tileEntityTick = new CustomTimingsHandler(name + "tileEntityTick"); + activationCheck = new CustomTimingsHandler("** " + name + "activateEntities"); } } diff --git a/src/main/resources/configurations/bukkit.yml b/src/main/resources/configurations/bukkit.yml index 3122a14..5933a88 100644 --- a/src/main/resources/configurations/bukkit.yml +++ b/src/main/resources/configurations/bukkit.yml @@ -50,6 +50,9 @@ world-settings: sugar-growth-modifier: 100 tree-growth-modifier: 100 mushroom-growth-modifier: 100 + entity-activation-range-animals: 48 + entity-activation-range-monsters: 48 + entity-activation-range-misc: 16 world: growth-chunks-per-tick: 1000 world_nether: -- 1.8.1-rc2