diff --git a/luminol-server/minecraft-patches/features/0007-Add-a-simple-regionbar.patch b/luminol-server/minecraft-patches/features/0007-Add-a-simple-regionbar.patch new file mode 100644 index 0000000..2510007 --- /dev/null +++ b/luminol-server/minecraft-patches/features/0007-Add-a-simple-regionbar.patch @@ -0,0 +1,30 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: adabugra <57899270+adabugra@users.noreply.github.com> +Date: Fri, 31 Jan 2025 01:21:44 +0300 +Subject: [PATCH] Add a simple regionbar + + +diff --git a/net/minecraft/server/dedicated/DedicatedServer.java b/net/minecraft/server/dedicated/DedicatedServer.java +index cc80198a5d5f4e9188ef35944d077200f03ac43b..c6490516215ad94323174de81771f258cecc7742 100644 +--- a/net/minecraft/server/dedicated/DedicatedServer.java ++++ b/net/minecraft/server/dedicated/DedicatedServer.java +@@ -766,6 +766,7 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface + public void stopServer() { + me.earthme.luminol.functions.GlobalServerTpsBar.cancelBarUpdateTask(); //Luminol - Tpsbar + me.earthme.luminol.functions.GlobalServerMemoryBar.cancelBarUpdateTask(); //Luminol - Memory bar ++ me.earthme.luminol.functions.GlobalServerRegionBar.cancelBarUpdateTask(); //Luminol - Region bar + super.stopServer(); + //Util.shutdownExecutors(); // Paper - Improved watchdog support; moved into super + SkullBlockEntity.clear(); +diff --git a/net/minecraft/server/level/ServerPlayer.java b/net/minecraft/server/level/ServerPlayer.java +index a07e02132bdda2693686440b9932992641cb6957..f1239f1a7b6b8fd958333df8092359eea7197fc0 100644 +--- a/net/minecraft/server/level/ServerPlayer.java ++++ b/net/minecraft/server/level/ServerPlayer.java +@@ -395,6 +395,7 @@ public class ServerPlayer extends Player implements ca.spottedleaf.moonrise.patc + 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 ++ public volatile boolean isRegionBarVisible = false; //Luminol - Region 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/luminol-server/paper-patches/features/0008-Add-a-simple-regionbar.patch b/luminol-server/paper-patches/features/0008-Add-a-simple-regionbar.patch new file mode 100644 index 0000000..11b46a3 --- /dev/null +++ b/luminol-server/paper-patches/features/0008-Add-a-simple-regionbar.patch @@ -0,0 +1,330 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: adabugra <57899270+adabugra@users.noreply.github.com> +Date: Fri, 31 Jan 2025 01:21:44 +0300 +Subject: [PATCH] Add a simple regionbar + + +diff --git a/src/main/java/me/earthme/luminol/commands/RegionBarCommand.java b/src/main/java/me/earthme/luminol/commands/RegionBarCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..68618b0364b7da6018888b06db165591da951e57 +--- /dev/null ++++ b/src/main/java/me/earthme/luminol/commands/RegionBarCommand.java +@@ -0,0 +1,47 @@ ++package me.earthme.luminol.commands; ++ ++import me.earthme.luminol.config.modules.misc.RegionBarConfig; ++import me.earthme.luminol.functions.GlobalServerRegionBar; ++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 RegionBarCommand extends Command { ++ public RegionBarCommand(@NotNull String name) { ++ super(name); ++ this.setPermission("luminol.commands.regionbar"); ++ this.setDescription("Show info about your current region through a bossbar"); ++ this.setUsage("/regionbar"); ++ } ++ ++ @Override ++ public boolean execute(@NotNull CommandSender sender, @NotNull String commandLabel, @NotNull String[] args) { ++ if (!testPermission(sender)) { ++ return true; ++ } ++ ++ if (!RegionBarConfig.regionbarEnabled) { ++ sender.sendMessage(Component.text("Regionbar 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 (GlobalServerRegionBar.isPlayerVisible(player)) { ++ player.sendMessage(Component.text("Disabled region bar").color(TextColor.color(0, 255, 0))); ++ GlobalServerRegionBar.setVisibilityForPlayer(player, false); ++ return true; ++ } ++ ++ player.sendMessage(Component.text("Enabled region bar").color(TextColor.color(0, 255, 0))); ++ GlobalServerRegionBar.setVisibilityForPlayer(player, true); ++ ++ return true; ++ } ++} +\ No newline at end of file +diff --git a/src/main/java/me/earthme/luminol/config/modules/misc/RegionBarConfig.java b/src/main/java/me/earthme/luminol/config/modules/misc/RegionBarConfig.java +new file mode 100644 +index 0000000000000000000000000000000000000000..34ebfe9e22623985bdc33c3f475f709b030c50e9 +--- /dev/null ++++ b/src/main/java/me/earthme/luminol/config/modules/misc/RegionBarConfig.java +@@ -0,0 +1,50 @@ ++package me.earthme.luminol.config.modules.misc; ++ ++import com.electronwill.nightconfig.core.file.CommentedFileConfig; ++import me.earthme.luminol.commands.RegionBarCommand; ++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.GlobalServerRegionBar; ++import org.bukkit.Bukkit; ++ ++import java.util.List; ++ ++public class RegionBarConfig implements IConfigModule { ++ @ConfigInfo(baseName = "enabled") ++ public static boolean regionbarEnabled = false; ++ @ConfigInfo(baseName = "format") ++ public static String regionBarFormat = "Util: Chunks: Players: Entities: "; ++ @ConfigInfo(baseName = "util_color_list") ++ public static List utilColors = 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 "regionbar"; ++ } ++ ++ @Override ++ public void onLoaded(CommentedFileConfig configInstance) { ++ if (regionbarEnabled) { ++ GlobalServerRegionBar.init(); ++ } else { ++ GlobalServerRegionBar.cancelBarUpdateTask(); ++ } ++ ++ if (!inited) { ++ Bukkit.getCommandMap().register("regionbar", "luminol", new RegionBarCommand("regionbar")); ++ inited = true; ++ } ++ } ++} +diff --git a/src/main/java/me/earthme/luminol/functions/GlobalServerRegionBar.java b/src/main/java/me/earthme/luminol/functions/GlobalServerRegionBar.java +new file mode 100644 +index 0000000000000000000000000000000000000000..ef9d69e73fbd1fea7d04e9df9d11dfd694836077 +--- /dev/null ++++ b/src/main/java/me/earthme/luminol/functions/GlobalServerRegionBar.java +@@ -0,0 +1,184 @@ ++package me.earthme.luminol.functions; ++ ++import com.google.common.collect.Maps; ++import com.mojang.logging.LogUtils; ++import io.papermc.paper.threadedregions.*; ++import io.papermc.paper.threadedregions.scheduler.ScheduledTask; ++import me.earthme.luminol.config.modules.misc.RegionBarConfig; ++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.text.DecimalFormat; ++import java.util.*; ++ ++public class GlobalServerRegionBar { ++ protected static final NullPlugin NULL_PLUGIN = new NullPlugin(); ++ protected static final Map uuid2Bossbars = Maps.newConcurrentMap(); ++ protected static final Map scheduledTasks = new HashMap<>(); ++ ++ protected static volatile ScheduledTask scannerTask = null; ++ private static final Logger logger = LogUtils.getLogger(); ++ ++ private static final ThreadLocal ONE_DECIMAL_PLACES = ThreadLocal.withInitial(() -> new DecimalFormat("#,##0.0")); ++ ++ public static void init() { ++ cancelBarUpdateTask(); ++ ++ scannerTask = Bukkit.getGlobalRegionScheduler().runAtFixedRate(NULL_PLUGIN, unused -> { ++ try { ++ update(); ++ cleanUp(); ++ } catch (Exception e) { ++ logger.error(e.getLocalizedMessage()); ++ } ++ }, 1, RegionBarConfig.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().isRegionBarVisible; ++ } ++ ++ public static void setVisibilityForPlayer(Player target, boolean canSee) { ++ ((CraftPlayer) target).getHandle().isRegionBarVisible = canSee; ++ } ++ ++ private static void update() { ++ for (Player player : Bukkit.getOnlinePlayers()) { ++ scheduledTasks.computeIfAbsent(player.getUniqueId(), unused -> createBossBarForPlayer(player)); ++ } ++ } ++ ++ private static void cleanUp() { ++ final List toCleanUp = new ArrayList<>(); ++ ++ for (Map.Entry toCheck : scheduledTasks.entrySet()) { ++ if (toCheck.getValue().isCancelled()) { ++ toCleanUp.add(toCheck.getKey()); ++ } ++ } ++ ++ for (UUID uuid : toCleanUp) { ++ scheduledTasks.remove(uuid); ++ } ++ } ++ ++ public static ScheduledTask createBossBarForPlayer(@NotNull Player apiPlayer) { ++ final UUID playerUUID = apiPlayer.getUniqueId(); ++ ++ return apiPlayer.getScheduler().runAtFixedRate(NULL_PLUGIN, (n) -> { ++ if (!isPlayerVisible(apiPlayer)) { ++ final BossBar removed = uuid2Bossbars.remove(playerUUID); ++ ++ if (removed != null) { ++ apiPlayer.hideBossBar(removed); ++ } ++ return; ++ } ++ ++ final ThreadedRegionizer.ThreadedRegion region = TickRegionScheduler.getCurrentRegion(); ++ final TickData.TickReportData reportData = region.getData().getRegionSchedulingHandle().getTickReport5s(System.nanoTime()); ++ final TickRegions.RegionStats regionStats = region.getData().getRegionStats(); ++ ++ BossBar targetBossbar = uuid2Bossbars.computeIfAbsent( ++ playerUUID, ++ unused -> BossBar.bossBar(Component.text(""), 0.0F, BossBar.Color.GREEN, BossBar.Overlay.NOTCHED_20) ++ ); ++ ++ apiPlayer.showBossBar(targetBossbar); ++ ++ if (reportData != null) { ++ final double utilisation = reportData.utilisation(); ++ final int chunkCount = regionStats.getChunkCount(); ++ final int playerCount = regionStats.getPlayerCount(); ++ final int entityCount = regionStats.getEntityCount(); ++ ++ updateRegionBar(utilisation, chunkCount, playerCount, entityCount, targetBossbar); ++ } ++ }, () -> { ++ final BossBar removed = uuid2Bossbars.remove(playerUUID); // Auto clean up it ++ ++ if (removed != null) { ++ apiPlayer.hideBossBar(removed); ++ } ++ }, 1, RegionBarConfig.updateInterval); ++ } ++ ++ private static void updateRegionBar(double utilisation, int chunks, int players, int entities, @NotNull BossBar bar) { ++ final double utilisationPercent = utilisation * 100.0; ++ final String formattedUtil = ONE_DECIMAL_PLACES.get().format(utilisationPercent); ++ ++ bar.name(MiniMessage.miniMessage().deserialize( ++ RegionBarConfig.regionBarFormat, ++ Placeholder.component("util", getUtilComponent(formattedUtil)), ++ Placeholder.component("chunks", getChunksComponent(chunks)), ++ Placeholder.component("players", getPlayersComponent(players)), ++ Placeholder.component("entities", getEntitiesComponent(entities)) ++ )); ++ ++ bar.color(barColorFromUtil(utilisationPercent)); ++ bar.progress((float) Math.min(1.0, Math.max(utilisation, 0))); ++ } ++ ++ private static @NotNull Component getEntitiesComponent(int entities) { ++ final String content = ""; ++ return MiniMessage.miniMessage().deserialize(content, Placeholder.parsed("text", String.valueOf(entities))); ++ } ++ ++ private static @NotNull Component getPlayersComponent(int players) { ++ final String content = ""; ++ return MiniMessage.miniMessage().deserialize(content, Placeholder.parsed("text", String.valueOf(players))); ++ } ++ ++ private static @NotNull Component getChunksComponent(int chunks) { ++ final String content = ""; ++ return MiniMessage.miniMessage().deserialize(content, Placeholder.parsed("text", String.valueOf(chunks))); ++ } ++ ++ private static @NotNull Component getUtilComponent(String formattedUtil) { ++ final BossBar.Color colorBukkit = barColorFromUtil(Double.parseDouble(formattedUtil)); ++ final String colorString = colorBukkit.name(); ++ ++ final String content = "<%s>"; ++ final String replaced = String.format(content, colorString, colorString); ++ ++ return MiniMessage.miniMessage().deserialize(replaced, Placeholder.parsed("text", formattedUtil + "%")); ++ } ++ ++ private static BossBar.Color barColorFromUtil(double util) { ++ if (util >= 100) { ++ return BossBar.Color.valueOf(RegionBarConfig.utilColors.get(3)); // PURPLE ++ } ++ ++ if (util >= 70) { ++ return BossBar.Color.valueOf(RegionBarConfig.utilColors.get(2)); // RED ++ } ++ ++ if (util >= 50) { ++ return BossBar.Color.valueOf(RegionBarConfig.utilColors.get(1)); // YELLOW ++ } ++ ++ return BossBar.Color.valueOf(RegionBarConfig.utilColors.get(0)); // GREEN ++ } ++} +diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +index 7a112a3a0a9411a166666c657dac6b7888105545..03a9f4d8e83f53326c30f00286efd1ddaaac84d2 100644 +--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java ++++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java +@@ -2409,6 +2409,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + //Luminol start - Membar + getHandle().isMemBarVisible = data.getBoolean("membarVisible"); + //Luminol end ++ //Luminol start - Regionbar ++ getHandle().isRegionBarVisible = data.getBoolean("regionbarVisible"); ++ //Luminol end + } + } + +@@ -2436,6 +2439,9 @@ public class CraftPlayer extends CraftHumanEntity implements Player { + //Luminol start - Membar + data.putBoolean("membarVisible", handle.isMemBarVisible); + //Luminol end ++ //Luminol start - Regionbar ++ data.putBoolean("regionbarVisible", handle.isRegionBarVisible); ++ //Luminol end + // Paper start - persist for use in offline save data + if (!nbttagcompound.contains("Paper")) { + nbttagcompound.put("Paper", new CompoundTag());