Paper/Spigot-Server-Patches/0045-Ensure-commands-are-not-ran-async.patch
Aikar 2365b308c8
Updated Upstream (Bukkit/CraftBukkit/Spigot)
Upstream has released updates that appears to apply and compile correctly.

This update has been tested to ensure that World Conversion still occurs correctly.

Bukkit Changes:
0812ce2c SPIGOT-4397: isChunkGenerated API

CraftBukkit Changes:
4824655c SPIGOT-4398: Upgrade to ASM 6.2.1 for better Java 11 support
eea43870 MC-134115: Fix issues converting tile entities
1a7f2d10 SPIGOT-4397: isChunkGenerated API
40aed54d SPIGOT-4396: Improve vehicle movement

Spigot Changes:
f6a273b1 Rebuild patches
2018-09-26 22:35:46 -04:00

123 lines
6.1 KiB
Diff

From 320d0a1aa6bf90279d552231fde09b66da0ef1ca Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 3 Mar 2016 01:17:12 -0600
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
index fa20e1d26c..7362b73b20 100644
--- a/src/main/java/net/minecraft/server/PlayerConnection.java
+++ b/src/main/java/net/minecraft/server/PlayerConnection.java
@@ -1560,6 +1560,29 @@ public class PlayerConnection implements PacketListenerPlayIn, ITickable {
}
if (!async && s.startsWith("/")) {
+ // Paper Start
+ if (!org.spigotmc.AsyncCatcher.shuttingDown && !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());
+ 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());
+ }
+ }
+ // Paper End
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
index 83f2cfc66c..7f711cd642 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -696,6 +696,29 @@ public final class CraftServer implements Server {
Validate.notNull(commandLine, "CommandLine cannot be null");
org.spigotmc.AsyncCatcher.catchOp( "command dispatch" ); // Spigot
+ // Paper Start
+ if (!org.spigotmc.AsyncCatcher.shuttingDown && !Bukkit.isPrimaryThread()) {
+ 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());
+ }
+ }
+ // Paper End
+
if (commandMap.dispatch(sender, commandLine)) {
return true;
}
diff --git a/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java b/src/main/java/org/bukkit/craftbukkit/util/ServerShutdownThread.java
index a0cdd2317c..984df4083d 100644
--- 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
index 4b3aa85c97..e44c230165 100644
--- 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
index 49768734d0..947c43a5d0 100644
--- 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() )
--
2.19.0