2018-08-18 17:12:06 +02:00
From 0000000000000000000000000000000000000000 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
2018-08-18 18:42:17 +02:00
index 0000000000..35104542c5
2018-08-18 17:12:06 +02:00
--- /dev/null
+++ b/src/main/java/com/destroystokyo/paper/PaperWorldEntityList.java
@@ -0,0 +0,0 @@
+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;
+ if (amt > 0 && entityinsentient.isDespawnableByDefault() && entityinsentient.isPersistent()) {
+ return;
2018-08-18 18:42:17 +02:00
+ }
+ }
+ if (amt < 0) {
+ if (!entity.hasBeenCounted) {
2018-08-18 17:12:06 +02:00
+ return;
+ }
2018-08-18 18:42:17 +02:00
+ // 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;
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
2018-08-27 03:21:28 +02:00
index 4252e8c02a..2b1affd03c 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
@@ -0,0 +0,0 @@ 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;
2018-08-18 17:12:06 +02:00
diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java
2018-09-01 00:56:57 +02:00
index 141db48f16..56542d531a 100644
2018-08-18 17:12:06 +02:00
--- a/src/main/java/net/minecraft/server/EntityInsentient.java
+++ b/src/main/java/net/minecraft/server/EntityInsentient.java
@@ -0,0 +0,0 @@ public abstract class EntityInsentient extends EntityLiving {
return true;
}
+ public boolean isDespawnableByDefault() { return isTypeNotPersistent(); } // Paper - sortof an obf helper, too annoying to change visibility here
protected boolean isTypeNotPersistent() {
return true;
}
diff --git a/src/main/java/net/minecraft/server/EnumCreatureType.java b/src/main/java/net/minecraft/server/EnumCreatureType.java
2018-09-01 00:56:57 +02:00
index 8874a05be5..0af387c059 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
@@ -0,0 +0,0 @@ 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
2018-08-27 03:21:28 +02:00
index 32808558bb..62b4c64f68 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
@@ -0,0 +0,0 @@ public final class SpawnerCreature {
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
2018-09-01 00:56:57 +02:00
index f99cdf1003..c479828d49 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
2018-08-26 20:11:49 +02:00
@@ -0,0 +0,0 @@ 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)
2018-08-26 20:11:49 +02:00
@@ -0,0 +0,0 @@ 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
2018-08-26 20:11:49 +02:00
@@ -0,0 +0,0 @@ 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
public static boolean haveWeSilencedAPhysicsCrash;
public static String blockLocation;
private org.spigotmc.TickLimiter entityLimiter;
2018-08-26 20:11:49 +02:00
@@ -0,0 +0,0 @@ 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
--