From 25c5c2cb167a096a190a0297f22e426ba356279c Mon Sep 17 00:00:00 2001 From: Aikar Date: Sun, 19 Oct 2014 17:58:49 -0500 Subject: [PATCH] Implement performance improvements from the EMC-CraftBukkit fork See the individual patch files for more details --- .../0005-Add-getTPS-method.patch | 27 +++ ...041-Further-improve-server-tick-loop.patch | 222 ++++++++++++++++++ ...rove-Network-Manager-packet-handling.patch | 59 +++++ ...043-Only-refresh-abilities-if-needed.patch | 28 +++ .../0044-Player-lookup-improvements.patch | 187 +++++++++++++++ .../0045-Improve-autosave-mechanism.patch | 63 +++++ 6 files changed, 586 insertions(+) create mode 100644 Spigot-API-Patches/0005-Add-getTPS-method.patch create mode 100644 Spigot-Server-Patches/0041-Further-improve-server-tick-loop.patch create mode 100644 Spigot-Server-Patches/0042-Improve-Network-Manager-packet-handling.patch create mode 100644 Spigot-Server-Patches/0043-Only-refresh-abilities-if-needed.patch create mode 100644 Spigot-Server-Patches/0044-Player-lookup-improvements.patch create mode 100644 Spigot-Server-Patches/0045-Improve-autosave-mechanism.patch diff --git a/Spigot-API-Patches/0005-Add-getTPS-method.patch b/Spigot-API-Patches/0005-Add-getTPS-method.patch new file mode 100644 index 0000000000..05661846f2 --- /dev/null +++ b/Spigot-API-Patches/0005-Add-getTPS-method.patch @@ -0,0 +1,27 @@ +From 9ad95be7f945f498bd4a58c63ffc4d3c2d7c8e73 Mon Sep 17 00:00:00 2001 +From: Zach Brown +Date: Sun, 19 Oct 2014 18:22:18 -0500 +Subject: [PATCH] Add getTPS method + + +diff --git a/src/main/java/org/bukkit/Server.java b/src/main/java/org/bukkit/Server.java +index 199060d..560364e 100644 +--- a/src/main/java/org/bukkit/Server.java ++++ b/src/main/java/org/bukkit/Server.java +@@ -926,6 +926,13 @@ public interface Server extends PluginMessageRecipient { + { + throw new UnsupportedOperationException( "Not supported yet." ); + } ++ ++ // PaperSpigot start - Add getTPS method ++ public double[] getTPS() ++ { ++ throw new UnsupportedOperationException( "Not supported yet." ); ++ } ++ // PaperSpigot end + } + + Spigot spigot(); +-- +1.9.1 + diff --git a/Spigot-Server-Patches/0041-Further-improve-server-tick-loop.patch b/Spigot-Server-Patches/0041-Further-improve-server-tick-loop.patch new file mode 100644 index 0000000000..717b790658 --- /dev/null +++ b/Spigot-Server-Patches/0041-Further-improve-server-tick-loop.patch @@ -0,0 +1,222 @@ +From 157babb3ad74bf4f148b50f07f0112775b845130 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 19 Oct 2014 15:56:39 -0500 +Subject: [PATCH] Further improve server tick loop + +Improves how the catchup buffer is handled, allowing it to roll both ways +increasing the effeciency of the thread sleep so it only will sleep once. + +Also increases the buffer of the catchup to ensure server stays at 20 TPS unless extreme conditions + +Previous implementation did not calculate TPS correctly. +Switch to a realistic rolling average and factor in std deviation as an extra reporting variable + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index fa10ea1..953a88c 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -102,17 +102,11 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo + public org.bukkit.command.ConsoleCommandSender console; + public org.bukkit.command.RemoteConsoleCommandSender remoteConsole; + public ConsoleReader reader; +- public static int currentTick = (int) (System.currentTimeMillis() / 50); ++ public static int currentTick = 0; // PaperSpigot - Further improve tick loop + public final Thread primaryThread; + public java.util.Queue processQueue = new java.util.concurrent.ConcurrentLinkedQueue(); + public int autosavePeriod; + // CraftBukkit end +- // Spigot start +- private static final int TPS = 20; +- private static final int TICK_TIME = 1000000000 / TPS; +- private static final int SAMPLE_INTERVAL = 100; +- public final double[] recentTps = new double[ 3 ]; +- // Spigot end + + public MinecraftServer(OptionSet options, Proxy proxy) { // CraftBukkit - signature file -> OptionSet + net.minecraft.util.io.netty.util.ResourceLeakDetector.setEnabled( false ); // Spigot - disable +@@ -446,12 +440,53 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo + this.isRunning = false; + } + +- // Spigot Start +- private static double calcTps(double avg, double exp, double tps) +- { +- return ( avg * exp ) + ( tps * ( 1 - exp ) ); ++ // PaperSpigot start - Further improve tick loop ++ private static final int TPS = 20; ++ private static final long SEC_IN_NANO = 1000000000; ++ private static final long TICK_TIME = SEC_IN_NANO / TPS; ++ private static final long MAX_CATCHUP_BUFFER = TICK_TIME * TPS * 60L; ++ private static final int SAMPLE_INTERVAL = 20; ++ public final RollingAverage tps1 = new RollingAverage(60); ++ public final RollingAverage tps5 = new RollingAverage(60*5); ++ public final RollingAverage tps15 = new RollingAverage(60*15); ++ ++ public static class RollingAverage { ++ private final int size; ++ private long time; ++ private double total; ++ private int index = 0; ++ private final double[] samples; ++ private final long[] times; ++ ++ RollingAverage(int size) { ++ this.size = size; ++ this.time = size * SEC_IN_NANO; ++ this.total = TPS * SEC_IN_NANO * size; ++ this.samples = new double[size]; ++ this.times = new long[size]; ++ for (int i = 0; i < size; i++) { ++ this.samples[i] = TPS; ++ this.times[i] = SEC_IN_NANO; ++ } ++ } ++ ++ public void add(double x, long t) { ++ time -= times[index]; ++ total -= samples[index]*times[index]; ++ samples[index] = x; ++ times[index] = t; ++ time += t; ++ total += x*t; ++ if (++index == size) { ++ index = 0; ++ } ++ } ++ ++ public double getAverage() { ++ return total / time; ++ } + } +- // Spigot End ++ // PaperSpigot End + + public void run() { + try { +@@ -464,26 +499,45 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo + this.a(this.q); + + // Spigot start +- Arrays.fill( recentTps, 20 ); +- long lastTick = System.nanoTime(), catchupTime = 0, curTime, wait, tickSection = lastTick; ++ // PaperSpigot start - Further improve tick loop ++ //Arrays.fill( recentTps, 20 ); ++ //long lastTick = System.nanoTime(), catchupTime = 0, curTime, wait, tickSection = lastTick; ++ long start = System.nanoTime(), lastTick = start - TICK_TIME, catchupTime = 0, curTime, wait, tickSection = start; ++ // PaperSpigot end + while (this.isRunning) { + curTime = System.nanoTime(); +- wait = TICK_TIME - (curTime - lastTick) - catchupTime; ++ // PaperSpigot start - Further improve tick loop ++ wait = TICK_TIME - (curTime - lastTick); ++ if (wait > 0) { ++ if (catchupTime < 2E6) { ++ wait += Math.abs(catchupTime); ++ } ++ if (wait < catchupTime) { ++ catchupTime -= wait; ++ wait = 0; ++ } else if (catchupTime > 2E6) { ++ wait -= catchupTime; ++ catchupTime -= catchupTime; ++ } ++ } + if (wait > 0) { + Thread.sleep(wait / 1000000); +- catchupTime = 0; +- continue; +- } else { +- catchupTime = Math.min(1000000000, Math.abs(wait)); ++ wait = TICK_TIME - (curTime - lastTick); + } + +- if ( MinecraftServer.currentTick++ % SAMPLE_INTERVAL == 0 ) ++ catchupTime = Math.min(MAX_CATCHUP_BUFFER, catchupTime - wait); ++ // Paperspigot end ++ ++ if ( ++MinecraftServer.currentTick % SAMPLE_INTERVAL == 0 ) // PaperSpigot - Further improve tick loop + { +- double currentTps = 1E9 / ( curTime - tickSection ) * SAMPLE_INTERVAL; +- recentTps[0] = calcTps( recentTps[0], 0.92, currentTps ); // 1/exp(5sec/1min) +- recentTps[1] = calcTps( recentTps[1], 0.9835, currentTps ); // 1/exp(5sec/5min) +- recentTps[2] = calcTps( recentTps[2], 0.9945, currentTps ); // 1/exp(5sec/15min) ++ // PaperSpigot start - Further improve tick loop ++ final long diff = curTime - tickSection; ++ double currentTps = 1E9 / diff * SAMPLE_INTERVAL; ++ tps1.add(currentTps, diff); ++ tps5.add(currentTps, diff); ++ tps15.add(currentTps, diff); + tickSection = curTime; ++ // PaperSpigot end + } + lastTick = curTime; + +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index 1548042..ed5bb2e 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -1842,6 +1842,17 @@ public final class CraftServer implements Server { + return org.spigotmc.SpigotConfig.config; + } + ++ // PaperSpigot start - Add getTPS (Further improve tick loop) ++ @Override ++ public double[] getTPS() { ++ return new double[] { ++ MinecraftServer.getServer().tps1.getAverage(), ++ MinecraftServer.getServer().tps5.getAverage(), ++ MinecraftServer.getServer().tps15.getAverage() ++ }; ++ } ++ // PaperSpigot end ++ + @Override + public void broadcast( BaseComponent component ) + { +diff --git a/src/main/java/org/spigotmc/TicksPerSecondCommand.java b/src/main/java/org/spigotmc/TicksPerSecondCommand.java +index 2b8343d..1780e35 100644 +--- a/src/main/java/org/spigotmc/TicksPerSecondCommand.java ++++ b/src/main/java/org/spigotmc/TicksPerSecondCommand.java +@@ -1,8 +1,7 @@ + package org.spigotmc; + +-import com.google.common.base.Joiner; +-import net.minecraft.server.MinecraftServer; +-import net.minecraft.util.com.google.common.collect.Iterables; ++import org.apache.commons.lang.StringUtils; ++import org.bukkit.Bukkit; + import org.bukkit.ChatColor; + import org.bukkit.command.Command; + import org.bukkit.command.CommandSender; +@@ -26,18 +25,21 @@ public class TicksPerSecondCommand extends Command + return true; + } + +- StringBuilder sb = new StringBuilder( ChatColor.GOLD + "TPS from last 1m, 5m, 15m: " ); +- for ( double tps : MinecraftServer.getServer().recentTps ) +- { +- sb.append( format( tps ) ); +- sb.append( ", " ); ++ // PaperSpigot start - Further improve tick handling ++ double[] tps = Bukkit.spigot().getTPS(); ++ String[] tpsAvg = new String[tps.length]; ++ ++ for ( int i = 0; i < tps.length; i++) { ++ tpsAvg[i] = format( tps[i] ); + } +- sender.sendMessage( sb.substring( 0, sb.length() - 2 ) ); ++ ++ sender.sendMessage( ChatColor.GOLD + "TPS from last 1m, 5m, 15m: " + StringUtils.join(tpsAvg, ", ")); ++ // PaperSpigot end + + return true; + } + +- private String format(double tps) ++ private static String format(double tps) // PaperSpigot - made static + { + return ( ( tps > 18.0 ) ? ChatColor.GREEN : ( tps > 16.0 ) ? ChatColor.YELLOW : ChatColor.RED ).toString() + + ( ( tps > 20.0 ) ? "*" : "" ) + Math.min( Math.round( tps * 100.0 ) / 100.0, 20.0 ); +-- +1.9.1 + diff --git a/Spigot-Server-Patches/0042-Improve-Network-Manager-packet-handling.patch b/Spigot-Server-Patches/0042-Improve-Network-Manager-packet-handling.patch new file mode 100644 index 0000000000..70d7c42668 --- /dev/null +++ b/Spigot-Server-Patches/0042-Improve-Network-Manager-packet-handling.patch @@ -0,0 +1,59 @@ +From 2ee5580338ca7624f1425f8f7626ea4f93c9ca38 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 19 Oct 2014 16:01:51 -0500 +Subject: [PATCH] Improve Network Manager packet handling + +Removes an unnecessary "peek at head of queue", and also makes the number of packets +processed per player per tick configurable, so larger servers can slow down incoming processing. + +diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java +index 76a5403..5a5604c 100644 +--- a/src/main/java/net/minecraft/server/NetworkManager.java ++++ b/src/main/java/net/minecraft/server/NetworkManager.java +@@ -154,11 +154,12 @@ public class NetworkManager extends SimpleChannelInboundHandler { + + private void i() { + if (this.m != null && this.m.isOpen()) { +- while (!this.l.isEmpty()) { +- QueuedPacket queuedpacket = (QueuedPacket) this.l.poll(); +- ++ // PaperSpigot start - Improve Network Manager packet handling ++ QueuedPacket queuedpacket; ++ while ((queuedpacket = (QueuedPacket) this.l.poll()) != null) { + this.b(QueuedPacket.a(queuedpacket), QueuedPacket.b(queuedpacket)); + } ++ // PaperSpigot end + } + } + +@@ -175,8 +176,10 @@ public class NetworkManager extends SimpleChannelInboundHandler { + } + + if (this.o != null) { +- for (int i = 1000; !this.k.isEmpty() && i >= 0; --i) { +- Packet packet = (Packet) this.k.poll(); ++ // PaperSpigot start - Improve Network Manager packet handling - Configurable packets per player per tick processing ++ Packet packet; ++ for (int i = org.github.paperspigot.PaperSpigotConfig.maxPacketsPerPlayer; (packet = (Packet) this.k.poll()) != null && i >= 0; --i) { ++ // PaperSpigot end + + // CraftBukkit start + if (!this.isConnected() || !this.m.config().isAutoRead()) { +diff --git a/src/main/java/org/github/paperspigot/PaperSpigotConfig.java b/src/main/java/org/github/paperspigot/PaperSpigotConfig.java +index a7b18e4..df42019 100644 +--- a/src/main/java/org/github/paperspigot/PaperSpigotConfig.java ++++ b/src/main/java/org/github/paperspigot/PaperSpigotConfig.java +@@ -169,4 +169,10 @@ public class PaperSpigotConfig + strengthEffectModifier = getDouble( "effect-modifiers.strength", 1.3D ); + weaknessEffectModifier = getDouble( "effect-modifiers.weakness", -0.5D ); + } ++ ++ public static int maxPacketsPerPlayer; ++ private static void maxPacketsPerPlayer() ++ { ++ maxPacketsPerPlayer = getInt( "max-packets-per-player", 1000 ); ++ } + } +-- +1.9.1 + diff --git a/Spigot-Server-Patches/0043-Only-refresh-abilities-if-needed.patch b/Spigot-Server-Patches/0043-Only-refresh-abilities-if-needed.patch new file mode 100644 index 0000000000..0e936cf85a --- /dev/null +++ b/Spigot-Server-Patches/0043-Only-refresh-abilities-if-needed.patch @@ -0,0 +1,28 @@ +From 33b18cbf6cb34aaa872507e4c20dc731ec877916 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 19 Oct 2014 16:04:28 -0500 +Subject: [PATCH] Only refresh abilities if needed + + +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 096615e..73ec0f7 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -1176,12 +1176,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + } + + public void setFlying(boolean value) { ++ boolean needsUpdate = getHandle().abilities.canFly != value; // PaperSpigot - Only refresh abilities if needed + if (!getAllowFlight() && value) { + throw new IllegalArgumentException("Cannot make player fly if getAllowFlight() is false"); + } + + getHandle().abilities.isFlying = value; +- getHandle().updateAbilities(); ++ if (needsUpdate) getHandle().updateAbilities(); // PaperSpigot - Only refresh abilities if needed + } + + public boolean getAllowFlight() { +-- +1.9.1 + diff --git a/Spigot-Server-Patches/0044-Player-lookup-improvements.patch b/Spigot-Server-Patches/0044-Player-lookup-improvements.patch new file mode 100644 index 0000000000..f51c2fa4b2 --- /dev/null +++ b/Spigot-Server-Patches/0044-Player-lookup-improvements.patch @@ -0,0 +1,187 @@ +From 882a1d79c385949277aade9a1d35aa0cfd339c0d Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 19 Oct 2014 16:26:55 -0500 +Subject: [PATCH] Player lookup improvements + +Minecraft and CraftBukkit both use Arrays to store online players, +and any time a player needs to be looked up by name or UUID, +the system iterates all online players and does a name or UUID comparison. + +This is very ineffecient and can reduce performance on servers with high player count. + +By using a map based approach for player lookups, player lookup should +be consistent in performance regardless of how many players are online. + +diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java +index 79fc999..c6dca57 100644 +--- a/src/main/java/net/minecraft/server/PlayerList.java ++++ b/src/main/java/net/minecraft/server/PlayerList.java +@@ -49,6 +49,39 @@ public abstract class PlayerList { + private static final SimpleDateFormat h = new SimpleDateFormat("yyyy-MM-dd \'at\' HH:mm:ss z"); + private final MinecraftServer server; + public final List players = new java.util.concurrent.CopyOnWriteArrayList(); // CraftBukkit - ArrayList -> CopyOnWriteArrayList: Iterator safety ++ // PaperSpigot start - Player lookup improvements ++ public final Map playerMap = new java.util.HashMap() { ++ @Override ++ public EntityPlayer put(String key, EntityPlayer value) { ++ return super.put(key.toLowerCase(), value); ++ } ++ ++ @Override ++ public EntityPlayer get(Object key) { ++ // put the .playerConnection check done in other places here ++ EntityPlayer player = super.get(key instanceof String ? ((String)key).toLowerCase() : key); ++ return (player != null && player.playerConnection != null) ? player : null; ++ } ++ ++ @Override ++ public boolean containsKey(Object key) { ++ return get(key) != null; ++ } ++ ++ @Override ++ public EntityPlayer remove(Object key) { ++ return super.remove(key instanceof String ? ((String)key).toLowerCase() : key); ++ } ++ }; ++ public final Map uuidMap = new java.util.HashMap() { ++ @Override ++ public EntityPlayer get(Object key) { ++ // put the .playerConnection check done in other places here ++ EntityPlayer player = super.get(key instanceof String ? ((String)key).toLowerCase() : key); ++ return (player != null && player.playerConnection != null) ? player : null; ++ } ++ }; ++ // PaperSpigot end + private final GameProfileBanList j; + private final IpBanList k; + private final OpList operators; +@@ -258,6 +291,8 @@ public abstract class PlayerList { + cserver.detectListNameConflict(entityplayer); // CraftBukkit + // this.sendAll(new PacketPlayOutPlayerInfo(entityplayer.getName(), true, 1000)); // CraftBukkit - replaced with loop below + this.players.add(entityplayer); ++ this.playerMap.put(entityplayer.getName(), entityplayer); // PaperSpigot ++ this.uuidMap.put(entityplayer.getUniqueID(), entityplayer); // PaperSpigot + WorldServer worldserver = this.server.getWorldServer(entityplayer.dimension); + + // CraftBukkit start +@@ -344,6 +379,8 @@ public abstract class PlayerList { + worldserver.kill(entityplayer); + worldserver.getPlayerChunkMap().removePlayer(entityplayer); + this.players.remove(entityplayer); ++ this.uuidMap.remove(entityplayer.getUniqueID()); // PaperSpigot ++ this.playerMap.remove(entityplayer.getName()); // PaperSpigot + this.n.remove(entityplayer.getUniqueID()); + ChunkIOExecutor.adjustPoolSize(this.getPlayerCount()); // CraftBukkit + +@@ -424,6 +461,7 @@ public abstract class PlayerList { + + EntityPlayer entityplayer; + ++ /* // PaperSpigot start - Use exact lookup below + for (int i = 0; i < this.players.size(); ++i) { + entityplayer = (EntityPlayer) this.players.get(i); + if (entityplayer.getUniqueID().equals(uuid)) { +@@ -435,6 +473,9 @@ public abstract class PlayerList { + + while (iterator.hasNext()) { + entityplayer = (EntityPlayer) iterator.next(); ++ */ ++ if ((entityplayer = uuidMap.get(uuid)) != null) { ++ // PaperSpigot end + entityplayer.playerConnection.disconnect("You logged in from another location"); + } + +@@ -952,6 +993,7 @@ public abstract class PlayerList { + } + + public EntityPlayer getPlayer(String s) { ++ if (true) { return playerMap.get(s); } // PaperSpigot + Iterator iterator = this.players.iterator(); + + EntityPlayer entityplayer; +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +index 1328c17..f1fa713 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftOfflinePlayer.java +@@ -144,14 +144,10 @@ public class CraftOfflinePlayer implements OfflinePlayer, ConfigurationSerializa + } + + public Player getPlayer() { +- for (Object obj : server.getHandle().players) { +- EntityPlayer player = (EntityPlayer) obj; +- if (player.getUniqueID().equals(getUniqueId())) { +- return (player.playerConnection != null) ? player.playerConnection.getPlayer() : null; +- } +- } +- +- return null; ++ // PaperSpigot - Improved player lookup, replace entire method ++ final EntityPlayer playerEntity = server.getHandle().uuidMap.get(getUniqueId()); ++ return playerEntity != null ? playerEntity.getBukkitEntity() : null; ++ // PaperSpigot end + } + + @Override +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +index ed5bb2e..87eab98 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java +@@ -522,7 +522,12 @@ public final class CraftServer implements Server { + public Player getPlayer(final String name) { + Validate.notNull(name, "Name cannot be null"); + +- Player found = null; ++ // PaperSpigot start - Improved player lookup changes ++ Player found = getPlayerExact(name); ++ if (found != null) { ++ return found; ++ } ++ // PaperSpigot end + String lowerName = name.toLowerCase(); + int delta = Integer.MAX_VALUE; + for (Player player : getOnlinePlayers()) { +@@ -541,17 +546,10 @@ public final class CraftServer implements Server { + @Override + @Deprecated + public Player getPlayerExact(String name) { +- Validate.notNull(name, "Name cannot be null"); +- +- String lname = name.toLowerCase(); +- +- for (Player player : getOnlinePlayers()) { +- if (player.getName().equalsIgnoreCase(lname)) { +- return player; +- } +- } +- +- return null; ++ // PaperSpigot start - Improved player lookup, replace whole method ++ EntityPlayer player = playerList.playerMap.get(name); ++ return player != null ? player.getBukkitEntity() : null; ++ // PaperSpigot end + } + + // TODO: In 1.8+ this should use the server's UUID->EntityPlayer map +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 73ec0f7..52760c0 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -102,13 +102,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + } + + public boolean isOnline() { +- for (Object obj : server.getHandle().players) { +- EntityPlayer player = (EntityPlayer) obj; +- if (player.getName().equalsIgnoreCase(getName())) { +- return true; +- } +- } +- return false; ++ return server.getHandle().uuidMap.get(getUniqueId()) != null; // PaperSpigot - replace whole method + } + + public InetSocketAddress getAddress() { +-- +1.9.1 + diff --git a/Spigot-Server-Patches/0045-Improve-autosave-mechanism.patch b/Spigot-Server-Patches/0045-Improve-autosave-mechanism.patch new file mode 100644 index 0000000000..3a7e099142 --- /dev/null +++ b/Spigot-Server-Patches/0045-Improve-autosave-mechanism.patch @@ -0,0 +1,63 @@ +From d5ecea59249849d642ab6a6612650528627f2b2f Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Sun, 19 Oct 2014 16:30:48 -0500 +Subject: [PATCH] Improve autosave mechanism + +Only save modified chunks, or chunks with entities after 4 auto save passes + +diff --git a/src/main/java/net/minecraft/server/Chunk.java b/src/main/java/net/minecraft/server/Chunk.java +index 9454d4f..86a13e7 100644 +--- a/src/main/java/net/minecraft/server/Chunk.java ++++ b/src/main/java/net/minecraft/server/Chunk.java +@@ -910,7 +910,7 @@ public class Chunk { + if (this.o && this.world.getTime() != this.lastSaved || this.n) { + return true; + } +- } else if (this.o && this.world.getTime() >= this.lastSaved + 600L) { ++ } else if (this.o && this.world.getTime() >= this.lastSaved + MinecraftServer.getServer().autosavePeriod * 4) { // PaperSpigot - Only save if we've passed 2 auto save intervals without modification + return true; + } + +diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java +index 953a88c..692e74f 100644 +--- a/src/main/java/net/minecraft/server/MinecraftServer.java ++++ b/src/main/java/net/minecraft/server/MinecraftServer.java +@@ -657,9 +657,10 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo + // Spigot Start + // We replace this with saving each individual world as this.saveChunks(...) is broken, + // and causes the main thread to sleep for random amounts of time depending on chunk activity ++ // Also pass flag to only save modified chunks -- PaperSpigot + server.playerCommandState = true; + for (World world : worlds) { +- world.getWorld().save(); ++ world.getWorld().save(true); + } + server.playerCommandState = false; + // this.saveChunks(true); +diff --git a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +index ea786ae..d67ba8f 100644 +--- a/src/main/java/org/bukkit/craftbukkit/CraftWorld.java ++++ b/src/main/java/org/bukkit/craftbukkit/CraftWorld.java +@@ -687,12 +687,18 @@ public class CraftWorld implements World { + } + + public void save() { ++ // PaperSpigot start - Improved autosave ++ save(true); ++ } ++ ++ public void save(boolean forceSave) { ++ // PaperSpigot end + this.server.checkSaveState(); + try { + boolean oldSave = world.savingDisabled; + + world.savingDisabled = false; +- world.save(true, null); ++ world.save(forceSave, null); // PaperSpigot + + world.savingDisabled = oldSave; + } catch (ExceptionWorldConflict ex) { +-- +1.9.1 +