From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: BillyGalbreath <blake.galbreath@gmail.com>
Date: Sat, 12 Dec 2020 21:19:05 -0600
Subject: [PATCH] Implement TPSBar


diff --git a/src/main/java/net/minecraft/commands/CommandDispatcher.java b/src/main/java/net/minecraft/commands/CommandDispatcher.java
index d402a896a0094e6c379d71c63d744b4ee443c097..3bcaafb1fe3af67fce2be1f62c7f1aa86ebd51ef 100644
--- a/src/main/java/net/minecraft/commands/CommandDispatcher.java
+++ b/src/main/java/net/minecraft/commands/CommandDispatcher.java
@@ -193,6 +193,7 @@ public class CommandDispatcher {
             CommandWhitelist.a(this.b);
             net.pl3x.purpur.command.DemoCommand.register(dispatcher()); // Purpur
             net.pl3x.purpur.command.PingCommand.register(dispatcher()); // Purpur
+            net.pl3x.purpur.command.TPSBarCommand.register(dispatcher()); // Purpur
         }
 
         if (commanddispatcher_servertype.d) {
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index f6637353fb358e7720edabc355ea036d37d039ca..5e2c7f3bf9aa99b10260454b9957caff81a7cd26 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -989,6 +989,7 @@ public abstract class MinecraftServer extends IAsyncTaskHandlerReentrant<TickTas
         this.safeShutdown(flag, false);
     }
     public void safeShutdown(boolean flag, boolean isRestarting) {
+        net.pl3x.purpur.task.TPSBarTask.stop(); // Purpur
         this.isRunning = false;
         this.isRestarting = isRestarting;
         this.hasLoggedStop = true; // Paper
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 28d47ef97939309ce26b6e4cae14925b510755fd..6d39b777a0f708d61f611d9c16c3b3c48f89d477 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -323,6 +323,8 @@ public class DedicatedServer extends MinecraftServer implements IMinecraftServer
                 MinecraftServerBeans.a((MinecraftServer) this);
             }
 
+            net.pl3x.purpur.task.TPSBarTask.start(); // Purpur
+
             return true;
         }
     }
diff --git a/src/main/java/net/minecraft/server/players/PlayerList.java b/src/main/java/net/minecraft/server/players/PlayerList.java
index c316b0468c53825f90d9bc2ad40d655d92857715..d710834422afa1d4336f68e2425aaffd64d444e2 100644
--- a/src/main/java/net/minecraft/server/players/PlayerList.java
+++ b/src/main/java/net/minecraft/server/players/PlayerList.java
@@ -598,6 +598,8 @@ public abstract class PlayerList {
         if (entityplayer.didPlayerJoinEvent) cserver.getPluginManager().callEvent(playerQuitEvent); // Paper - if we disconnected before join ever fired, don't fire quit
         entityplayer.getBukkitEntity().disconnect(playerQuitEvent.getQuitMessage());
 
+        net.pl3x.purpur.task.TPSBarTask.removePlayer(cserver.getPlayer(entityplayer)); // Purpur
+
         if (server.isMainThread()) entityplayer.playerTick(); // SPIGOT-924 // Paper - don't tick during emergency shutdowns (Watchdog)
         // CraftBukkit end
 
diff --git a/src/main/java/net/pl3x/purpur/command/TPSBarCommand.java b/src/main/java/net/pl3x/purpur/command/TPSBarCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..807f5709af7fd9497633ecfc9e932f0dfd4df231
--- /dev/null
+++ b/src/main/java/net/pl3x/purpur/command/TPSBarCommand.java
@@ -0,0 +1,27 @@
+package net.pl3x.purpur.command;
+
+import net.minecraft.commands.CommandDispatcher;
+import net.minecraft.commands.CommandListenerWrapper;
+import net.minecraft.server.level.EntityPlayer;
+import net.pl3x.purpur.task.TPSBarTask;
+
+public class TPSBarCommand {
+    public static void register(com.mojang.brigadier.CommandDispatcher<CommandListenerWrapper> dispatcher) {
+        dispatcher.register(CommandDispatcher.literal("tpsbar")
+                .requires((listener) -> {
+                    return listener.hasPermission(2);
+                })
+                .executes((context) -> {
+                    return execute(context.getSource(), context.getSource().getPlayerOrException());
+                })
+        ).setPermission("bukkit.command.tpsbar");
+    }
+
+    private static int execute(CommandListenerWrapper sender, EntityPlayer player) {
+        if (player != null) {
+            TPSBarTask.togglePlayer(player.getBukkitEntity());
+            return 1;
+        }
+        return 0;
+    }
+}
diff --git a/src/main/java/net/pl3x/purpur/task/TPSBarTask.java b/src/main/java/net/pl3x/purpur/task/TPSBarTask.java
new file mode 100644
index 0000000000000000000000000000000000000000..a9d71d2b769b8e4e0f5039e997fc5ebc1cc9bfbb
--- /dev/null
+++ b/src/main/java/net/pl3x/purpur/task/TPSBarTask.java
@@ -0,0 +1,116 @@
+package net.pl3x.purpur.task;
+
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.NamespacedKey;
+import org.bukkit.boss.BarColor;
+import org.bukkit.boss.BarStyle;
+import org.bukkit.boss.BossBar;
+import org.bukkit.craftbukkit.scheduler.MinecraftInternalPlugin;
+import org.bukkit.entity.Player;
+import org.bukkit.scheduler.BukkitRunnable;
+
+public class TPSBarTask extends BukkitRunnable {
+    private static TPSBarTask instance;
+    private final NamespacedKey key;
+    private BossBar bossbar;
+
+    private TPSBarTask() {
+        this.key = new NamespacedKey("purpur", "tpsbar");
+    }
+
+    private static TPSBarTask instance() {
+        if (instance == null) {
+            instance = new TPSBarTask();
+        }
+        return instance;
+    }
+
+    private BossBar bossbar() {
+        if (bossbar == null) {
+            bossbar = Bukkit.getBossBar(key);
+            if (bossbar == null) {
+                bossbar = Bukkit.createBossBar(key, "TPS: 20.0", BarColor.RED, BarStyle.SEGMENTED_20);
+            }
+            bossbar.setVisible(true);
+            bossbar.setProgress(1.0D);
+        }
+        return bossbar;
+    }
+
+    @Override
+    public void run() {
+        BossBar bossbar = bossbar();
+        if (bossbar.getPlayers().isEmpty()) {
+            return;
+        }
+
+        double tps = Bukkit.getTPS()[0];
+        if (tps > 20.0D) {
+            tps = 20.0D;
+        } else if (tps < 0.0D) {
+            tps = 0.0D;
+        }
+
+        bossbar.setVisible(true);
+        bossbar.setProgress(Math.max(Math.min(tps / 20.0D, 1.0D), 0.0D));
+
+        String tpsColor;
+        if (tps >= 18) {
+            tpsColor = "&2";
+            bossbar.setColor(BarColor.GREEN);
+        } else if (tps >= 15) {
+            tpsColor = "&e";
+            bossbar.setColor(BarColor.YELLOW);
+        } else {
+            tpsColor = "&4";
+            bossbar.setColor(BarColor.RED);
+        }
+
+        double mspt = Bukkit.getAverageTickTime();
+        String msptColor;
+        if (mspt < 40) {
+            msptColor = "&2";
+        } else if (mspt < 50) {
+            msptColor = "&e";
+        } else {
+            msptColor = "&4";
+        }
+
+        bossbar.setTitle(ChatColor.translateAlternateColorCodes('&', "&eTPS&3: " + tpsColor + String.format("%.2f", tps) + " &eMSPT&3: " + msptColor + String.format("%.3f", mspt)));
+    }
+
+    @Override
+    public void cancel() {
+        super.cancel();
+        BossBar bossbar = bossbar();
+        bossbar.setVisible(false);
+        bossbar.removeAll();
+        Bukkit.removeBossBar(key);
+    }
+
+    public static void removePlayer(Player player) {
+        instance().bossbar().removePlayer(player);
+    }
+
+    public static void togglePlayer(Player player) {
+        BossBar bossbar = instance().bossbar();
+        if (bossbar.getPlayers().contains(player)) {
+            bossbar.removePlayer(player);
+        } else {
+            bossbar.addPlayer(player);
+            instance.run();
+        }
+    }
+
+    public static void start() {
+        stop();
+        instance().runTaskTimerAsynchronously(new MinecraftInternalPlugin(), 20L, 20L);
+    }
+
+    public static void stop() {
+        if (instance != null) {
+            instance.cancel();
+        }
+    }
+}
