mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-29 05:55:44 +01:00
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:
parent
5ffeb70186
commit
e294802977
@ -4,102 +4,50 @@ Date: Mon, 29 Feb 2016 21:02:09 -0600
|
|||||||
Subject: [PATCH] Paper command
|
Subject: [PATCH] Paper command
|
||||||
|
|
||||||
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
diff --git a/src/main/java/io/papermc/paper/command/CommandUtil.java b/src/main/java/io/papermc/paper/command/CommandUtil.java
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000000000000000000000000000000000000..b506cd11b76901827cbe66f46db8df400f7015de
|
index 0000000000000000000000000000000000000000..953c30500892e5f0c55b8597bc708ea85bf56d6e
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/CommandUtil.java
|
||||||
@@ -0,0 +1,287 @@
|
@@ -0,0 +1,69 @@
|
||||||
+package com.destroystokyo.paper;
|
+package io.papermc.paper.command;
|
||||||
+
|
+
|
||||||
+import com.google.common.base.Functions;
|
+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.Iterables;
|
||||||
+import com.google.common.collect.Lists;
|
+import com.google.common.collect.Lists;
|
||||||
+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.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.ArrayList;
|
||||||
+import java.util.Arrays;
|
+import java.util.Arrays;
|
||||||
+import java.util.Collection;
|
+import java.util.Collection;
|
||||||
+import java.util.Collections;
|
|
||||||
+import java.util.Iterator;
|
+import java.util.Iterator;
|
||||||
+import java.util.List;
|
+import java.util.List;
|
||||||
+import java.util.Locale;
|
+import net.minecraft.resources.ResourceLocation;
|
||||||
+import java.util.Map;
|
+import org.bukkit.command.CommandSender;
|
||||||
+import java.util.Set;
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
+import java.util.stream.Collectors;
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
+
|
+
|
||||||
+import static net.kyori.adventure.text.Component.text;
|
+@DefaultQualifier(NonNull.class)
|
||||||
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
+public final class CommandUtil {
|
||||||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
+ private CommandUtil() {
|
||||||
+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();
|
|
||||||
+
|
|
||||||
+ 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<String> 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, Registry.ENTITY_TYPE.keySet().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new));
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ return Collections.emptyList();
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ // Code from Mojang - copyright them
|
+ // Code from Mojang - copyright them
|
||||||
+ public static List<String> getListMatchingLast(CommandSender sender, String[] args, String... matches) {
|
+ public static List<String> getListMatchingLast(
|
||||||
+ return getListMatchingLast(sender, args, (Collection) Arrays.asList(matches));
|
+ final CommandSender sender,
|
||||||
|
+ final String[] args,
|
||||||
|
+ final String... matches
|
||||||
|
+ ) {
|
||||||
|
+ return getListMatchingLast(sender, args, Arrays.asList(matches));
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ public static boolean matches(String s, String s1) {
|
+ public static boolean matches(final String s, final String s1) {
|
||||||
+ return s1.regionMatches(true, 0, s, 0, s.length());
|
+ return s1.regionMatches(true, 0, s, 0, s.length());
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ public static List<String> getListMatchingLast(CommandSender sender, String[] strings, Collection<?> collection) {
|
+ public static List<String> getListMatchingLast(
|
||||||
|
+ final CommandSender sender,
|
||||||
|
+ final String[] strings,
|
||||||
|
+ final Collection<?> collection
|
||||||
|
+ ) {
|
||||||
+ String last = strings[strings.length - 1];
|
+ String last = strings[strings.length - 1];
|
||||||
+ ArrayList<String> results = Lists.newArrayList();
|
+ ArrayList<String> results = Lists.newArrayList();
|
||||||
+
|
+
|
||||||
@ -109,7 +57,7 @@ index 0000000000000000000000000000000000000000..b506cd11b76901827cbe66f46db8df40
|
|||||||
+ while (iterator.hasNext()) {
|
+ while (iterator.hasNext()) {
|
||||||
+ String s1 = (String) iterator.next();
|
+ String s1 = (String) iterator.next();
|
||||||
+
|
+
|
||||||
+ if (matches(last, s1) && (sender.hasPermission(BASE_PERM + s1) || sender.hasPermission("bukkit.command.paper"))) {
|
+ if (matches(last, s1) && (sender.hasPermission(PaperCommand.BASE_PERM + s1) || sender.hasPermission("bukkit.command.paper"))) {
|
||||||
+ results.add(s1);
|
+ results.add(s1);
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
@ -130,188 +78,175 @@ index 0000000000000000000000000000000000000000..b506cd11b76901827cbe66f46db8df40
|
|||||||
+ return results;
|
+ return results;
|
||||||
+ }
|
+ }
|
||||||
+ // end copy stuff
|
+ // end copy stuff
|
||||||
|
+}
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..b3a58bf4b654e336826dc04da9e2f80ff8b9a9a7
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
|
@@ -0,0 +1,145 @@
|
||||||
|
+package io.papermc.paper.command;
|
||||||
|
+
|
||||||
|
+import io.papermc.paper.command.subcommands.EntityCommand;
|
||||||
|
+import io.papermc.paper.command.subcommands.HeapDumpCommand;
|
||||||
|
+import io.papermc.paper.command.subcommands.ReloadCommand;
|
||||||
|
+import io.papermc.paper.command.subcommands.VersionCommand;
|
||||||
|
+import it.unimi.dsi.fastutil.Pair;
|
||||||
|
+import java.util.ArrayList;
|
||||||
|
+import java.util.Arrays;
|
||||||
|
+import java.util.Collections;
|
||||||
|
+import java.util.HashMap;
|
||||||
|
+import java.util.List;
|
||||||
|
+import java.util.Locale;
|
||||||
|
+import java.util.Map;
|
||||||
|
+import java.util.Set;
|
||||||
|
+import java.util.stream.Collectors;
|
||||||
|
+import net.minecraft.Util;
|
||||||
|
+import org.bukkit.Bukkit;
|
||||||
|
+import org.bukkit.Location;
|
||||||
|
+import org.bukkit.command.Command;
|
||||||
|
+import org.bukkit.command.CommandSender;
|
||||||
|
+import org.bukkit.permissions.Permission;
|
||||||
|
+import org.bukkit.permissions.PermissionDefault;
|
||||||
|
+import org.bukkit.plugin.PluginManager;
|
||||||
|
+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.RED;
|
||||||
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public final class PaperCommand extends Command {
|
||||||
|
+ static final String BASE_PERM = "bukkit.command.paper.";
|
||||||
|
+ // subcommand label -> subcommand
|
||||||
|
+ private static final Map<String, PaperSubcommand> SUBCOMMANDS = Util.make(() -> {
|
||||||
|
+ final Map<Set<String>, PaperSubcommand> commands = new HashMap<>();
|
||||||
|
+
|
||||||
|
+ commands.put(Set.of("heap"), new HeapDumpCommand());
|
||||||
|
+ commands.put(Set.of("entity"), new EntityCommand());
|
||||||
|
+ commands.put(Set.of("reload"), new ReloadCommand());
|
||||||
|
+ commands.put(Set.of("version"), new VersionCommand());
|
||||||
|
+
|
||||||
|
+ return commands.entrySet().stream()
|
||||||
|
+ .flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
|
||||||
|
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
+ });
|
||||||
|
+ // alias -> subcommand label
|
||||||
|
+ private static final Map<String, String> ALIASES = Util.make(() -> {
|
||||||
|
+ final Map<String, Set<String>> aliases = new HashMap<>();
|
||||||
|
+
|
||||||
|
+ aliases.put("version", Set.of("ver"));
|
||||||
|
+
|
||||||
|
+ return aliases.entrySet().stream()
|
||||||
|
+ .flatMap(entry -> entry.getValue().stream().map(s -> Map.entry(s, entry.getKey())))
|
||||||
|
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||||
|
+ });
|
||||||
|
+
|
||||||
|
+ public PaperCommand(final String name) {
|
||||||
|
+ super(name);
|
||||||
|
+ this.description = "Paper related commands";
|
||||||
|
+ this.usageMessage = "/paper [" + String.join(" | ", SUBCOMMANDS.keySet()) + "]";
|
||||||
|
+ final List<String> permissions = new ArrayList<>();
|
||||||
|
+ permissions.add("bukkit.command.paper");
|
||||||
|
+ permissions.addAll(SUBCOMMANDS.keySet().stream().map(s -> BASE_PERM + s).toList());
|
||||||
|
+ this.setPermission(String.join(";", permissions));
|
||||||
|
+ final PluginManager pluginManager = Bukkit.getServer().getPluginManager();
|
||||||
|
+ for (final String perm : permissions) {
|
||||||
|
+ pluginManager.addPermission(new Permission(perm, PermissionDefault.OP));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private static boolean testPermission(final CommandSender sender, final String permission) {
|
||||||
|
+ if (sender.hasPermission(BASE_PERM + permission) || sender.hasPermission("bukkit.command.paper")) {
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+ sender.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
|
+ @Override
|
||||||
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
+ public List<String> tabComplete(
|
||||||
+ if (!testPermission(sender)) return true;
|
+ final CommandSender sender,
|
||||||
|
+ final String alias,
|
||||||
|
+ final String[] args,
|
||||||
|
+ final @Nullable Location location
|
||||||
|
+ ) throws IllegalArgumentException {
|
||||||
|
+ if (args.length <= 1) {
|
||||||
|
+ return CommandUtil.getListMatchingLast(sender, args, SUBCOMMANDS.keySet());
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ final @Nullable Pair<String, PaperSubcommand> subCommand = resolveCommand(args[0]);
|
||||||
|
+ if (subCommand != null) {
|
||||||
|
+ return subCommand.second().tabComplete(sender, subCommand.first(), Arrays.copyOfRange(args, 1, args.length));
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ return Collections.emptyList();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ public boolean execute(
|
||||||
|
+ final CommandSender sender,
|
||||||
|
+ final String commandLabel,
|
||||||
|
+ final String[] args
|
||||||
|
+ ) {
|
||||||
|
+ if (!testPermission(sender)) {
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
+
|
+
|
||||||
+ if (args.length == 0) {
|
+ if (args.length == 0) {
|
||||||
+ sender.sendMessage(text("Usage: " + this.usageMessage, RED));
|
+ sender.sendMessage(text("Usage: " + this.usageMessage, RED));
|
||||||
+ return false;
|
+ return false;
|
||||||
+ }
|
+ }
|
||||||
+ if (SUBCOMMANDS.contains(args[0].toLowerCase(Locale.ENGLISH))) {
|
+ final @Nullable Pair<String, PaperSubcommand> subCommand = resolveCommand(args[0]);
|
||||||
+ if (!testPermission(sender, args[0].toLowerCase(Locale.ENGLISH))) return true;
|
+
|
||||||
+ }
|
+ if (subCommand == null) {
|
||||||
+ 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));
|
+ sender.sendMessage(text("Usage: " + this.usageMessage, RED));
|
||||||
+ return false;
|
+ return false;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
+ if (!testPermission(sender, subCommand.first())) {
|
||||||
+ return true;
|
+ return true;
|
||||||
+ }
|
+ }
|
||||||
+
|
+ final String[] choppedArgs = Arrays.copyOfRange(args, 1, args.length);
|
||||||
+ /*
|
+ return subCommand.second().execute(sender, subCommand.first(), choppedArgs);
|
||||||
+ * Ported from MinecraftForge - author: LexManos <LexManos@gmail.com> - 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)) {
|
+ private static @Nullable Pair<String, PaperSubcommand> resolveCommand(String label) {
|
||||||
+ case "list":
|
+ label = label.toLowerCase(Locale.ENGLISH);
|
||||||
+ String filter = "*";
|
+ @Nullable PaperSubcommand subCommand = SUBCOMMANDS.get(label);
|
||||||
+ if (args.length > 2) {
|
+ if (subCommand == null) {
|
||||||
+ if (args[2].toLowerCase(Locale.ENGLISH).equals("help")) {
|
+ final @Nullable String command = ALIASES.get(label);
|
||||||
+ sender.sendMessage(text("Use /paper entity list [filter] [worldName] to get entity info that matches the optional filter.", RED));
|
+ if (command != null) {
|
||||||
+ return;
|
+ label = command;
|
||||||
+ }
|
+ subCommand = SUBCOMMANDS.get(command);
|
||||||
+ filter = args[2];
|
|
||||||
+ }
|
|
||||||
+ final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?");
|
|
||||||
+ Set<ResourceLocation> names = Registry.ENTITY_TYPE.keySet().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<ResourceLocation, MutablePair<Integer, Map<ChunkPos, Integer>>> 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<ResourceLocation, Integer> nonEntityTicking = Maps.newHashMap();
|
|
||||||
+ ServerChunkCache chunkProviderServer = world.getChunkSource();
|
|
||||||
+
|
|
||||||
+ world.getAllEntities().forEach(e -> {
|
|
||||||
+ ResourceLocation key = EntityType.getKey(e.getType());
|
|
||||||
+
|
|
||||||
+ MutablePair<Integer, Map<ChunkPos, Integer>> 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<Integer, Map<ChunkPos, Integer>> 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<Pair<ResourceLocation, Integer>> 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) {
|
+ if (subCommand != null) {
|
||||||
+ java.nio.file.Path dir = java.nio.file.Paths.get("./dumps");
|
+ return Pair.of(label, subCommand);
|
||||||
+ 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) {
|
+ return null;
|
||||||
+ 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 server = ((CraftServer) sender.getServer()).getServer();
|
|
||||||
+ server.paperConfigurations.reloadConfigs(server);
|
|
||||||
+ server.server.reloadCount++;
|
|
||||||
+
|
|
||||||
+ Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
|
||||||
+ }
|
+ }
|
||||||
+}
|
+}
|
||||||
diff --git a/src/main/java/io/papermc/paper/command/PaperCommands.java b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
diff --git a/src/main/java/io/papermc/paper/command/PaperCommands.java b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000000000000000000000000000000000000..82bce89dc174c8c4a4ab8efeffaaf22ddeb377fb
|
index 0000000000000000000000000000000000000000..6a00f3d38da8107825ab1d405f337fd077b09f72
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
+++ b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||||
@@ -0,0 +1,25 @@
|
@@ -0,0 +1,27 @@
|
||||||
+package io.papermc.paper.command;
|
+package io.papermc.paper.command;
|
||||||
+
|
+
|
||||||
+import com.destroystokyo.paper.PaperCommand;
|
|
||||||
+import net.minecraft.server.MinecraftServer;
|
+import net.minecraft.server.MinecraftServer;
|
||||||
+import org.bukkit.command.Command;
|
+import org.bukkit.command.Command;
|
||||||
+
|
+
|
||||||
+import java.util.HashMap;
|
+import java.util.HashMap;
|
||||||
+import java.util.Map;
|
+import java.util.Map;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
+
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
+public final class PaperCommands {
|
+public final class PaperCommands {
|
||||||
+
|
+
|
||||||
+ private PaperCommands() {
|
+ private PaperCommands() {
|
||||||
@ -328,6 +263,289 @@ index 0000000000000000000000000000000000000000..82bce89dc174c8c4a4ab8efeffaaf22d
|
|||||||
+ });
|
+ });
|
||||||
+ }
|
+ }
|
||||||
+}
|
+}
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/command/PaperSubcommand.java b/src/main/java/io/papermc/paper/command/PaperSubcommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..6ff5d42a866d2752c73a766815aa190b2b0dc36f
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/PaperSubcommand.java
|
||||||
|
@@ -0,0 +1,16 @@
|
||||||
|
+package io.papermc.paper.command;
|
||||||
|
+
|
||||||
|
+import java.util.Collections;
|
||||||
|
+import java.util.List;
|
||||||
|
+import org.bukkit.command.CommandSender;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public interface PaperSubcommand {
|
||||||
|
+ boolean execute(CommandSender sender, String subCommand, String[] args);
|
||||||
|
+
|
||||||
|
+ default List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ return Collections.emptyList();
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..68f99e93ed3e843b4001a7a27620f88a48b85e67
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||||
|
@@ -0,0 +1,145 @@
|
||||||
|
+package io.papermc.paper.command.subcommands;
|
||||||
|
+
|
||||||
|
+import com.google.common.collect.Maps;
|
||||||
|
+import io.papermc.paper.command.CommandUtil;
|
||||||
|
+import io.papermc.paper.command.PaperSubcommand;
|
||||||
|
+import java.util.Collections;
|
||||||
|
+import java.util.List;
|
||||||
|
+import java.util.Locale;
|
||||||
|
+import java.util.Map;
|
||||||
|
+import java.util.Set;
|
||||||
|
+import java.util.stream.Collectors;
|
||||||
|
+import net.minecraft.core.Registry;
|
||||||
|
+import net.minecraft.resources.ResourceLocation;
|
||||||
|
+import net.minecraft.server.level.ServerChunkCache;
|
||||||
|
+import net.minecraft.server.level.ServerLevel;
|
||||||
|
+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.World;
|
||||||
|
+import org.bukkit.command.CommandSender;
|
||||||
|
+import org.bukkit.craftbukkit.CraftWorld;
|
||||||
|
+import org.bukkit.entity.Player;
|
||||||
|
+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.RED;
|
||||||
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public final class EntityCommand implements PaperSubcommand {
|
||||||
|
+ @Override
|
||||||
|
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ this.listEntities(sender, args);
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ if (args.length == 1) {
|
||||||
|
+ return CommandUtil.getListMatchingLast(sender, args, "help", "list");
|
||||||
|
+ } else if (args.length == 2) {
|
||||||
|
+ return CommandUtil.getListMatchingLast(sender, args, Registry.ENTITY_TYPE.keySet().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new));
|
||||||
|
+ }
|
||||||
|
+ return Collections.emptyList();
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /*
|
||||||
|
+ * Ported from MinecraftForge - author: LexManos <LexManos@gmail.com> - License: LGPLv2.1
|
||||||
|
+ */
|
||||||
|
+ private void listEntities(final CommandSender sender, final String[] args) {
|
||||||
|
+ // help
|
||||||
|
+ if (args.length < 1 || !args[0].toLowerCase(Locale.ENGLISH).equals("list")) {
|
||||||
|
+ sender.sendMessage(text("Use /paper entity [list] help for more information on a specific command", RED));
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if ("list".equals(args[0].toLowerCase(Locale.ENGLISH))) {
|
||||||
|
+ String filter = "*";
|
||||||
|
+ if (args.length > 1) {
|
||||||
|
+ if (args[1].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[1];
|
||||||
|
+ }
|
||||||
|
+ final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?");
|
||||||
|
+ Set<ResourceLocation> names = Registry.ENTITY_TYPE.keySet().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 > 2) {
|
||||||
|
+ worldName = args[2];
|
||||||
|
+ } 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<ResourceLocation, MutablePair<Integer, Map<ChunkPos, Integer>>> list = Maps.newHashMap();
|
||||||
|
+ @Nullable 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) bukkitWorld).getHandle();
|
||||||
|
+ Map<ResourceLocation, Integer> nonEntityTicking = Maps.newHashMap();
|
||||||
|
+ ServerChunkCache chunkProviderServer = world.getChunkSource();
|
||||||
|
+ world.getAllEntities().forEach(e -> {
|
||||||
|
+ ResourceLocation key = EntityType.getKey(e.getType());
|
||||||
|
+
|
||||||
|
+ MutablePair<Integer, Map<ChunkPos, Integer>> 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, 1, Integer::sum);
|
||||||
|
+ }
|
||||||
|
+ });
|
||||||
|
+ if (names.size() == 1) {
|
||||||
|
+ ResourceLocation name = names.iterator().next();
|
||||||
|
+ Pair<Integer, Map<ChunkPos, Integer>> info = list.get(name);
|
||||||
|
+ int nonTicking = nonEntityTicking.getOrDefault(name, 0);
|
||||||
|
+ 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<Pair<ResourceLocation, Integer>> 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()))
|
||||||
|
+ .toList();
|
||||||
|
+
|
||||||
|
+ if (info.isEmpty()) {
|
||||||
|
+ 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(), 0);
|
||||||
|
+ sender.sendMessage(" " + (e.getValue() - nonTicking) + " (" + nonTicking + ") " + ": " + e.getKey());
|
||||||
|
+ });
|
||||||
|
+ sender.sendMessage("* First number is ticking entities, second number is non-ticking entities");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/command/subcommands/HeapDumpCommand.java b/src/main/java/io/papermc/paper/command/subcommands/HeapDumpCommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..cd2e4d792e972b8bf1e07b8961594a670ae949cf
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/HeapDumpCommand.java
|
||||||
|
@@ -0,0 +1,38 @@
|
||||||
|
+package io.papermc.paper.command.subcommands;
|
||||||
|
+
|
||||||
|
+import io.papermc.paper.command.PaperSubcommand;
|
||||||
|
+import java.time.LocalDateTime;
|
||||||
|
+import java.time.format.DateTimeFormatter;
|
||||||
|
+import org.bukkit.command.Command;
|
||||||
|
+import org.bukkit.command.CommandSender;
|
||||||
|
+import org.bukkit.craftbukkit.CraftServer;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
+
|
||||||
|
+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;
|
||||||
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public final class HeapDumpCommand implements PaperSubcommand {
|
||||||
|
+ @Override
|
||||||
|
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ this.dumpHeap(sender);
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private void dumpHeap(final 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));
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/command/subcommands/ReloadCommand.java b/src/main/java/io/papermc/paper/command/subcommands/ReloadCommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..bd68139ae635f2ad7ec8e7a21e0056a139c4c62e
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/ReloadCommand.java
|
||||||
|
@@ -0,0 +1,33 @@
|
||||||
|
+package io.papermc.paper.command.subcommands;
|
||||||
|
+
|
||||||
|
+import io.papermc.paper.command.PaperSubcommand;
|
||||||
|
+import net.minecraft.server.MinecraftServer;
|
||||||
|
+import org.bukkit.command.Command;
|
||||||
|
+import org.bukkit.command.CommandSender;
|
||||||
|
+import org.bukkit.craftbukkit.CraftServer;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
+
|
||||||
|
+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;
|
||||||
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public final class ReloadCommand implements PaperSubcommand {
|
||||||
|
+ @Override
|
||||||
|
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ this.doReload(sender);
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private void doReload(final 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 server = ((CraftServer) sender.getServer()).getServer();
|
||||||
|
+ server.paperConfigurations.reloadConfigs(server);
|
||||||
|
+ server.server.reloadCount++;
|
||||||
|
+
|
||||||
|
+ Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/command/subcommands/VersionCommand.java b/src/main/java/io/papermc/paper/command/subcommands/VersionCommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..ae60bd96b5284d54676d8e7e4dd5d170b526ec1e
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/VersionCommand.java
|
||||||
|
@@ -0,0 +1,21 @@
|
||||||
|
+package io.papermc.paper.command.subcommands;
|
||||||
|
+
|
||||||
|
+import io.papermc.paper.command.PaperSubcommand;
|
||||||
|
+import net.minecraft.server.MinecraftServer;
|
||||||
|
+import org.bukkit.command.Command;
|
||||||
|
+import org.bukkit.command.CommandSender;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public final class VersionCommand implements PaperSubcommand {
|
||||||
|
+ @Override
|
||||||
|
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ final @Nullable Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version");
|
||||||
|
+ if (ver != null) {
|
||||||
|
+ ver.execute(sender, "paper", new String[0]);
|
||||||
|
+ }
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||||
index e476f93547f386ded0174693a6218d793ccc450b..393e465b0bac55d407f2ec66d7b11ed0537c9641 100644
|
index e476f93547f386ded0174693a6218d793ccc450b..393e465b0bac55d407f2ec66d7b11ed0537c9641 100644
|
||||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||||
|
@ -4,16 +4,16 @@ Date: Sun, 18 Nov 2018 19:49:56 +0000
|
|||||||
Subject: [PATCH] Make the default permission message configurable
|
Subject: [PATCH] Make the default permission message configurable
|
||||||
|
|
||||||
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
index b506cd11b76901827cbe66f46db8df400f7015de..13a5062e539f6f43e6fe582318c36302bd6520ee 100644
|
index b3a58bf4b654e336826dc04da9e2f80ff8b9a9a7..cd4936ef114b504df8649fba8f1823d94a4bb2a2 100644
|
||||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
@@ -57,7 +57,7 @@ public class PaperCommand extends Command {
|
@@ -74,7 +74,7 @@ public final class PaperCommand extends Command {
|
||||||
|
if (sender.hasPermission(BASE_PERM + permission) || sender.hasPermission("bukkit.command.paper")) {
|
||||||
private static boolean testPermission(CommandSender commandSender, String permission) {
|
return true;
|
||||||
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));
|
- sender.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.permissionMessage());
|
+ sender.sendMessage(Bukkit.permissionMessage());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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=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
|
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
|
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d156c74b66 100644
|
index 12c5b6b740eeb986e4b1acc4b2eda6c2c77b63ac..b7b0d134e3a99a630e493aceb06f6188b2c538c3 100644
|
||||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
@@ -9,11 +9,13 @@ import com.google.common.collect.Maps;
|
@@ -1,5 +1,6 @@
|
||||||
import net.minecraft.core.Registry;
|
package io.papermc.paper.command;
|
||||||
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;
|
|
||||||
|
|
||||||
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.BLUE;
|
||||||
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_AQUA;
|
+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.GREEN;
|
||||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||||
import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
public class PaperCommand extends Command {
|
+public final class ChunkDebugCommand implements PaperSubcommand {
|
||||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
+ @Override
|
||||||
- private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version").build();
|
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo").build();
|
+ switch (subCommand) {
|
||||||
|
+ case "debug" -> this.doDebug(sender, args);
|
||||||
public PaperCommand(String name) {
|
+ case "chunkinfo" -> this.doChunkInfo(sender, args);
|
||||||
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");
|
|
||||||
+ }
|
+ }
|
||||||
+ break;
|
+ return true;
|
||||||
+ case "chunkinfo":
|
+ }
|
||||||
|
+
|
||||||
|
+ @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");
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ case "chunkinfo" -> {
|
||||||
+ List<String> worldNames = new ArrayList<>();
|
+ List<String> worldNames = new ArrayList<>();
|
||||||
+ worldNames.add("*");
|
+ worldNames.add("*");
|
||||||
+ for (org.bukkit.World world : Bukkit.getWorlds()) {
|
+ for (org.bukkit.World world : Bukkit.getWorlds()) {
|
||||||
+ worldNames.add(world.getName());
|
+ worldNames.add(world.getName());
|
||||||
+ }
|
+ }
|
||||||
+ if (args.length == 2) {
|
+ if (args.length == 1) {
|
||||||
+ return getListMatchingLast(sender, args, worldNames);
|
+ return CommandUtil.getListMatchingLast(sender, args, worldNames);
|
||||||
+ }
|
+ }
|
||||||
+ break;
|
+ }
|
||||||
}
|
+ }
|
||||||
return Collections.emptyList();
|
+ return Collections.emptyList();
|
||||||
}
|
+ }
|
||||||
@@ -140,6 +159,12 @@ public class PaperCommand extends Command {
|
+
|
||||||
case "reload":
|
+ private void doChunkInfo(final CommandSender sender, final String[] args) {
|
||||||
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) {
|
|
||||||
+ List<org.bukkit.World> worlds;
|
+ List<org.bukkit.World> worlds;
|
||||||
+ if (args.length < 2 || args[1].equals("*")) {
|
+ if (args.length < 1 || args[0].equals("*")) {
|
||||||
+ worlds = Bukkit.getWorlds();
|
+ worlds = Bukkit.getWorlds();
|
||||||
+ } else {
|
+ } else {
|
||||||
+ worlds = new ArrayList<>(args.length - 1);
|
+ worlds = new ArrayList<>(args.length);
|
||||||
+ for (int i = 1; i < args.length; ++i) {
|
+ for (final String arg : args) {
|
||||||
+ org.bukkit.World world = Bukkit.getWorld(args[i]);
|
+ org.bukkit.@Nullable World world = Bukkit.getWorld(arg);
|
||||||
+ if (world == null) {
|
+ if (world == null) {
|
||||||
+ sender.sendMessage(text("World '" + args[i] + "' is invalid", RED));
|
+ sender.sendMessage(text("World '" + arg + "' is invalid", RED));
|
||||||
+ return;
|
+ return;
|
||||||
+ }
|
+ }
|
||||||
+ worlds.add(world);
|
+ worlds.add(world);
|
||||||
@ -127,8 +139,8 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
|
|||||||
+ int accumulatedTicking = 0;
|
+ int accumulatedTicking = 0;
|
||||||
+ int accumulatedEntityTicking = 0;
|
+ int accumulatedEntityTicking = 0;
|
||||||
+
|
+
|
||||||
+ for (org.bukkit.World bukkitWorld : worlds) {
|
+ for (final org.bukkit.World bukkitWorld : worlds) {
|
||||||
+ ServerLevel world = ((CraftWorld)bukkitWorld).getHandle();
|
+ final ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
|
||||||
+
|
+
|
||||||
+ int total = 0;
|
+ int total = 0;
|
||||||
+ int inactive = 0;
|
+ int inactive = 0;
|
||||||
@ -136,7 +148,7 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
|
|||||||
+ int ticking = 0;
|
+ int ticking = 0;
|
||||||
+ int entityTicking = 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) {
|
+ if (chunk.getFullChunkNowUnchecked() == null) {
|
||||||
+ continue;
|
+ continue;
|
||||||
+ }
|
+ }
|
||||||
@ -146,18 +158,10 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
|
|||||||
+ ChunkHolder.FullChunkStatus state = ChunkHolder.getFullChunkStatus(chunk.getTicketLevel());
|
+ ChunkHolder.FullChunkStatus state = ChunkHolder.getFullChunkStatus(chunk.getTicketLevel());
|
||||||
+
|
+
|
||||||
+ switch (state) {
|
+ switch (state) {
|
||||||
+ case INACCESSIBLE:
|
+ case INACCESSIBLE -> ++inactive;
|
||||||
+ ++inactive;
|
+ case BORDER -> ++border;
|
||||||
+ continue;
|
+ case TICKING -> ++ticking;
|
||||||
+ case BORDER:
|
+ case ENTITY_TICKING -> ++entityTicking;
|
||||||
+ ++border;
|
|
||||||
+ continue;
|
|
||||||
+ case TICKING:
|
|
||||||
+ ++ticking;
|
|
||||||
+ continue;
|
|
||||||
+ case ENTITY_TICKING:
|
|
||||||
+ ++entityTicking;
|
|
||||||
+ continue;
|
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
@ -188,16 +192,16 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
|
|||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ private void doDebug(CommandSender sender, String[] args) {
|
+ private void doDebug(final CommandSender sender, final String[] args) {
|
||||||
+ if (args.length < 2) {
|
+ if (args.length < 1) {
|
||||||
+ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
|
+ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
|
||||||
+ return;
|
+ return;
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ String debugType = args[1].toLowerCase(Locale.ENGLISH);
|
+ final String debugType = args[0].toLowerCase(Locale.ENGLISH);
|
||||||
+ switch (debugType) {
|
+ switch (debugType) {
|
||||||
+ case "chunks":
|
+ case "chunks" -> {
|
||||||
+ if (args.length >= 3 && args[2].toLowerCase(Locale.ENGLISH).equals("help")) {
|
+ 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));
|
+ sender.sendMessage(text("Use /paper debug chunks [world] to dump loaded chunk information to a file", RED));
|
||||||
+ break;
|
+ break;
|
||||||
+ }
|
+ }
|
||||||
@ -211,19 +215,13 @@ index 13a5062e539f6f43e6fe582318c36302bd6520ee..527b37e2740d6ca0d8d7695f069111d1
|
|||||||
+ MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr);
|
+ MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr);
|
||||||
+ sender.sendMessage(text("Failed to dump chunk information, see console", RED));
|
+ sender.sendMessage(text("Failed to dump chunk information, see console", RED));
|
||||||
+ }
|
+ }
|
||||||
+
|
+ }
|
||||||
+ break;
|
+ // "help" & default
|
||||||
+ case "help":
|
+ default -> sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
|
||||||
+ // fall through to default
|
|
||||||
+ default:
|
|
||||||
+ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
|
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
/*
|
+}
|
||||||
* 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
|
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
|
||||||
index 162aa7718488a74980843944e0d026ccfd5a65a5..4e29c0a983727fc839a4bcde01d3286396b3587d 100644
|
index 162aa7718488a74980843944e0d026ccfd5a65a5..4e29c0a983727fc839a4bcde01d3286396b3587d 100644
|
||||||
--- a/src/main/java/net/minecraft/server/MCUtil.java
|
--- a/src/main/java/net/minecraft/server/MCUtil.java
|
||||||
|
@ -6,76 +6,87 @@ Subject: [PATCH] Fix Light Command
|
|||||||
This lets you run /paper fixlight <chunkRadius> (max 5) to automatically
|
This lets you run /paper fixlight <chunkRadius> (max 5) to automatically
|
||||||
fix all light data in the chunks.
|
fix all light data in the chunks.
|
||||||
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
index 527b37e2740d6ca0d8d7695f069111d156c74b66..c1c95d6abfc7f1698a74387319ff90ad3a7e39af 100644
|
index 395c43f6440c1e0e47919eef096ea8a8d552ccec..f44ab1d71210e84328661c0feb662989a5635b6d 100644
|
||||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
@@ -12,7 +12,8 @@ import net.minecraft.server.MinecraftServer;
|
@@ -2,6 +2,7 @@ package io.papermc.paper.command;
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
import io.papermc.paper.command.subcommands.ChunkDebugCommand;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import io.papermc.paper.command.subcommands.EntityCommand;
|
||||||
-import net.minecraft.world.entity.Entity;
|
+import io.papermc.paper.command.subcommands.FixLightCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.HeapDumpCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.ReloadCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.VersionCommand;
|
||||||
|
@@ -42,6 +43,7 @@ public final class PaperCommand extends Command {
|
||||||
|
commands.put(Set.of("reload"), new ReloadCommand());
|
||||||
|
commands.put(Set.of("version"), new VersionCommand());
|
||||||
|
commands.put(Set.of("debug", "chunkinfo"), new ChunkDebugCommand());
|
||||||
|
+ commands.put(Set.of("fixlight"), new FixLightCommand());
|
||||||
|
|
||||||
|
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/FixLightCommand.java b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..190df802cb24aa360f6cf4d291e38b4b3fe4a2ac
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||||
|
@@ -0,0 +1,121 @@
|
||||||
|
+package io.papermc.paper.command.subcommands;
|
||||||
|
+
|
||||||
|
+import io.papermc.paper.command.PaperSubcommand;
|
||||||
|
+import java.util.ArrayDeque;
|
||||||
|
+import java.util.Deque;
|
||||||
|
+import net.minecraft.server.MCUtil;
|
||||||
|
+import net.minecraft.server.MinecraftServer;
|
||||||
|
+import net.minecraft.server.level.ChunkHolder;
|
||||||
|
+import net.minecraft.server.level.ServerLevel;
|
||||||
+import net.minecraft.server.level.ServerPlayer;
|
+import net.minecraft.server.level.ServerPlayer;
|
||||||
+import net.minecraft.server.level.ThreadedLevelLightEngine;
|
+import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||||
import net.minecraft.world.entity.EntityType;
|
+import net.minecraft.world.level.ChunkPos;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
+import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
import net.minecraft.server.MCUtil;
|
+import org.bukkit.command.CommandSender;
|
||||||
@@ -25,15 +26,18 @@ import org.bukkit.command.Command;
|
|
||||||
import org.bukkit.command.CommandSender;
|
|
||||||
import org.bukkit.craftbukkit.CraftServer;
|
|
||||||
import org.bukkit.craftbukkit.CraftWorld;
|
|
||||||
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||||
import org.bukkit.entity.Player;
|
+import org.bukkit.entity.Player;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
import java.io.File;
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import java.time.LocalDateTime;
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
import java.time.format.DateTimeFormatter;
|
+
|
||||||
+import java.util.ArrayDeque;
|
+import static net.kyori.adventure.text.Component.text;
|
||||||
import java.util.ArrayList;
|
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
||||||
import java.util.Arrays;
|
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||||
import java.util.Collection;
|
+
|
||||||
import java.util.Collections;
|
+@DefaultQualifier(NonNull.class)
|
||||||
+import java.util.Deque;
|
+public final class FixLightCommand implements PaperSubcommand {
|
||||||
import java.util.Iterator;
|
+ @Override
|
||||||
import java.util.List;
|
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
import java.util.Locale;
|
|
||||||
@@ -50,7 +54,7 @@ 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", "debug", "chunkinfo").build();
|
|
||||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight").build();
|
|
||||||
|
|
||||||
public PaperCommand(String name) {
|
|
||||||
super(name);
|
|
||||||
@@ -165,6 +169,9 @@ public class PaperCommand extends Command {
|
|
||||||
case "chunkinfo":
|
|
||||||
doChunkInfo(sender, args);
|
|
||||||
break;
|
|
||||||
+ case "fixlight":
|
|
||||||
+ this.doFixLight(sender, args);
|
+ this.doFixLight(sender, args);
|
||||||
+ break;
|
+ return true;
|
||||||
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":
|
|
||||||
@@ -425,4 +432,74 @@ public class PaperCommand extends Command {
|
|
||||||
|
|
||||||
Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
|
||||||
}
|
|
||||||
+ private void doFixLight(CommandSender sender, String[] args) {
|
|
||||||
+ if (!(sender instanceof Player)) {
|
|
||||||
+ sender.sendMessage("Only players can use this command");
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+ int radius = 2;
|
|
||||||
+ if (args.length > 1) {
|
|
||||||
+ try {
|
|
||||||
+ radius = Math.min(5, Integer.parseInt(args[1]));
|
|
||||||
+ } catch (Exception e) {
|
|
||||||
+ sender.sendMessage("Not a number");
|
|
||||||
+ return;
|
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
|
+ private void doFixLight(final CommandSender sender, final String[] args) {
|
||||||
|
+ if (!(sender instanceof Player)) {
|
||||||
|
+ sender.sendMessage(text("Only players can use this command", RED));
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ @Nullable Runnable post = null;
|
||||||
|
+ int radius = 2;
|
||||||
|
+ if (args.length > 0) {
|
||||||
|
+ try {
|
||||||
|
+ final int parsed = Integer.parseInt(args[0]);
|
||||||
|
+ if (parsed < 0) {
|
||||||
|
+ sender.sendMessage(text("Radius cannot be negative!", RED));
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ final int maxRadius = 5;
|
||||||
|
+ radius = Math.min(maxRadius, parsed);
|
||||||
|
+ if (radius != parsed) {
|
||||||
|
+ post = () -> sender.sendMessage(text("Radius '" + parsed + "' was not in the required range [0, " + maxRadius + "], it was lowered to the maximum (" + maxRadius + " chunks).", RED));
|
||||||
|
+ }
|
||||||
|
+ } catch (final Exception e) {
|
||||||
|
+ sender.sendMessage(text("'" + args[0] + "' is not a valid number.", RED));
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ CraftPlayer player = (CraftPlayer) sender;
|
+ CraftPlayer player = (CraftPlayer) sender;
|
||||||
@ -85,28 +96,37 @@ index 527b37e2740d6ca0d8d7695f069111d156c74b66..c1c95d6abfc7f1698a74387319ff90ad
|
|||||||
+
|
+
|
||||||
+ net.minecraft.core.BlockPos center = MCUtil.toBlockPosition(player.getLocation());
|
+ net.minecraft.core.BlockPos center = MCUtil.toBlockPosition(player.getLocation());
|
||||||
+ Deque<ChunkPos> queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius));
|
+ Deque<ChunkPos> queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius));
|
||||||
+ updateLight(sender, world, lightengine, queue);
|
+ updateLight(sender, world, lightengine, queue, post);
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ private void updateLight(CommandSender sender, ServerLevel world, ThreadedLevelLightEngine lightengine, Deque<ChunkPos> queue) {
|
+ private void updateLight(
|
||||||
+ ChunkPos coord = queue.poll();
|
+ final CommandSender sender,
|
||||||
|
+ final ServerLevel world,
|
||||||
|
+ final ThreadedLevelLightEngine lightengine,
|
||||||
|
+ final Deque<ChunkPos> queue,
|
||||||
|
+ final @Nullable Runnable done
|
||||||
|
+ ) {
|
||||||
|
+ @Nullable ChunkPos coord = queue.poll();
|
||||||
+ if (coord == null) {
|
+ if (coord == null) {
|
||||||
+ sender.sendMessage("All Chunks Light updated");
|
+ sender.sendMessage(text("All Chunks Light updated", GREEN));
|
||||||
|
+ if (done != null) {
|
||||||
|
+ done.run();
|
||||||
|
+ }
|
||||||
+ return;
|
+ return;
|
||||||
+ }
|
+ }
|
||||||
+ world.getChunkSource().getChunkAtAsynchronously(coord.x, coord.z, false, false).whenCompleteAsync((either, ex) -> {
|
+ world.getChunkSource().getChunkAtAsynchronously(coord.x, coord.z, false, false).whenCompleteAsync((either, ex) -> {
|
||||||
+ if (ex != null) {
|
+ if (ex != null) {
|
||||||
+ sender.sendMessage("Error loading chunk " + coord);
|
+ sender.sendMessage(text("Error loading chunk " + coord, RED));
|
||||||
+ updateLight(sender, world, lightengine, queue);
|
+ updateLight(sender, world, lightengine, queue, done);
|
||||||
+ return;
|
+ return;
|
||||||
+ }
|
+ }
|
||||||
+ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null);
|
+ @Nullable LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null);
|
||||||
+ if (chunk == null) {
|
+ if (chunk == null) {
|
||||||
+ updateLight(sender, world, lightengine, queue);
|
+ updateLight(sender, world, lightengine, queue, done);
|
||||||
+ return;
|
+ return;
|
||||||
+ }
|
+ }
|
||||||
+ lightengine.setTaskPerBatch(world.paperConfig().misc.lightQueueSize + 16 * 256); // ensure full chunk can fit into queue
|
+ lightengine.setTaskPerBatch(world.paperConfig().misc.lightQueueSize + 16 * 256); // ensure full chunk can fit into queue
|
||||||
+ sender.sendMessage("Updating Light " + coord);
|
+ sender.sendMessage(text("Updating Light " + coord));
|
||||||
+ int cx = chunk.getPos().x << 4;
|
+ int cx = chunk.getPos().x << 4;
|
||||||
+ int cz = chunk.getPos().z << 4;
|
+ int cz = chunk.getPos().z << 4;
|
||||||
+ for (int y = 0; y < world.getHeight(); y++) {
|
+ for (int y = 0; y < world.getHeight(); y++) {
|
||||||
@ -118,21 +138,21 @@ index 527b37e2740d6ca0d8d7695f069111d156c74b66..c1c95d6abfc7f1698a74387319ff90ad
|
|||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ lightengine.tryScheduleUpdate();
|
+ lightengine.tryScheduleUpdate();
|
||||||
+ ChunkHolder visibleChunk = world.getChunkSource().chunkMap.getVisibleChunkIfPresent(chunk.coordinateKey);
|
+ @Nullable ChunkHolder visibleChunk = world.getChunkSource().chunkMap.getVisibleChunkIfPresent(chunk.coordinateKey);
|
||||||
+ if (visibleChunk != null) {
|
+ if (visibleChunk != null) {
|
||||||
+ world.getChunkSource().chunkMap.addLightTask(visibleChunk, () -> {
|
+ world.getChunkSource().chunkMap.addLightTask(visibleChunk, () -> {
|
||||||
+ MinecraftServer.getServer().processQueue.add(() -> {
|
+ MinecraftServer.getServer().processQueue.add(() -> {
|
||||||
+ visibleChunk.broadcast(new net.minecraft.network.protocol.game.ClientboundLightUpdatePacket(chunk.getPos(), lightengine, null, null, true), false);
|
+ visibleChunk.broadcast(new net.minecraft.network.protocol.game.ClientboundLightUpdatePacket(chunk.getPos(), lightengine, null, null, true), false);
|
||||||
+ updateLight(sender, world, lightengine, queue);
|
+ updateLight(sender, world, lightengine, queue, done);
|
||||||
+ });
|
+ });
|
||||||
+ });
|
+ });
|
||||||
+ } else {
|
+ } else {
|
||||||
+ updateLight(sender, world, lightengine, queue);
|
+ updateLight(sender, world, lightengine, queue, done);
|
||||||
+ }
|
+ }
|
||||||
+ lightengine.setTaskPerBatch(world.paperConfig().misc.lightQueueSize);
|
+ lightengine.setTaskPerBatch(world.paperConfig().misc.lightQueueSize);
|
||||||
+ }, MinecraftServer.getServer());
|
+ }, MinecraftServer.getServer());
|
||||||
+ }
|
+ }
|
||||||
}
|
+}
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||||
index 375f66bf1617e255b3465b736076a65e080eb36e..d95db45e21861eb9f1623c44dd797429ae158760 100644
|
index 375f66bf1617e255b3465b736076a65e080eb36e..d95db45e21861eb9f1623c44dd797429ae158760 100644
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||||
|
@ -12,123 +12,6 @@ chunks, however it must be enabled by setting the startup flag
|
|||||||
- To clear clear the currently stored sync load info, use
|
- To clear clear the currently stored sync load info, use
|
||||||
/paper syncloadinfo clear
|
/paper syncloadinfo clear
|
||||||
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
|
||||||
index c1c95d6abfc7f1698a74387319ff90ad3a7e39af..fb2cb76e4954d9d69b4a631d6e20bdcc511b6b3f 100644
|
|
||||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
|
||||||
@@ -1,5 +1,6 @@
|
|
||||||
package com.destroystokyo.paper;
|
|
||||||
|
|
||||||
+import com.destroystokyo.paper.io.SyncLoadFinder;
|
|
||||||
import com.google.common.base.Functions;
|
|
||||||
import com.google.common.base.Joiner;
|
|
||||||
import com.google.common.collect.ImmutableSet;
|
|
||||||
@@ -8,6 +9,9 @@ import com.google.common.collect.Lists;
|
|
||||||
import com.google.common.collect.Maps;
|
|
||||||
import net.minecraft.core.Registry;
|
|
||||||
import net.minecraft.resources.ResourceLocation;
|
|
||||||
+import com.google.gson.JsonObject;
|
|
||||||
+import com.google.gson.internal.Streams;
|
|
||||||
+import com.google.gson.stream.JsonWriter;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
|
||||||
@@ -30,6 +34,9 @@ import org.bukkit.craftbukkit.entity.CraftPlayer;
|
|
||||||
import org.bukkit.entity.Player;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
+import java.io.FileOutputStream;
|
|
||||||
+import java.io.PrintStream;
|
|
||||||
+import java.io.StringWriter;
|
|
||||||
import java.time.LocalDateTime;
|
|
||||||
import java.time.format.DateTimeFormatter;
|
|
||||||
import java.util.ArrayDeque;
|
|
||||||
@@ -48,13 +55,14 @@ 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.";
|
|
||||||
- private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight").build();
|
|
||||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "syncloadinfo").build();
|
|
||||||
|
|
||||||
public PaperCommand(String name) {
|
|
||||||
super(name);
|
|
||||||
@@ -97,6 +105,11 @@ public class PaperCommand extends Command {
|
|
||||||
return getListMatchingLast(sender, args, worldNames);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
+ case "syncloadinfo":
|
|
||||||
+ if (args.length == 2) {
|
|
||||||
+ return getListMatchingLast(sender, args, "clear");
|
|
||||||
+ }
|
|
||||||
+ break;
|
|
||||||
}
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
@@ -172,6 +185,9 @@ public class PaperCommand extends Command {
|
|
||||||
case "fixlight":
|
|
||||||
this.doFixLight(sender, args);
|
|
||||||
break;
|
|
||||||
+ case "syncloadinfo":
|
|
||||||
+ this.doSyncLoadInfo(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":
|
|
||||||
@@ -189,6 +205,47 @@ public class PaperCommand extends Command {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ private void doSyncLoadInfo(CommandSender sender, String[] args) {
|
|
||||||
+ if (!SyncLoadFinder.ENABLED) {
|
|
||||||
+ 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(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(text("Writing sync load info to " + file, GREEN));
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+ try {
|
|
||||||
+ final JsonObject data = SyncLoadFinder.serialize();
|
|
||||||
+
|
|
||||||
+ StringWriter stringWriter = new StringWriter();
|
|
||||||
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
|
||||||
+ jsonWriter.setIndent(" ");
|
|
||||||
+ jsonWriter.setLenient(false);
|
|
||||||
+ Streams.write(data, jsonWriter);
|
|
||||||
+
|
|
||||||
+ String fileData = stringWriter.toString();
|
|
||||||
+
|
|
||||||
+ try (
|
|
||||||
+ PrintStream out = new PrintStream(new FileOutputStream(file), false, "UTF-8")
|
|
||||||
+ ) {
|
|
||||||
+ out.print(fileData);
|
|
||||||
+ }
|
|
||||||
+ sender.sendMessage(text("Successfully written sync load information!", GREEN));
|
|
||||||
+ } catch (Throwable thr) {
|
|
||||||
+ sender.sendMessage(text("Failed to write sync load information!", RED));
|
|
||||||
+ thr.printStackTrace();
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
private void doChunkInfo(CommandSender sender, String[] args) {
|
|
||||||
List<org.bukkit.World> worlds;
|
|
||||||
if (args.length < 2 || args[1].equals("*")) {
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
diff --git a/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java b/src/main/java/com/destroystokyo/paper/io/SyncLoadFinder.java
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000000000000000000000000000000000000..0bb4aaa546939b67a5d22865190f30478a9337c1
|
index 0000000000000000000000000000000000000000..0bb4aaa546939b67a5d22865190f30478a9337c1
|
||||||
@ -310,6 +193,110 @@ index 0000000000000000000000000000000000000000..0bb4aaa546939b67a5d22865190f3047
|
|||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+}
|
+}
|
||||||
|
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
|
index b5b4025c0ec07fbd0ec9bf6f6812d6422f549289..40cd55e610cbee25a7cff0e6001bad40b4418551 100644
|
||||||
|
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
|
@@ -5,6 +5,7 @@ import io.papermc.paper.command.subcommands.EntityCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.FixLightCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.HeapDumpCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.ReloadCommand;
|
||||||
|
+import io.papermc.paper.command.subcommands.SyncLoadInfoCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.VersionCommand;
|
||||||
|
import it.unimi.dsi.fastutil.Pair;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
@@ -44,6 +45,7 @@ public final class PaperCommand extends Command {
|
||||||
|
commands.put(Set.of("version"), new VersionCommand());
|
||||||
|
commands.put(Set.of("debug", "chunkinfo"), new ChunkDebugCommand());
|
||||||
|
commands.put(Set.of("fixlight"), new FixLightCommand());
|
||||||
|
+ commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand());
|
||||||
|
|
||||||
|
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/SyncLoadInfoCommand.java b/src/main/java/io/papermc/paper/command/subcommands/SyncLoadInfoCommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..1120aef5b0dd983c467167f77245884e1198552a
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/SyncLoadInfoCommand.java
|
||||||
|
@@ -0,0 +1,78 @@
|
||||||
|
+package io.papermc.paper.command.subcommands;
|
||||||
|
+
|
||||||
|
+import com.destroystokyo.paper.io.SyncLoadFinder;
|
||||||
|
+import com.google.gson.JsonObject;
|
||||||
|
+import com.google.gson.internal.Streams;
|
||||||
|
+import com.google.gson.stream.JsonWriter;
|
||||||
|
+import io.papermc.paper.command.CommandUtil;
|
||||||
|
+import io.papermc.paper.command.PaperSubcommand;
|
||||||
|
+import java.io.File;
|
||||||
|
+import java.io.FileOutputStream;
|
||||||
|
+import java.io.PrintStream;
|
||||||
|
+import java.io.StringWriter;
|
||||||
|
+import java.time.LocalDateTime;
|
||||||
|
+import java.time.format.DateTimeFormatter;
|
||||||
|
+import java.util.List;
|
||||||
|
+import org.bukkit.command.CommandSender;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
+
|
||||||
|
+import static net.kyori.adventure.text.Component.text;
|
||||||
|
+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;
|
||||||
|
+
|
||||||
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public final class SyncLoadInfoCommand implements PaperSubcommand {
|
||||||
|
+ @Override
|
||||||
|
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ this.doSyncLoadInfo(sender, args);
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ return CommandUtil.getListMatchingLast(sender, args, "clear");
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private void doSyncLoadInfo(final CommandSender sender, final String[] args) {
|
||||||
|
+ if (!SyncLoadFinder.ENABLED) {
|
||||||
|
+ sender.sendMessage(text("This command requires the server startup flag '-Dpaper.debug-sync-loads=true' to be set.", RED));
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ if (args.length > 0 && args[0].equals("clear")) {
|
||||||
|
+ SyncLoadFinder.clear();
|
||||||
|
+ 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(text("Writing sync load info to " + file, GREEN));
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+ try {
|
||||||
|
+ final JsonObject data = SyncLoadFinder.serialize();
|
||||||
|
+
|
||||||
|
+ StringWriter stringWriter = new StringWriter();
|
||||||
|
+ JsonWriter jsonWriter = new JsonWriter(stringWriter);
|
||||||
|
+ jsonWriter.setIndent(" ");
|
||||||
|
+ jsonWriter.setLenient(false);
|
||||||
|
+ Streams.write(data, jsonWriter);
|
||||||
|
+
|
||||||
|
+ String fileData = stringWriter.toString();
|
||||||
|
+
|
||||||
|
+ try (
|
||||||
|
+ PrintStream out = new PrintStream(new FileOutputStream(file), false, "UTF-8")
|
||||||
|
+ ) {
|
||||||
|
+ out.print(fileData);
|
||||||
|
+ }
|
||||||
|
+ sender.sendMessage(text("Successfully written sync load information!", GREEN));
|
||||||
|
+ } catch (Throwable thr) {
|
||||||
|
+ sender.sendMessage(text("Failed to write sync load information!", RED));
|
||||||
|
+ thr.printStackTrace();
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
diff --git a/src/main/java/net/minecraft/server/level/ServerChunkCache.java b/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||||
index c12c03b9e79f264ee593373f8a72ed37c0ae8514..509b2ee115584ce80717cc12a7ab548d103b4b92 100644
|
index c12c03b9e79f264ee593373f8a72ed37c0ae8514..509b2ee115584ce80717cc12a7ab548d103b4b92 100644
|
||||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||||
|
@ -4,13 +4,13 @@ Date: Sun, 5 Apr 2020 22:23:14 -0500
|
|||||||
Subject: [PATCH] Add tick times API and /mspt command
|
Subject: [PATCH] Add tick times API and /mspt command
|
||||||
|
|
||||||
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/MSPTCommand.java b/src/main/java/com/destroystokyo/paper/MSPTCommand.java
|
diff --git a/src/main/java/io/papermc/paper/command/MSPTCommand.java b/src/main/java/io/papermc/paper/command/MSPTCommand.java
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000000000000000000000000000000000000..874f0c2a071994c2145848886caa385e0e0bfb9b
|
index 0000000000000000000000000000000000000000..8b5293b0c696ef21d0101493ffa41b60bf0bc86b
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/MSPTCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/MSPTCommand.java
|
||||||
@@ -0,0 +1,99 @@
|
@@ -0,0 +1,102 @@
|
||||||
+package com.destroystokyo.paper;
|
+package io.papermc.paper.command;
|
||||||
+
|
+
|
||||||
+import net.kyori.adventure.text.Component;
|
+import net.kyori.adventure.text.Component;
|
||||||
+import net.minecraft.server.MinecraftServer;
|
+import net.minecraft.server.MinecraftServer;
|
||||||
@ -23,6 +23,8 @@ index 0000000000000000000000000000000000000000..874f0c2a071994c2145848886caa385e
|
|||||||
+import java.util.Arrays;
|
+import java.util.Arrays;
|
||||||
+import java.util.Collections;
|
+import java.util.Collections;
|
||||||
+import java.util.List;
|
+import java.util.List;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
+
|
+
|
||||||
+import static net.kyori.adventure.text.Component.text;
|
+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.GOLD;
|
||||||
@ -31,11 +33,12 @@ index 0000000000000000000000000000000000000000..874f0c2a071994c2145848886caa385e
|
|||||||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||||
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||||
+
|
+
|
||||||
+public class MSPTCommand extends Command {
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public final class MSPTCommand extends Command {
|
||||||
+ private static final DecimalFormat DF = new DecimalFormat("########0.0");
|
+ private static final DecimalFormat DF = new DecimalFormat("########0.0");
|
||||||
+ private static final Component SLASH = text("/");
|
+ private static final Component SLASH = text("/");
|
||||||
+
|
+
|
||||||
+ public MSPTCommand(String name) {
|
+ public MSPTCommand(final String name) {
|
||||||
+ super(name);
|
+ super(name);
|
||||||
+ this.description = "View server tick times";
|
+ this.description = "View server tick times";
|
||||||
+ this.usageMessage = "/mspt";
|
+ this.usageMessage = "/mspt";
|
||||||
@ -110,17 +113,10 @@ index 0000000000000000000000000000000000000000..874f0c2a071994c2145848886caa385e
|
|||||||
+ }
|
+ }
|
||||||
+}
|
+}
|
||||||
diff --git a/src/main/java/io/papermc/paper/command/PaperCommands.java b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
diff --git a/src/main/java/io/papermc/paper/command/PaperCommands.java b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||||
index 82bce89dc174c8c4a4ab8efeffaaf22ddeb377fb..37d28625b9528bbe0cd6d9623e702bbbc2b07883 100644
|
index 6a00f3d38da8107825ab1d405f337fd077b09f72..d31b5ed47cffc61c90c926a0cd2005b72ebddfc5 100644
|
||||||
--- a/src/main/java/io/papermc/paper/command/PaperCommands.java
|
--- a/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
+++ b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||||
@@ -1,5 +1,6 @@
|
@@ -17,6 +17,7 @@ public final class PaperCommands {
|
||||||
package io.papermc.paper.command;
|
|
||||||
|
|
||||||
+import com.destroystokyo.paper.MSPTCommand;
|
|
||||||
import com.destroystokyo.paper.PaperCommand;
|
|
||||||
import net.minecraft.server.MinecraftServer;
|
|
||||||
import org.bukkit.command.Command;
|
|
||||||
@@ -15,6 +16,7 @@ public final class PaperCommands {
|
|
||||||
private static final Map<String, Command> COMMANDS = new HashMap<>();
|
private static final Map<String, Command> COMMANDS = new HashMap<>();
|
||||||
static {
|
static {
|
||||||
COMMANDS.put("paper", new PaperCommand("paper"));
|
COMMANDS.put("paper", new PaperCommand("paper"));
|
||||||
|
@ -5,60 +5,88 @@ Subject: [PATCH] Paper dumpitem command
|
|||||||
|
|
||||||
Let's you quickly view the item in your hands NBT data
|
Let's you quickly view the item in your hands NBT data
|
||||||
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
index fb2cb76e4954d9d69b4a631d6e20bdcc511b6b3f..27c350c123b4349b8b401a06f204de46c88da7ac 100644
|
index 40cd55e610cbee25a7cff0e6001bad40b4418551..4c85d0a61ce508e4a0e3770f46242fb2b1d17420 100644
|
||||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
@@ -31,7 +31,9 @@ import org.bukkit.command.CommandSender;
|
@@ -1,6 +1,7 @@
|
||||||
import org.bukkit.craftbukkit.CraftServer;
|
package io.papermc.paper.command;
|
||||||
import org.bukkit.craftbukkit.CraftWorld;
|
|
||||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
import io.papermc.paper.command.subcommands.ChunkDebugCommand;
|
||||||
|
+import io.papermc.paper.command.subcommands.DumpItemCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.EntityCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.FixLightCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.HeapDumpCommand;
|
||||||
|
@@ -46,6 +47,7 @@ public final class PaperCommand extends Command {
|
||||||
|
commands.put(Set.of("debug", "chunkinfo"), new ChunkDebugCommand());
|
||||||
|
commands.put(Set.of("fixlight"), new FixLightCommand());
|
||||||
|
commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand());
|
||||||
|
+ commands.put(Set.of("dumpitem"), new DumpItemCommand());
|
||||||
|
|
||||||
|
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/DumpItemCommand.java b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..771503ff637fea10d4d8be0f37f3f146c41791d9
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java
|
||||||
|
@@ -0,0 +1,59 @@
|
||||||
|
+package io.papermc.paper.command.subcommands;
|
||||||
|
+
|
||||||
|
+import io.papermc.paper.adventure.PaperAdventure;
|
||||||
|
+import io.papermc.paper.command.PaperSubcommand;
|
||||||
|
+import java.util.Objects;
|
||||||
|
+import net.kyori.adventure.text.Component;
|
||||||
|
+import net.kyori.adventure.text.event.ClickEvent;
|
||||||
|
+import net.minecraft.core.Registry;
|
||||||
|
+import net.minecraft.nbt.CompoundTag;
|
||||||
|
+import net.minecraft.world.item.ItemStack;
|
||||||
|
+import org.bukkit.Bukkit;
|
||||||
|
+import org.bukkit.command.CommandSender;
|
||||||
|
+import org.bukkit.craftbukkit.CraftWorld;
|
||||||
|
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||||
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
+import org.bukkit.craftbukkit.inventory.CraftItemStack;
|
||||||
import org.bukkit.entity.Player;
|
+import org.bukkit.entity.Player;
|
||||||
+import org.bukkit.inventory.ItemStack;
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import java.io.File;
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
import java.io.FileOutputStream;
|
+
|
||||||
@@ -62,7 +64,7 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
+import static net.kyori.adventure.text.Component.text;
|
||||||
|
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
|
||||||
public class PaperCommand extends Command {
|
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
+import static net.kyori.adventure.text.format.TextDecoration.ITALIC;
|
||||||
- private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "syncloadinfo").build();
|
+
|
||||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "syncloadinfo", "dumpitem").build();
|
+@DefaultQualifier(NonNull.class)
|
||||||
|
+public final class DumpItemCommand implements PaperSubcommand {
|
||||||
public PaperCommand(String name) {
|
+ @Override
|
||||||
super(name);
|
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
@@ -176,6 +178,9 @@ public class PaperCommand extends Command {
|
+ this.doDumpItem(sender);
|
||||||
case "reload":
|
+ return true;
|
||||||
doReload(sender);
|
+ }
|
||||||
break;
|
+
|
||||||
+ case "dumpitem":
|
+ private void doDumpItem(final CommandSender sender) {
|
||||||
+ doDumpItem(sender);
|
|
||||||
+ break;
|
|
||||||
case "debug":
|
|
||||||
doDebug(sender, args);
|
|
||||||
break;
|
|
||||||
@@ -489,6 +494,23 @@ public class PaperCommand extends Command {
|
|
||||||
|
|
||||||
Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
|
||||||
}
|
|
||||||
+ private void doDumpItem(CommandSender sender) {
|
|
||||||
+ if (!(sender instanceof Player)) {
|
+ if (!(sender instanceof Player)) {
|
||||||
+ sender.sendMessage("Only players can use this command");
|
+ sender.sendMessage("Only players can use this command");
|
||||||
+ return;
|
+ return;
|
||||||
+ }
|
+ }
|
||||||
+ ItemStack itemInHand = ((CraftPlayer) sender).getItemInHand();
|
+ final ItemStack itemStack = CraftItemStack.asNMSCopy(((CraftPlayer) sender).getItemInHand());
|
||||||
+ net.minecraft.world.item.ItemStack itemStack = CraftItemStack.asNMSCopy(itemInHand);
|
+ final @Nullable CompoundTag tag = itemStack.getTag();
|
||||||
+ net.minecraft.nbt.CompoundTag tag = itemStack.getTag();
|
+ final @Nullable Component nbtComponent = tag == null ? null : PaperAdventure.asAdventure(net.minecraft.nbt.NbtUtils.toPrettyComponent(tag));
|
||||||
+ if (tag != null) {
|
+ final String itemId = Objects.requireNonNull(((CraftWorld) ((CraftPlayer) sender).getWorld()).getHandle().registryAccess()
|
||||||
+ net.kyori.adventure.text.Component nbtComponent = io.papermc.paper.adventure.PaperAdventure.asAdventure(net.minecraft.nbt.NbtUtils.toPrettyComponent(tag));
|
+ .registryOrThrow(Registry.ITEM_REGISTRY).getKey(itemStack.getItem())).toString();
|
||||||
+ Bukkit.getConsoleSender().sendMessage(nbtComponent);
|
+ final Component message = text()
|
||||||
+ sender.sendMessage(nbtComponent);
|
+ .append(text(itemId, YELLOW))
|
||||||
+ } else {
|
+ .apply(b -> {
|
||||||
+ sender.sendMessage("Item does not have NBT");
|
+ if (nbtComponent != null) {
|
||||||
|
+ b.append(nbtComponent);
|
||||||
+ }
|
+ }
|
||||||
|
+ })
|
||||||
|
+ .build();
|
||||||
|
+ Bukkit.getConsoleSender().sendMessage(message);
|
||||||
|
+ sender.sendMessage(message);
|
||||||
|
+ sender.sendMessage(text().content(" Click to copy item to clipboard")
|
||||||
|
+ .color(GRAY)
|
||||||
|
+ .decorate(ITALIC)
|
||||||
|
+ .clickEvent(ClickEvent.copyToClipboard(tag == null ? itemId : (itemId + tag))));
|
||||||
+ }
|
+ }
|
||||||
+
|
+}
|
||||||
private void doFixLight(CommandSender sender, String[] args) {
|
|
||||||
if (!(sender instanceof Player)) {
|
|
||||||
sender.sendMessage("Only players can use this command");
|
|
||||||
|
@ -9,88 +9,66 @@ each player when per-player mob spawning is enabled.
|
|||||||
Also has a hover text on each mob category listing what entity types are
|
Also has a hover text on each mob category listing what entity types are
|
||||||
in said category
|
in said category
|
||||||
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
index 27c350c123b4349b8b401a06f204de46c88da7ac..008bb8896657892fcaf64e134684cc49e62f23f6 100644
|
index d2536f4ffae721f4df714b5345fa3329c3b8e3f5..60b0ce4557390ee7030efe4c90933402c57bab59 100644
|
||||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||||
@@ -3,6 +3,7 @@ package com.destroystokyo.paper;
|
@@ -5,6 +5,7 @@ import io.papermc.paper.command.subcommands.DumpItemCommand;
|
||||||
import com.destroystokyo.paper.io.SyncLoadFinder;
|
import io.papermc.paper.command.subcommands.EntityCommand;
|
||||||
import com.google.common.base.Functions;
|
import io.papermc.paper.command.subcommands.FixLightCommand;
|
||||||
import com.google.common.base.Joiner;
|
import io.papermc.paper.command.subcommands.HeapDumpCommand;
|
||||||
|
+import io.papermc.paper.command.subcommands.MobcapsCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.ReloadCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.SyncLoadInfoCommand;
|
||||||
|
import io.papermc.paper.command.subcommands.VersionCommand;
|
||||||
|
@@ -48,6 +49,7 @@ public final class PaperCommand extends Command {
|
||||||
|
commands.put(Set.of("fixlight"), new FixLightCommand());
|
||||||
|
commands.put(Set.of("syncloadinfo"), new SyncLoadInfoCommand());
|
||||||
|
commands.put(Set.of("dumpitem"), new DumpItemCommand());
|
||||||
|
+ commands.put(Set.of("mobcaps", "playermobcaps"), new MobcapsCommand());
|
||||||
|
|
||||||
|
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/MobcapsCommand.java b/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..2e02d94e2903c48f6d08e743c1cf8bad9f9662df
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java
|
||||||
|
@@ -0,0 +1,229 @@
|
||||||
|
+package io.papermc.paper.command.subcommands;
|
||||||
|
+
|
||||||
+import com.google.common.collect.ImmutableMap;
|
+import com.google.common.collect.ImmutableMap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
+import io.papermc.paper.command.CommandUtil;
|
||||||
import com.google.common.collect.Iterables;
|
+import io.papermc.paper.command.PaperSubcommand;
|
||||||
import com.google.common.collect.Lists;
|
+import java.util.ArrayList;
|
||||||
@@ -12,6 +13,12 @@ import net.minecraft.resources.ResourceLocation;
|
+import java.util.Collections;
|
||||||
import com.google.gson.JsonObject;
|
+import java.util.List;
|
||||||
import com.google.gson.internal.Streams;
|
+import java.util.Map;
|
||||||
import com.google.gson.stream.JsonWriter;
|
+import java.util.function.ToIntFunction;
|
||||||
+import net.kyori.adventure.text.Component;
|
+import net.kyori.adventure.text.Component;
|
||||||
+import net.kyori.adventure.text.ComponentLike;
|
+import net.kyori.adventure.text.ComponentLike;
|
||||||
+import net.kyori.adventure.text.JoinConfiguration;
|
+import net.kyori.adventure.text.JoinConfiguration;
|
||||||
+import net.kyori.adventure.text.TextComponent;
|
+import net.kyori.adventure.text.TextComponent;
|
||||||
+import net.kyori.adventure.text.format.NamedTextColor;
|
+import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
+import net.kyori.adventure.text.format.TextColor;
|
+import net.kyori.adventure.text.format.TextColor;
|
||||||
import net.minecraft.server.MinecraftServer;
|
+import net.minecraft.core.Registry;
|
||||||
import net.minecraft.server.level.ChunkHolder;
|
+import net.minecraft.server.level.ServerLevel;
|
||||||
import net.minecraft.server.level.ServerChunkCache;
|
+import net.minecraft.server.level.ServerPlayer;
|
||||||
@@ -19,8 +26,10 @@ import net.minecraft.server.level.ServerLevel;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
|
||||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
|
||||||
import net.minecraft.world.entity.EntityType;
|
|
||||||
+import net.minecraft.world.entity.MobCategory;
|
+import net.minecraft.world.entity.MobCategory;
|
||||||
import net.minecraft.world.level.ChunkPos;
|
|
||||||
import net.minecraft.server.MCUtil;
|
|
||||||
+import net.minecraft.world.level.NaturalSpawner;
|
+import net.minecraft.world.level.NaturalSpawner;
|
||||||
import org.apache.commons.lang3.tuple.MutablePair;
|
+import org.bukkit.Bukkit;
|
||||||
import org.apache.commons.lang3.tuple.Pair;
|
+import org.bukkit.World;
|
||||||
import org.bukkit.Bukkit;
|
+import org.bukkit.command.CommandSender;
|
||||||
@@ -52,6 +61,7 @@ import java.util.List;
|
+import org.bukkit.craftbukkit.CraftWorld;
|
||||||
import java.util.Locale;
|
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||||
import java.util.Map;
|
+import org.bukkit.entity.Player;
|
||||||
import java.util.Set;
|
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||||
+import java.util.function.ToIntFunction;
|
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||||
import java.util.stream.Collectors;
|
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||||
|
+
|
||||||
import static net.kyori.adventure.text.Component.text;
|
+@DefaultQualifier(NonNull.class)
|
||||||
@@ -64,7 +74,7 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
+public final class MobcapsCommand implements PaperSubcommand {
|
||||||
|
+ static final Map<MobCategory, TextColor> MOB_CATEGORY_COLORS = ImmutableMap.<MobCategory, TextColor>builder()
|
||||||
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", "debug", "chunkinfo", "fixlight", "syncloadinfo", "dumpitem").build();
|
|
||||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight", "syncloadinfo", "dumpitem", "mobcaps", "playermobcaps").build();
|
|
||||||
|
|
||||||
public PaperCommand(String name) {
|
|
||||||
super(name);
|
|
||||||
@@ -97,6 +107,10 @@ public class PaperCommand extends Command {
|
|
||||||
return getListMatchingLast(sender, args, "help", "chunks");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
+ case "mobcaps":
|
|
||||||
+ return getListMatchingLast(sender, args, this.suggestMobcaps(sender, args));
|
|
||||||
+ case "playermobcaps":
|
|
||||||
+ return getListMatchingLast(sender, args, this.suggestPlayerMobcaps(sender, args));
|
|
||||||
case "chunkinfo":
|
|
||||||
List<String> worldNames = new ArrayList<>();
|
|
||||||
worldNames.add("*");
|
|
||||||
@@ -193,6 +207,12 @@ public class PaperCommand extends Command {
|
|
||||||
case "syncloadinfo":
|
|
||||||
this.doSyncLoadInfo(sender, args);
|
|
||||||
break;
|
|
||||||
+ case "mobcaps":
|
|
||||||
+ this.printMobcaps(sender, args);
|
|
||||||
+ break;
|
|
||||||
+ case "playermobcaps":
|
|
||||||
+ this.printPlayerMobcaps(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":
|
|
||||||
@@ -251,6 +271,184 @@ public class PaperCommand extends Command {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+ public static final Map<MobCategory, TextColor> MOB_CATEGORY_COLORS = ImmutableMap.<MobCategory, TextColor>builder()
|
|
||||||
+ .put(MobCategory.MONSTER, NamedTextColor.RED)
|
+ .put(MobCategory.MONSTER, NamedTextColor.RED)
|
||||||
+ .put(MobCategory.CREATURE, NamedTextColor.GREEN)
|
+ .put(MobCategory.CREATURE, NamedTextColor.GREEN)
|
||||||
+ .put(MobCategory.AMBIENT, NamedTextColor.GRAY)
|
+ .put(MobCategory.AMBIENT, NamedTextColor.GRAY)
|
||||||
@ -101,8 +79,26 @@ index 27c350c123b4349b8b401a06f204de46c88da7ac..008bb8896657892fcaf64e134684cc49
|
|||||||
+ .put(MobCategory.MISC, TextColor.color(0x636363))
|
+ .put(MobCategory.MISC, TextColor.color(0x636363))
|
||||||
+ .build();
|
+ .build();
|
||||||
+
|
+
|
||||||
+ private List<String> suggestMobcaps(CommandSender sender, String[] args) {
|
+ @Override
|
||||||
+ if (args.length == 2) {
|
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ switch (subCommand) {
|
||||||
|
+ case "mobcaps" -> this.printMobcaps(sender, args);
|
||||||
|
+ case "playermobcaps" -> this.printPlayerMobcaps(sender, args);
|
||||||
|
+ }
|
||||||
|
+ return true;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ @Override
|
||||||
|
+ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
|
||||||
|
+ return switch (subCommand) {
|
||||||
|
+ case "mobcaps" -> CommandUtil.getListMatchingLast(sender, args, this.suggestMobcaps(args));
|
||||||
|
+ case "playermobcaps" -> CommandUtil.getListMatchingLast(sender, args, this.suggestPlayerMobcaps(sender, args));
|
||||||
|
+ default -> throw new IllegalArgumentException();
|
||||||
|
+ };
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ private List<String> suggestMobcaps(final String[] args) {
|
||||||
|
+ if (args.length == 1) {
|
||||||
+ final List<String> worlds = new ArrayList<>(Bukkit.getWorlds().stream().map(World::getName).toList());
|
+ final List<String> worlds = new ArrayList<>(Bukkit.getWorlds().stream().map(World::getName).toList());
|
||||||
+ worlds.add("*");
|
+ worlds.add("*");
|
||||||
+ return worlds;
|
+ return worlds;
|
||||||
@ -111,8 +107,8 @@ index 27c350c123b4349b8b401a06f204de46c88da7ac..008bb8896657892fcaf64e134684cc49
|
|||||||
+ return Collections.emptyList();
|
+ return Collections.emptyList();
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ private List<String> suggestPlayerMobcaps(CommandSender sender, String[] args) {
|
+ private List<String> suggestPlayerMobcaps(final CommandSender sender, final String[] args) {
|
||||||
+ if (args.length == 2) {
|
+ if (args.length == 1) {
|
||||||
+ final List<String> list = new ArrayList<>();
|
+ final List<String> list = new ArrayList<>();
|
||||||
+ for (final Player player : Bukkit.getOnlinePlayers()) {
|
+ for (final Player player : Bukkit.getOnlinePlayers()) {
|
||||||
+ if (!(sender instanceof Player senderPlayer) || senderPlayer.canSee(player)) {
|
+ if (!(sender instanceof Player senderPlayer) || senderPlayer.canSee(player)) {
|
||||||
@ -125,21 +121,21 @@ index 27c350c123b4349b8b401a06f204de46c88da7ac..008bb8896657892fcaf64e134684cc49
|
|||||||
+ return Collections.emptyList();
|
+ return Collections.emptyList();
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ private void printMobcaps(CommandSender sender, String[] args) {
|
+ private void printMobcaps(final CommandSender sender, final String[] args) {
|
||||||
+ final List<World> worlds;
|
+ final List<World> worlds;
|
||||||
+ if (args.length == 1) {
|
+ if (args.length == 0) {
|
||||||
+ if (sender instanceof Player player) {
|
+ if (sender instanceof Player player) {
|
||||||
+ worlds = List.of(player.getWorld());
|
+ worlds = List.of(player.getWorld());
|
||||||
+ } else {
|
+ } else {
|
||||||
+ sender.sendMessage(Component.text("Must specify a world! ex: '/paper mobcaps world'", NamedTextColor.RED));
|
+ sender.sendMessage(Component.text("Must specify a world! ex: '/paper mobcaps world'", NamedTextColor.RED));
|
||||||
+ return;
|
+ return;
|
||||||
+ }
|
+ }
|
||||||
+ } else if (args.length == 2) {
|
+ } else if (args.length == 1) {
|
||||||
+ final String input = args[1];
|
+ final String input = args[0];
|
||||||
+ if (input.equals("*")) {
|
+ if (input.equals("*")) {
|
||||||
+ worlds = Bukkit.getWorlds();
|
+ worlds = Bukkit.getWorlds();
|
||||||
+ } else {
|
+ } else {
|
||||||
+ final World world = Bukkit.getWorld(input);
|
+ final @Nullable World world = Bukkit.getWorld(input);
|
||||||
+ if (world == null) {
|
+ if (world == null) {
|
||||||
+ sender.sendMessage(Component.text("'" + input + "' is not a valid world!", NamedTextColor.RED));
|
+ sender.sendMessage(Component.text("'" + input + "' is not a valid world!", NamedTextColor.RED));
|
||||||
+ return;
|
+ return;
|
||||||
@ -154,7 +150,7 @@ index 27c350c123b4349b8b401a06f204de46c88da7ac..008bb8896657892fcaf64e134684cc49
|
|||||||
+
|
+
|
||||||
+ for (final World world : worlds) {
|
+ for (final World world : worlds) {
|
||||||
+ final ServerLevel level = ((CraftWorld) world).getHandle();
|
+ final ServerLevel level = ((CraftWorld) world).getHandle();
|
||||||
+ final NaturalSpawner.SpawnState state = level.getChunkSource().getLastSpawnState();
|
+ final NaturalSpawner.@Nullable SpawnState state = level.getChunkSource().getLastSpawnState();
|
||||||
+
|
+
|
||||||
+ final int chunks;
|
+ final int chunks;
|
||||||
+ if (state == null) {
|
+ if (state == null) {
|
||||||
@ -168,7 +164,7 @@ index 27c350c123b4349b8b401a06f204de46c88da7ac..008bb8896657892fcaf64e134684cc49
|
|||||||
+ Component.text(" (" + chunks + " spawnable chunks)")
|
+ Component.text(" (" + chunks + " spawnable chunks)")
|
||||||
+ ));
|
+ ));
|
||||||
+
|
+
|
||||||
+ sender.sendMessage(this.buildMobcapsComponent(
|
+ sender.sendMessage(createMobcapsComponent(
|
||||||
+ category -> {
|
+ category -> {
|
||||||
+ if (state == null) {
|
+ if (state == null) {
|
||||||
+ return 0;
|
+ return 0;
|
||||||
@ -181,17 +177,17 @@ index 27c350c123b4349b8b401a06f204de46c88da7ac..008bb8896657892fcaf64e134684cc49
|
|||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ private void printPlayerMobcaps(CommandSender sender, String[] args) {
|
+ private void printPlayerMobcaps(final CommandSender sender, final String[] args) {
|
||||||
+ final Player player;
|
+ final @Nullable Player player;
|
||||||
+ if (args.length == 1) {
|
+ if (args.length == 0) {
|
||||||
+ if (sender instanceof Player pl) {
|
+ if (sender instanceof Player pl) {
|
||||||
+ player = pl;
|
+ player = pl;
|
||||||
+ } else {
|
+ } else {
|
||||||
+ sender.sendMessage(Component.text("Must specify a player! ex: '/paper playermobcount playerName'", NamedTextColor.RED));
|
+ sender.sendMessage(Component.text("Must specify a player! ex: '/paper playermobcount playerName'", NamedTextColor.RED));
|
||||||
+ return;
|
+ return;
|
||||||
+ }
|
+ }
|
||||||
+ } else if (args.length == 2) {
|
+ } else if (args.length == 1) {
|
||||||
+ final String input = args[1];
|
+ final String input = args[0];
|
||||||
+ player = Bukkit.getPlayerExact(input);
|
+ player = Bukkit.getPlayerExact(input);
|
||||||
+ if (player == null) {
|
+ if (player == null) {
|
||||||
+ sender.sendMessage(Component.text("Could not find player named '" + input + "'", NamedTextColor.RED));
|
+ sender.sendMessage(Component.text("Could not find player named '" + input + "'", NamedTextColor.RED));
|
||||||
@ -211,13 +207,13 @@ index 27c350c123b4349b8b401a06f204de46c88da7ac..008bb8896657892fcaf64e134684cc49
|
|||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), Component.text("Mobcaps for player: "), Component.text(player.getName(), NamedTextColor.GREEN)));
|
+ sender.sendMessage(Component.join(JoinConfiguration.noSeparators(), Component.text("Mobcaps for player: "), Component.text(player.getName(), NamedTextColor.GREEN)));
|
||||||
+ sender.sendMessage(this.buildMobcapsComponent(
|
+ sender.sendMessage(createMobcapsComponent(
|
||||||
+ category -> level.chunkSource.chunkMap.getMobCountNear(serverPlayer, category),
|
+ category -> level.chunkSource.chunkMap.getMobCountNear(serverPlayer, category),
|
||||||
+ category -> level.getWorld().getSpawnLimitUnsafe(org.bukkit.craftbukkit.util.CraftSpawnCategory.toBukkit(category))
|
+ category -> level.getWorld().getSpawnLimitUnsafe(org.bukkit.craftbukkit.util.CraftSpawnCategory.toBukkit(category))
|
||||||
+ ));
|
+ ));
|
||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+ private Component buildMobcapsComponent(final ToIntFunction<MobCategory> countGetter, final ToIntFunction<MobCategory> limitGetter) {
|
+ private static Component createMobcapsComponent(final ToIntFunction<MobCategory> countGetter, final ToIntFunction<MobCategory> limitGetter) {
|
||||||
+ return MOB_CATEGORY_COLORS.entrySet().stream()
|
+ return MOB_CATEGORY_COLORS.entrySet().stream()
|
||||||
+ .map(entry -> {
|
+ .map(entry -> {
|
||||||
+ final MobCategory category = entry.getKey();
|
+ final MobCategory category = entry.getKey();
|
||||||
@ -267,10 +263,7 @@ index 27c350c123b4349b8b401a06f204de46c88da7ac..008bb8896657892fcaf64e134684cc49
|
|||||||
+ .map(ComponentLike::asComponent)
|
+ .map(ComponentLike::asComponent)
|
||||||
+ .collect(Component.toComponent(Component.newline()));
|
+ .collect(Component.toComponent(Component.newline()));
|
||||||
+ }
|
+ }
|
||||||
+
|
+}
|
||||||
private void doChunkInfo(CommandSender sender, String[] args) {
|
|
||||||
List<org.bukkit.World> worlds;
|
|
||||||
if (args.length < 2 || args[1].equals("*")) {
|
|
||||||
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
diff --git a/src/main/java/net/minecraft/world/level/NaturalSpawner.java b/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||||
index fa23e9c476d4edc6176d8b8a6cb13c52d2f66a87..4150e8cd7197eac53042d56f0a53a4951f8824ce 100644
|
index fa23e9c476d4edc6176d8b8a6cb13c52d2f66a87..4150e8cd7197eac53042d56f0a53a4951f8824ce 100644
|
||||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||||
@ -293,7 +286,7 @@ index fa23e9c476d4edc6176d8b8a6cb13c52d2f66a87..4150e8cd7197eac53042d56f0a53a495
|
|||||||
// Paper start - add parameters and int ret type
|
// Paper start - add parameters and int ret type
|
||||||
spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
|
spawnCategoryForChunk(group, world, chunk, checker, runner, Integer.MAX_VALUE, null);
|
||||||
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||||
index 8417136c411c91e4c163446d7e1c2ced5f0084e1..874f317ebae64ba9f42573e3a412dedbeeb90cc7 100644
|
index b84d0c8225a9d5f64dda7ba91a3836e1e21a525c..268a70912722a9ae25b7f88a90daf83f5444781a 100644
|
||||||
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||||
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
|
||||||
@@ -2154,6 +2154,11 @@ public final class CraftServer implements Server {
|
@@ -2154,6 +2154,11 @@ public final class CraftServer implements Server {
|
||||||
@ -328,30 +321,29 @@ index 9d3f7f196c4331662c4c78cac0b047bcd2ff5e77..21bf39ef5b20ecc989881b07c4e6b90c
|
|||||||
}
|
}
|
||||||
return limit;
|
return limit;
|
||||||
}
|
}
|
||||||
diff --git a/src/test/java/io/papermc/paper/PaperCommandTest.java b/src/test/java/io/papermc/paper/PaperCommandTest.java
|
diff --git a/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java b/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java
|
||||||
new file mode 100644
|
new file mode 100644
|
||||||
index 0000000000000000000000000000000000000000..4b5b368ef17bdb90f50e6ccc1f814cf93c7c0590
|
index 0000000000000000000000000000000000000000..f1dd3bca7fa0df8b6ed177bb435877229af1c0c5
|
||||||
--- /dev/null
|
--- /dev/null
|
||||||
+++ b/src/test/java/io/papermc/paper/PaperCommandTest.java
|
+++ b/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java
|
||||||
@@ -0,0 +1,21 @@
|
@@ -0,0 +1,20 @@
|
||||||
+package io.papermc.paper;
|
+package io.papermc.paper.command.subcommands;
|
||||||
+
|
+
|
||||||
+import com.destroystokyo.paper.PaperCommand;
|
|
||||||
+import java.util.HashSet;
|
+import java.util.HashSet;
|
||||||
+import java.util.Set;
|
+import java.util.Set;
|
||||||
+import net.minecraft.world.entity.MobCategory;
|
+import net.minecraft.world.entity.MobCategory;
|
||||||
+import org.junit.Assert;
|
+import org.junit.Assert;
|
||||||
+import org.junit.Test;
|
+import org.junit.Test;
|
||||||
+
|
+
|
||||||
+public class PaperCommandTest {
|
+public class MobcapsCommandTest {
|
||||||
+ @Test
|
+ @Test
|
||||||
+ public void testMobCategoryColors() {
|
+ public void testMobCategoryColors() {
|
||||||
+ final Set<String> missing = new HashSet<>();
|
+ final Set<String> missing = new HashSet<>();
|
||||||
+ for (final MobCategory value : MobCategory.values()) {
|
+ for (final MobCategory value : MobCategory.values()) {
|
||||||
+ if (!PaperCommand.MOB_CATEGORY_COLORS.containsKey(value)) {
|
+ if (!MobcapsCommand.MOB_CATEGORY_COLORS.containsKey(value)) {
|
||||||
+ missing.add(value.getName());
|
+ missing.add(value.getName());
|
||||||
+ }
|
+ }
|
||||||
+ }
|
+ }
|
||||||
+ Assert.assertTrue("PaperCommand.MOB_CATEGORY_COLORS map missing TextColors for [" + String.join(", ", missing + "]"), missing.isEmpty());
|
+ Assert.assertTrue("MobcapsCommand.MOB_CATEGORY_COLORS map missing TextColors for [" + String.join(", ", missing + "]"), missing.isEmpty());
|
||||||
+ }
|
+ }
|
||||||
+}
|
+}
|
||||||
|
@ -8,16 +8,16 @@ tickDistanceManager call can take up quite a bit in
|
|||||||
the function. I saw approximately 1/3rd of the function
|
the function. I saw approximately 1/3rd of the function
|
||||||
on the copy.
|
on the copy.
|
||||||
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
diff --git a/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
|
||||||
index 008bb8896657892fcaf64e134684cc49e62f23f6..a538473f4a75791c7c657f9f1e3ddf96042ab071 100644
|
index 029ad37df71e74d9feb57e4b31b3602e55d49113..4b367982fae4662c326aa247824e9c4185896de1 100644
|
||||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
--- a/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
|
||||||
@@ -480,7 +480,7 @@ public class PaperCommand extends Command {
|
@@ -90,7 +90,7 @@ public final class ChunkDebugCommand implements PaperSubcommand {
|
||||||
int ticking = 0;
|
int ticking = 0;
|
||||||
int entityTicking = 0;
|
int entityTicking = 0;
|
||||||
|
|
||||||
- for (ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) {
|
- for (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) {
|
||||||
+ for (ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunks.getUpdatingMap().values()) { // Paper - change updating chunks map
|
+ for (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunks.getUpdatingMap().values()) { // Paper - change updating chunks map
|
||||||
if (chunk.getFullChunkNowUnchecked() == null) {
|
if (chunk.getFullChunkNowUnchecked() == null) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -4357,24 +4357,68 @@ index 0000000000000000000000000000000000000000..dd995e25ae620ae36cd5eecb2fe10ad0
|
|||||||
+ }
|
+ }
|
||||||
+
|
+
|
||||||
+}
|
+}
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
diff --git a/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||||
index a538473f4a75791c7c657f9f1e3ddf96042ab071..86c41f9ad92dc5fb762d56107f58e3acc07d8890 100644
|
index 190df802cb24aa360f6cf4d291e38b4b3fe4a2ac..68645bbbab9b4225048b647252d8f462028a9c84 100644
|
||||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
--- a/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||||
@@ -709,6 +709,46 @@ public class PaperCommand extends Command {
|
@@ -10,6 +10,7 @@ import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
|
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||||
|
import net.minecraft.world.level.ChunkPos;
|
||||||
|
+import net.minecraft.world.level.chunk.ChunkAccess;
|
||||||
|
import net.minecraft.world.level.chunk.LevelChunk;
|
||||||
|
import org.bukkit.command.CommandSender;
|
||||||
|
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||||
|
@@ -19,6 +20,8 @@ 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;
|
||||||
|
|
||||||
|
@@ -44,7 +47,7 @@ public final class FixLightCommand implements PaperSubcommand {
|
||||||
|
sender.sendMessage(text("Radius cannot be negative!", RED));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
- final int maxRadius = 5;
|
||||||
|
+ final int maxRadius = 32; // Paper - MOOOOOORE
|
||||||
|
radius = Math.min(maxRadius, parsed);
|
||||||
|
if (radius != parsed) {
|
||||||
|
post = () -> sender.sendMessage(text("Radius '" + parsed + "' was not in the required range [0, " + maxRadius + "], it was lowered to the maximum (" + maxRadius + " chunks).", RED));
|
||||||
|
@@ -59,12 +62,67 @@ public final class FixLightCommand implements PaperSubcommand {
|
||||||
|
ServerPlayer handle = player.getHandle();
|
||||||
|
ServerLevel world = (ServerLevel) handle.level;
|
||||||
|
ThreadedLevelLightEngine lightengine = world.getChunkSource().getLightEngine();
|
||||||
|
+ // Paper start - rewrite light engine
|
||||||
|
+ if (true) {
|
||||||
|
+ this.starlightFixLight(handle, world, lightengine, radius, post);
|
||||||
|
+ return;
|
||||||
|
+ }
|
||||||
|
+ // Paper end - rewrite light engine
|
||||||
|
|
||||||
|
net.minecraft.core.BlockPos center = MCUtil.toBlockPosition(player.getLocation());
|
||||||
|
Deque<ChunkPos> queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius));
|
||||||
|
updateLight(sender, world, lightengine, queue, post);
|
||||||
}
|
}
|
||||||
|
|
||||||
+ // Paper start - rewrite light engine
|
+ // Paper start - rewrite light engine
|
||||||
+ private void starlightFixLight(ServerPlayer sender, ServerLevel world, ThreadedLevelLightEngine lightengine, int radius) {
|
+ private void starlightFixLight(
|
||||||
|
+ final ServerPlayer sender,
|
||||||
|
+ final ServerLevel world,
|
||||||
|
+ final ThreadedLevelLightEngine lightengine,
|
||||||
|
+ final int radius,
|
||||||
|
+ final @Nullable Runnable done
|
||||||
|
+ ) {
|
||||||
+ long start = System.nanoTime();
|
+ long start = System.nanoTime();
|
||||||
+ java.util.LinkedHashSet<ChunkPos> chunks = new java.util.LinkedHashSet<>(MCUtil.getSpiralOutChunks(sender.blockPosition(), radius)); // getChunkCoordinates is actually just bad mappings, this function rets position as blockpos
|
+ java.util.LinkedHashSet<ChunkPos> chunks = new java.util.LinkedHashSet<>(MCUtil.getSpiralOutChunks(sender.blockPosition(), radius)); // getChunkCoordinates is actually just bad mappings, this function rets position as blockpos
|
||||||
+
|
+
|
||||||
+ int[] pending = new int[1];
|
+ int[] pending = new int[1];
|
||||||
+ for (java.util.Iterator<ChunkPos> iterator = chunks.iterator(); iterator.hasNext();) {
|
+ for (java.util.Iterator<ChunkPos> iterator = chunks.iterator(); iterator.hasNext(); ) {
|
||||||
+ final ChunkPos chunkPos = iterator.next();
|
+ final ChunkPos chunkPos = iterator.next();
|
||||||
+
|
+
|
||||||
+ final net.minecraft.world.level.chunk.ChunkAccess chunk = (net.minecraft.world.level.chunk.ChunkAccess) world.getChunkSource().getChunkForLighting(chunkPos.x, chunkPos.z);
|
+ final @Nullable ChunkAccess chunk = (ChunkAccess) world.getChunkSource().getChunkForLighting(chunkPos.x, chunkPos.z);
|
||||||
+ if (chunk == null || !chunk.isLightCorrect() || !chunk.getStatus().isOrAfter(net.minecraft.world.level.chunk.ChunkStatus.LIGHT)) {
|
+ if (chunk == null || !chunk.isLightCorrect() || !chunk.getStatus().isOrAfter(net.minecraft.world.level.chunk.ChunkStatus.LIGHT)) {
|
||||||
+ // cannot relight this chunk
|
+ // cannot relight this chunk
|
||||||
+ iterator.remove();
|
+ iterator.remove();
|
||||||
@ -4390,47 +4434,27 @@ index a538473f4a75791c7c657f9f1e3ddf96042ab071..86c41f9ad92dc5fb762d56107f58e3ac
|
|||||||
+ ++relitChunks[0];
|
+ ++relitChunks[0];
|
||||||
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
|
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
|
||||||
+ text("Relit chunk ", BLUE), text(chunkPos.toString()),
|
+ text("Relit chunk ", BLUE), text(chunkPos.toString()),
|
||||||
+ text(", progress: ", BLUE), text((int)(Math.round(100.0 * (double)(relitChunks[0])/(double)pending[0])) + "%")
|
+ text(", progress: ", BLUE), text((int) (Math.round(100.0 * (double) (relitChunks[0]) / (double) pending[0])) + "%")
|
||||||
+ ));
|
+ ));
|
||||||
+ },
|
+ },
|
||||||
+ (int totalRelit) -> {
|
+ (int totalRelit) -> {
|
||||||
+ final long end = System.nanoTime();
|
+ final long end = System.nanoTime();
|
||||||
+ final long diff = Math.round(1.0e-6*(end - start));
|
+ final long diff = Math.round(1.0e-6 * (end - start));
|
||||||
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
|
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
|
||||||
+ text("Relit ", BLUE), text(totalRelit),
|
+ text("Relit ", BLUE), text(totalRelit),
|
||||||
+ text(" chunks. Took ", BLUE), text(diff + "ms")
|
+ text(" chunks. Took ", BLUE), text(diff + "ms")
|
||||||
+ ));
|
+ ));
|
||||||
|
+ if (done != null) {
|
||||||
|
+ done.run();
|
||||||
|
+ }
|
||||||
+ });
|
+ });
|
||||||
+ sender.getBukkitEntity().sendMessage(text().color(BLUE).append(text("Relighting "), text(pending[0], DARK_AQUA), text(" chunks")));
|
+ sender.getBukkitEntity().sendMessage(text().color(BLUE).append(text("Relighting "), text(pending[0], DARK_AQUA), text(" chunks")));
|
||||||
+ }
|
+ }
|
||||||
+ // Paper end - rewrite light engine
|
+ // Paper end - rewrite light engine
|
||||||
+
|
+
|
||||||
private void doFixLight(CommandSender sender, String[] args) {
|
private void updateLight(
|
||||||
if (!(sender instanceof Player)) {
|
final CommandSender sender,
|
||||||
sender.sendMessage("Only players can use this command");
|
final ServerLevel world,
|
||||||
@@ -717,7 +757,7 @@ public class PaperCommand extends Command {
|
|
||||||
int radius = 2;
|
|
||||||
if (args.length > 1) {
|
|
||||||
try {
|
|
||||||
- radius = Math.min(5, Integer.parseInt(args[1]));
|
|
||||||
+ radius = Math.min(32, Integer.parseInt(args[1])); // Paper - MOOOOOORE
|
|
||||||
} catch (Exception e) {
|
|
||||||
sender.sendMessage("Not a number");
|
|
||||||
return;
|
|
||||||
@@ -730,6 +770,13 @@ public class PaperCommand extends Command {
|
|
||||||
ServerLevel world = (ServerLevel) handle.level;
|
|
||||||
ThreadedLevelLightEngine lightengine = world.getChunkSource().getLightEngine();
|
|
||||||
|
|
||||||
+ // Paper start - rewrite light engine
|
|
||||||
+ if (true) {
|
|
||||||
+ this.starlightFixLight(handle, world, lightengine, radius);
|
|
||||||
+ return;
|
|
||||||
+ }
|
|
||||||
+ // Paper end - rewrite light engine
|
|
||||||
+
|
|
||||||
net.minecraft.core.BlockPos center = MCUtil.toBlockPosition(player.getLocation());
|
|
||||||
Deque<ChunkPos> queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius));
|
|
||||||
updateLight(sender, world, lightengine, queue);
|
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||||
index 5b4c3ca92dffff876af18db106310cb14e8612b1..5482be03a667939ff009b6810d5cc90c8601e983 100644
|
index 5b4c3ca92dffff876af18db106310cb14e8612b1..5482be03a667939ff009b6810d5cc90c8601e983 100644
|
||||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||||
|
@ -8,17 +8,17 @@ tick list at all and ignoring them in Spigot's activation range checks. The enti
|
|||||||
list is only used in the tick and tickPassenger methods, so we can safely not add the
|
list is only used in the tick and tickPassenger methods, so we can safely not add the
|
||||||
markers to it.
|
markers to it.
|
||||||
|
|
||||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
diff --git a/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||||
index a96b3f62a7a6aa5c87976dcda93f4b47bc2cd252..8ec20f17a3f8c39ae3ebf3fb630f98b35283ba88 100644
|
index 68f99e93ed3e843b4001a7a27620f88a48b85e67..0dc96c39151ec4dbeec3947cb17606f53a6392d4 100644
|
||||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
--- a/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
+++ b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||||
@@ -626,7 +626,7 @@ public class PaperCommand extends Command {
|
@@ -103,7 +103,7 @@ public final class EntityCommand implements PaperSubcommand {
|
||||||
ChunkPos chunk = e.chunkPosition();
|
ChunkPos chunk = e.chunkPosition();
|
||||||
info.left++;
|
info.left++;
|
||||||
info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
|
info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
|
||||||
- if (!chunkProviderServer.isPositionTicking(e)) {
|
- if (!chunkProviderServer.isPositionTicking(e)) {
|
||||||
+ if (!chunkProviderServer.isPositionTicking(e) || e instanceof net.minecraft.world.entity.Marker) { // Markers aren't ticked.
|
+ if (!chunkProviderServer.isPositionTicking(e) || e instanceof net.minecraft.world.entity.Marker) { // Markers aren't ticked.
|
||||||
nonEntityTicking.merge(key, Integer.valueOf(1), Integer::sum);
|
nonEntityTicking.merge(key, 1, Integer::sum);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
diff --git a/src/main/java/net/minecraft/server/level/ServerLevel.java b/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||||
|
Loading…
Reference in New Issue
Block a user