2019-03-29 07:51:08 +01:00
From d0ac0272da649db7ffd5c1edec1356600a145029 Mon Sep 17 00:00:00 2001
2018-08-18 17:12:06 +02:00
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
2019-03-29 07:51:08 +01:00
index 000000000..d3e6c9e74
2018-08-18 17:12:06 +02:00
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java
2018-08-18 18:42:17 +02:00
@@ -0,0 +1,121 @@
2018-08-18 17:12:06 +02:00
+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) {
2018-08-18 18:42:17 +02:00
+ if (e instanceof Entity && ((Entity) e).getWorld() == world) {
+ updateEntityCount((Entity) e, -1);
2018-08-18 17:12:06 +02:00
+ }
+ }
+
2018-08-18 18:42:17 +02:00
+ return super.removeAll(c);
2018-08-18 17:12:06 +02:00
+ }
+
+ @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;
2019-03-29 07:51:08 +01:00
+ if (amt > 0 && entityinsentient.isPersistent()) {
2018-08-18 17:12:06 +02:00
+ return;
2018-08-18 18:42:17 +02:00
+ }
+ }
+ 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) {
2018-08-18 17:12:06 +02:00
+ return;
+ }
2018-08-18 18:42:17 +02:00
+ entity.hasBeenCounted = true;
2018-08-18 17:12:06 +02:00
+ }
+
+ for (EnumCreatureType type : EnumCreatureType.values()) {
+ if (type.matches(entity)) {
+ updateEntityCount(type, amt);
+ break;
+ }
+ }
+ }
+}
2018-08-18 18:42:17 +02:00
diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
2019-03-20 02:46:00 +01:00
index 2d2edbd33..47d3609c3 100644
2018-08-18 18:42:17 +02:00
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
2019-01-01 04:15:55 +01:00
@@ -122,6 +122,7 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
2018-08-18 18:42:17 +02:00
private boolean az;
public boolean dead;
public boolean shouldBeRemoved; // Paper
+ public boolean hasBeenCounted = false; // Paper
public float width;
public float length;
public float J;
2019-03-29 07:51:08 +01:00
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) {
2018-08-18 17:12:06 +02:00
diff --git a/src/main/java/net/minecraft/server/EnumCreatureType.java b/src/main/java/net/minecraft/server/EnumCreatureType.java
2019-01-01 04:15:55 +01:00
index 79e52f7ba..42f6a6a93 100644
2018-08-18 17:12:06 +02:00
--- a/src/main/java/net/minecraft/server/EnumCreatureType.java
+++ b/src/main/java/net/minecraft/server/EnumCreatureType.java
2019-01-01 04:15:55 +01:00
@@ -16,6 +16,8 @@ public enum EnumCreatureType {
2018-08-18 17:12:06 +02:00
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
2019-01-01 04:15:55 +01:00
index e62616552..bfbe4d3e3 100644
2018-08-18 17:12:06 +02:00
--- a/src/main/java/net/minecraft/server/SpawnerCreature.java
+++ b/src/main/java/net/minecraft/server/SpawnerCreature.java
2018-08-26 20:11:49 +02:00
@@ -114,7 +114,7 @@ public final class SpawnerCreature {
2018-08-18 17:12:06 +02:00
if ((!enumcreaturetype.c() || flag1) && (enumcreaturetype.c() || flag) && (!enumcreaturetype.d() || flag2)) {
2018-08-26 20:11:49 +02:00
k = limit * i / SpawnerCreature.b; // CraftBukkit - use per-world limits
- int l1 = worldserver.a(enumcreaturetype.a(), k);
2018-08-27 03:21:28 +02:00
+ int l1 = worldserver.entityList.getCreatureCount(enumcreaturetype); // Paper - entity count cache
2018-08-18 17:12:06 +02:00
2018-08-26 20:11:49 +02:00
if (l1 <= k) {
BlockPosition.MutableBlockPosition blockposition_mutableblockposition = new BlockPosition.MutableBlockPosition();
2018-08-18 17:12:06 +02:00
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
2019-03-29 07:51:08 +01:00
index bd8d9ef48..80bf95028 100644
2018-08-18 17:12:06 +02:00
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
2019-01-01 04:15:55 +01:00
@@ -45,7 +45,8 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
2018-08-18 17:12:06 +02:00
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)
2019-01-01 04:15:55 +01:00
@@ -69,6 +70,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
2018-08-18 17:12:06 +02:00
}
}
};
+ */ // Paper end
// Spigot end
2018-08-26 20:11:49 +02:00
protected final Set<Entity> g = com.google.common.collect.Sets.newHashSet(); public Set<Entity> getEntityUnloadQueue() { return g; };// Paper - OBFHELPER
2018-08-18 17:12:06 +02:00
//public final List<TileEntity> tileEntityList = Lists.newArrayList(); // Paper - remove unused list
2019-02-03 16:34:04 +01:00
@@ -139,8 +141,10 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
2018-08-18 17:12:06 +02:00
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
2019-02-03 16:34:04 +01:00
public static BlockPosition lastPhysicsProblem; // Spigot
+ public static boolean haveWeSilencedAPhysicsCrash;
+ public static String blockLocation;
2018-08-18 17:12:06 +02:00
private org.spigotmc.TickLimiter entityLimiter;
2019-02-03 16:34:04 +01:00
private org.spigotmc.TickLimiter tileLimiter;
private int tileTickPosition;
@@ -1122,6 +1126,7 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
2018-08-18 17:12:06 +02:00
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
2019-03-29 07:51:08 +01:00
@@ -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;
}
}
2018-08-18 17:12:06 +02:00
--
2019-03-20 02:46:00 +01:00
2.21.0
2018-08-18 17:12:06 +02:00