Paper/Spigot-Server-Patches/0023-Optimize-TileEntity-Ticking.patch

325 lines
14 KiB
Diff

From 0ed1de26c7933ca520548d35cefc535b278aff93 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Sun, 8 Mar 2015 01:56:22 -0600
Subject: [PATCH] Optimize TileEntity Ticking
diff --git a/src/main/java/net/minecraft/server/TileEntity.java b/src/main/java/net/minecraft/server/TileEntity.java
index 0de85da..0a21872 100644
--- a/src/main/java/net/minecraft/server/TileEntity.java
+++ b/src/main/java/net/minecraft/server/TileEntity.java
@@ -21,6 +21,12 @@ public abstract class TileEntity {
private int h;
protected Block e;
+ // PaperSpigot start - Optimized TileEntity tick changes
+ private static int tileEntityCounter = 0;
+ public boolean isAdded = false;
+ public int tileId = tileEntityCounter++;
+ // PaperSpigot end
+
public TileEntity() {
this.position = BlockPosition.ZERO;
this.h = -1;
diff --git a/src/main/java/net/minecraft/server/TileEntityBeacon.java b/src/main/java/net/minecraft/server/TileEntityBeacon.java
index da43134..88e4fa9 100644
--- a/src/main/java/net/minecraft/server/TileEntityBeacon.java
+++ b/src/main/java/net/minecraft/server/TileEntityBeacon.java
@@ -48,7 +48,7 @@ public class TileEntityBeacon extends TileEntityContainer implements IUpdatePlay
public TileEntityBeacon() {}
public void c() {
- if (this.world.getTime() % 80L == 0L) {
+ if (true || this.world.getTime() % 80L == 0L) { // PaperSpigot - Controlled by improved tick handling
this.m();
}
diff --git a/src/main/java/net/minecraft/server/TileEntityChest.java b/src/main/java/net/minecraft/server/TileEntityChest.java
index a2bd9e1..8af9e32 100644
--- a/src/main/java/net/minecraft/server/TileEntityChest.java
+++ b/src/main/java/net/minecraft/server/TileEntityChest.java
@@ -246,7 +246,7 @@ public class TileEntityChest extends TileEntityContainer implements IUpdatePlaye
++this.n;
float f;
- if (!this.world.isClientSide && this.l != 0 && (this.n + i + j + k) % 200 == 0) {
+ if (!this.world.isClientSide && this.l != 0 && (this.n + i + j + k) % 10 == 0) { // PaperSpigot - Reduced 200 -> 10 due to reduced interval from improved tick handling
this.l = 0;
f = 5.0F;
List list = this.world.a(EntityHuman.class, new AxisAlignedBB((double) ((float) i - f), (double) ((float) j - f), (double) ((float) k - f), (double) ((float) (i + 1) + f), (double) ((float) (j + 1) + f), (double) ((float) (k + 1) + f)));
diff --git a/src/main/java/net/minecraft/server/TileEntityEnderChest.java b/src/main/java/net/minecraft/server/TileEntityEnderChest.java
index 794cdc8..f712885 100644
--- a/src/main/java/net/minecraft/server/TileEntityEnderChest.java
+++ b/src/main/java/net/minecraft/server/TileEntityEnderChest.java
@@ -10,7 +10,7 @@ public class TileEntityEnderChest extends TileEntity implements IUpdatePlayerLis
public TileEntityEnderChest() {}
public void c() {
- if (++this.h % 20 * 4 == 0) {
+ if (++this.h % 4 == 0) { // PaperSpigot - Reduced (20 * 4) -> 4 interval due to reduced tick rate from improved tick handling
this.world.playBlockAction(this.position, Blocks.ENDER_CHEST, 1, this.g);
}
diff --git a/src/main/java/net/minecraft/server/TileEntityLightDetector.java b/src/main/java/net/minecraft/server/TileEntityLightDetector.java
index f75e2de..7119612 100644
--- a/src/main/java/net/minecraft/server/TileEntityLightDetector.java
+++ b/src/main/java/net/minecraft/server/TileEntityLightDetector.java
@@ -5,7 +5,7 @@ public class TileEntityLightDetector extends TileEntity implements IUpdatePlayer
public TileEntityLightDetector() {}
public void c() {
- if (this.world != null && !this.world.isClientSide && this.world.getTime() % 20L == 0L) {
+ if (this.world != null && !this.world.isClientSide /*&& this.world.getTime() % 20L == 0L*/) { // PaperSpigot - interval controlled by improved tick handling
this.e = this.w();
if (this.e instanceof BlockDaylightDetector) {
((BlockDaylightDetector) this.e).f(this.world, this.position);
diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java
index c563d62..15fe976 100644
--- a/src/main/java/net/minecraft/server/World.java
+++ b/src/main/java/net/minecraft/server/World.java
@@ -65,7 +65,7 @@ public abstract class World implements IBlockAccess {
// Spigot end
protected final List<Entity> g = Lists.newArrayList();
public final List<TileEntity> h = Lists.newArrayList();
- public final List<TileEntity> tileEntityList = Lists.newArrayList();
+ public final Set<TileEntity> tileEntityList = new org.github.paperspigot.WorldTileEntityList(this); // PaperSpigot
private final List<TileEntity> b = Lists.newArrayList();
private final List<TileEntity> c = Lists.newArrayList();
public final List<EntityHuman> players = Lists.newArrayList();
@@ -138,8 +138,10 @@ public abstract class World implements IBlockAccess {
public static boolean haveWeSilencedAPhysicsCrash;
public static String blockLocation;
private org.spigotmc.TickLimiter entityLimiter;
- private org.spigotmc.TickLimiter tileLimiter;
- private int tileTickPosition;
+ // PaperSpigot start - Disable Spigot's TE handling in favor of our own
+ //private org.spigotmc.TickLimiter tileLimiter;
+ //private int tileTickPosition;
+ // PaperSpigot end
public static long chunkToKey(int x, int z)
{
@@ -233,7 +235,7 @@ public abstract class World implements IBlockAccess {
this.keepSpawnInMemory = this.paperSpigotConfig.keepSpawnInMemory; // PaperSpigot
timings = new SpigotTimings.WorldTimingsHandler(this); // Spigot - code below can generate new world and access timings
this.entityLimiter = new org.spigotmc.TickLimiter(spigotConfig.entityMaxTickTime);
- this.tileLimiter = new org.spigotmc.TickLimiter(spigotConfig.tileMaxTickTime);
+ //this.tileLimiter = new org.spigotmc.TickLimiter(spigotConfig.tileMaxTickTime); // PaperSpigot - Disable Spigot's TE handling in favor of our own
}
public World b() {
@@ -1467,6 +1469,12 @@ public abstract class World implements IBlockAccess {
}
// CraftBukkit end
+ // PaperSpigot start - Return to previous behavior, theoretically tile entity ticks should no longer be long enough for this to be an issue
+ Iterator iterator = this.tileEntityList.iterator();
+
+ while (iterator.hasNext()) {
+ TileEntity tileentity = (TileEntity) iterator.next();
+ /*
// Spigot start
int tilesThisCycle = 0;
for (tileLimiter.initTick();
@@ -1474,10 +1482,12 @@ public abstract class World implements IBlockAccess {
tileTickPosition++, tilesThisCycle++) {
tileTickPosition = (tileTickPosition < tileEntityList.size()) ? tileTickPosition : 0;
TileEntity tileentity = (TileEntity) this.tileEntityList.get(tileTickPosition);
+ */
+ // PaperSpigot end
// Spigot start
if (tileentity == null) {
getServer().getLogger().severe("Spigot has detected a null entity and has removed it, preventing a crash");
- this.tileEntityList.remove(tileTickPosition--);
+ iterator.remove(); // PaperSpigot - Remove Spigot's TE handling in favor of our own
continue;
}
// Spigot end
@@ -1505,7 +1515,7 @@ public abstract class World implements IBlockAccess {
}
if (tileentity.x()) {
- this.tileEntityList.remove(tileTickPosition--);
+ iterator.remove(); // PaperSpigot - Remove Spigot's TE handling in favor of our own
this.h.remove(tileentity);
if (this.isLoaded(tileentity.getPosition())) {
this.getChunkAtWorldCoords(tileentity.getPosition()).e(tileentity.getPosition());
diff --git a/src/main/java/org/github/paperspigot/WorldTileEntityList.java b/src/main/java/org/github/paperspigot/WorldTileEntityList.java
new file mode 100644
index 0000000..5af5dcc
--- /dev/null
+++ b/src/main/java/org/github/paperspigot/WorldTileEntityList.java
@@ -0,0 +1,168 @@
+package org.github.paperspigot;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import net.minecraft.server.*;
+import 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[]{
+ BlockJukeBox.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);
+
+ // 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();
+ }
+ }
+}
\ No newline at end of file
--
1.9.1