Implement optimized Tile Entity ticking

This commit is contained in:
Aikar 2014-08-24 21:19:36 -05:00
parent 2377cf1414
commit a4e202676e
2 changed files with 303 additions and 16 deletions

View File

@ -1,22 +1,25 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Iceee <andrew@opticgaming.tv>
Date: Fri, 11 Jul 2014 01:31:43 -0500
Subject: [PATCH] Don't tick chests
Date: Mon, 11 Aug 2014 23:03:47 -0500
Subject: [PATCH] Move sound handling out of the chest tick loop
This allows us to disable ticking chests and enderchests without any
noticeable difference to players
diff --git a/src/main/java/net/minecraft/server/TileEntityChest.java b/src/main/java/net/minecraft/server/TileEntityChest.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/TileEntityChest.java
+++ b/src/main/java/net/minecraft/server/TileEntityChest.java
@@ -0,0 +0,0 @@ public class TileEntityChest extends TileEntity implements IInventory {
}
}
public void h() {
+ // PaperSpigot start - Ixnay on the Chest tick-ay
this.n = this.m;
+
+ // PaperSpigot start - Move chest sound handling out of the tick loop
+ /*
super.h();
if (this.world == null) return; // CraftBukkit
this.i();
f = 0.1F;
double d0;
@@ -0,0 +0,0 @@ public class TileEntityChest extends TileEntity implements IInventory {
this.m = 0.0F;
}
@ -30,7 +33,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (this.world == null) return; // CraftBukkit
this.world.playBlockAction(this.x, this.y, this.z, this.q(), 1, this.o);
+ // PaperSpigot start - Move chest open sound handling down here
+ // PaperSpigot start - Move chest open sound handling down to here
+ this.i();
+ double d0;
+
@ -57,7 +60,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
if (this.world == null) return; // CraftBukkit
this.world.playBlockAction(this.x, this.y, this.z, this.q(), 1, this.o);
+ // PaperSpigot start - Move chest close sound handling down here
+ // PaperSpigot start - Move chest close sound handling down to here
+ this.i();
+ double d0;
+
@ -85,14 +88,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
--- a/src/main/java/net/minecraft/server/TileEntityEnderChest.java
+++ b/src/main/java/net/minecraft/server/TileEntityEnderChest.java
@@ -0,0 +0,0 @@ public class TileEntityEnderChest extends TileEntity {
public TileEntityEnderChest() {}
}
public void h() {
+ // PaperSpigot start - Ixnay on the EnderChest tick-ay
this.i = this.a;
+
+ // PaperSpigot start - Move chest sound handling out of the tick loop
+ /*
super.h();
if (++this.k % 20 * 4 == 0) {
this.world.playBlockAction(this.x, this.y, this.z, Blocks.ENDER_CHEST, 1, this.j);
float f = 0.1F;
double d0;
@@ -0,0 +0,0 @@ public class TileEntityEnderChest extends TileEntity {
this.a = 0.0F;
}

View File

@ -0,0 +1,283 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 24 Aug 2014 21:35:11 -0400
Subject: [PATCH] Optimize TileEntity Ticking
Re-organizes the servers TileEntity Tick List to be bucketed based on their tick interval.
We now will not even store a Tile Entity that is known to not have any tick function
(half of them), skipping time spent iterating them and checking if they are valid
and in a loaded chunk. In other words, a lot of "meta" time wasted on tile entities
that would never do anything anyways.
Then by reducing chests to 1 in 20 ticks, we cut out 95% of isLoaded checks and findPlayer
calls on chests, and 100% of the checks for Signs, the 2 most popular Tile Entities.
This cuts out a massive amount of checks revolving around TileEntity ticking.
Servers with large amounts of TileEntities should see significant improvement.
Finally, this then spreads out the ticking of reduced-rate TileEntities so that they
do not all tick on the same tick, distributing the load of some TileEntities like Chest.
diff --git a/src/main/java/net/minecraft/server/TileEntity.java b/src/main/java/net/minecraft/server/TileEntity.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/TileEntity.java
+++ b/src/main/java/net/minecraft/server/TileEntity.java
@@ -0,0 +0,0 @@ public class TileEntity {
}
}
}
+
+ // Optimized TileEntity Tick changes
+ private static int tileEntityCounter = 0;
+ public boolean isAdded = false;
+ public int tileId = tileEntityCounter++;
+
// Spigot end
public TileEntity() {}
diff --git a/src/main/java/net/minecraft/server/TileEntityBeacon.java b/src/main/java/net/minecraft/server/TileEntityBeacon.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/TileEntityBeacon.java
+++ b/src/main/java/net/minecraft/server/TileEntityBeacon.java
@@ -0,0 +0,0 @@ public class TileEntityBeacon extends TileEntity implements IInventory {
public TileEntityBeacon() {}
public void h() {
- if (this.world.getTime() % 80L == 0L) {
+ if (true || this.world.getTime() % 80L == 0L) { // PaperSpigot - controlled by Improved Tick handling
this.y();
this.x();
}
diff --git a/src/main/java/net/minecraft/server/TileEntityChest.java b/src/main/java/net/minecraft/server/TileEntityChest.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/TileEntityChest.java
+++ b/src/main/java/net/minecraft/server/TileEntityChest.java
@@ -0,0 +0,0 @@ public class TileEntityChest extends TileEntity implements IInventory {
++this.ticks;
float f;
- if (!this.world.isStatic && this.o != 0 && (this.ticks + this.x + this.y + this.z) % 200 == 0) {
+ if (!this.world.isStatic && this.o != 0 && (this.ticks + this.x + this.y + this.z) % 10 == 0) { // PaperSpigot Reduced 200 -> 10 interval due to reduced tick rate from Improved Tick Handling
this.o = 0;
f = 5.0F;
List list = this.world.a(EntityHuman.class, AxisAlignedBB.a((double) ((float) this.x - f), (double) ((float) this.y - f), (double) ((float) this.z - f), (double) ((float) (this.x + 1) + f), (double) ((float) (this.y + 1) + f), (double) ((float) (this.z + 1) + f)));
diff --git a/src/main/java/net/minecraft/server/TileEntityEnderChest.java b/src/main/java/net/minecraft/server/TileEntityEnderChest.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/TileEntityEnderChest.java
+++ b/src/main/java/net/minecraft/server/TileEntityEnderChest.java
@@ -0,0 +0,0 @@ public class TileEntityEnderChest extends TileEntity {
public void h() {
super.h();
- if (++this.k % 20 * 4 == 0) {
+ if (++this.k % 4 == 0) { // PaperSpigot Reduced (20 * 4) -> 4 interval due to reduced tick rate from Improved Tick Handling
this.world.playBlockAction(this.x, this.y, this.z, Blocks.ENDER_CHEST, 1, this.j);
}
diff --git a/src/main/java/net/minecraft/server/TileEntityLightDetector.java b/src/main/java/net/minecraft/server/TileEntityLightDetector.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
--- a/src/main/java/net/minecraft/server/TileEntityLightDetector.java
+++ b/src/main/java/net/minecraft/server/TileEntityLightDetector.java
@@ -0,0 +0,0 @@ public class TileEntityLightDetector extends TileEntity {
public TileEntityLightDetector() {}
public void h() {
- if (this.world != null && !this.world.isStatic && this.world.getTime() % 20L == 0L) {
+ if (this.world != null && !this.world.isStatic /*&& this.world.getTime() % 20L == 0L*/) { // PaperSpigot - interval controlled by Improved Tick Handling
this.h = this.q();
if (this.h instanceof BlockDaylightDetector) {
((BlockDaylightDetector) this.h).e(this.world, this.x, this.y, this.z);
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 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 IBlockAccess {
};
// Spigot end
protected List f = new ArrayList();
- public Set tileEntityList = new HashSet(); // CraftBukkit - ArrayList -> HashSet
+ public Set tileEntityList = new org.spigotmc.WorldTileEntityList(this); // CraftBukkit - ArrayList -> HashSet
private List a = new ArrayList();
private List b = new ArrayList();
public List players = new ArrayList();
diff --git a/src/main/java/org/spigotmc/WorldTileEntityList.java b/src/main/java/org/spigotmc/WorldTileEntityList.java
new file mode 100644
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
--- /dev/null
+++ b/src/main/java/org/spigotmc/WorldTileEntityList.java
@@ -0,0 +0,0 @@
+package org.spigotmc;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import net.minecraft.server.*;
+import net.minecraft.util.gnu.trove.map.hash.TObjectIntHashMap;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+
+public class WorldTileEntityList extends HashSet<TileEntity> {
+ private static final TObjectIntHashMap<Class<? extends TileEntity>> tileEntityTickIntervals =
+ new TObjectIntHashMap<Class<? extends TileEntity>>() {{
+ // Use -1 for no ticking
+ // These TE's have empty tick methods, doing nothing. Never bother ticking them.
+ for (Class<? extends TileEntity> ignored : new Class[]{
+ TileEntityChest.class, // PaperSpigot - Don't tick chests either
+ TileEntityEnderChest.class, // PaperSpigot - Don't tick chests either
+ TileEntityRecordPlayer.class,
+ TileEntityDispenser.class,
+ TileEntityDropper.class,
+ TileEntitySign.class,
+ TileEntityNote.class,
+ TileEntityEnderPortal.class,
+ TileEntityCommand.class,
+ TileEntitySkull.class,
+ TileEntityComparator.class,
+ TileEntityFlowerPot.class
+ }) {
+ put(ignored, -1);
+ }
+
+ // does findPlayer lookup, so this helps performance to slow down
+ put(TileEntityEnchantTable.class, 20);
+
+ // Slow things down that players won't notice due to craftbukkit "wall time" patches.
+ put(TileEntityFurnace.class, 10);
+ put(TileEntityBrewingStand.class, 10);
+
+ // Vanilla controlled values - These are checks already done in vanilla, so don't tick on ticks we know
+ // won't do anything anyways
+ put(TileEntityBeacon.class, 80);
+ put(TileEntityLightDetector.class, 20);
+ }};
+ private static int getInterval(Class<? extends TileEntity> cls) {
+ int tickInterval = tileEntityTickIntervals.get(cls);
+ return tickInterval != 0 ? tickInterval : 1;
+ }
+
+ private static int getBucketId(TileEntity entity, Integer interval) {
+ return entity.tileId % interval;
+ }
+
+ private final Map<Integer, Multimap<Integer, TileEntity>> tickList = Maps.newHashMap();
+ private final WorldServer world;
+
+ public WorldTileEntityList(World world) {
+ this.world = (WorldServer) world;
+ }
+
+
+ private Multimap<Integer, TileEntity> getBucket(int interval) {
+ Multimap<Integer, TileEntity> intervalBucket = tickList.get(interval);
+ if (intervalBucket == null) {
+ intervalBucket = ArrayListMultimap.create();
+ tickList.put(interval, intervalBucket);
+ }
+ return intervalBucket;
+ }
+
+ /**
+ * Adds the TileEntity to the tick list only if it is expected to tick
+ */
+ @Override
+ public boolean add(TileEntity entity) {
+ if (entity.isAdded) {
+ return false;
+ }
+
+ int interval = getInterval(entity.getClass());
+ if (interval > 0) {
+ entity.isAdded = true;
+ int bucket = getBucketId(entity, interval);
+ Multimap<Integer, TileEntity> typeBucket = getBucket(interval);
+ return typeBucket.put(bucket, entity);
+ }
+ return false;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (!(o instanceof TileEntity)) {
+ return false;
+ }
+ TileEntity entity = (TileEntity) o;
+ if (!entity.isAdded) {
+ return false;
+ }
+ entity.isAdded = false;
+ int interval = getInterval(entity.getClass());
+ int bucket = getBucketId(entity, interval);
+ Multimap<Integer, TileEntity> typeBucket = getBucket(interval);
+ return typeBucket.remove(bucket, entity);
+ }
+
+ @Override
+ public Iterator iterator() {
+ return new WorldTileEntityIterator();
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return o instanceof TileEntity && ((TileEntity) o).isAdded;
+ }
+
+ private class WorldTileEntityIterator implements Iterator<TileEntity> {
+ private final Iterator<Map.Entry<Integer, Multimap<Integer, TileEntity>>> intervalIterator;
+ private Map.Entry<Integer, Multimap<Integer, TileEntity>> intervalMap = null;
+ private Iterator<TileEntity> listIterator = null;
+
+ protected WorldTileEntityIterator() {
+ intervalIterator = tickList.entrySet().iterator();
+ nextInterval();
+ }
+
+ private boolean nextInterval() {
+ listIterator = null;
+ if (intervalIterator.hasNext()) {
+ intervalMap = intervalIterator.next();
+
+ final Integer interval = intervalMap.getKey();
+ final Multimap<Integer, TileEntity> buckets = intervalMap.getValue();
+
+ int bucket = (int) (world.getTime() % interval);
+
+ if (!buckets.isEmpty() && buckets.containsKey(bucket)) {
+ final Collection<TileEntity> tileList = buckets.get(bucket);
+
+ if (tileList != null && !tileList.isEmpty()) {
+ listIterator = tileList.iterator();
+ return true;
+ }
+ }
+ }
+
+ return false;
+
+ }
+
+ @Override
+ public boolean hasNext() {
+ do {
+ if (listIterator != null && listIterator.hasNext()) {
+ return true;
+ }
+ } while (nextInterval());
+ return false;
+ }
+
+ @Override
+ public TileEntity next() {
+ return listIterator.next();
+ }
+
+ @Override
+ public void remove() {
+ listIterator.remove();
+ }
+ }
+}
--