Paper/Spigot-Server-Patches/0330-Cache-World-Entity-Type-counts.patch
Aikar f4f4396f0e
Restore vanilla behavior of mob counts for persistent mobs
Now decounts entities that got name tagged or persistent state changed.

Removes a bad CB check that ignored some persistent mobs persistence state.

Vanilla code explicitly only cares 'is this mob persistent?' and if
it is, it is not counted. CB should not of been checking its type state.

Fixes #1922
2019-03-29 03:38:23 -04:00

239 lines
9.7 KiB
Diff

From d0ac0272da649db7ffd5c1edec1356600a145029 Mon Sep 17 00:00:00 2001
From: Colin Godsey <crgodsey@gmail.com>
Date: Wed, 8 Aug 2018 10:10:06 -0600
Subject: [PATCH] Cache World Entity Type counts
Optimizes mob spawning by keeping a count of entities by type
diff --git a/src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java b/src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java
new file mode 100644
index 000000000..d3e6c9e74
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java
@@ -0,0 +1,121 @@
+package com.destroystokyo.paper;
+
+import net.minecraft.server.Entity;
+import net.minecraft.server.EntityInsentient;
+import net.minecraft.server.EnumCreatureType;
+import net.minecraft.server.IAnimal;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.World;
+import net.minecraft.server.WorldServer;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+public class PaperWorldEntityList extends ArrayList<Entity> {
+
+ private final WorldServer world;
+ private final int[] entityCounts = new int[EnumCreatureType.values().length];
+
+
+ public PaperWorldEntityList(World world) {
+ this.world = (WorldServer) world;
+ }
+
+ @Override
+ public boolean addAll(Collection<? extends Entity> c) {
+ for (Entity e : c) {
+ updateEntityCount(e, 1);
+ }
+
+ return super.addAll(c);
+ }
+
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ for (Object e : c) {
+ if (e instanceof Entity && ((Entity) e).getWorld() == world) {
+ updateEntityCount((Entity) e, -1);
+ }
+ }
+
+ return super.removeAll(c);
+ }
+
+ @Override
+ public boolean add(Entity e) {
+ updateEntityCount(e, 1);
+
+ return super.add(e);
+ }
+
+ @Override
+ public Entity remove(int index) {
+ guard();
+ Entity entity = super.remove(index);
+ if (entity != null) updateEntityCount(entity, -1);
+ return entity;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ guard();
+ if (super.remove(o)) {
+ updateEntityCount((Entity) o, -1);
+ return true;
+ }
+ return false;
+ }
+
+ private void guard() {
+ if (world.guardEntityList) {
+ throw new java.util.ConcurrentModificationException();
+ }
+ }
+
+ public int getCreatureCount(EnumCreatureType type) {
+ return entityCounts[type.ordinal()];
+ }
+
+ private void updateEntityCount(EnumCreatureType type, int amt) {
+ int count = entityCounts[type.ordinal()];
+
+ count += amt;
+
+ if (count < 0) {
+ MinecraftServer.LOGGER.error("Paper - Entity count cache has gone negative");
+ count = 0;
+ }
+
+ entityCounts[type.ordinal()] = count;
+ }
+
+ public void updateEntityCount(Entity entity, int amt) {
+ if (!(entity instanceof IAnimal)) return;
+
+ if (entity instanceof EntityInsentient) {
+ EntityInsentient entityinsentient = (EntityInsentient) entity;
+ if (amt > 0 && entityinsentient.isPersistent()) {
+ return;
+ }
+ }
+ if (amt < 0) {
+ if (!entity.hasBeenCounted) {
+ return;
+ }
+ // Only remove once, we remove from if the entity list is guarded, but may be called later
+ entity.hasBeenCounted = false;
+ } else {
+ if (entity.hasBeenCounted) {
+ return;
+ }
+ entity.hasBeenCounted = true;
+ }
+
+ for (EnumCreatureType type : EnumCreatureType.values()) {
+ if (type.matches(entity)) {
+ updateEntityCount(type, amt);
+ break;
+ }
+ }
+ }
+}
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index 2d2edbd33..47d3609c3 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -122,6 +122,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
private boolean az;
public boolean dead;
public boolean shouldBeRemoved; // Paper
+ public boolean hasBeenCounted = false; // Paper
public float width;
public float length;
public float J;
diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java
index ee5078370..693973e97 100644
--- a/src/main/java/net/minecraft/server/EntityInsentient.java
+++ b/src/main/java/net/minecraft/server/EntityInsentient.java
@@ -259,6 +259,7 @@ public abstract class EntityInsentient extends EntityLiving {
public void tick() {
super.tick();
+ if (hasBeenCounted == this.isPersistent()) this.world.entityList.updateEntityCount(this, hasBeenCounted ? -1 : 1); // Paper - adjust count if persistence state changes
if (!this.world.isClientSide) {
this.dl();
if (this.ticksLived % 5 == 0) {
diff --git a/src/main/java/net/minecraft/server/EnumCreatureType.java b/src/main/java/net/minecraft/server/EnumCreatureType.java
index 79e52f7ba..42f6a6a93 100644
--- a/src/main/java/net/minecraft/server/EnumCreatureType.java
+++ b/src/main/java/net/minecraft/server/EnumCreatureType.java
@@ -16,6 +16,8 @@ public enum EnumCreatureType {
this.h = flag1;
}
+ public boolean matches(Entity entity) { return innerClass().isAssignableFrom(entity.getClass()); } // Paper
+ public Class<? extends IAnimal> innerClass() { return this.a(); } // Paper - OBFHELPER
public Class<? extends IAnimal> a() {
return this.e;
}
diff --git a/src/main/java/net/minecraft/server/SpawnerCreature.java b/src/main/java/net/minecraft/server/SpawnerCreature.java
index e62616552..bfbe4d3e3 100644
--- a/src/main/java/net/minecraft/server/SpawnerCreature.java
+++ b/src/main/java/net/minecraft/server/SpawnerCreature.java
@@ -114,7 +114,7 @@ public final class SpawnerCreature {
if ((!enumcreaturetype.c() || flag1) && (enumcreaturetype.c() || flag) && (!enumcreaturetype.d() || flag2)) {
k = limit * i / SpawnerCreature.b; // CraftBukkit - use per-world limits
- int l1 = worldserver.a(enumcreaturetype.a(), k);
+ int l1 = worldserver.entityList.getCreatureCount(enumcreaturetype); // Paper - entity count cache
if (l1 <= k) {
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index bd8d9ef48..80bf95028 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -45,7 +45,8 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
private static final EnumDirection[] a = EnumDirection.values();
private int b = 63;
// Spigot start - guard entity list from removals
- public final List<Entity> entityList = new java.util.ArrayList<Entity>()
+ public final com.destroystokyo.paper.PaperWorldEntityList entityList = new com.destroystokyo.paper.PaperWorldEntityList(this);
+ /* // Paper start
{
@Override
public Entity remove(int index)
@@ -69,6 +70,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
}
}
};
+ */ // Paper end
// Spigot end
protected final Set<Entity> g = com.google.common.collect.Sets.newHashSet(); public Set<Entity> getEntityUnloadQueue() { return g; };// Paper - OBFHELPER
//public final List<TileEntity> tileEntityList = Lists.newArrayList(); // Paper - remove unused list
@@ -139,8 +141,10 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
public final com.destroystokyo.paper.PaperWorldConfig paperConfig; // Paper
public final co.aikar.timings.WorldTimingsHandler timings; // Paper
- private boolean guardEntityList; // Spigot
+ public boolean guardEntityList; // Spigot // Paper - public
public static BlockPosition lastPhysicsProblem; // Spigot
+ public static boolean haveWeSilencedAPhysicsCrash;
+ public static String blockLocation;
private org.spigotmc.TickLimiter entityLimiter;
private org.spigotmc.TickLimiter tileLimiter;
private int tileTickPosition;
@@ -1122,6 +1126,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
this.getChunkAt(i, j).b(entity);
}
entity.shouldBeRemoved = true; // Paper
+ entityList.updateEntityCount(entity, -1); // Paper
if (!guardEntityList) { // Spigot - It will get removed after the tick if we are ticking // Paper - always remove from current chunk above
// CraftBukkit start - Decrement loop variable field if we've already ticked this entity
@@ -2423,7 +2428,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
// CraftBukkit start - Split out persistent check, don't apply it to special persistent mobs
if (entity instanceof EntityInsentient) {
EntityInsentient entityinsentient = (EntityInsentient) entity;
- if (entityinsentient.isTypeNotPersistent() && entityinsentient.isPersistent()) {
+ if (/*entityinsentient.isTypeNotPersistent() && */ entityinsentient.isPersistent()) { // Paper - while this code is inused, remove isTypeNotPersistent() as vanilla does not check that. Patch this method incase anything reflects into it.
continue;
}
}
--
2.21.0