From 9d9e5c193ef5fcd158c15e8064ab4fb8c46ad35e Mon Sep 17 00:00:00 2001 From: Colin Godsey 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..a10a5bc13 --- /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 { + + 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 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.isTypeNotPersistent() && 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..06b18555b 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 (isTypeNotPersistent() && 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 innerClass() { return this.a(); } // Paper - OBFHELPER public Class 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..c25c82bf1 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 entityList = new java.util.ArrayList() + 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 g = com.google.common.collect.Sets.newHashSet(); public Set getEntityUnloadQueue() { return g; };// Paper - OBFHELPER //public final List 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 -- 2.21.0