Files
LuminolMC/patches/server/0009-Add-a-simple-membar.patch
2024-12-15 10:49:36 +08:00

341 lines
14 KiB
Diff

From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MrHua269 <wangxyper@163.com>
Date: Wed, 31 Jul 2024 12:18:09 +0800
Subject: [PATCH] Add a simple membar
diff --git a/src/main/java/me/earthme/luminol/commands/MembarCommand.java b/src/main/java/me/earthme/luminol/commands/MembarCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..a5cbad6b947b1c3e1499bc6d311e17a5eb570c2f
--- /dev/null
+++ b/src/main/java/me/earthme/luminol/commands/MembarCommand.java
@@ -0,0 +1,47 @@
+package me.earthme.luminol.commands;
+
+import me.earthme.luminol.config.modules.misc.MembarConfig;
+import me.earthme.luminol.functions.GlobalServerMemoryBar;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.format.TextColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+
+public class MembarCommand extends Command {
+ public MembarCommand(@NotNull String name) {
+ super(name);
+ this.setPermission("luminol.commands.membar");
+ this.setDescription("Show the memory usage through a bossbar");
+ this.setUsage("/membar");
+ }
+
+ @Override
+ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) {
+ if (!testPermission(sender)){
+ return true;
+ }
+
+ if (!MembarConfig.memoryBarEnabled){
+ sender.sendMessage(Component.text("Membar was already disabled!").color(TextColor.color(255,0,0)));
+ return true;
+ }
+
+ if (!(sender instanceof Player player)){
+ sender.sendMessage(Component.text("Only player can use this command!").color(TextColor.color(255,0,0)));
+ return true;
+ }
+
+ if (GlobalServerMemoryBar.isPlayerVisible(player)) {
+ player.sendMessage(Component.text("Disabled mem bar").color(TextColor.color(0,255,0)));
+ GlobalServerMemoryBar.setVisibilityForPlayer(player,false);
+ return true;
+ }
+
+ player.sendMessage(Component.text("Enabled mem bar").color(TextColor.color(0,255,0)));
+ GlobalServerMemoryBar.setVisibilityForPlayer(player,true);
+
+ return true;
+ }
+}
diff --git a/src/main/java/me/earthme/luminol/config/modules/misc/MembarConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/MembarConfig.java
new file mode 100644
index 0000000000000000000000000000000000000000..b632c4a636974535bf001f010de1dcb6b25868c0
--- /dev/null
+++ b/src/main/java/me/earthme/luminol/config/modules/misc/MembarConfig.java
@@ -0,0 +1,50 @@
+package me.earthme.luminol.config.modules.misc;
+
+import com.electronwill.nightconfig.core.file.CommentedFileConfig;
+import me.earthme.luminol.commands.MembarCommand;
+import me.earthme.luminol.config.ConfigInfo;
+import me.earthme.luminol.config.DoNotLoad;
+import me.earthme.luminol.config.EnumConfigCategory;
+import me.earthme.luminol.config.IConfigModule;
+import me.earthme.luminol.functions.GlobalServerMemoryBar;
+import org.bukkit.Bukkit;
+
+import java.util.List;
+
+public class MembarConfig implements IConfigModule {
+ @ConfigInfo(baseName = "enabled")
+ public static boolean memoryBarEnabled = false;
+ @ConfigInfo(baseName = "format")
+ public static String memBarFormat = "<gray>Memory usage <yellow>:</yellow> <used>MB<yellow>/</yellow><available>MB";
+ @ConfigInfo(baseName = "memory_color_list")
+ public static List<String> memColors = List.of("GREEN","YELLOW","RED","PURPLE");
+ @ConfigInfo(baseName = "update_interval_ticks")
+ public static int updateInterval = 15;
+
+ @DoNotLoad
+ private static boolean inited = false;
+
+ @Override
+ public EnumConfigCategory getCategory() {
+ return EnumConfigCategory.MISC;
+ }
+
+ @Override
+ public String getBaseName() {
+ return "membar";
+ }
+
+ @Override
+ public void onLoaded(CommentedFileConfig configInstance){
+ if (memoryBarEnabled){
+ GlobalServerMemoryBar.init();
+ }else{
+ GlobalServerMemoryBar.cancelBarUpdateTask();
+ }
+
+ if (!inited){
+ Bukkit.getCommandMap().register("membar","luminol",new MembarCommand("membar"));
+ inited = true;
+ }
+ }
+}
diff --git a/src/main/java/me/earthme/luminol/functions/GlobalServerMemoryBar.java b/src/main/java/me/earthme/luminol/functions/GlobalServerMemoryBar.java
new file mode 100644
index 0000000000000000000000000000000000000000..3535cf03e7855b4d8b312ccf3a7b0564ea753019
--- /dev/null
+++ b/src/main/java/me/earthme/luminol/functions/GlobalServerMemoryBar.java
@@ -0,0 +1,171 @@
+package me.earthme.luminol.functions;
+
+import com.google.common.collect.Maps;
+import com.mojang.logging.LogUtils;
+import io.papermc.paper.threadedregions.scheduler.ScheduledTask;
+import me.earthme.luminol.config.modules.misc.MembarConfig;
+import me.earthme.luminol.utils.NullPlugin;
+import net.kyori.adventure.bossbar.BossBar;
+import net.kyori.adventure.text.Component;
+import net.kyori.adventure.text.minimessage.MiniMessage;
+import net.kyori.adventure.text.minimessage.tag.resolver.Placeholder;
+import org.bukkit.Bukkit;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+import org.jetbrains.annotations.NotNull;
+import org.slf4j.Logger;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryUsage;
+import java.util.*;
+
+public class GlobalServerMemoryBar {
+ protected static final NullPlugin NULL_PLUGIN = new NullPlugin();
+ protected static final Map<UUID, BossBar> uuid2Bossbars = Maps.newConcurrentMap();
+ protected static final Map<UUID, ScheduledTask> scheduledTasks = new HashMap<>();
+ protected static volatile ScheduledTask scannerTask = null;
+ private static final Logger logger = LogUtils.getLogger();
+
+ public static void init(){
+ cancelBarUpdateTask();
+
+ scannerTask = Bukkit.getGlobalRegionScheduler().runAtFixedRate(NULL_PLUGIN, unused -> {
+ try {
+ update();
+ }catch (Exception e){
+ logger.error(e.getLocalizedMessage());
+ }
+ }, 1, MembarConfig.updateInterval);
+ }
+
+
+ public static void cancelBarUpdateTask(){
+ if (scannerTask == null || scannerTask.isCancelled()){
+ return;
+ }
+
+ scannerTask.cancel();
+
+ for (ScheduledTask task : scheduledTasks.values()) {
+ if (!task.isCancelled()) {
+ task.cancel();
+ }
+ }
+ }
+
+ public static boolean isPlayerVisible(Player player){
+ return ((CraftPlayer) player).getHandle().isMemBarVisible;
+ }
+
+ public static void setVisibilityForPlayer(Player target,boolean canSee){
+ ((CraftPlayer) target).getHandle().isMemBarVisible = canSee;
+ }
+
+ private static void update(){
+ doUpdate();
+ cleanUp();
+ }
+
+ private static void cleanUp(){
+ final List<UUID> toCleanUp = new ArrayList<>();
+
+ for (Map.Entry<UUID, ScheduledTask> toCheck : scheduledTasks.entrySet()) {
+ if (toCheck.getValue().isCancelled()) {
+ toCleanUp.add(toCheck.getKey());
+ }
+ }
+
+ for (UUID uuid : toCleanUp) {
+ scheduledTasks.remove(uuid);
+ }
+ }
+
+ private static void doUpdate(){
+ for (Player player : Bukkit.getOnlinePlayers()) {
+ scheduledTasks.computeIfAbsent(player.getUniqueId(), unused -> createBossBarForPlayer(player));
+ }
+ }
+
+ private static ScheduledTask createBossBarForPlayer(Player apiPlayer) {
+ return apiPlayer.getScheduler().runAtFixedRate(NULL_PLUGIN, (unused) -> {
+ final UUID playerUUID = apiPlayer.getUniqueId();
+
+ if (!isPlayerVisible(apiPlayer)) {
+ final BossBar removed = uuid2Bossbars.remove(playerUUID);
+
+ if (removed != null) {
+ apiPlayer.hideBossBar(removed);
+ }
+
+ return;
+ }
+
+ MemoryUsage heap = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
+
+ long used = heap.getUsed();
+ long xmx = heap.getMax();
+
+ BossBar targetBossbar = uuid2Bossbars.computeIfAbsent(
+ playerUUID,
+ (unused1) -> BossBar.bossBar(Component.text(""),0.0F, BossBar.Color.valueOf(MembarConfig.memColors.get(3)), BossBar.Overlay.NOTCHED_20)
+ );
+
+ apiPlayer.showBossBar(targetBossbar);
+
+ updateMembar(targetBossbar, used, xmx);
+ }, () -> {
+ final BossBar removed = uuid2Bossbars.remove(apiPlayer.getUniqueId());
+
+ if (removed != null) {
+ apiPlayer.hideBossBar(removed);
+ }
+ }, 1, MembarConfig.updateInterval);
+ }
+
+ private static void updateMembar(@NotNull BossBar bar, long used, long xmx){
+ double percent = Math.max(Math.min((float) used / xmx, 1.0F), 0.0F);
+ bar.name(MiniMessage.miniMessage().deserialize(
+ MembarConfig.memBarFormat,
+ Placeholder.component("used", getMemoryComponent(used,xmx)),
+ Placeholder.component("available",getMaxMemComponent(xmx))
+ ));
+ bar.color(barColorFromMemory(percent));
+ bar.progress((float) percent);
+ }
+
+ private static @NotNull Component getMaxMemComponent(double max){
+ final BossBar.Color colorBukkit = BossBar.Color.GREEN;
+ final String colorString = colorBukkit.name();
+
+ final String content = "<%s><text></%s>";
+ final String replaced = String.format(content,colorString,colorString);
+
+ return MiniMessage.miniMessage().deserialize(replaced,Placeholder.parsed("text", String.format("%.2f", max / (1024 * 1024))));
+ }
+
+ private static @NotNull Component getMemoryComponent(long used,long max){
+ final BossBar.Color colorBukkit = barColorFromMemory(Math.max(Math.min((float) used / max, 1.0F), 0.0F));
+ final String colorString = colorBukkit.name();
+
+ final String content = "<%s><text></%s>";
+ final String replaced = String.format(content,colorString,colorString);
+
+ return MiniMessage.miniMessage().deserialize(replaced,Placeholder.parsed("text", String.format("%.2f", (double)used / (1024 * 1024))));
+ }
+
+ private static BossBar.Color barColorFromMemory(double memPercent){
+ if (memPercent == -1){
+ return BossBar.Color.valueOf(MembarConfig.memColors.get(3));
+ }
+
+ if (memPercent <= 50){
+ return BossBar.Color.valueOf(MembarConfig.memColors.getFirst());
+ }
+
+ if (memPercent <= 70){
+ return BossBar.Color.valueOf(MembarConfig.memColors.get(1));
+ }
+
+ return BossBar.Color.valueOf(MembarConfig.memColors.get(2));
+ }
+}
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
index 279bc6eede3a1ae77b810f0553efe36adeeefaa4..4ff5105d32c41a8ea145e7833ea6a3cee0108031 100644
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
+++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
@@ -803,6 +803,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface
@Override
public void stopServer() {
me.earthme.luminol.functions.GlobalServerTpsBar.cancelBarUpdateTask(); //Luminol - Tpsbar
+ me.earthme.luminol.functions.GlobalServerMemoryBar.cancelBarUpdateTask(); //Luminol - Memory bar
super.stopServer();
//Util.shutdownExecutors(); // Paper - moved into super
SkullBlockEntity.clear();
diff --git a/src/main/java/net/minecraft/server/level/ServerPlayer.java b/src/main/java/net/minecraft/server/level/ServerPlayer.java
index 0752d13febc5f1831ae58f7216fba89e1290c780..93c038ba1fd216fd11ab8b5cec5807453f34e152 100644
--- a/src/main/java/net/minecraft/server/level/ServerPlayer.java
+++ b/src/main/java/net/minecraft/server/level/ServerPlayer.java
@@ -328,6 +328,7 @@ public class ServerPlayer extends net.minecraft.world.entity.player.Player imple
public @Nullable String clientBrandName = null; // Paper - Brand support
public org.bukkit.event.player.PlayerQuitEvent.QuitReason quitReason = null; // Paper - Add API for quit reason; there are a lot of changes to do if we change all methods leading to the event
public volatile boolean isTpsBarVisible = false; //Luminol - Tps bar
+ public volatile boolean isMemBarVisible = false; //Luminol - Memory bar
// Paper start - rewrite chunk system
private ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.PlayerChunkLoaderData chunkLoader;
private final ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder viewDistanceHolder = new ca.spottedleaf.moonrise.patches.chunk_system.player.RegionizedPlayerChunkLoader.ViewDistanceHolder();
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index c39e9ea1ce46864623a6d15027ce337611e6b712..1872b19dfefb40e6673fd2b8ddb8d3c384020073 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -2417,6 +2417,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
//Luminol start - Tpsbar
getHandle().isTpsBarVisible = data.getBoolean("tpsbarVisible");
//Luminol end
+ //Luminol start - Membar
+ getHandle().isMemBarVisible = data.getBoolean("membarVisible");
+ //Luminol end
}
}
@@ -2441,6 +2444,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
//Luminol start - Tpsbar
data.putBoolean("tpsbarVisible",handle.isTpsBarVisible);
//Luminol end
+ //Luminol start - Membar
+ data.putBoolean("membarVisible", handle.isMemBarVisible);
+ //Luminol end
// Paper start - persist for use in offline save data
if (!nbttagcompound.contains("Paper")) {
nbttagcompound.put("Paper", new CompoundTag());