mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-29 15:49:00 +01:00
29399ffc02
While it wasn't really "broken" before, if plugins use NMS (which they really should't be) and mess with entity management themselves, and get it wrong, they could ultimately corrupt our state expectations. I've been unable to reproduce any issues locally, but these changes are the result of me analyzing the code pretty deeply and seeing about how to make it more durable to abnormal usage. Any servers seeing oddities, please run with -Ddebug.entities=true and send me any logs triggered.
227 lines
No EOL
9.2 KiB
Diff
227 lines
No EOL
9.2 KiB
Diff
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
|
|
index 0000000000..a10a5bc138
|
|
--- /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) {
|
|
+ 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 4dc7c8ba68..90e0d9d453 100644
|
|
--- 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;
|
|
diff --git a/src/main/java/net/minecraft/server/EntityInsentient.java b/src/main/java/net/minecraft/server/EntityInsentient.java
|
|
index ee5078370c..856ddf2a74 100644
|
|
--- 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 {
|
|
|
|
public void tick() {
|
|
super.tick();
|
|
+ if (isTypeNotPersistent() && hasBeenCounted == this.isPersistent()) ((com.destroystokyo.paper.PaperWorldEntityList) 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 79e52f7bac..42f6a6a93a 100644
|
|
--- 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
|
|
index e626165520..d125fae03b 100644
|
|
--- 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)) {
|
|
k = limit * i / SpawnerCreature.b; // CraftBukkit - use per-world limits
|
|
- int l1 = worldserver.a(enumcreaturetype.a(), k);
|
|
+ int l1 = ((com.destroystokyo.paper.PaperWorldEntityList) 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 e31e366249..b007eb36c7 100644
|
|
--- a/src/main/java/net/minecraft/server/World.java
|
|
+++ b/src/main/java/net/minecraft/server/World.java
|
|
@@ -0,0 +0,0 @@ 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 List<Entity> entityList = new com.destroystokyo.paper.PaperWorldEntityList(this);
|
|
+ /* // Paper start
|
|
{
|
|
@Override
|
|
public Entity remove(int index)
|
|
@@ -0,0 +0,0 @@ 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
|
|
@@ -0,0 +0,0 @@ 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;
|
|
@@ -0,0 +0,0 @@ public abstract class World implements IEntityAccess, GeneratorAccess, IIBlockAc
|
|
Chunk chunk = entity.getCurrentChunk(); // Paper
|
|
if (chunk != null) chunk.removeEntity(entity); // Paper
|
|
entity.shouldBeRemoved = true; // Paper
|
|
+ ((com.destroystokyo.paper.PaperWorldEntityList) 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
|
|
--
|