From f65eead418a5eafb6ea119509ce78680c1cc76ac Mon Sep 17 00:00:00 2001 From: Jake Potrebic Date: Thu, 2 Jun 2022 21:26:56 -0700 Subject: [PATCH] Remove ChatColor usages (#7543) --- ...eload-permissions.yml-and-require-co.patch | 6 +- patches/api/Adventure.patch | 37 +++ .../Allow-Reloading-of-Command-Aliases.patch | 6 +- patches/api/Timings-v2.patch | 23 +- .../Add-debug-for-sync-chunk-loads.patch | 17 +- ...aper-mobcaps-and-paper-playermobcaps.patch | 3 + .../Add-tick-times-API-and-mspt-command.patch | 49 ++- patches/server/Chunk-debug-command.patch | 48 ++- patches/server/Fix-Light-Command.patch | 4 +- ...ault-permission-message-configurable.patch | 2 +- patches/server/Paper-command.patch | 301 ++++++++++++++++++ patches/server/Paper-config-files.patch | 291 ----------------- patches/server/Paper-dumpitem-command.patch | 4 +- patches/server/Rewrite-the-light-engine.patch | 18 +- patches/server/Timings-v2.patch | 25 +- 15 files changed, 472 insertions(+), 362 deletions(-) create mode 100644 patches/server/Paper-command.patch diff --git a/patches/api/Add-command-to-reload-permissions.yml-and-require-co.patch b/patches/api/Add-command-to-reload-permissions.yml-and-require-co.patch index d3103ec730..d74beb5762 100644 --- a/patches/api/Add-command-to-reload-permissions.yml-and-require-co.patch +++ b/patches/api/Add-command-to-reload-permissions.yml-and-require-co.patch @@ -58,17 +58,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + if (args.length == 1) { + if (args[0].equalsIgnoreCase("permissions")) { + Bukkit.getServer().reloadPermissions(); -+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Permissions successfully reloaded."); ++ Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("Permissions successfully reloaded.", net.kyori.adventure.text.format.NamedTextColor.GREEN)); + return true; + } else if ("confirm".equalsIgnoreCase(args[0])) { + confirmed = true; + } else { -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Usage: " + usageMessage); ++ Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("Usage: " + usageMessage, net.kyori.adventure.text.format.NamedTextColor.RED)); + return true; + } + } + if (!confirmed) { -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Are you sure you wish to reload your server? Doing so may cause bugs and memory leaks. It is recommended to restart instead of using /reload. To confirm, please type " + ChatColor.YELLOW + "/reload confirm"); ++ Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("Are you sure you wish to reload your server? Doing so may cause bugs and memory leaks. It is recommended to restart instead of using /reload. To confirm, please type ", net.kyori.adventure.text.format.NamedTextColor.RED).append(net.kyori.adventure.text.Component.text("/reload confirm", net.kyori.adventure.text.format.NamedTextColor.YELLOW))); + return true; + } + // Paper end diff --git a/patches/api/Adventure.patch b/patches/api/Adventure.patch index 1ec4c6c86f..3e056cf4c4 100644 --- a/patches/api/Adventure.patch +++ b/patches/api/Adventure.patch @@ -1366,6 +1366,43 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 public static void broadcastCommandMessage(@NotNull CommandSender source, @NotNull String message) { broadcastCommandMessage(source, message, true); + } + + public static void broadcastCommandMessage(@NotNull CommandSender source, @NotNull String message, boolean sendToSource) { +- String result = source.getName() + ": " + message; ++ // Paper start ++ broadcastCommandMessage(source, net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection().deserialize(message), sendToSource); ++ } ++ ++ public static void broadcastCommandMessage(@NotNull CommandSender source, @NotNull net.kyori.adventure.text.Component message) { ++ broadcastCommandMessage(source, message, true); ++ } ++ ++ public static void broadcastCommandMessage(@NotNull CommandSender source, @NotNull net.kyori.adventure.text.Component message, boolean sendToSource) { ++ net.kyori.adventure.text.TextComponent.Builder result = net.kyori.adventure.text.Component.text() ++ .color(net.kyori.adventure.text.format.NamedTextColor.WHITE) ++ .decoration(net.kyori.adventure.text.format.TextDecoration.ITALIC, false) ++ .append(source.name()) ++ .append(net.kyori.adventure.text.Component.text(": ")) ++ .append(message); ++ // Paper end + + if (source instanceof BlockCommandSender) { + BlockCommandSender blockCommandSender = (BlockCommandSender) source; +@@ -0,0 +0,0 @@ public abstract class Command { + } + + Set users = Bukkit.getPluginManager().getPermissionSubscriptions(Server.BROADCAST_CHANNEL_ADMINISTRATIVE); +- String colored = ChatColor.GRAY + "" + ChatColor.ITALIC + "[" + result + ChatColor.GRAY + ChatColor.ITALIC + "]"; ++ // Paper start ++ net.kyori.adventure.text.TextComponent.Builder colored = net.kyori.adventure.text.Component.text() ++ .color(net.kyori.adventure.text.format.NamedTextColor.GRAY) ++ .decorate(net.kyori.adventure.text.format.TextDecoration.ITALIC) ++ .append(net.kyori.adventure.text.Component.text("["), result, net.kyori.adventure.text.Component.text("]")); ++ // Paper end + + if (sendToSource && !(source instanceof ConsoleCommandSender)) { + source.sendMessage(message); diff --git a/src/main/java/org/bukkit/command/CommandSender.java b/src/main/java/org/bukkit/command/CommandSender.java index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644 --- a/src/main/java/org/bukkit/command/CommandSender.java diff --git a/patches/api/Allow-Reloading-of-Command-Aliases.patch b/patches/api/Allow-Reloading-of-Command-Aliases.patch index 00b1aaf938..611915c39d 100644 --- a/patches/api/Allow-Reloading-of-Command-Aliases.patch +++ b/patches/api/Allow-Reloading-of-Command-Aliases.patch @@ -86,13 +86,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 } @@ -0,0 +0,0 @@ public class ReloadCommand extends BukkitCommand { Bukkit.getServer().reloadPermissions(); - Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Permissions successfully reloaded."); + Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("Permissions successfully reloaded.", net.kyori.adventure.text.format.NamedTextColor.GREEN)); return true; + } else if ("commands".equalsIgnoreCase(args[0])) { + if (Bukkit.getServer().reloadCommandAliases()) { -+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Command aliases successfully reloaded."); ++ Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("Command aliases successfully reloaded.", net.kyori.adventure.text.format.NamedTextColor.GREEN)); + } else { -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "An error occurred while trying to reload command aliases."); ++ Command.broadcastCommandMessage(sender, net.kyori.adventure.text.Component.text("An error occurred while trying to reload command aliases.", net.kyori.adventure.text.format.NamedTextColor.RED)); + } + return true; } else if ("confirm".equalsIgnoreCase(args[0])) { diff --git a/patches/api/Timings-v2.patch b/patches/api/Timings-v2.patch index 40c0d8cff5..c07f4c66ae 100644 --- a/patches/api/Timings-v2.patch +++ b/patches/api/Timings-v2.patch @@ -1589,8 +1589,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +package co.aikar.timings; + +import com.google.common.collect.ImmutableList; ++import net.kyori.adventure.text.format.NamedTextColor; +import org.apache.commons.lang.Validate; -+import org.bukkit.ChatColor; +import org.bukkit.command.CommandSender; +import org.bukkit.command.defaults.BukkitCommand; +import org.bukkit.util.StringUtil; @@ -1598,7 +1598,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import java.util.ArrayList; +import java.util.List; +import org.jetbrains.annotations.NotNull; -+import org.jetbrains.annotations.Nullable; ++ ++import static net.kyori.adventure.text.Component.text; + + +public class TimingsCommand extends BukkitCommand { @@ -1618,41 +1619,41 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return true; + } + if (args.length < 1) { -+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); ++ sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED)); + return true; + } + final String arg = args[0]; + if ("on".equalsIgnoreCase(arg)) { + Timings.setTimingsEnabled(true); -+ sender.sendMessage("Enabled Timings & Reset"); ++ sender.sendMessage(text("Enabled Timings & Reset")); + return true; + } else if ("off".equalsIgnoreCase(arg)) { + Timings.setTimingsEnabled(false); -+ sender.sendMessage("Disabled Timings"); ++ sender.sendMessage(text("Disabled Timings")); + return true; + } + + if (!Timings.isTimingsEnabled()) { -+ sender.sendMessage("Please enable timings by typing /timings on"); ++ sender.sendMessage(text("Please enable timings by typing /timings on")); + return true; + } + + long now = System.currentTimeMillis(); + if ("verbon".equalsIgnoreCase(arg)) { + Timings.setVerboseTimingsEnabled(true); -+ sender.sendMessage("Enabled Verbose Timings"); ++ sender.sendMessage(text("Enabled Verbose Timings")); + return true; + } else if ("verboff".equalsIgnoreCase(arg)) { + Timings.setVerboseTimingsEnabled(false); -+ sender.sendMessage("Disabled Verbose Timings"); ++ sender.sendMessage(text("Disabled Verbose Timings")); + return true; + } else if ("reset".equalsIgnoreCase(arg)) { + if (now - lastResetAttempt < 30000) { + TimingsManager.reset(); -+ sender.sendMessage(ChatColor.RED + "Timings reset. Please wait 5-10 minutes before using /timings report."); ++ sender.sendMessage(text("Timings reset. Please wait 5-10 minutes before using /timings report.", NamedTextColor.RED)); + } else { + lastResetAttempt = now; -+ sender.sendMessage(ChatColor.RED + "WARNING: Timings v2 should not be reset. If you are encountering lag, please wait 3 minutes and then issue a report. The best timings will include 10+ minutes, with data before and after your lag period. If you really want to reset, run this command again within 30 seconds."); ++ sender.sendMessage(text("WARNING: Timings v2 should not be reset. If you are experiencing lag, please wait 3 minutes and then issue a report. The best timings will include 10+ minutes, with data before and after your lag period. If you really want to reset, run this command again within 30 seconds.", NamedTextColor.RED)); + } + } else if ( + "paste".equalsIgnoreCase(arg) || @@ -1663,7 +1664,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + ) { + Timings.generateReport(sender); + } else { -+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); ++ sender.sendMessage(text("Usage: " + this.usageMessage, NamedTextColor.RED)); + } + return true; + } diff --git a/patches/server/Add-debug-for-sync-chunk-loads.patch b/patches/server/Add-debug-for-sync-chunk-loads.patch index 3c3ff8ee17..fd952601fd 100644 --- a/patches/server/Add-debug-for-sync-chunk-loads.patch +++ b/patches/server/Add-debug-for-sync-chunk-loads.patch @@ -45,6 +45,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 import java.time.format.DateTimeFormatter; import java.util.ArrayDeque; @@ -0,0 +0,0 @@ import java.util.stream.Collectors; + import static net.kyori.adventure.text.Component.text; + import static net.kyori.adventure.text.format.NamedTextColor.BLUE; + import static net.kyori.adventure.text.format.NamedTextColor.DARK_AQUA; ++import static net.kyori.adventure.text.format.NamedTextColor.GRAY; + import static net.kyori.adventure.text.format.NamedTextColor.GREEN; + import static net.kyori.adventure.text.format.NamedTextColor.RED; + import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; public class PaperCommand extends Command { private static final String BASE_PERM = "bukkit.command.paper."; @@ -81,20 +88,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + private void doSyncLoadInfo(CommandSender sender, String[] args) { + if (!SyncLoadFinder.ENABLED) { -+ sender.sendMessage(ChatColor.RED + "This command requires the server startup flag '-Dpaper.debug-sync-loads=true' to be set."); ++ sender.sendMessage(text("This command requires the server startup flag '-Dpaper.debug-sync-loads=true' to be set.", RED)); + return; + } + + if (args.length > 1 && args[1].equals("clear")) { + SyncLoadFinder.clear(); -+ sender.sendMessage(ChatColor.GRAY + "Sync load data cleared."); ++ sender.sendMessage(text("Sync load data cleared.", GRAY)); + return; + } + + File file = new File(new File(new File("."), "debug"), + "sync-load-info" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + ".txt"); + file.getParentFile().mkdirs(); -+ sender.sendMessage(ChatColor.GREEN + "Writing sync load info to " + file.toString()); ++ sender.sendMessage(text("Writing sync load info to " + file, GREEN)); + + + try { @@ -113,9 +120,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + ) { + out.print(fileData); + } -+ sender.sendMessage(ChatColor.GREEN + "Successfully written sync load information!"); ++ sender.sendMessage(text("Successfully written sync load information!", GREEN)); + } catch (Throwable thr) { -+ sender.sendMessage(ChatColor.RED + "Failed to write sync load information"); ++ sender.sendMessage(text("Failed to write sync load information!", RED)); + thr.printStackTrace(); + } + } diff --git a/patches/server/Add-paper-mobcaps-and-paper-playermobcaps.patch b/patches/server/Add-paper-mobcaps-and-paper-playermobcaps.patch index 70a238a878..b3788c40ee 100644 --- a/patches/server/Add-paper-mobcaps-and-paper-playermobcaps.patch +++ b/patches/server/Add-paper-mobcaps-and-paper-playermobcaps.patch @@ -55,6 +55,9 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import java.util.function.ToIntFunction; import java.util.stream.Collectors; + import static net.kyori.adventure.text.Component.text; +@@ -0,0 +0,0 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; + public class PaperCommand extends Command { private static final String BASE_PERM = "bukkit.command.paper."; - private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "syncloadinfo", "dumpitem").build(); diff --git a/patches/server/Add-tick-times-API-and-mspt-command.patch b/patches/server/Add-tick-times-API-and-mspt-command.patch index ca1c84bf80..068cffed4c 100644 --- a/patches/server/Add-tick-times-API-and-mspt-command.patch +++ b/patches/server/Add-tick-times-API-and-mspt-command.patch @@ -12,8 +12,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 @@ -0,0 +0,0 @@ +package com.destroystokyo.paper; + ++import net.kyori.adventure.text.Component; +import net.minecraft.server.MinecraftServer; -+import org.bukkit.ChatColor; +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; @@ -24,8 +24,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import java.util.Collections; +import java.util.List; + ++import static net.kyori.adventure.text.Component.text; ++import static net.kyori.adventure.text.format.NamedTextColor.GOLD; ++import static net.kyori.adventure.text.format.NamedTextColor.GRAY; ++import static net.kyori.adventure.text.format.NamedTextColor.GREEN; ++import static net.kyori.adventure.text.format.NamedTextColor.RED; ++import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; ++ +public class MSPTCommand extends Command { + private static final DecimalFormat DF = new DecimalFormat("########0.0"); ++ private static final Component SLASH = text("/"); + + public MSPTCommand(String name) { + super(name); @@ -45,17 +53,44 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + + MinecraftServer server = MinecraftServer.getServer(); + -+ List times = new ArrayList<>(); ++ List times = new ArrayList<>(); + times.addAll(eval(server.tickTimes5s.getTimes())); + times.addAll(eval(server.tickTimes10s.getTimes())); + times.addAll(eval(server.tickTimes60s.getTimes())); + -+ sender.sendMessage("§6Server tick times §e(§7avg§e/§7min§e/§7max§e)§6 from last 5s§7,§6 10s§7,§6 1m§e:"); -+ sender.sendMessage(String.format("§6◴ %s§7/%s§7/%s§e, %s§7/%s§7/%s§e, %s§7/%s§7/%s", times.toArray())); ++ sender.sendMessage(text().content("Server tick times ").color(GOLD) ++ .append(text().color(YELLOW) ++ .append( ++ text("("), ++ text("avg", GRAY), ++ text("/"), ++ text("min", GRAY), ++ text("/"), ++ text("max", GRAY), ++ text(")") ++ ) ++ ).append( ++ text(" from last 5s"), ++ text(",", GRAY), ++ text(" 10s"), ++ text(",", GRAY), ++ text(" 1m"), ++ text(":", YELLOW) ++ ) ++ ); ++ sender.sendMessage(text().content("◴ ").color(GOLD) ++ .append(text().color(GRAY) ++ .append( ++ times.get(0), SLASH, times.get(1), SLASH, times.get(2), text(", ", YELLOW), ++ times.get(3), SLASH, times.get(4), SLASH, times.get(5), text(", ", YELLOW), ++ times.get(6), SLASH, times.get(7), SLASH, times.get(8) ++ ) ++ ) ++ ); + return true; + } + -+ private static List eval(long[] times) { ++ private static List eval(long[] times) { + long min = Integer.MAX_VALUE; + long max = 0L; + long total = 0L; @@ -70,8 +105,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return Arrays.asList(getColor(avgD), getColor(minD), getColor(maxD)); + } + -+ private static String getColor(double avg) { -+ return ChatColor.COLOR_CHAR + (avg >= 50 ? "c" : avg >= 40 ? "e" : "a") + DF.format(avg); ++ private static Component getColor(double avg) { ++ return text(DF.format(avg), avg >= 50 ? RED : avg >= 40 ? YELLOW : GREEN); + } +} diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java diff --git a/patches/server/Chunk-debug-command.patch b/patches/server/Chunk-debug-command.patch index 33319a737e..b97361140b 100644 --- a/patches/server/Chunk-debug-command.patch +++ b/patches/server/Chunk-debug-command.patch @@ -52,7 +52,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.Pair; import org.bukkit.Bukkit; -@@ -0,0 +0,0 @@ import java.util.stream.Collectors; +@@ -0,0 +0,0 @@ import java.util.Set; + import java.util.stream.Collectors; + + import static net.kyori.adventure.text.Component.text; ++import static net.kyori.adventure.text.format.NamedTextColor.BLUE; ++import static net.kyori.adventure.text.format.NamedTextColor.DARK_AQUA; + import static net.kyori.adventure.text.format.NamedTextColor.GREEN; + import static net.kyori.adventure.text.format.NamedTextColor.RED; + import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; public class PaperCommand extends Command { private static final String BASE_PERM = "bukkit.command.paper."; @@ -109,7 +117,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + for (int i = 1; i < args.length; ++i) { + org.bukkit.World world = Bukkit.getWorld(args[i]); + if (world == null) { -+ sender.sendMessage(ChatColor.RED + "World '" + args[i] + "' is invalid"); ++ sender.sendMessage(text("World '" + args[i] + "' is invalid", RED)); + return; + } + worlds.add(world); @@ -162,22 +170,30 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + accumulatedTicking += ticking; + accumulatedEntityTicking += entityTicking; + -+ sender.sendMessage(ChatColor.BLUE + "Chunks in " + ChatColor.GREEN + bukkitWorld.getName() + ChatColor.DARK_AQUA + ":"); -+ sender.sendMessage(ChatColor.BLUE + "Total: " + ChatColor.DARK_AQUA + total + ChatColor.BLUE + " Inactive: " + ChatColor.DARK_AQUA -+ + inactive + ChatColor.BLUE + " Border: " + ChatColor.DARK_AQUA + border + ChatColor.BLUE + " Ticking: " -+ + ChatColor.DARK_AQUA + ticking + ChatColor.BLUE + " Entity: " + ChatColor.DARK_AQUA + entityTicking); ++ sender.sendMessage(text().append(text("Chunks in ", BLUE), text(bukkitWorld.getName(), GREEN), text(":"))); ++ sender.sendMessage(text().color(DARK_AQUA).append( ++ text("Total: ", BLUE), text(total), ++ text(" Inactive: ", BLUE), text(inactive), ++ text(" Border: ", BLUE), text(border), ++ text(" Ticking: ", BLUE), text(ticking), ++ text(" Entity: ", BLUE), text(entityTicking) ++ )); + } + if (worlds.size() > 1) { -+ sender.sendMessage(ChatColor.BLUE + "Chunks in " + ChatColor.GREEN + "all listed worlds" + ChatColor.DARK_AQUA + ":"); -+ sender.sendMessage(ChatColor.BLUE + "Total: " + ChatColor.DARK_AQUA + accumulatedTotal + ChatColor.BLUE + " Inactive: " + ChatColor.DARK_AQUA -+ + accumulatedInactive + ChatColor.BLUE + " Border: " + ChatColor.DARK_AQUA + accumulatedBorder + ChatColor.BLUE + " Ticking: " -+ + ChatColor.DARK_AQUA + accumulatedTicking + ChatColor.BLUE + " Entity: " + ChatColor.DARK_AQUA + accumulatedEntityTicking); ++ sender.sendMessage(text().append(text("Chunks in ", BLUE), text("all listed worlds", GREEN), text(":", DARK_AQUA))); ++ sender.sendMessage(text().color(DARK_AQUA).append( ++ text("Total: ", BLUE), text(accumulatedTotal), ++ text(" Inactive: ", BLUE), text(accumulatedInactive), ++ text(" Border: ", BLUE), text(accumulatedBorder), ++ text(" Ticking: ", BLUE), text(accumulatedTicking), ++ text(" Entity: ", BLUE), text(accumulatedEntityTicking) ++ )); + } + } + + private void doDebug(CommandSender sender, String[] args) { + if (args.length < 2) { -+ sender.sendMessage(ChatColor.RED + "Use /paper debug [chunks] help for more information on a specific command"); ++ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED)); + return; + } + @@ -185,25 +201,25 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + switch (debugType) { + case "chunks": + if (args.length >= 3 && args[2].toLowerCase(Locale.ENGLISH).equals("help")) { -+ sender.sendMessage(ChatColor.RED + "Use /paper debug chunks to dump loaded chunk information to a file"); ++ sender.sendMessage(text("Use /paper debug chunks [world] to dump loaded chunk information to a file", RED)); + break; + } + File file = new File(new File(new File("."), "debug"), + "chunks-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()) + ".txt"); -+ sender.sendMessage(ChatColor.GREEN + "Writing chunk information dump to " + file.toString()); ++ sender.sendMessage(text("Writing chunk information dump to " + file, GREEN)); + try { + MCUtil.dumpChunks(file); -+ sender.sendMessage(ChatColor.GREEN + "Successfully written chunk information!"); ++ sender.sendMessage(text("Successfully written chunk information!", GREEN)); + } catch (Throwable thr) { + MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr); -+ sender.sendMessage(ChatColor.RED + "Failed to dump chunk information, see console"); ++ sender.sendMessage(text("Failed to dump chunk information, see console", RED)); + } + + break; + case "help": + // fall through to default + default: -+ sender.sendMessage(ChatColor.RED + "Use /paper debug [chunks] help for more information on a specific command"); ++ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED)); + return; + } + } diff --git a/patches/server/Fix-Light-Command.patch b/patches/server/Fix-Light-Command.patch index 27189c1572..d4ca2d5884 100644 --- a/patches/server/Fix-Light-Command.patch +++ b/patches/server/Fix-Light-Command.patch @@ -39,7 +39,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 import java.util.Iterator; import java.util.List; import java.util.Locale; -@@ -0,0 +0,0 @@ import java.util.stream.Collectors; +@@ -0,0 +0,0 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; public class PaperCommand extends Command { private static final String BASE_PERM = "bukkit.command.paper."; @@ -60,7 +60,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 case "version": @@ -0,0 +0,0 @@ public class PaperCommand extends Command { - Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Paper config reload complete."); + Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN)); } + private void doFixLight(CommandSender sender, String[] args) { + if (!(sender instanceof Player)) { diff --git a/patches/server/Make-the-default-permission-message-configurable.patch b/patches/server/Make-the-default-permission-message-configurable.patch index 86b276a717..d7417a11e9 100644 --- a/patches/server/Make-the-default-permission-message-configurable.patch +++ b/patches/server/Make-the-default-permission-message-configurable.patch @@ -12,7 +12,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 private static boolean testPermission(CommandSender commandSender, String permission) { if (commandSender.hasPermission(BASE_PERM + permission) || commandSender.hasPermission("bukkit.command.paper")) return true; -- commandSender.sendMessage(ChatColor.RED + "I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error."); // Sorry, kashike +- commandSender.sendMessage(text("I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.", RED)); + commandSender.sendMessage(Bukkit.getPermissionMessage()); // Sorry, kashike return false; } diff --git a/patches/server/Paper-command.patch b/patches/server/Paper-command.patch new file mode 100644 index 0000000000..9cdf876520 --- /dev/null +++ b/patches/server/Paper-command.patch @@ -0,0 +1,301 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Zach Brown <1254957+zachbr@users.noreply.github.com> +Date: Mon, 29 Feb 2016 21:02:09 -0600 +Subject: [PATCH] Paper command + + +diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java +new file mode 100644 +index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 +--- /dev/null ++++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java +@@ -0,0 +0,0 @@ ++package com.destroystokyo.paper; ++ ++import com.google.common.base.Functions; ++import com.google.common.base.Joiner; ++import com.google.common.collect.ImmutableSet; ++import com.google.common.collect.Iterables; ++import com.google.common.collect.Lists; ++import com.google.common.collect.Maps; ++import net.minecraft.resources.ResourceLocation; ++import net.minecraft.server.MinecraftServer; ++import net.minecraft.server.level.ServerChunkCache; ++import net.minecraft.server.level.ServerLevel; ++import net.minecraft.world.entity.Entity; ++import net.minecraft.world.entity.EntityType; ++import net.minecraft.world.level.ChunkPos; ++import org.apache.commons.lang3.tuple.MutablePair; ++import org.apache.commons.lang3.tuple.Pair; ++import org.bukkit.Bukkit; ++import org.bukkit.Location; ++import org.bukkit.World; ++import org.bukkit.command.Command; ++import org.bukkit.command.CommandSender; ++import org.bukkit.craftbukkit.CraftServer; ++import org.bukkit.craftbukkit.CraftWorld; ++import org.bukkit.entity.Player; ++ ++import java.io.File; ++import java.time.LocalDateTime; ++import java.time.format.DateTimeFormatter; ++import java.util.ArrayList; ++import java.util.Arrays; ++import java.util.Collection; ++import java.util.Collections; ++import java.util.Iterator; ++import java.util.List; ++import java.util.Locale; ++import java.util.Map; ++import java.util.Set; ++import java.util.stream.Collectors; ++ ++import static net.kyori.adventure.text.Component.text; ++import static net.kyori.adventure.text.format.NamedTextColor.GREEN; ++import static net.kyori.adventure.text.format.NamedTextColor.RED; ++import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; ++ ++public class PaperCommand extends Command { ++ private static final String BASE_PERM = "bukkit.command.paper."; ++ private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("heap", "entity", "reload", "version").build(); ++ ++ public PaperCommand(String name) { ++ super(name); ++ this.description = "Paper related commands"; ++ this.usageMessage = "/paper [" + Joiner.on(" | ").join(SUBCOMMANDS) + "]"; ++ this.setPermission("bukkit.command.paper;" + Joiner.on(';').join(SUBCOMMANDS.stream().map(s -> BASE_PERM + s).collect(Collectors.toSet()))); ++ } ++ ++ private static boolean testPermission(CommandSender commandSender, String permission) { ++ if (commandSender.hasPermission(BASE_PERM + permission) || commandSender.hasPermission("bukkit.command.paper")) return true; ++ commandSender.sendMessage(text("I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error.", RED)); ++ return false; ++ } ++ ++ @Override ++ public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { ++ if (args.length <= 1) ++ return getListMatchingLast(sender, args, SUBCOMMANDS); ++ ++ switch (args[0].toLowerCase(Locale.ENGLISH)) ++ { ++ case "entity": ++ if (args.length == 2) ++ return getListMatchingLast(sender, args, "help", "list"); ++ if (args.length == 3) ++ return getListMatchingLast(sender, args, EntityType.getEntityNameList().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new)); ++ break; ++ } ++ return Collections.emptyList(); ++ } ++ ++ // Code from Mojang - copyright them ++ public static List getListMatchingLast(CommandSender sender, String[] args, String... matches) { ++ return getListMatchingLast(sender, args, (Collection) Arrays.asList(matches)); ++ } ++ ++ public static boolean matches(String s, String s1) { ++ return s1.regionMatches(true, 0, s, 0, s.length()); ++ } ++ ++ public static List getListMatchingLast(CommandSender sender, String[] strings, Collection collection) { ++ String last = strings[strings.length - 1]; ++ ArrayList results = Lists.newArrayList(); ++ ++ if (!collection.isEmpty()) { ++ Iterator iterator = Iterables.transform(collection, Functions.toStringFunction()).iterator(); ++ ++ while (iterator.hasNext()) { ++ String s1 = (String) iterator.next(); ++ ++ if (matches(last, s1) && (sender.hasPermission(BASE_PERM + s1) || sender.hasPermission("bukkit.command.paper"))) { ++ results.add(s1); ++ } ++ } ++ ++ if (results.isEmpty()) { ++ iterator = collection.iterator(); ++ ++ while (iterator.hasNext()) { ++ Object object = iterator.next(); ++ ++ if (object instanceof ResourceLocation && matches(last, ((ResourceLocation) object).getPath())) { ++ results.add(String.valueOf(object)); ++ } ++ } ++ } ++ } ++ ++ return results; ++ } ++ // end copy stuff ++ ++ @Override ++ public boolean execute(CommandSender sender, String commandLabel, String[] args) { ++ if (!testPermission(sender)) return true; ++ ++ if (args.length == 0) { ++ sender.sendMessage(text("Usage: " + this.usageMessage, RED)); ++ return false; ++ } ++ if (SUBCOMMANDS.contains(args[0].toLowerCase(Locale.ENGLISH))) { ++ if (!testPermission(sender, args[0].toLowerCase(Locale.ENGLISH))) return true; ++ } ++ switch (args[0].toLowerCase(Locale.ENGLISH)) { ++ case "heap": ++ dumpHeap(sender); ++ break; ++ case "entity": ++ listEntities(sender, args); ++ break; ++ case "reload": ++ doReload(sender); ++ break; ++ case "ver": ++ if (!testPermission(sender, "version")) break; // "ver" needs a special check because it's an alias. All other commands are checked up before the switch statement (because they are present in the SUBCOMMANDS set) ++ case "version": ++ Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version"); ++ if (ver != null) { ++ ver.execute(sender, commandLabel, new String[0]); ++ break; ++ } ++ // else - fall through to default ++ default: ++ sender.sendMessage(text("Usage: " + this.usageMessage, RED)); ++ return false; ++ } ++ ++ return true; ++ } ++ ++ /* ++ * Ported from MinecraftForge - author: LexManos - License: LGPLv2.1 ++ */ ++ private void listEntities(CommandSender sender, String[] args) { ++ if (args.length < 2 || args[1].toLowerCase(Locale.ENGLISH).equals("help")) { ++ sender.sendMessage(text("Use /paper entity [list] help for more information on a specific command", RED)); ++ return; ++ } ++ ++ switch (args[1].toLowerCase(Locale.ENGLISH)) { ++ case "list": ++ String filter = "*"; ++ if (args.length > 2) { ++ if (args[2].toLowerCase(Locale.ENGLISH).equals("help")) { ++ sender.sendMessage(text("Use /paper entity list [filter] [worldName] to get entity info that matches the optional filter.", RED)); ++ return; ++ } ++ filter = args[2]; ++ } ++ final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?"); ++ Set names = EntityType.getEntityNameList().stream() ++ .filter(n -> n.toString().matches(cleanfilter)) ++ .collect(Collectors.toSet()); ++ ++ if (names.isEmpty()) { ++ sender.sendMessage(text("Invalid filter, does not match any entities. Use /paper entity list for a proper list", RED)); ++ sender.sendMessage(text("Usage: /paper entity list [filter] [worldName]", RED)); ++ return; ++ } ++ ++ String worldName; ++ if (args.length > 3) { ++ worldName = args[3]; ++ } else if (sender instanceof Player) { ++ worldName = ((Player) sender).getWorld().getName(); ++ } else { ++ sender.sendMessage(text("Please specify the name of a world", RED)); ++ sender.sendMessage(text("To do so without a filter, specify '*' as the filter", RED)); ++ sender.sendMessage(text("Usage: /paper entity list [filter] [worldName]", RED)); ++ return; ++ } ++ ++ Map>> list = Maps.newHashMap(); ++ World bukkitWorld = Bukkit.getWorld(worldName); ++ if (bukkitWorld == null) { ++ sender.sendMessage(text("Could not load world for " + worldName + ". Please select a valid world.", RED)); ++ sender.sendMessage(text("Usage: /paper entity list [filter] [worldName]", RED)); ++ return; ++ } ++ ServerLevel world = ((CraftWorld) Bukkit.getWorld(worldName)).getHandle(); ++ ++ Map nonEntityTicking = Maps.newHashMap(); ++ ServerChunkCache chunkProviderServer = world.getChunkSource(); ++ ++ world.getAllEntities().forEach(e -> { ++ ResourceLocation key = EntityType.getKey(e.getType()); ++ ++ MutablePair> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap())); ++ ChunkPos chunk = e.chunkPosition(); ++ info.left++; ++ info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1); ++ if (!chunkProviderServer.isPositionTicking(e)) { ++ nonEntityTicking.merge(key, Integer.valueOf(1), Integer::sum); ++ } ++ }); ++ ++ if (names.size() == 1) { ++ ResourceLocation name = names.iterator().next(); ++ Pair> info = list.get(name); ++ int nonTicking = nonEntityTicking.getOrDefault(name, Integer.valueOf(0)).intValue(); ++ if (info == null) { ++ sender.sendMessage(text("No entities found.", RED)); ++ return; ++ } ++ sender.sendMessage("Entity: " + name + " Total Ticking: " + (info.getLeft() - nonTicking) + ", Total Non-Ticking: " + nonTicking); ++ info.getRight().entrySet().stream() ++ .sorted((a, b) -> !a.getValue().equals(b.getValue()) ? b.getValue() - a.getValue() : a.getKey().toString().compareTo(b.getKey().toString())) ++ .limit(10).forEach(e -> sender.sendMessage(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z + (chunkProviderServer.isPositionTicking(e.getKey().toLong()) ? " (Ticking)" : " (Non-Ticking)"))); ++ } else { ++ List> info = list.entrySet().stream() ++ .filter(e -> names.contains(e.getKey())) ++ .map(e -> Pair.of(e.getKey(), e.getValue().left)) ++ .sorted((a, b) -> !a.getRight().equals(b.getRight()) ? b.getRight() - a.getRight() : a.getKey().toString().compareTo(b.getKey().toString())) ++ .collect(Collectors.toList()); ++ ++ if (info == null || info.size() == 0) { ++ sender.sendMessage(text("No entities found.", RED)); ++ return; ++ } ++ ++ int count = info.stream().mapToInt(Pair::getRight).sum(); ++ int nonTickingCount = nonEntityTicking.values().stream().mapToInt(Integer::intValue).sum(); ++ sender.sendMessage("Total Ticking: " + (count - nonTickingCount) + ", Total Non-Ticking: " + nonTickingCount); ++ info.forEach(e -> { ++ int nonTicking = nonEntityTicking.getOrDefault(e.getKey(), Integer.valueOf(0)).intValue(); ++ sender.sendMessage(" " + (e.getValue() - nonTicking) + " (" + nonTicking + ") " + ": " + e.getKey()); ++ }); ++ sender.sendMessage("* First number is ticking entities, second number is non-ticking entities"); ++ } ++ break; ++ } ++ } ++ ++ private void dumpHeap(CommandSender sender) { ++ java.nio.file.Path dir = java.nio.file.Paths.get("./dumps"); ++ String name = "heap-dump-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()); ++ ++ Command.broadcastCommandMessage(sender, text("Writing JVM heap data...", YELLOW)); ++ ++ java.nio.file.Path file = CraftServer.dumpHeap(dir, name); ++ if (file != null) { ++ Command.broadcastCommandMessage(sender, text("Heap dump saved to " + file, GREEN)); ++ } else { ++ Command.broadcastCommandMessage(sender, text("Failed to write heap dump, see server log for details", RED)); ++ } ++ } ++ ++ private void doReload(CommandSender sender) { ++ Command.broadcastCommandMessage(sender, text("Please note that this command is not supported and may cause issues.", RED)); ++ Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", RED)); ++ ++ MinecraftServer console = MinecraftServer.getServer(); ++ com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); ++ for (ServerLevel world : console.getAllLevels()) { ++ world.paperConfig.init(); ++ } ++ console.server.reloadCount++; ++ ++ Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN)); ++ } ++} diff --git a/patches/server/Paper-config-files.patch b/patches/server/Paper-config-files.patch index dbf68a4e26..765194ee17 100644 --- a/patches/server/Paper-config-files.patch +++ b/patches/server/Paper-config-files.patch @@ -5,297 +5,6 @@ Subject: [PATCH] Paper config files Loads each yml file for early init too so it can be used for early options -diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java -new file mode 100644 -index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 ---- /dev/null -+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java -@@ -0,0 +0,0 @@ -+package com.destroystokyo.paper; -+ -+import com.google.common.base.Functions; -+import com.google.common.base.Joiner; -+import com.google.common.collect.ImmutableSet; -+import com.google.common.collect.Iterables; -+import com.google.common.collect.Lists; -+import com.google.common.collect.Maps; -+import net.minecraft.resources.ResourceLocation; -+import net.minecraft.server.MinecraftServer; -+import net.minecraft.server.level.ServerChunkCache; -+import net.minecraft.server.level.ServerLevel; -+import net.minecraft.world.entity.Entity; -+import net.minecraft.world.entity.EntityType; -+import net.minecraft.world.level.ChunkPos; -+import org.apache.commons.lang3.tuple.MutablePair; -+import org.apache.commons.lang3.tuple.Pair; -+import org.bukkit.Bukkit; -+import org.bukkit.ChatColor; -+import org.bukkit.Location; -+import org.bukkit.World; -+import org.bukkit.command.Command; -+import org.bukkit.command.CommandSender; -+import org.bukkit.craftbukkit.CraftServer; -+import org.bukkit.craftbukkit.CraftWorld; -+import org.bukkit.entity.Player; -+ -+import java.io.File; -+import java.time.LocalDateTime; -+import java.time.format.DateTimeFormatter; -+import java.util.ArrayList; -+import java.util.Arrays; -+import java.util.Collection; -+import java.util.Collections; -+import java.util.Iterator; -+import java.util.List; -+import java.util.Locale; -+import java.util.Map; -+import java.util.Set; -+import java.util.stream.Collectors; -+ -+public class PaperCommand extends Command { -+ private static final String BASE_PERM = "bukkit.command.paper."; -+ private static final ImmutableSet SUBCOMMANDS = ImmutableSet.builder().add("heap", "entity", "reload", "version").build(); -+ -+ public PaperCommand(String name) { -+ super(name); -+ this.description = "Paper related commands"; -+ this.usageMessage = "/paper [" + Joiner.on(" | ").join(SUBCOMMANDS) + "]"; -+ this.setPermission("bukkit.command.paper;" + Joiner.on(';').join(SUBCOMMANDS.stream().map(s -> BASE_PERM + s).collect(Collectors.toSet()))); -+ } -+ -+ private static boolean testPermission(CommandSender commandSender, String permission) { -+ if (commandSender.hasPermission(BASE_PERM + permission) || commandSender.hasPermission("bukkit.command.paper")) return true; -+ commandSender.sendMessage(ChatColor.RED + "I'm sorry, but you do not have permission to perform this command. Please contact the server administrators if you believe that this is in error."); // Sorry, kashike -+ return false; -+ } -+ -+ @Override -+ public List tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException { -+ if (args.length <= 1) -+ return getListMatchingLast(sender, args, SUBCOMMANDS); -+ -+ switch (args[0].toLowerCase(Locale.ENGLISH)) -+ { -+ case "entity": -+ if (args.length == 2) -+ return getListMatchingLast(sender, args, "help", "list"); -+ if (args.length == 3) -+ return getListMatchingLast(sender, args, EntityType.getEntityNameList().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new)); -+ break; -+ } -+ return Collections.emptyList(); -+ } -+ -+ // Code from Mojang - copyright them -+ public static List getListMatchingLast(CommandSender sender, String[] args, String... matches) { -+ return getListMatchingLast(sender, args, (Collection) Arrays.asList(matches)); -+ } -+ -+ public static boolean matches(String s, String s1) { -+ return s1.regionMatches(true, 0, s, 0, s.length()); -+ } -+ -+ public static List getListMatchingLast(CommandSender sender, String[] strings, Collection collection) { -+ String last = strings[strings.length - 1]; -+ ArrayList results = Lists.newArrayList(); -+ -+ if (!collection.isEmpty()) { -+ Iterator iterator = Iterables.transform(collection, Functions.toStringFunction()).iterator(); -+ -+ while (iterator.hasNext()) { -+ String s1 = (String) iterator.next(); -+ -+ if (matches(last, s1) && (sender.hasPermission(BASE_PERM + s1) || sender.hasPermission("bukkit.command.paper"))) { -+ results.add(s1); -+ } -+ } -+ -+ if (results.isEmpty()) { -+ iterator = collection.iterator(); -+ -+ while (iterator.hasNext()) { -+ Object object = iterator.next(); -+ -+ if (object instanceof ResourceLocation && matches(last, ((ResourceLocation) object).getPath())) { -+ results.add(String.valueOf(object)); -+ } -+ } -+ } -+ } -+ -+ return results; -+ } -+ // end copy stuff -+ -+ @Override -+ public boolean execute(CommandSender sender, String commandLabel, String[] args) { -+ if (!testPermission(sender)) return true; -+ -+ if (args.length == 0) { -+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); -+ return false; -+ } -+ if (SUBCOMMANDS.contains(args[0].toLowerCase(Locale.ENGLISH))) { -+ if (!testPermission(sender, args[0].toLowerCase(Locale.ENGLISH))) return true; -+ } -+ switch (args[0].toLowerCase(Locale.ENGLISH)) { -+ case "heap": -+ dumpHeap(sender); -+ break; -+ case "entity": -+ listEntities(sender, args); -+ break; -+ case "reload": -+ doReload(sender); -+ break; -+ case "ver": -+ if (!testPermission(sender, "version")) break; // "ver" needs a special check because it's an alias. All other commands are checked up before the switch statement (because they are present in the SUBCOMMANDS set) -+ case "version": -+ Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version"); -+ if (ver != null) { -+ ver.execute(sender, commandLabel, new String[0]); -+ break; -+ } -+ // else - fall through to default -+ default: -+ sender.sendMessage(ChatColor.RED + "Usage: " + usageMessage); -+ return false; -+ } -+ -+ return true; -+ } -+ -+ /* -+ * Ported from MinecraftForge - author: LexManos - License: LGPLv2.1 -+ */ -+ private void listEntities(CommandSender sender, String[] args) { -+ if (args.length < 2 || args[1].toLowerCase(Locale.ENGLISH).equals("help")) { -+ sender.sendMessage(ChatColor.RED + "Use /paper entity [list] help for more information on a specific command."); -+ return; -+ } -+ -+ switch (args[1].toLowerCase(Locale.ENGLISH)) { -+ case "list": -+ String filter = "*"; -+ if (args.length > 2) { -+ if (args[2].toLowerCase(Locale.ENGLISH).equals("help")) { -+ sender.sendMessage(ChatColor.RED + "Use /paper entity list [filter] [worldName] to get entity info that matches the optional filter."); -+ return; -+ } -+ filter = args[2]; -+ } -+ final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?"); -+ Set names = EntityType.getEntityNameList().stream() -+ .filter(n -> n.toString().matches(cleanfilter)) -+ .collect(Collectors.toSet()); -+ -+ if (names.isEmpty()) { -+ sender.sendMessage(ChatColor.RED + "Invalid filter, does not match any entities. Use /paper entity list for a proper list"); -+ sender.sendMessage(ChatColor.RED + "Usage: /paper entity list [filter] [worldName]"); -+ return; -+ } -+ -+ String worldName; -+ if (args.length > 3) { -+ worldName = args[3]; -+ } else if (sender instanceof Player) { -+ worldName = ((Player) sender).getWorld().getName(); -+ } else { -+ sender.sendMessage(ChatColor.RED + "Please specify the name of a world"); -+ sender.sendMessage(ChatColor.RED + "To do so without a filter, specify '*' as the filter"); -+ sender.sendMessage(ChatColor.RED + "Usage: /paper entity list [filter] [worldName]"); -+ return; -+ } -+ -+ Map>> list = Maps.newHashMap(); -+ World bukkitWorld = Bukkit.getWorld(worldName); -+ if (bukkitWorld == null) { -+ sender.sendMessage(ChatColor.RED + "Could not load world for " + worldName + ". Please select a valid world."); -+ sender.sendMessage(ChatColor.RED + "Usage: /paper entity list [filter] [worldName]"); -+ return; -+ } -+ ServerLevel world = ((CraftWorld) Bukkit.getWorld(worldName)).getHandle(); -+ -+ Map nonEntityTicking = Maps.newHashMap(); -+ ServerChunkCache chunkProviderServer = world.getChunkSource(); -+ -+ world.getAllEntities().forEach(e -> { -+ ResourceLocation key = EntityType.getKey(e.getType()); -+ -+ MutablePair> info = list.computeIfAbsent(key, k -> MutablePair.of(0, Maps.newHashMap())); -+ ChunkPos chunk = e.chunkPosition(); -+ info.left++; -+ info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1); -+ if (!chunkProviderServer.isPositionTicking(e)) { -+ nonEntityTicking.merge(key, Integer.valueOf(1), Integer::sum); -+ } -+ }); -+ -+ if (names.size() == 1) { -+ ResourceLocation name = names.iterator().next(); -+ Pair> info = list.get(name); -+ int nonTicking = nonEntityTicking.getOrDefault(name, Integer.valueOf(0)).intValue(); -+ if (info == null) { -+ sender.sendMessage(ChatColor.RED + "No entities found."); -+ return; -+ } -+ sender.sendMessage("Entity: " + name + " Total Ticking: " + (info.getLeft() - nonTicking) + ", Total Non-Ticking: " + nonTicking); -+ info.getRight().entrySet().stream() -+ .sorted((a, b) -> !a.getValue().equals(b.getValue()) ? b.getValue() - a.getValue() : a.getKey().toString().compareTo(b.getKey().toString())) -+ .limit(10).forEach(e -> sender.sendMessage(" " + e.getValue() + ": " + e.getKey().x + ", " + e.getKey().z + (chunkProviderServer.isPositionTicking(e.getKey().toLong()) ? " (Ticking)" : " (Non-Ticking)"))); -+ } else { -+ List> info = list.entrySet().stream() -+ .filter(e -> names.contains(e.getKey())) -+ .map(e -> Pair.of(e.getKey(), e.getValue().left)) -+ .sorted((a, b) -> !a.getRight().equals(b.getRight()) ? b.getRight() - a.getRight() : a.getKey().toString().compareTo(b.getKey().toString())) -+ .collect(Collectors.toList()); -+ -+ if (info == null || info.size() == 0) { -+ sender.sendMessage(ChatColor.RED + "No entities found."); -+ return; -+ } -+ -+ int count = info.stream().mapToInt(Pair::getRight).sum(); -+ int nonTickingCount = nonEntityTicking.values().stream().mapToInt(Integer::intValue).sum(); -+ sender.sendMessage("Total Ticking: " + (count - nonTickingCount) + ", Total Non-Ticking: " + nonTickingCount); -+ info.forEach(e -> { -+ int nonTicking = nonEntityTicking.getOrDefault(e.getKey(), Integer.valueOf(0)).intValue(); -+ sender.sendMessage(" " + (e.getValue() - nonTicking) + " (" + nonTicking + ") " + ": " + e.getKey()); -+ }); -+ sender.sendMessage("* First number is ticking entities, second number is non-ticking entities"); -+ } -+ break; -+ } -+ } -+ -+ private void dumpHeap(CommandSender sender) { -+ java.nio.file.Path dir = java.nio.file.Paths.get("./dumps"); -+ String name = "heap-dump-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now()); -+ -+ Command.broadcastCommandMessage(sender, ChatColor.YELLOW + "Writing JVM heap data..."); -+ -+ java.nio.file.Path file = CraftServer.dumpHeap(dir, name); -+ if (file != null) { -+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Heap dump saved to " + file); -+ } else { -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Failed to write heap dump, see sever log for details"); -+ } -+ } -+ -+ private void doReload(CommandSender sender) { -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "Please note that this command is not supported and may cause issues."); -+ Command.broadcastCommandMessage(sender, ChatColor.RED + "If you encounter any issues please use the /stop command to restart your server."); -+ -+ MinecraftServer console = MinecraftServer.getServer(); -+ com.destroystokyo.paper.PaperConfig.init((File) console.options.valueOf("paper-settings")); -+ for (ServerLevel world : console.getAllLevels()) { -+ world.paperConfig.init(); -+ } -+ console.server.reloadCount++; -+ -+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Paper config reload complete."); -+ } -+} diff --git a/src/main/java/com/destroystokyo/paper/PaperConfig.java b/src/main/java/com/destroystokyo/paper/PaperConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 diff --git a/patches/server/Paper-dumpitem-command.patch b/patches/server/Paper-dumpitem-command.patch index 8a62c96486..d516f6a334 100644 --- a/patches/server/Paper-dumpitem-command.patch +++ b/patches/server/Paper-dumpitem-command.patch @@ -27,7 +27,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 import java.io.File; import java.io.FileOutputStream; -@@ -0,0 +0,0 @@ import java.util.stream.Collectors; +@@ -0,0 +0,0 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW; public class PaperCommand extends Command { private static final String BASE_PERM = "bukkit.command.paper."; @@ -48,7 +48,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 break; @@ -0,0 +0,0 @@ public class PaperCommand extends Command { - Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Paper config reload complete."); + Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN)); } + private void doDumpItem(CommandSender sender) { + if (!(sender instanceof Player)) { diff --git a/patches/server/Rewrite-the-light-engine.patch b/patches/server/Rewrite-the-light-engine.patch index 8995530e52..3dc0157b28 100644 --- a/patches/server/Rewrite-the-light-engine.patch +++ b/patches/server/Rewrite-the-light-engine.patch @@ -4375,20 +4375,20 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + lightengine.relight(chunks, + (ChunkPos chunkPos) -> { + ++relitChunks[0]; -+ sender.getBukkitEntity().sendMessage( -+ ChatColor.BLUE + "Relit chunk " + ChatColor.DARK_AQUA + chunkPos + ChatColor.BLUE + -+ ", progress: " + ChatColor.DARK_AQUA + (int)(Math.round(100.0 * (double)(relitChunks[0])/(double)pending[0])) + "%" -+ ); ++ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append( ++ text("Relit chunk ", BLUE), text(chunkPos.toString()), ++ text(", progress: ", BLUE), text((int)(Math.round(100.0 * (double)(relitChunks[0])/(double)pending[0])) + "%") ++ )); + }, + (int totalRelit) -> { + final long end = System.nanoTime(); + final long diff = Math.round(1.0e-6*(end - start)); -+ sender.getBukkitEntity().sendMessage( -+ ChatColor.BLUE + "Relit " + ChatColor.DARK_AQUA + totalRelit + ChatColor.BLUE + " chunks. Took " + -+ ChatColor.DARK_AQUA + diff + "ms" -+ ); ++ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append( ++ text("Relit ", BLUE), text(totalRelit), ++ text(" chunks. Took ", BLUE), text(diff + "ms") ++ )); + }); -+ sender.getBukkitEntity().sendMessage(ChatColor.BLUE + "Relighting " + ChatColor.DARK_AQUA + pending[0] + ChatColor.BLUE + " chunks"); ++ sender.getBukkitEntity().sendMessage(text().color(BLUE).append(text("Relighting "), text(pending[0], DARK_AQUA), text(" chunks"))); + } + // Paper end - rewrite light engine + diff --git a/patches/server/Timings-v2.patch b/patches/server/Timings-v2.patch index 6327625dc3..095c4b21b3 100644 --- a/patches/server/Timings-v2.patch +++ b/patches/server/Timings-v2.patch @@ -193,14 +193,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +package co.aikar.timings; + +import com.google.common.collect.Sets; ++import io.papermc.paper.adventure.PaperAdventure; ++import net.kyori.adventure.text.format.NamedTextColor; ++import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; +import net.minecraft.server.MinecraftServer; +import org.apache.commons.lang.StringUtils; +import org.bukkit.Bukkit; -+import org.bukkit.ChatColor; +import org.bukkit.Material; +import org.bukkit.configuration.ConfigurationSection; +import org.bukkit.configuration.MemorySection; -+import org.bukkit.craftbukkit.util.CraftChatMessage; +import org.bukkit.entity.EntityType; +import org.json.simple.JSONObject; +import org.json.simple.JSONValue; @@ -230,6 +231,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 +import static co.aikar.util.JSONUtil.toArray; +import static co.aikar.util.JSONUtil.toArrayMapper; +import static co.aikar.util.JSONUtil.toObjectMapper; ++import static net.kyori.adventure.text.Component.text; + +@SuppressWarnings({"rawtypes", "SuppressionAnnotation"}) +public class TimingsExport extends Thread { @@ -260,17 +262,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + long now = System.currentTimeMillis(); + final long lastReportDiff = now - lastReport; + if (lastReportDiff < 60000) { -+ listeners.sendMessage(ChatColor.RED + "Please wait at least 1 minute in between Timings reports. (" + (int)((60000 - lastReportDiff) / 1000) + " seconds)"); ++ listeners.sendMessage(text("Please wait at least 1 minute in between Timings reports. (" + (int)((60000 - lastReportDiff) / 1000) + " seconds)", NamedTextColor.RED)); + listeners.done(); + return; + } + final long lastStartDiff = now - TimingsManager.timingStart; + if (lastStartDiff < 180000) { -+ listeners.sendMessage(ChatColor.RED + "Please wait at least 3 minutes before generating a Timings report. Unlike Timings v1, v2 benefits from longer timings and is not as useful with short timings. (" + (int)((180000 - lastStartDiff) / 1000) + " seconds)"); ++ listeners.sendMessage(text("Please wait at least 3 minutes before generating a Timings report. Unlike Timings v1, v2 benefits from longer timings and is not as useful with short timings. (" + (int)((180000 - lastStartDiff) / 1000) + " seconds)", NamedTextColor.RED)); + listeners.done(); + return; + } -+ listeners.sendMessage(ChatColor.GREEN + "Preparing Timings Report..."); ++ listeners.sendMessage(text("Preparing Timings Report...", NamedTextColor.GREEN)); + lastReport = now; + Map parent = createObject( + // Get some basic system details about the server @@ -281,7 +283,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + pair("online-mode", Bukkit.getServer().getOnlineMode()), + pair("sampletime", (System.currentTimeMillis() - TimingsManager.timingStart) / 1000), + pair("datapacks", toArrayMapper(MinecraftServer.getServer().getPackRepository().getSelectedPacks(), pack -> { -+ return ChatColor.stripColor(CraftChatMessage.fromComponent(pack.getChatLink(true))); ++ return PlainTextComponentSerializer.plainText().serialize(PaperAdventure.asAdventure(pack.getChatLink(true))); + })) + ); + if (!TimingsManager.privacy) { @@ -500,9 +502,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + response = getResponse(con); + + if (con.getResponseCode() != 302) { -+ listeners.sendMessage( -+ ChatColor.RED + "Upload Error: " + con.getResponseCode() + ": " + con.getResponseMessage()); -+ listeners.sendMessage(ChatColor.RED + "Check your logs for more information"); ++ listeners.sendMessage(text( "Upload Error: " + con.getResponseCode() + ": " + con.getResponseMessage(), NamedTextColor.RED)); ++ listeners.sendMessage(text("Check your logs for more information", NamedTextColor.RED)); + if (response != null) { + Bukkit.getLogger().log(Level.SEVERE, response); + } @@ -510,13 +511,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + } + + timingsURL = con.getHeaderField("Location"); -+ listeners.sendMessage(ChatColor.GREEN + "View Timings Report: " + timingsURL); ++ listeners.sendMessage(text("View Timings Report: " + timingsURL, NamedTextColor.GREEN)); + + if (response != null && !response.isEmpty()) { + Bukkit.getLogger().log(Level.INFO, "Timing Response: " + response); + } + } catch (IOException ex) { -+ listeners.sendMessage(ChatColor.RED + "Error uploading timings, check your logs for more information"); ++ listeners.sendMessage(text("Error uploading timings, check your logs for more information", NamedTextColor.RED)); + if (response != null) { + Bukkit.getLogger().log(Level.SEVERE, response); + } @@ -540,7 +541,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000 + return bos.toString(); + + } catch (IOException ex) { -+ listeners.sendMessage(ChatColor.RED + "Error uploading timings, check your logs for more information"); ++ listeners.sendMessage(text("Error uploading timings, check your logs for more information", NamedTextColor.RED)); + Bukkit.getLogger().log(Level.WARNING, con.getResponseMessage(), ex); + return null; + } finally {