From 5b101db469123c2f92ff82fc04a8b77274296bcf Mon Sep 17 00:00:00 2001 From: Colin Godsey Date: Wed, 8 Aug 2018 10:10:06 -0600 Subject: [PATCH] Add entity count cache diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index d6b56d685..3d973deb7 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -1034,6 +1034,14 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati } // CraftBukkit end */ + /* + * Paper - clear count cache every once in a while. + * Cache can never be entirely trusted because + * an entity may become persistent or not + * during its lifetime. + */ + if (this.ticks % 600 == 0) worldserver.clearEntityCountCache(); + this.methodProfiler.a("tick"); CrashReport crashreport; diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java index f525fd1b4..494759a1c 100644 --- a/src/main/java/net/minecraft/server/SpawnerCreature.java +++ b/src/main/java/net/minecraft/server/SpawnerCreature.java @@ -113,7 +113,7 @@ public final class SpawnerCreature { // CraftBukkit end if ((!enumcreaturetype.c() || flag1) && (enumcreaturetype.c() || flag) && (!enumcreaturetype.d() || flag2)) { - k = worldserver.a(enumcreaturetype.a()); + k = worldserver.getCreatureCount(enumcreaturetype); // Paper - entity count cache int l1 = limit * i / b; // CraftBukkit - use per-world limits if (k <= l1) { diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java index 004c3ec47..baf73a411 100644 --- a/src/main/java/net/minecraft/server/World.java +++ b/src/main/java/net/minecraft/server/World.java @@ -1,28 +1,20 @@ package net.minecraft.server; -import co.aikar.timings.Timings; import com.destroystokyo.paper.event.server.ServerExceptionEvent; import com.destroystokyo.paper.exception.ServerInternalException; import com.google.common.base.MoreObjects; import com.google.common.collect.Lists; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.Random; -import java.util.UUID; + +import java.util.*; import java.util.function.Function; import java.util.function.Predicate; -import java.util.function.Supplier; import javax.annotation.Nullable; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; // CraftBukkit start import com.google.common.collect.Maps; -import java.util.HashMap; // Paper -import java.util.Map; import org.bukkit.Bukkit; import org.bukkit.block.BlockState; import org.bukkit.craftbukkit.CraftServer; @@ -30,16 +22,11 @@ import org.bukkit.craftbukkit.CraftWorld; import org.bukkit.craftbukkit.block.CraftBlockState; import org.bukkit.craftbukkit.block.data.CraftBlockData; import org.bukkit.craftbukkit.event.CraftEventFactory; -import org.bukkit.craftbukkit.util.CraftMagicNumbers; -import org.bukkit.craftbukkit.util.LongHashSet; // Paper -import org.bukkit.entity.Player; -import org.bukkit.event.block.BlockCanBuildEvent; import org.bukkit.event.block.BlockPhysicsEvent; import org.bukkit.event.entity.CreatureSpawnEvent.SpawnReason; import org.bukkit.generator.ChunkGenerator; // CraftBukkit end // Paper start -import java.util.Set; import com.google.common.collect.Sets; // Paper end public abstract class World implements GeneratorAccess, IIBlockAccess, AutoCloseable { @@ -50,10 +37,41 @@ public abstract class World implements GeneratorAccess, IIBlockAccess, AutoClose // Spigot start - guard entity list from removals public final List entityList = new java.util.ArrayList() { + // Paper start - entity count cache + @Override + public boolean addAll(Collection c) { + for (Entity e : c) { + updateEntityCount(e, true); + } + + return super.addAll(c); + } + + @Override + public boolean removeAll(Collection c) { + for (Object e : c) { + if (e instanceof Entity) { + updateEntityCount((Entity)e, false); + } + } + + return super.removeAll(c); + } + + @Override + public boolean add(Entity e) { + updateEntityCount(e, true); + + return super.add(e); + } + + // Paper end + @Override public Entity remove(int index) { guard(); + updateEntityCount(get(index), false); // Paper return super.remove( index ); } @@ -61,6 +79,7 @@ public abstract class World implements GeneratorAccess, IIBlockAccess, AutoClose public boolean remove(Object o) { guard(); + if (o instanceof Entity) updateEntityCount((Entity)o, false); // Paper return super.remove( o ); } @@ -82,6 +101,7 @@ public abstract class World implements GeneratorAccess, IIBlockAccess, AutoClose public final Map playersByName = Maps.newHashMap(); // Paper - World EntityHuman Lookup Optimizations public final List k = Lists.newArrayList(); protected final IntHashMap entitiesById = new IntHashMap(); + private Map countCache = new EnumMap(EnumCreatureType.class); // Paper - entity count cache private final long G = 16777215L; private int H; public int getSkylightSubtracted() { return this.H; } public void setSkylightSubtracted(int value) { this.H = value;} // Paper - OBFHELPER protected int m = (new Random()).nextInt(); @@ -2439,6 +2459,7 @@ public abstract class World implements GeneratorAccess, IIBlockAccess, AutoClose } + public int getEntityCount(Class oclass) { return a(oclass); } // Paper - OBFHELPER public int a(Class oclass) { int i = 0; Iterator iterator = this.entityList.iterator(); @@ -2464,6 +2485,61 @@ public abstract class World implements GeneratorAccess, IIBlockAccess, AutoClose return i; } + // Paper start - entity count cache + public int getCreatureCount(EnumCreatureType typ) { + Integer count = countCache.get(typ); + + if (count == null) { + count = getEntityCount(typ.a()); + + countCache.put(typ, count); + } + + return count; + } + + void clearEntityCountCache() { + countCache.clear(); + } + + protected void updateEntityCount(EnumCreatureType typ, boolean incr) { + Integer countObject = countCache.get(typ); + + if (countObject == null) return; + + int count = countObject; + + if (incr) count++; + else count--; + + if (count < 0) { + e.warn("Entity count cache has gone negative"); + count = 0; + } + + countCache.put(typ, count); + } + + protected void updateEntityCount(Entity entity, boolean incr) { + if (entity == null || !(entity instanceof IAnimal)) return; + + if (entity instanceof EntityInsentient) { + EntityInsentient entityinsentient = (EntityInsentient) entity; + if (entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) { + return; + } + } + + Class clazz = entity.getClass(); + + for (EnumCreatureType typ : EnumCreatureType.values()) { + if (typ.a().isAssignableFrom(clazz)) { + updateEntityCount(typ, incr); + } + } + } + // Paper end + public void addChunkEntities(Collection collection) { a(collection); } // Paper - OBFHELPER public void a(Collection collection) { org.spigotmc.AsyncCatcher.catchOp( "entity world add"); // Spigot -- 2.15.2 (Apple Git-101.1)