From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Thu, 26 Mar 2020 21:59:32 -0700
Subject: [PATCH] Detail more information in watchdog dumps

- Dump position, world, velocity, and uuid for currently ticking entities
- Dump player name, player uuid, position, and world for packet handling

diff --git a/src/main/java/net/minecraft/server/Entity.java b/src/main/java/net/minecraft/server/Entity.java
index 6eee9c858dffad0bb0ce06d850deec0725789c9f..32dadb91a30b67202a463cf5c9abc998efecad80 100644
--- a/src/main/java/net/minecraft/server/Entity.java
+++ b/src/main/java/net/minecraft/server/Entity.java
@@ -599,7 +599,39 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
         return this.onGround;
     }
 
+    // Tuinity start - detailed watchdog information
+    private Vec3D moveVector;
+    private double moveStartX;
+    private double moveStartY;
+    private double moveStartZ;
+
+    public final Vec3D getMoveVector() {
+        return this.moveVector;
+    }
+
+    public final double getMoveStartX() {
+        return this.moveStartX;
+    }
+
+    public final double getMoveStartY() {
+        return this.moveStartY;
+    }
+
+    public final double getMoveStartZ() {
+        return this.moveStartZ;
+    }
+    // Tuinity end - detailed watchdog information
     public void move(EnumMoveType enummovetype, Vec3D vec3d) {
+        // Tuinity start - detailed watchdog information
+        com.tuinity.tuinity.util.TickThread.ensureTickThread("Cannot move an entity off-main");
+        synchronized (this.posLock) {
+            this.moveStartX = this.locX();
+            this.moveStartY = this.locY();
+            this.moveStartZ = this.locZ();
+            this.moveVector = vec3d;
+        }
+        try {
+        // Tuinity end - detailed watchdog information
         if (this.noclip) {
             this.a(this.getBoundingBox().c(vec3d));
             this.recalcPosition();
@@ -743,6 +775,13 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
 
             this.world.getMethodProfiler().exit();
         }
+        // Tuinity start - detailed watchdog information
+        } finally {
+            synchronized (this.posLock) { // Tuinity
+                this.moveVector = null;
+            } // Tuinity
+        }
+        // Tuinity end - detailed watchdog information
     }
 
     protected BlockPosition ap() {
@@ -3316,12 +3355,16 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
         return this.locBlock;
     }
 
+    public final Object posLock = new Object(); // Tuinity - log detailed entity tick information
+
     public Vec3D getMot() {
         return this.mot;
     }
 
     public void setMot(Vec3D vec3d) {
+        synchronized (this.posLock) { // Tuinity
         this.mot = vec3d;
+        } // Tuinity
     }
 
     public void setMot(double d0, double d1, double d2) {
@@ -3376,7 +3419,9 @@ public abstract class Entity implements INamableTileEntity, ICommandListener, Ke
         }
         // Paper end
         if (this.loc.x != d0 || this.loc.y != d1 || this.loc.z != d2) {
+            synchronized (this.posLock) { // Tuinity
             this.loc = new Vec3D(d0, d1, d2);
+            } // Tuinity
             int i = MathHelper.floor(d0);
             int j = MathHelper.floor(d1);
             int k = MathHelper.floor(d2);
diff --git a/src/main/java/net/minecraft/server/NetworkManager.java b/src/main/java/net/minecraft/server/NetworkManager.java
index fb1e3c705b8abee13695762cdfd0e9f1bfdb5ad8..ed9c06c91a3ee77dad6e616cf28a34a4177ce6e3 100644
--- a/src/main/java/net/minecraft/server/NetworkManager.java
+++ b/src/main/java/net/minecraft/server/NetworkManager.java
@@ -387,7 +387,14 @@ public class NetworkManager extends SimpleChannelInboundHandler<Packet<?>> {
         }
 
         if (this.packetListener instanceof PlayerConnection) {
+            // Tuinity start - detailed watchdog information
+            PlayerConnectionUtils.packetProcessing.push(this.packetListener);
+            try {
+            // Tuinity end - detailed watchdog information
             ((PlayerConnection) this.packetListener).tick();
+            } finally { // Tuinity start - detailed watchdog information
+                PlayerConnectionUtils.packetProcessing.pop();
+            } // Tuinity start - detailed watchdog information
         }
 
         if (this.channel != null) {
diff --git a/src/main/java/net/minecraft/server/PlayerConnectionUtils.java b/src/main/java/net/minecraft/server/PlayerConnectionUtils.java
index 8c7080777b370f97e1291dfedde5b419290f39cc..8112b01f15c33f50fe5ec9aebf75d8091d440994 100644
--- a/src/main/java/net/minecraft/server/PlayerConnectionUtils.java
+++ b/src/main/java/net/minecraft/server/PlayerConnectionUtils.java
@@ -13,10 +13,30 @@ public class PlayerConnectionUtils {
         ensureMainThread(packet, t0, (IAsyncTaskHandler) worldserver.getMinecraftServer());
     }
 
+    // Tuinity start - detailed watchdog information
+    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;
+    }
+    // Tuinity end - detailed watchdog information
+
     public static <T extends PacketListener> void ensureMainThread(Packet<T> packet, T t0, IAsyncTaskHandler<?> iasynctaskhandler) throws CancelledPacketHandleException {
         if (!iasynctaskhandler.isMainThread()) {
             Timing timing = MinecraftTimings.getPacketTiming(packet); // Paper - timings
             iasynctaskhandler.execute(() -> {
+                packetProcessing.push(t0); // Tuinity - detailed watchdog information
+                try { // Tuinity - detailed watchdog information
                 if (MinecraftServer.getServer().hasStopped() || (t0 instanceof PlayerConnection && ((PlayerConnection) t0).processedDisconnect)) return; // CraftBukkit, MC-142590
                 if (t0.a().isConnected()) {
                     try (Timing ignored = timing.startTiming()) { // Paper - timings
@@ -40,6 +60,12 @@ public class PlayerConnectionUtils {
                 } else {
                     PlayerConnectionUtils.LOGGER.debug("Ignoring packet due to disconnection: " + packet);
                 }
+                // Tuinity start - detailed watchdog information
+                } finally {
+                    totalMainThreadPacketsProcessed.getAndIncrement();
+                    packetProcessing.pop();
+                }
+                // Tuinity end - detailed watchdog information
 
             });
             throw CancelledPacketHandleException.INSTANCE;
diff --git a/src/main/java/net/minecraft/server/WorldServer.java b/src/main/java/net/minecraft/server/WorldServer.java
index e285038154d83cbf0d7c0b6b8a247c11b9e20119..97a9a201a055520a7ab92b3ecc8987eb560bf263 100644
--- a/src/main/java/net/minecraft/server/WorldServer.java
+++ b/src/main/java/net/minecraft/server/WorldServer.java
@@ -901,7 +901,26 @@ public class WorldServer extends World implements GeneratorAccessSeed {
 
     }
 
+    // Tuinity start - log detailed entity tick information
+    static final java.util.concurrent.ConcurrentLinkedDeque<Entity> currentlyTickingEntities = new java.util.concurrent.ConcurrentLinkedDeque<>();
+
+    public static List<Entity> getCurrentlyTickingEntities() {
+        List<Entity> ret = Lists.newArrayListWithCapacity(4);
+
+        for (Entity entity : currentlyTickingEntities) {
+            ret.add(entity);
+        }
+
+        return ret;
+    }
+    // Tuinity end - log detailed entity tick information
+
     public void entityJoinedWorld(Entity entity) {
+        // Tuinity start - log detailed entity tick information
+        com.tuinity.tuinity.util.TickThread.ensureTickThread("Cannot tick an entity off-main");
+        try {
+            currentlyTickingEntities.push(entity);
+            // Tuinity end - log detailed entity tick information
         if (!(entity instanceof EntityHuman) && !this.getChunkProvider().a(entity)) {
             this.chunkCheck(entity);
         } else {
@@ -954,6 +973,11 @@ public class WorldServer extends World implements GeneratorAccessSeed {
             //} finally { timer.stopTiming(); } // Paper - timings - move up
 
         }
+        // Tuinity start - log detailed entity tick information
+        } finally {
+            currentlyTickingEntities.pop();
+        }
+        // Tuinity end - log detailed entity tick information
     }
 
     public void a(Entity entity, Entity entity1) {
diff --git a/src/main/java/org/spigotmc/WatchdogThread.java b/src/main/java/org/spigotmc/WatchdogThread.java
index ae8903ee1decd22e2ad6138f29fbc757b807e0a7..089ddda8892c3660c5dcb62d0fd56544857a2eba 100644
--- a/src/main/java/org/spigotmc/WatchdogThread.java
+++ b/src/main/java/org/spigotmc/WatchdogThread.java
@@ -65,6 +65,78 @@ public class WatchdogThread extends Thread
         }
     }
 
+    // Tuinity start - log detailed tick information
+    private void dumpEntity(net.minecraft.server.Entity entity) {
+        Logger log = Bukkit.getServer().getLogger();
+        double posX, posY, posZ;
+        net.minecraft.server.Vec3D mot;
+        double moveStartX, moveStartY, moveStartZ;
+        net.minecraft.server.Vec3D moveVec;
+        synchronized (entity.posLock) {
+            posX = entity.locX();
+            posY = entity.locY();
+            posZ = entity.locZ();
+            mot = entity.getMot();
+            moveStartX = entity.getMoveStartX();
+            moveStartY = entity.getMoveStartY();
+            moveStartZ = entity.getMoveStartZ();
+            moveVec = entity.getMoveVector();
+        }
+
+        String entityType = entity.getMinecraftKey().toString();
+        java.util.UUID entityUUID = entity.getUniqueID();
+        net.minecraft.server.World world = entity.getWorld();
+
+        log.log(Level.SEVERE, "Ticking entity: " + entityType + ", entity class: " + entity.getClass().getName());
+        log.log(Level.SEVERE, "Entity status: dead: " + entity.dead + ", valid: " + entity.valid + ", alive: " + entity.isAlive() + ", is passenger: " + entity.isPassenger());
+        log.log(Level.SEVERE, "Entity UUID: " + entityUUID);
+        log.log(Level.SEVERE, "Position: world: '" + (world == null ? "unknown world?" : world.getWorld().getName()) + "' at location (" + posX + ", " + posY + ", " + posZ + ")");
+        log.log(Level.SEVERE, "Velocity: " + (mot == null ? "unknown velocity" : mot.toString()) + " (in blocks per tick)");
+        log.log(Level.SEVERE, "Entity AABB: " + entity.getBoundingBox());
+        if (moveVec != null) {
+            log.log(Level.SEVERE, "Move call information: ");
+            log.log(Level.SEVERE, "Start position: (" + moveStartX + ", " + moveStartY + ", " + moveStartZ + ")");
+            log.log(Level.SEVERE, "Move vector: " + moveVec.toString());
+        }
+    }
+
+    private void dumpTickingInfo() {
+        Logger log = Bukkit.getServer().getLogger();
+
+        // ticking entities
+        for (net.minecraft.server.Entity entity : net.minecraft.server.WorldServer.getCurrentlyTickingEntities()) {
+            this.dumpEntity(entity);
+            net.minecraft.server.Entity vehicle = entity.getVehicle();
+            if (vehicle != null) {
+                log.log(Level.SEVERE, "Detailing vehicle for above entity:");
+                this.dumpEntity(vehicle);
+            }
+        }
+
+        // packet processors
+        for (net.minecraft.server.PacketListener packetListener : net.minecraft.server.PlayerConnectionUtils.getCurrentPacketProcessors()) {
+            if (packetListener instanceof net.minecraft.server.PlayerConnection) {
+                net.minecraft.server.EntityPlayer player = ((net.minecraft.server.PlayerConnection)packetListener).player;
+                long totalPackets = net.minecraft.server.PlayerConnectionUtils.getTotalProcessedPackets();
+                if (player == null) {
+                    log.log(Level.SEVERE, "Handling packet for player connection or ticking player connection (null player): " + packetListener);
+                    log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets);
+                } else {
+                    this.dumpEntity(player);
+                    net.minecraft.server.Entity vehicle = player.getVehicle();
+                    if (vehicle != null) {
+                        log.log(Level.SEVERE, "Detailing vehicle for above entity:");
+                        this.dumpEntity(vehicle);
+                    }
+                    log.log(Level.SEVERE, "Total packets processed on the main thread for all players: " + totalPackets);
+                }
+            } else {
+                log.log(Level.SEVERE, "Handling packet for connection: " + packetListener);
+            }
+        }
+    }
+    // Tuinity end - log detailed tick information
+
     @Override
     public void run()
     {
@@ -121,6 +193,7 @@ public class WatchdogThread extends Thread
                 log.log( Level.SEVERE, "------------------------------" );
                 log.log( Level.SEVERE, "Server thread dump (Look for plugins here before reporting to Paper!):" ); // Paper
                 ChunkTaskManager.dumpAllChunkLoadInfo(); // Paper
+                this.dumpTickingInfo(); // Tuinity - log detailed tick information
                 dumpThread( ManagementFactory.getThreadMXBean().getThreadInfo( server.serverThread.getId(), Integer.MAX_VALUE ), log );
                 log.log( Level.SEVERE, "------------------------------" );
                 //
