From 0000000000000000000000000000000000000000 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 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 { 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 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.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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java +++ b/src/main/java/org/github/paperspigot/PaperSpigotWorldConfig.java @@ -0,0 +0,0 @@ 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; @@ -0,0 +0,0 @@ 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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 --- /dev/null +++ b/src/main/java/org/github/paperspigot/WorldTileEntityList.java @@ -0,0 +0,0 @@ +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 --