diff --git a/Spigot-Server-Patches/0033-Optimize-TileEntity-ticking.patch b/Spigot-Server-Patches/0033-Optimize-TileEntity-ticking.patch new file mode 100644 index 000000000..e77de7b3f --- /dev/null +++ b/Spigot-Server-Patches/0033-Optimize-TileEntity-ticking.patch @@ -0,0 +1,233 @@ +From 8f1adb02e9ea4bafa794110eb9980f168d88239d Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Mon, 11 Aug 2014 16:03:05 -0500 +Subject: [PATCH] Optimize TileEntity ticking + +Re-organizes the servers TileEntity Tick List to be bucketed by type. + +This allows the server to skip buckets of Tile Entities 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. + +This change also adds control into the interval of every TileEntity, giving +the server owner control on how fast a TileEntity ticks, slowing it down if they must +(Such as chest), to improve performance. + +diff --git a/src/main/java/net/minecraft/server/TileEntity.java b/src/main/java/net/minecraft/server/TileEntity.java +index 3c5ec6f..9fda298 100644 +--- a/src/main/java/net/minecraft/server/TileEntity.java ++++ b/src/main/java/net/minecraft/server/TileEntity.java +@@ -16,6 +16,8 @@ public class TileEntity { + private static final Logger a = LogManager.getLogger(); + private static Map i = new HashMap(); + private static Map j = new HashMap(); ++ public boolean isAdded = false; // PaperSpigot - optimize contains checks ++ public static Map getTileEntityMap() { return i;} // PaperSpigot - reference TE map + protected World world; + public int x; + public int y; +diff --git a/src/main/java/net/minecraft/server/World.java b/src/main/java/net/minecraft/server/World.java +index 33c228b..393988e 100644 +--- a/src/main/java/net/minecraft/server/World.java ++++ b/src/main/java/net/minecraft/server/World.java +@@ -58,7 +58,7 @@ 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.github.paperspigot.WorldTileEntityList(this); // PaperSpigot // 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/github/paperspigot/PaperSpigotWorldConfig.java b/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java +index 16eb1ca..4eeb865 100644 +--- a/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java ++++ b/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java +@@ -1,6 +1,11 @@ + package org.github.paperspigot; + ++import java.util.HashMap; + import java.util.List; ++import java.util.Map; ++ ++import com.google.common.collect.Maps; ++import net.minecraft.server.TileEntity; + import org.bukkit.Bukkit; + import org.bukkit.configuration.file.YamlConfiguration; + +@@ -170,4 +175,51 @@ public class PaperSpigotWorldConfig + hangingTickFrequency = getInt( "hanging-tick-frequency", 100); + log( "Hanging entities tick frequency: " + hangingTickFrequency); + } ++ ++ public final Map tileEntityTickIntervals = Maps.newHashMap(); ++ private static final Map defaultTileEntityTickIntervals = new HashMap() {{ ++ // Use 0 for no ticking ++ // does findPlayer lookup, so this helps performance to slow down ++ put("chest", 10); ++ put("enderchest", 10); ++ put("enchanttable", 10); ++ ++ // These TE's have empty tick methods, doing nothing. Never bother ticking them. ++ put("recordplayer", 0); ++ put("trap", 0); // Dispenser ++ put("dropper", 0); ++ put("sign", 0); ++ put("music", 0); ++ put("airportal", 0); // Ender Portal ++ put("control", 0); // Command Block ++ put("skull", 0); ++ put("comparator", 0); ++ put("flowerpot", 0); ++ ++ // Slow things down that players won't notice due to craftbukkit "wall time" patches. ++ put("furnace", 4); ++ put("cauldron", 4); ++ ++ // Vanilla controlled values - These are checks already done in vanilla, so don't tick on ticks we know ++ // won't do anything anyways ++ put("beacon", 80); ++ put("dldetector", 20); ++ }}; ++ private void tileEntityTickIntervals() { ++ final Map tileEntityMap = TileEntity.getTileEntityMap(); ++ for (Map.Entry entry : tileEntityMap.entrySet()) { ++ String key = entry.getKey().toLowerCase(); ++ Class cls = entry.getValue(); ++ Integer def = defaultTileEntityTickIntervals.get(key); ++ if (def == null) { ++ def = 1; ++ } ++ Integer tickInterval = getInt("tile-entity-tick-intervals." + key, def); ++ if (!tickInterval.equals(def)) { ++ log("TileEntity - " + entry.getKey() +" - Tick Interval: " + tickInterval); ++ } ++ tileEntityTickIntervals.put(cls, tickInterval); ++ } ++ } ++ + } +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..de78287 +--- /dev/null ++++ b/src/main/java/org/github/paperspigot/WorldTileEntityList.java +@@ -0,0 +1,113 @@ ++package org.github.paperspigot; ++ ++import com.google.common.collect.Lists; ++import com.google.common.collect.Maps; ++import net.minecraft.server.TileEntity; ++import net.minecraft.server.World; ++ ++import java.util.HashSet; ++import java.util.Iterator; ++import java.util.List; ++import java.util.Map; ++ ++public class WorldTileEntityList extends HashSet { ++ final Map> tickList = Maps.newHashMap(); ++ ++ private final World world; ++ ++ public WorldTileEntityList(World world) { ++ this.world = world; ++ } ++ ++ @Override ++ public boolean add(Object o) { ++ if (getInterval(o.getClass()) != 0) { ++ add((TileEntity) o); ++ } ++ return true; ++ } ++ ++ public void add(TileEntity entity) { ++ if (entity.isAdded) { ++ return; ++ } ++ Class cls = entity.getClass(); ++ List list = tickList.get(cls); ++ if (list == null) { ++ list = Lists.newArrayList(); ++ tickList.put(cls, list); ++ } ++ list.add(entity); ++ entity.isAdded = true; ++ } ++ ++ @Override ++ public boolean remove(Object o) { ++ final Class cls = o.getClass(); ++ final List list = tickList.get(cls); ++ if (list != null) { ++ list.remove(o); ++ ((TileEntity) o).isAdded = false; ++ } ++ return true; ++ } ++ ++ @Override ++ public Iterator iterator() { ++ return new Iterator() { ++ Iterator>> typeIterator; ++ Map.Entry> curType = null; ++ Iterator listIterator = null; ++ { ++ typeIterator = tickList.entrySet().iterator(); ++ nextType(); ++ } ++ ++ private boolean nextType() { ++ if (typeIterator.hasNext()) { ++ curType = typeIterator.next(); ++ final Integer interval = getInterval(curType.getKey()); ++ if (world.getTime() % interval != 0) { ++ listIterator = curType.getValue().iterator(); ++ } else { ++ listIterator = null; ++ } ++ return true; ++ } else { ++ curType = null; ++ listIterator = null; ++ return false; ++ } ++ } ++ ++ @Override ++ public boolean hasNext() { ++ do { ++ if (listIterator != null && listIterator.hasNext()) { ++ return true; ++ } ++ } while (nextType()); ++ return false; ++ } ++ ++ @Override ++ public Object next() { ++ return listIterator.next(); ++ } ++ ++ @Override ++ public void remove() { ++ listIterator.remove(); ++ } ++ }; ++ } ++ ++ @Override ++ public boolean contains(Object o) { ++ return ((TileEntity) o).isAdded; ++ } ++ public Integer getInterval(Class cls) { ++ Integer tickInterval = world.paperSpigotConfig.tileEntityTickIntervals.get(cls); ++ return tickInterval != null ? tickInterval : 1; ++ } ++} +\ No newline at end of file +-- +1.9.1 +