2018-12-08 10:09:55 +00:00
From ab619f67d419bb011f610c5097fc77c654032378 Mon Sep 17 00:00:00 2001
2016-01-13 05:00:58 +00:00
From: Aikar <aikar@aikar.co>
2016-02-29 23:09:49 +00:00
Date: Thu, 3 Mar 2016 01:17:12 -0600
2016-01-13 05:00:58 +00:00
Subject: [PATCH] Ensure commands are not ran async
Plugins calling Player.chat("/foo") or Server.dispatchCommand() could
trigger the server to execute a command while on another thread.
These commands would then process EXPECTING to be on the main thread, leaving to
very hard to trace concurrency issues.
This change will synchronize the command execution back to the main thread, causing a
big slowdown in execution but throwing an exception at same time to raise awareness
that it is happening so that plugin authors can fix their code to stop executing commands async.
diff --git a/src/main/java/net/minecraft/server/PlayerConnection.java b/src/main/java/net/minecraft/server/PlayerConnection.java
2018-12-08 10:09:55 +00:00
index 67c993795..a5bd908fd 100644
2016-01-13 05:00:58 +00:00
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
2018-12-08 10:09:55 +00:00
@@ -1553,6 +1553,29 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
2016-01-13 05:00:58 +00:00
}
if (!async && s.startsWith("/")) {
2016-02-29 23:09:49 +00:00
+ // Paper Start
2018-02-15 05:34:58 +00:00
+ if (!org.spigotmc.AsyncCatcher.shuttingDown && !org.bukkit.Bukkit.isPrimaryThread()) {
2016-01-13 05:00:58 +00:00
+ 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());
+ Waitable wait = new Waitable() {
+ @Override
+ protected Object evaluate() {
+ chat(fCommandLine, false);
+ return null;
+ }
+ };
+ minecraftServer.processQueue.add(wait);
+ try {
+ wait.get();
+ return;
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on!
+ } catch (Exception e) {
+ throw new RuntimeException("Exception processing chat command", e.getCause());
+ }
+ }
2016-02-29 23:09:49 +00:00
+ // Paper End
2016-01-13 05:00:58 +00:00
this.handleCommand(s);
} else if (this.player.getChatFlags() == EntityHuman.EnumChatVisibility.SYSTEM) {
// Do nothing, this is coming from a plugin
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
2018-12-08 10:09:55 +00:00
index 4858265a8..4c177a674 100644
2016-01-13 05:00:58 +00:00
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
2018-11-11 05:37:15 +00:00
@@ -699,6 +699,29 @@ public final class CraftServer implements Server {
2016-01-13 05:00:58 +00:00
Validate.notNull(commandLine, "CommandLine cannot be null");
2018-07-18 04:52:33 +00:00
org.spigotmc.AsyncCatcher.catchOp( "command dispatch" ); // Spigot
2016-01-13 05:00:58 +00:00
2016-02-29 23:09:49 +00:00
+ // Paper Start
2018-02-15 05:34:58 +00:00
+ if (!org.spigotmc.AsyncCatcher.shuttingDown && !Bukkit.isPrimaryThread()) {
2016-01-13 05:00:58 +00:00
+ final CommandSender fSender = sender;
+ final String fCommandLine = commandLine;
+ Bukkit.getLogger().log(Level.SEVERE, "Command Dispatched Async: " + commandLine);
+ Bukkit.getLogger().log(Level.SEVERE, "Please notify author of plugin causing this execution to fix this bug! see: http://bit.ly/1oSiM6C", new Throwable());
+ org.bukkit.craftbukkit.util.Waitable<Boolean> wait = new org.bukkit.craftbukkit.util.Waitable<Boolean>() {
+ @Override
+ protected Boolean evaluate() {
+ return dispatchCommand(fSender, fCommandLine);
+ }
+ };
+ net.minecraft.server.MinecraftServer.getServer().processQueue.add(wait);
+ try {
+ return wait.get();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt(); // This is proper habit for java. If we aren't handling it, pass it on!
+ } catch (Exception e) {
+ throw new RuntimeException("Exception processing dispatch command", e.getCause());
+ }
+ }
2016-02-29 23:09:49 +00:00
+ // Paper End
2016-01-13 05:00:58 +00:00
+
if (commandMap.dispatch(sender, commandLine)) {
return true;
}
2018-02-15 05:34:58 +00:00
diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
2018-11-11 05:37:15 +00:00
index a0cdd2317..984df4083 100644
2018-02-15 05:34:58 +00:00
--- a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
+++ b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
@@ -14,6 +14,7 @@ public class ServerShutdownThread extends Thread {
public void run() {
try {
org.spigotmc.AsyncCatcher.enabled = false; // Spigot
+ org.spigotmc.AsyncCatcher.shuttingDown = true; // Paper
server.stop();
} catch (ExceptionWorldConflict ex) {
ex.printStackTrace();
diff --git a/src/main/java/org/spigotmc/AsyncCatcher.java b/src/main/java/org/spigotmc/AsyncCatcher.java
2018-11-11 05:37:15 +00:00
index 4b3aa85c9..e44c23016 100644
2018-02-15 05:34:58 +00:00
--- a/src/main/java/org/spigotmc/AsyncCatcher.java
+++ b/src/main/java/org/spigotmc/AsyncCatcher.java
@@ -6,6 +6,7 @@ public class AsyncCatcher
{
public static boolean enabled = true;
+ public static boolean shuttingDown = false; // Paper
public static void catchOp(String reason)
{
diff --git a/src/main/java/org/spigotmc/RestartCommand.java b/src/main/java/org/spigotmc/RestartCommand.java
2018-11-11 05:37:15 +00:00
index 49768734d..947c43a5d 100644
2018-02-15 05:34:58 +00:00
--- a/src/main/java/org/spigotmc/RestartCommand.java
+++ b/src/main/java/org/spigotmc/RestartCommand.java
@@ -43,6 +43,7 @@ public class RestartCommand extends Command
public static void restart(final File script)
{
AsyncCatcher.enabled = false; // Disable async catcher incase it interferes with us
+ org.spigotmc.AsyncCatcher.shuttingDown = true; // Paper
try
{
if ( script.isFile() )
2016-01-13 05:00:58 +00:00
--
2018-12-08 10:09:55 +00:00
2.19.2
2016-01-13 05:00:58 +00:00