Refactor paper command (#8112)

* Refactor paper command

* Improve paper dumpitem output

* Register paper command permissions

Would be nice to add descriptions for these too, but that's an enhancement for another time

* Update MobcapsCommandTest fail message

* Notify on bad radius for fix light

* fixup rebase
This commit is contained in:
Jason 2022-07-08 16:01:42 -07:00 committed by GitHub
parent 5ffeb70186
commit e294802977
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 1035 additions and 772 deletions

View file

@ -31,90 +31,102 @@ For references on certain keywords (ticket, status, etc), please see:
https://bugs.mojang.com/browse/MC-141484?focusedCommentId=528273&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-528273
https://bugs.mojang.com/browse/MC-141484?focusedCommentId=528577&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-528577
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d156c74b66 100644
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
@@ -9,11 +9,13 @@ import com.google.common.collect.Maps;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ChunkHolder;
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 net.minecraft.server.MCUtil;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.bukkit.Bukkit;
@@ -40,13 +42,15 @@ import java.util.Set;
import java.util.stream.Collectors;
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
index 12c5b6b740eeb986e4b1acc4b2eda6c2c77b63ac..b7b0d134e3a99a630e493aceb06f6188b2c538c3 100644
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
@@ -1,5 +1,6 @@
package io.papermc.paper.command;
import static net.kyori.adventure.text.Component.text;
+import io.papermc.paper.command.subcommands.ChunkDebugCommand;
import io.papermc.paper.command.subcommands.EntityCommand;
import io.papermc.paper.command.subcommands.HeapDumpCommand;
import io.papermc.paper.command.subcommands.ReloadCommand;
@@ -40,6 +41,7 @@ public final class PaperCommand extends Command {
commands.put(Set.of("entity"), new EntityCommand());
commands.put(Set.of("reload"), new ReloadCommand());
commands.put(Set.of("version"), new VersionCommand());
+ commands.put(Set.of("debug", "chunkinfo"), new ChunkDebugCommand());
return commands.entrySet().stream()
.flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
diff --git a/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
new file mode 100644
index 0000000000000000000000000000000000000000..029ad37df71e74d9feb57e4b31b3602e55d49113
--- /dev/null
+++ b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
@@ -0,0 +1,166 @@
+package io.papermc.paper.command.subcommands;
+
+import io.papermc.paper.command.CommandUtil;
+import io.papermc.paper.command.PaperSubcommand;
+import java.io.File;
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Locale;
+import net.minecraft.server.MCUtil;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.level.ChunkHolder;
+import net.minecraft.server.level.ServerLevel;
+import org.bukkit.Bukkit;
+import org.bukkit.command.CommandSender;
+import org.bukkit.craftbukkit.CraftWorld;
+import org.checkerframework.checker.nullness.qual.NonNull;
+import org.checkerframework.checker.nullness.qual.Nullable;
+import org.checkerframework.framework.qual.DefaultQualifier;
+
+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.";
- private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version").build();
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo").build();
public PaperCommand(String name) {
super(name);
@@ -74,6 +78,21 @@ public class PaperCommand extends Command {
if (args.length == 3)
return getListMatchingLast(sender, args, Registry.ENTITY_TYPE.keySet().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new));
break;
+ case "debug":
+ if (args.length == 2) {
+ return getListMatchingLast(sender, args, "help", "chunks");
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
+
+@DefaultQualifier(NonNull.class)
+public final class ChunkDebugCommand implements PaperSubcommand {
+ @Override
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
+ switch (subCommand) {
+ case "debug" -> this.doDebug(sender, args);
+ case "chunkinfo" -> this.doChunkInfo(sender, args);
+ }
+ return true;
+ }
+
+ @Override
+ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
+ switch (subCommand) {
+ case "debug" -> {
+ if (args.length == 1) {
+ return CommandUtil.getListMatchingLast(sender, args, "help", "chunks");
+ }
+ break;
+ case "chunkinfo":
+ }
+ case "chunkinfo" -> {
+ List<String> worldNames = new ArrayList<>();
+ worldNames.add("*");
+ for (org.bukkit.World world : Bukkit.getWorlds()) {
+ worldNames.add(world.getName());
+ }
+ if (args.length == 2) {
+ return getListMatchingLast(sender, args, worldNames);
+ if (args.length == 1) {
+ return CommandUtil.getListMatchingLast(sender, args, worldNames);
+ }
+ break;
}
return Collections.emptyList();
}
@@ -140,6 +159,12 @@ public class PaperCommand extends Command {
case "reload":
doReload(sender);
break;
+ case "debug":
+ doDebug(sender, args);
+ break;
+ case "chunkinfo":
+ doChunkInfo(sender, args);
+ 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":
@@ -157,6 +182,122 @@ public class PaperCommand extends Command {
return true;
}
+ private void doChunkInfo(CommandSender sender, String[] args) {
+ }
+ }
+ return Collections.emptyList();
+ }
+
+ private void doChunkInfo(final CommandSender sender, final String[] args) {
+ List<org.bukkit.World> worlds;
+ if (args.length < 2 || args[1].equals("*")) {
+ if (args.length < 1 || args[0].equals("*")) {
+ worlds = Bukkit.getWorlds();
+ } else {
+ worlds = new ArrayList<>(args.length - 1);
+ for (int i = 1; i < args.length; ++i) {
+ org.bukkit.World world = Bukkit.getWorld(args[i]);
+ worlds = new ArrayList<>(args.length);
+ for (final String arg : args) {
+ org.bukkit.@Nullable World world = Bukkit.getWorld(arg);
+ if (world == null) {
+ sender.sendMessage(text("World '" + args[i] + "' is invalid", RED));
+ sender.sendMessage(text("World '" + arg + "' is invalid", RED));
+ return;
+ }
+ worlds.add(world);
@ -127,8 +139,8 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
+ int accumulatedTicking = 0;
+ int accumulatedEntityTicking = 0;
+
+ for (org.bukkit.World bukkitWorld : worlds) {
+ ServerLevel world = ((CraftWorld)bukkitWorld).getHandle();
+ for (final org.bukkit.World bukkitWorld : worlds) {
+ final ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
+
+ int total = 0;
+ int inactive = 0;
@ -136,7 +148,7 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
+ int ticking = 0;
+ int entityTicking = 0;
+
+ for (ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) {
+ for (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) {
+ if (chunk.getFullChunkNowUnchecked() == null) {
+ continue;
+ }
@ -146,18 +158,10 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
+ ChunkHolder.FullChunkStatus state = ChunkHolder.getFullChunkStatus(chunk.getTicketLevel());
+
+ switch (state) {
+ case INACCESSIBLE:
+ ++inactive;
+ continue;
+ case BORDER:
+ ++border;
+ continue;
+ case TICKING:
+ ++ticking;
+ continue;
+ case ENTITY_TICKING:
+ ++entityTicking;
+ continue;
+ case INACCESSIBLE -> ++inactive;
+ case BORDER -> ++border;
+ case TICKING -> ++ticking;
+ case ENTITY_TICKING -> ++entityTicking;
+ }
+ }
+
@ -188,16 +192,16 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
+ }
+ }
+
+ private void doDebug(CommandSender sender, String[] args) {
+ if (args.length < 2) {
+ private void doDebug(final CommandSender sender, final String[] args) {
+ if (args.length < 1) {
+ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
+ return;
+ }
+
+ String debugType = args[1].toLowerCase(Locale.ENGLISH);
+ final String debugType = args[0].toLowerCase(Locale.ENGLISH);
+ switch (debugType) {
+ case "chunks":
+ if (args.length >= 3 && args[2].toLowerCase(Locale.ENGLISH).equals("help")) {
+ case "chunks" -> {
+ if (args.length >= 2 && args[1].toLowerCase(Locale.ENGLISH).equals("help")) {
+ sender.sendMessage(text("Use /paper debug chunks [world] to dump loaded chunk information to a file", RED));
+ break;
+ }
@ -211,19 +215,13 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
+ MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr);
+ sender.sendMessage(text("Failed to dump chunk information, see console", RED));
+ }
+
+ break;
+ case "help":
+ // fall through to default
+ default:
+ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
+ return;
+ }
+ // "help" & default
+ default -> sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
+ }
+ }
+
/*
* Ported from MinecraftForge - author: LexManos <LexManos@gmail.com> - License: LGPLv2.1
*/
+}
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
index 162aa7718488a74980843944e0d026ccfd5a65a5..4e29c0a983727fc839a4bcde01d3286396b3587d 100644
--- a/src/main/java/net/minecraft/server/MCUtil.java