From dbfd5f4f2d4f597e4dc70cff52eccd2e1e2fa8d7 Mon Sep 17 00:00:00 2001 From: Sotr Date: Tue, 19 Mar 2019 05:00:23 +0800 Subject: [PATCH] 21 TPS --- scripts/build.sh | 4 +- .../co/aikar/timings/ThreadAssertion.java | 13 + .../main/java/co/aikar/timings/Timing.java | 78 ++++++ .../java/co/aikar/timings/TimingHandler.java | 222 ++++++++++++++++++ .../server/core/AkarinGlobalConfig.java | 19 +- .../command/defaults/VersionCommand.java | 0 .../io/akarin/server/core/PacketType.java | 2 +- .../java/net/minecraft/server/ChunkMap.java | 4 +- .../java/net/minecraft/server/MCUtil.java | 7 +- .../net/minecraft/server/MinecraftServer.java | 4 +- .../minecraft/server/PlayerConnection.java | 3 +- .../org/bukkit/craftbukkit/CraftServer.java | 5 +- .../craftbukkit/chunkio/ChunkIOProvider.java | 2 +- src/main/java/org/spigotmc/AsyncCatcher.java | 3 +- 14 files changed, 346 insertions(+), 20 deletions(-) create mode 100644 src/api/main/java/co/aikar/timings/ThreadAssertion.java create mode 100644 src/api/main/java/co/aikar/timings/Timing.java create mode 100644 src/api/main/java/co/aikar/timings/TimingHandler.java rename src/{ => api}/main/java/io/akarin/server/core/AkarinGlobalConfig.java (90%) rename src/api/{ => main/java}/org/bukkit/command/defaults/VersionCommand.java (100%) diff --git a/scripts/build.sh b/scripts/build.sh index 442f8d772..1b978ffe8 100644 --- a/scripts/build.sh +++ b/scripts/build.sh @@ -31,7 +31,7 @@ echo "[Akarin] Ready to build" cd "$paperbasedir" if [ "$2" == "--fast" ] || [ "$3" == "--fast" ] || [ "$4" == "--fast" ]; then echo "[Akarin] Test and repatch has been skipped" - \cp -rf "$basedir/src/api/org" "$paperbasedir/Paper-API/src/main/java" + \cp -rf "$basedir/src/api/main" "$paperbasedir/Paper-API/src/" \cp -rf "$basedir/src" "$paperbasedir/Paper-Server/" \cp -rf "$basedir/pom.xml" "$paperbasedir/Paper-Server/" mvn clean install -DskipTests @@ -39,7 +39,7 @@ echo "[Akarin] Ready to build" rm -rf Paper-API/src rm -rf Paper-Server/src ./paper patch - \cp -rf "$basedir/src/api/org" "$paperbasedir/Paper-API/src/main/java" + \cp -rf "$basedir/src/api/main" "$paperbasedir/Paper-API/src/" \cp -rf "$basedir/src" "$paperbasedir/Paper-Server/" \cp -rf "$basedir/pom.xml" "$paperbasedir/Paper-Server/" mvn clean install -DskipTests diff --git a/src/api/main/java/co/aikar/timings/ThreadAssertion.java b/src/api/main/java/co/aikar/timings/ThreadAssertion.java new file mode 100644 index 000000000..ad4c11b7d --- /dev/null +++ b/src/api/main/java/co/aikar/timings/ThreadAssertion.java @@ -0,0 +1,13 @@ +package co.aikar.timings; + +public class ThreadAssertion { + private static boolean mainThread; + + public static boolean isMainThread() { + return mainThread; + } + + static boolean setMainThread(boolean is) { + return mainThread = is; + } +} \ No newline at end of file diff --git a/src/api/main/java/co/aikar/timings/Timing.java b/src/api/main/java/co/aikar/timings/Timing.java new file mode 100644 index 000000000..b7d4bdbf2 --- /dev/null +++ b/src/api/main/java/co/aikar/timings/Timing.java @@ -0,0 +1,78 @@ +/* + * This file is licensed under the MIT License (MIT). + * + * Copyright (c) 2014 Daniel Ennis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package co.aikar.timings; + +/** + * Provides an ability to time sections of code within the Minecraft Server + */ +public interface Timing extends AutoCloseable { + /** + * Starts timing the execution until {@link #stopTiming()} is called. + * + * @return Timing + */ + Timing startTiming(); + default Timing startTiming(boolean assertThread) { return startTiming(); }; // Akarin + + /** + *

Stops timing and records the data. Propagates the data up to group handlers.

+ * + * Will automatically be called when this Timing is used with try-with-resources + */ + void stopTiming(); + + /** + * Starts timing the execution until {@link #stopTiming()} is called. + * + * But only if we are on the primary thread. + * + * @return Timing + */ + Timing startTimingIfSync(); + default Timing startTimingIfSync(boolean assertThread) { return startTimingIfSync(); }; // Akarin + + /** + *

Stops timing and records the data. Propagates the data up to group handlers.

+ * + *

Will automatically be called when this Timing is used with try-with-resources

+ * + * But only if we are on the primary thread. + */ + void stopTimingIfSync(); + + /** + * Stops timing and disregards current timing data. + */ + void abort(); + + /** + * Used internally to get the actual backing Handler in the case of delegated Handlers + * + * @return TimingHandler + */ + TimingHandler getTimingHandler(); + + @Override + void close(); +} diff --git a/src/api/main/java/co/aikar/timings/TimingHandler.java b/src/api/main/java/co/aikar/timings/TimingHandler.java new file mode 100644 index 000000000..283c08391 --- /dev/null +++ b/src/api/main/java/co/aikar/timings/TimingHandler.java @@ -0,0 +1,222 @@ +/* + * This file is licensed under the MIT License (MIT). + * + * Copyright (c) 2014 Daniel Ennis + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package co.aikar.timings; + +import co.aikar.util.LoadingIntMap; +import io.akarin.server.core.AkarinGlobalConfig; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import org.bukkit.Bukkit; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; + +class TimingHandler implements Timing { + + private static AtomicInteger idPool = new AtomicInteger(1); + final int id = idPool.getAndIncrement(); + + final String name; + private final boolean verbose; + + private final Int2ObjectOpenHashMap children = new LoadingIntMap<>(TimingData::new); + + final TimingData record; + private final TimingHandler groupHandler; + + private long start = 0; + private int timingDepth = 0; + private boolean added; + private boolean timed; + private boolean enabled; + private TimingHandler parent; + + TimingHandler(TimingIdentifier id) { + if (id.name.startsWith("##")) { + verbose = true; + this.name = id.name.substring(3); + } else { + this.name = id.name; + verbose = false; + } + + this.record = new TimingData(this.id); + this.groupHandler = id.groupHandler; + + TimingIdentifier.getGroup(id.group).handlers.add(this); + checkEnabled(); + } + + final void checkEnabled() { + enabled = Timings.timingsEnabled && (!verbose || Timings.verboseEnabled); + } + + void processTick(boolean violated) { + if (timingDepth != 0 || record.getCurTickCount() == 0) { + timingDepth = 0; + start = 0; + return; + } + + record.processTick(violated); + for (TimingData handler : children.values()) { + handler.processTick(violated); + } + } + + @Override + // Akarin start + public Timing startTimingIfSync() { + return startTiming(false); + } + @Override + // Akarin end + public Timing startTimingIfSync(boolean assertThread) { + startTiming(assertThread); + return this; + } + + @Override + public void stopTimingIfSync() { + stopTiming(); + } + + // Akarin start + @Override + public Timing startTiming() { + return startTiming(false); + } + + @Override + public Timing startTiming(boolean assertThread) { + if (enabled && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) /*&& ++timingDepth == 1*/) { + if (AkarinGlobalConfig.lazyThreadAssertion && assertThread) ThreadAssertion.setMainThread(true); + if (++timingDepth != 1) return this; + // Akarin end + start = System.nanoTime(); + parent = TimingsManager.CURRENT; + TimingsManager.CURRENT = this; + } + return this; + } + + @Override + public void stopTiming() { + // Akarin start + if (enabled && timingDepth > 0 && (ThreadAssertion.isMainThread() || Bukkit.isPrimaryThread()) /*&& --timingDepth == 0 && start != 0*/) { + if (AkarinGlobalConfig.lazyThreadAssertion) ThreadAssertion.setMainThread(false); + if (--timingDepth != 0 || start == 0) return; + // Akarin end + addDiff(System.nanoTime() - start); + start = 0; + } + } + + @Override + public void abort() { + if (enabled && timingDepth > 0) { + start = 0; + } + } + + void addDiff(long diff) { + if (TimingsManager.CURRENT == this) { + TimingsManager.CURRENT = parent; + if (parent != null) { + parent.children.get(id).add(diff); + } + } + record.add(diff); + if (!added) { + added = true; + timed = true; + TimingsManager.HANDLERS.add(this); + } + if (groupHandler != null) { + groupHandler.addDiff(diff); + groupHandler.children.get(id).add(diff); + } + } + + /** + * Reset this timer, setting all values to zero. + * + * @param full + */ + void reset(boolean full) { + record.reset(); + if (full) { + timed = false; + } + start = 0; + timingDepth = 0; + added = false; + children.clear(); + checkEnabled(); + } + + @Override + public TimingHandler getTimingHandler() { + return this; + } + + @Override + public boolean equals(Object o) { + return (this == o); + } + + @Override + public int hashCode() { + return id; + } + + /** + * This is simply for the Closeable interface so it can be used with + * try-with-resources () + */ + @Override + public void close() { + stopTimingIfSync(); + } + + public boolean isSpecial() { + return this == TimingsManager.FULL_SERVER_TICK || this == TimingsManager.TIMINGS_TICK; + } + + boolean isTimed() { + return timed; + } + + public boolean isEnabled() { + return enabled; + } + + TimingData[] cloneChildren() { + final TimingData[] clonedChildren = new TimingData[children.size()]; + int i = 0; + for (TimingData child : children.values()) { + clonedChildren[i++] = child.clone(); + } + return clonedChildren; + } +} diff --git a/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java b/src/api/main/java/io/akarin/server/core/AkarinGlobalConfig.java similarity index 90% rename from src/main/java/io/akarin/server/core/AkarinGlobalConfig.java rename to src/api/main/java/io/akarin/server/core/AkarinGlobalConfig.java index 33d575504..312aceaee 100644 --- a/src/main/java/io/akarin/server/core/AkarinGlobalConfig.java +++ b/src/api/main/java/io/akarin/server/core/AkarinGlobalConfig.java @@ -10,15 +10,12 @@ import java.util.List; import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; +import org.bukkit.Bukkit; import org.bukkit.configuration.InvalidConfigurationException; import org.bukkit.configuration.file.YamlConfiguration; @SuppressWarnings({"UnusedIsStillUsed", "unused"}) public class AkarinGlobalConfig { - public final static Logger LOGGER = LogManager.getLogger("Akarin"); - private static File CONFIG_FILE; private static final String HEADER = "This is the global configuration file for Akarin.\n" + "Some options may impact gameplay, so use with caution,\n" @@ -36,7 +33,8 @@ public class AkarinGlobalConfig { config.load(CONFIG_FILE); } catch (IOException ex) { } catch (InvalidConfigurationException ex) { - LOGGER.error("Could not load akarin.yml, please correct your syntax errors", ex); + Bukkit.getLogger().severe("Could not load akarin.yml, please correct your syntax errors"); + ex.printStackTrace(); throw Throwables.propagate(ex); } config.options().header(HEADER); @@ -57,7 +55,8 @@ public class AkarinGlobalConfig { } catch (InvocationTargetException ex) { throw Throwables.propagate(ex.getCause()); } catch (Exception ex) { - LOGGER.error("Error invoking " + method, ex); + Bukkit.getLogger().severe("Error invoking " + method); + ex.printStackTrace(); } } } @@ -66,7 +65,8 @@ public class AkarinGlobalConfig { try { config.save(CONFIG_FILE); } catch (IOException ex) { - LOGGER.error("Could not save " + CONFIG_FILE, ex); + Bukkit.getLogger().severe("Could not save " + CONFIG_FILE); + ex.printStackTrace(); } } @@ -165,4 +165,9 @@ public class AkarinGlobalConfig { private static void fixPhysicsEventBehavior() { fixPhysicsEventBehaviour = getBoolean("alternative.fix-physics-event-behaviour", fixPhysicsEventBehaviour); } + + public static boolean lazyThreadAssertion = true; + private static void lazyThreadAssertion() { + lazyThreadAssertion = getBoolean("core.lazy-thread-assertion", lazyThreadAssertion); + } } \ No newline at end of file diff --git a/src/api/org/bukkit/command/defaults/VersionCommand.java b/src/api/main/java/org/bukkit/command/defaults/VersionCommand.java similarity index 100% rename from src/api/org/bukkit/command/defaults/VersionCommand.java rename to src/api/main/java/org/bukkit/command/defaults/VersionCommand.java diff --git a/src/main/java/io/akarin/server/core/PacketType.java b/src/main/java/io/akarin/server/core/PacketType.java index cd0c99cc5..c163401d6 100644 --- a/src/main/java/io/akarin/server/core/PacketType.java +++ b/src/main/java/io/akarin/server/core/PacketType.java @@ -1,6 +1,6 @@ package io.akarin.server.core; -public enum PacketType { // unused yet +public enum PacketType { STATUS_OUT_SERVER_INFO, STATUS_OUT_PONG, diff --git a/src/main/java/net/minecraft/server/ChunkMap.java b/src/main/java/net/minecraft/server/ChunkMap.java index 154ab09e0..b6cbfee4a 100644 --- a/src/main/java/net/minecraft/server/ChunkMap.java +++ b/src/main/java/net/minecraft/server/ChunkMap.java @@ -14,8 +14,8 @@ public class ChunkMap extends Long2ObjectOpenHashMap { } public Chunk put(long i, Chunk chunk) { - org.spigotmc.AsyncCatcher.catchOp("Async Chunk put"); // Paper chunk.world.timings.syncChunkLoadPostTimer.startTiming(); // Paper + //org.spigotmc.AsyncCatcher.catchOp("Async Chunk put"); // Paper // Akarin - comment lastChunkByPos = chunk; // Paper // Paper start Chunk chunk1; @@ -81,7 +81,7 @@ public class ChunkMap extends Long2ObjectOpenHashMap { public Chunk remove(long i) { // Paper start - org.spigotmc.AsyncCatcher.catchOp("Async Chunk remove"); + //org.spigotmc.AsyncCatcher.catchOp("Async Chunk remove"); // Akarin - comment Chunk chunk; synchronized (this) { // synchronize so any async gets are safe diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java index f70f5899f..c4572716b 100644 --- a/src/main/java/net/minecraft/server/MCUtil.java +++ b/src/main/java/net/minecraft/server/MCUtil.java @@ -5,6 +5,9 @@ import com.destroystokyo.paper.profile.CraftPlayerProfile; import com.destroystokyo.paper.profile.PlayerProfile; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.mojang.authlib.GameProfile; + +import co.aikar.timings.ThreadAssertion; + import org.apache.commons.lang.exception.ExceptionUtils; import org.bukkit.Location; import org.bukkit.block.BlockFace; @@ -115,7 +118,7 @@ public final class MCUtil { * @return */ public static void ensureMain(String reason, Runnable run) { - if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { + if (AsyncCatcher.enabled && !ThreadAssertion.isMainThread() && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { // Akarin if (reason != null) { new IllegalStateException("Asynchronous " + reason + "!").printStackTrace(); } @@ -140,7 +143,7 @@ public final class MCUtil { * @return */ public static T ensureMain(String reason, Supplier run) { - if (AsyncCatcher.enabled && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { + if (AsyncCatcher.enabled && !ThreadAssertion.isMainThread() && Thread.currentThread() != MinecraftServer.getServer().primaryThread) { // Akarin if (reason != null) { new IllegalStateException("Asynchronous " + reason + "! Blocking thread until it returns ").printStackTrace(); } diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java index 58f87e5a4..eabdec579 100644 --- a/src/main/java/net/minecraft/server/MinecraftServer.java +++ b/src/main/java/net/minecraft/server/MinecraftServer.java @@ -16,6 +16,7 @@ import com.mojang.authlib.GameProfileRepository; import com.mojang.authlib.minecraft.MinecraftSessionService; import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService; import com.mojang.datafixers.DataFixer; + import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.Unpooled; @@ -65,6 +66,7 @@ import org.bukkit.event.server.ServerLoadEvent; // CraftBukkit end import org.spigotmc.SlackActivityAccountant; // Spigot import co.aikar.timings.MinecraftTimings; // Paper +import co.aikar.timings.ThreadAssertion; public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStatistics, ICommandListener, Runnable { @@ -1704,7 +1706,7 @@ public abstract class MinecraftServer implements IAsyncTaskHandler, IMojangStati } public boolean isMainThread() { - return Thread.currentThread() == this.serverThread; + return ThreadAssertion.isMainThread() || Thread.currentThread() == this.serverThread; // Akarin } public int aw() { diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java index e1dedf860..3e9370009 100644 --- a/src/main/java/net/minecraft/server/PlayerConnection.java +++ b/src/main/java/net/minecraft/server/PlayerConnection.java @@ -69,6 +69,7 @@ import com.destroystokyo.paper.event.player.IllegalPacketEvent; // Paper import com.destroystokyo.paper.event.player.PlayerJumpEvent; // Paper import co.aikar.timings.MinecraftTimings; // Paper // CraftBukkit end +import co.aikar.timings.ThreadAssertion; public class PlayerConnection implements PacketListenerPlayIn, ITickable { @@ -1756,7 +1757,7 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable { if (!async && s.startsWith("/")) { // Paper Start - if (!org.spigotmc.AsyncCatcher.shuttingDown && !org.bukkit.Bukkit.isPrimaryThread()) { + if (!org.spigotmc.AsyncCatcher.shuttingDown && !ThreadAssertion.isMainThread() && !org.bukkit.Bukkit.isPrimaryThread()) { final String fCommandLine = s; MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Command Dispatched Async: " + fCommandLine); MinecraftServer.LOGGER.log(org.apache.logging.log4j.Level.ERROR, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable()); diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index 38fdc5d20..caccb7bdc 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -139,6 +139,7 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; +import co.aikar.timings.ThreadAssertion; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufOutputStream; import io.netty.buffer.Unpooled; @@ -709,7 +710,7 @@ public final class CraftServer implements Server { org.spigotmc.AsyncCatcher.catchOp( "command dispatch" ); // Spigot // Paper Start - if (!org.spigotmc.AsyncCatcher.shuttingDown && !Bukkit.isPrimaryThread()) { + if (!org.spigotmc.AsyncCatcher.shuttingDown && !ThreadAssertion.isMainThread() && !Bukkit.isPrimaryThread()) { // Akarin final CommandSender fSender = sender; final String fCommandLine = commandLine; Bukkit.getLogger().log(Level.SEVERE, "Command Dispatched Async: " + commandLine); @@ -1723,7 +1724,7 @@ public final class CraftServer implements Server { @Override public boolean isPrimaryThread() { - return Thread.currentThread().equals(console.primaryThread); + return ThreadAssertion.isMainThread() || Thread.currentThread().equals(console.primaryThread); } @Override diff --git a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java index 4cfe24df1..cd98d6ecd 100644 --- a/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java +++ b/src/main/java/org/bukkit/craftbukkit/chunkio/ChunkIOProvider.java @@ -40,7 +40,7 @@ class ChunkIOProvider implements AsynchronousExecutor.CallBackProvider