--- a/net/minecraft/network/protocol/PacketUtils.java
+++ b/net/minecraft/network/protocol/PacketUtils.java
@@ -2,37 +2,81 @@
 
 import com.mojang.logging.LogUtils;
 import net.minecraft.network.PacketListener;
+import org.slf4j.Logger;
+
+// CraftBukkit start
+import net.minecraft.server.MinecraftServer;
 import net.minecraft.server.RunningOnDifferentThreadException;
 import net.minecraft.server.level.ServerLevel;
+import net.minecraft.server.network.ServerGamePacketListenerImpl;
 import net.minecraft.util.thread.BlockableEventLoop;
-import org.slf4j.Logger;
 
 public class PacketUtils {
+
     private static final Logger LOGGER = LogUtils.getLogger();
 
+    // Paper start - detailed watchdog information
+    public static final java.util.concurrent.ConcurrentLinkedDeque<PacketListener> packetProcessing = new java.util.concurrent.ConcurrentLinkedDeque<>();
+    static final java.util.concurrent.atomic.AtomicLong totalMainThreadPacketsProcessed = new java.util.concurrent.atomic.AtomicLong();
+
+    public static long getTotalProcessedPackets() {
+        return totalMainThreadPacketsProcessed.get();
+    }
+
+    public static java.util.List<PacketListener> getCurrentPacketProcessors() {
+        java.util.List<PacketListener> ret = new java.util.ArrayList<>(4);
+        for (PacketListener listener : packetProcessing) {
+            ret.add(listener);
+        }
+
+        return ret;
+    }
+    // Paper end - detailed watchdog information
+
+    public PacketUtils() {}
+
     public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T listener, ServerLevel world) throws RunningOnDifferentThreadException {
-        ensureRunningOnSameThread(packet, listener, world.getServer());
+        PacketUtils.ensureRunningOnSameThread(packet, listener, (BlockableEventLoop) world.getServer());
     }
 
     public static <T extends PacketListener> void ensureRunningOnSameThread(Packet<T> packet, T listener, BlockableEventLoop<?> engine) throws RunningOnDifferentThreadException {
         if (!engine.isSameThread()) {
-            engine.executeIfPossible(() -> {
+            engine.execute(() -> { // Paper - Fix preemptive player kick on a server shutdown.
+                packetProcessing.push(listener); // Paper - detailed watchdog information
+                try { // Paper - detailed watchdog information
+                if (MinecraftServer.getServer().hasStopped() || (listener instanceof ServerGamePacketListenerImpl && ((ServerGamePacketListenerImpl) listener).processedDisconnect)) return; // CraftBukkit, MC-142590
                 if (listener.getConnection().isConnected()) {
-                    try {
+                    co.aikar.timings.Timing timing = co.aikar.timings.MinecraftTimings.getPacketTiming(packet); // Paper - timings
+                    try (co.aikar.timings.Timing ignored = timing.startTiming()) { // Paper - timings
                         packet.handle(listener);
-                    } catch (Exception var3) {
-                        if (listener.shouldPropagateHandlingExceptions()) {
-                            throw var3;
+                    } catch (Exception exception) {
+                        net.minecraft.network.Connection networkmanager = listener.getConnection();
+                        String playerIP = io.papermc.paper.configuration.GlobalConfiguration.get().logging.logPlayerIpAddresses ? String.valueOf(networkmanager.getRemoteAddress()) : "<ip address withheld>"; // Paper
+                        if (networkmanager.getPlayer() != null) {
+                            LOGGER.error("Error whilst processing packet {} for {}[{}]", packet, networkmanager.getPlayer().getScoreboardName(), playerIP, exception); // Paper
+                        } else {
+                            LOGGER.error("Error whilst processing packet {} for connection from {}", packet, playerIP, exception); // Paper
                         }
-
-                        LOGGER.error("Failed to handle packet {}, suppressing error", packet, var3);
+                        net.minecraft.network.chat.Component error = net.minecraft.network.chat.Component.literal("Packet processing error");
+                        networkmanager.send(new net.minecraft.network.protocol.game.ClientboundDisconnectPacket(error), net.minecraft.network.PacketSendListener.thenRun(() -> networkmanager.disconnect(error)));
+                        networkmanager.setReadOnly();
                     }
                 } else {
-                    LOGGER.debug("Ignoring packet due to disconnection: {}", (Object)packet);
+                    PacketUtils.LOGGER.debug("Ignoring packet due to disconnection: {}", packet);
+                }
+                // Paper start - detailed watchdog information
+                } finally {
+                    totalMainThreadPacketsProcessed.getAndIncrement();
+                    packetProcessing.pop();
                 }
+                // Paper end - detailed watchdog information
 
             });
             throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD;
+            // CraftBukkit start - SPIGOT-5477, MC-142590
+        } else if (MinecraftServer.getServer().hasStopped() || (listener instanceof ServerGamePacketListenerImpl && ((ServerGamePacketListenerImpl) listener).processedDisconnect)) {
+            throw RunningOnDifferentThreadException.RUNNING_ON_DIFFERENT_THREAD;
+            // CraftBukkit end
         }
     }
 }
