mirror of
https://github.com/PaperMC/Paper.git
synced 2024-12-29 15:49:00 +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
21e92425e9
commit
35eca19853
11 changed files with 1016 additions and 753 deletions
|
@ -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
|
||||
/paper syncloadinfo clear
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
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;
|
||||
@@ -0,0 +0,0 @@ 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;
|
||||
@@ -0,0 +0,0 @@ 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;
|
||||
@@ -0,0 +0,0 @@ import java.util.stream.Collectors;
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.BLUE;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.DARK_AQUA;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.GRAY;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
||||
- 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);
|
||||
@@ -0,0 +0,0 @@ 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();
|
||||
}
|
||||
@@ -0,0 +0,0 @@ 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":
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
|
||||
|
@ -310,6 +193,110 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ 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;
|
||||
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/SyncLoadInfoCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerChunkCache.java
|
||||
|
|
|
@ -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
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ package com.destroystokyo.paper;
|
||||
import com.destroystokyo.paper.io.SyncLoadFinder;
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.base.Joiner;
|
||||
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ 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;
|
||||
+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;
|
||||
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/MobcapsCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+package io.papermc.paper.command.subcommands;
|
||||
+
|
||||
+import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -0,0 +0,0 @@ import net.minecraft.resources.ResourceLocation;
|
||||
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.util.ArrayList;
|
||||
+import java.util.Collections;
|
||||
+import java.util.List;
|
||||
+import java.util.Map;
|
||||
+import java.util.function.ToIntFunction;
|
||||
+import net.kyori.adventure.text.Component;
|
||||
+import net.kyori.adventure.text.ComponentLike;
|
||||
+import net.kyori.adventure.text.JoinConfiguration;
|
||||
+import net.kyori.adventure.text.TextComponent;
|
||||
+import net.kyori.adventure.text.format.NamedTextColor;
|
||||
+import net.kyori.adventure.text.format.TextColor;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
@@ -0,0 +0,0 @@ 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.core.Registry;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.server.level.ServerPlayer;
|
||||
+import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.server.MCUtil;
|
||||
+import net.minecraft.world.level.NaturalSpawner;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -0,0 +0,0 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
+import java.util.function.ToIntFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
@@ -0,0 +0,0 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
||||
- private static final ImmutableSet<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);
|
||||
@@ -0,0 +0,0 @@ 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("*");
|
||||
@@ -0,0 +0,0 @@ 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":
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
}
|
||||
}
|
||||
|
||||
+ public static final Map<MobCategory, TextColor> MOB_CATEGORY_COLORS = ImmutableMap.<MobCategory, TextColor>builder()
|
||||
+import org.bukkit.Bukkit;
|
||||
+import org.bukkit.World;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.bukkit.craftbukkit.CraftWorld;
|
||||
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
+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;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class MobcapsCommand implements PaperSubcommand {
|
||||
+ static final Map<MobCategory, TextColor> MOB_CATEGORY_COLORS = ImmutableMap.<MobCategory, TextColor>builder()
|
||||
+ .put(MobCategory.MONSTER, NamedTextColor.RED)
|
||||
+ .put(MobCategory.CREATURE, NamedTextColor.GREEN)
|
||||
+ .put(MobCategory.AMBIENT, NamedTextColor.GRAY)
|
||||
|
@ -101,8 +79,26 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ .put(MobCategory.MISC, TextColor.color(0x636363))
|
||||
+ .build();
|
||||
+
|
||||
+ private List<String> suggestMobcaps(CommandSender sender, String[] args) {
|
||||
+ if (args.length == 2) {
|
||||
+ @Override
|
||||
+ 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());
|
||||
+ worlds.add("*");
|
||||
+ return worlds;
|
||||
|
@ -111,8 +107,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ return Collections.emptyList();
|
||||
+ }
|
||||
+
|
||||
+ private List<String> suggestPlayerMobcaps(CommandSender sender, String[] args) {
|
||||
+ if (args.length == 2) {
|
||||
+ private List<String> suggestPlayerMobcaps(final CommandSender sender, final String[] args) {
|
||||
+ if (args.length == 1) {
|
||||
+ final List<String> list = new ArrayList<>();
|
||||
+ for (final Player player : Bukkit.getOnlinePlayers()) {
|
||||
+ if (!(sender instanceof Player senderPlayer) || senderPlayer.canSee(player)) {
|
||||
|
@ -125,21 +121,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ return Collections.emptyList();
|
||||
+ }
|
||||
+
|
||||
+ private void printMobcaps(CommandSender sender, String[] args) {
|
||||
+ private void printMobcaps(final CommandSender sender, final String[] args) {
|
||||
+ final List<World> worlds;
|
||||
+ if (args.length == 1) {
|
||||
+ if (args.length == 0) {
|
||||
+ if (sender instanceof Player player) {
|
||||
+ worlds = List.of(player.getWorld());
|
||||
+ } else {
|
||||
+ sender.sendMessage(Component.text("Must specify a world! ex: '/paper mobcaps world'", NamedTextColor.RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ } else if (args.length == 2) {
|
||||
+ final String input = args[1];
|
||||
+ } else if (args.length == 1) {
|
||||
+ final String input = args[0];
|
||||
+ if (input.equals("*")) {
|
||||
+ worlds = Bukkit.getWorlds();
|
||||
+ } else {
|
||||
+ final World world = Bukkit.getWorld(input);
|
||||
+ final @Nullable World world = Bukkit.getWorld(input);
|
||||
+ if (world == null) {
|
||||
+ sender.sendMessage(Component.text("'" + input + "' is not a valid world!", NamedTextColor.RED));
|
||||
+ return;
|
||||
|
@ -154,7 +150,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ for (final World world : worlds) {
|
||||
+ final ServerLevel level = ((CraftWorld) world).getHandle();
|
||||
+ final NaturalSpawner.SpawnState state = level.getChunkSource().getLastSpawnState();
|
||||
+ final NaturalSpawner.@Nullable SpawnState state = level.getChunkSource().getLastSpawnState();
|
||||
+
|
||||
+ final int chunks;
|
||||
+ if (state == null) {
|
||||
|
@ -168,7 +164,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ Component.text(" (" + chunks + " spawnable chunks)")
|
||||
+ ));
|
||||
+
|
||||
+ sender.sendMessage(this.buildMobcapsComponent(
|
||||
+ sender.sendMessage(createMobcapsComponent(
|
||||
+ category -> {
|
||||
+ if (state == null) {
|
||||
+ return 0;
|
||||
|
@ -181,17 +177,17 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void printPlayerMobcaps(CommandSender sender, String[] args) {
|
||||
+ final Player player;
|
||||
+ if (args.length == 1) {
|
||||
+ private void printPlayerMobcaps(final CommandSender sender, final String[] args) {
|
||||
+ final @Nullable Player player;
|
||||
+ if (args.length == 0) {
|
||||
+ if (sender instanceof Player pl) {
|
||||
+ player = pl;
|
||||
+ } else {
|
||||
+ sender.sendMessage(Component.text("Must specify a player! ex: '/paper playermobcount playerName'", NamedTextColor.RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ } else if (args.length == 2) {
|
||||
+ final String input = args[1];
|
||||
+ } else if (args.length == 1) {
|
||||
+ final String input = args[0];
|
||||
+ player = Bukkit.getPlayerExact(input);
|
||||
+ if (player == null) {
|
||||
+ sender.sendMessage(Component.text("Could not find player named '" + input + "'", NamedTextColor.RED));
|
||||
|
@ -211,13 +207,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+
|
||||
+ 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.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()
|
||||
+ .map(entry -> {
|
||||
+ final MobCategory category = entry.getKey();
|
||||
|
@ -267,10 +263,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ .map(ComponentLike::asComponent)
|
||||
+ .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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/world/level/NaturalSpawner.java
|
||||
|
@ -328,30 +321,29 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
}
|
||||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/test/java/io/papermc/paper/PaperCommandTest.java
|
||||
+++ b/src/test/java/io/papermc/paper/command/subcommands/MobcapsCommandTest.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+package io.papermc.paper;
|
||||
+package io.papermc.paper.command.subcommands;
|
||||
+
|
||||
+import com.destroystokyo.paper.PaperCommand;
|
||||
+import java.util.HashSet;
|
||||
+import java.util.Set;
|
||||
+import net.minecraft.world.entity.MobCategory;
|
||||
+import org.junit.Assert;
|
||||
+import org.junit.Test;
|
||||
+
|
||||
+public class PaperCommandTest {
|
||||
+public class MobcapsCommandTest {
|
||||
+ @Test
|
||||
+ public void testMobCategoryColors() {
|
||||
+ final Set<String> missing = new HashSet<>();
|
||||
+ for (final MobCategory value : MobCategory.values()) {
|
||||
+ if (!PaperCommand.MOB_CATEGORY_COLORS.containsKey(value)) {
|
||||
+ if (!MobcapsCommand.MOB_CATEGORY_COLORS.containsKey(value)) {
|
||||
+ 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());
|
||||
+ }
|
||||
+}
|
||||
|
|
|
@ -4,13 +4,13 @@ Date: Sun, 5 Apr 2020 22:23:14 -0500
|
|||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/MSPTCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/MSPTCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+package com.destroystokyo.paper;
|
||||
+package io.papermc.paper.command;
|
||||
+
|
||||
+import net.kyori.adventure.text.Component;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
|
@ -23,6 +23,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+import java.util.Arrays;
|
||||
+import java.util.Collections;
|
||||
+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.format.NamedTextColor.GOLD;
|
||||
|
@ -31,11 +33,12 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
+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 Component SLASH = text("/");
|
||||
+
|
||||
+ public MSPTCommand(String name) {
|
||||
+ public MSPTCommand(final String name) {
|
||||
+ super(name);
|
||||
+ this.description = "View server tick times";
|
||||
+ this.usageMessage = "/mspt";
|
||||
|
@ -113,13 +116,6 @@ diff --git a/src/main/java/io/papermc/paper/command/PaperCommands.java b/src/mai
|
|||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||
@@ -0,0 +0,0 @@
|
||||
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;
|
||||
@@ -0,0 +0,0 @@ public final class PaperCommands {
|
||||
private static final Map<String, Command> COMMANDS = new HashMap<>();
|
||||
static {
|
||||
|
|
|
@ -31,90 +31,102 @@ For references on certain keywords (ticket, status, etc), please see:
|
|||
https://bugs.mojang.com/browse/MC-141484?focusedCommentId=528273&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-528273
|
||||
https://bugs.mojang.com/browse/MC-141484?focusedCommentId=528577&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-528577
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ import com.google.common.collect.Maps;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
+import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerChunkCache;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
+import net.minecraft.server.MCUtil;
|
||||
import org.apache.commons.lang3.tuple.MutablePair;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.bukkit.Bukkit;
|
||||
@@ -0,0 +0,0 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
package io.papermc.paper.command;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
+import io.papermc.paper.command.subcommands.ChunkDebugCommand;
|
||||
import io.papermc.paper.command.subcommands.EntityCommand;
|
||||
import io.papermc.paper.command.subcommands.HeapDumpCommand;
|
||||
import io.papermc.paper.command.subcommands.ReloadCommand;
|
||||
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+package io.papermc.paper.command.subcommands;
|
||||
+
|
||||
+import io.papermc.paper.command.CommandUtil;
|
||||
+import io.papermc.paper.command.PaperSubcommand;
|
||||
+import java.io.File;
|
||||
+import java.time.LocalDateTime;
|
||||
+import java.time.format.DateTimeFormatter;
|
||||
+import java.util.ArrayList;
|
||||
+import java.util.Collections;
|
||||
+import java.util.List;
|
||||
+import java.util.Locale;
|
||||
+import net.minecraft.server.MCUtil;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import net.minecraft.server.level.ChunkHolder;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
+import org.bukkit.Bukkit;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.bukkit.craftbukkit.CraftWorld;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+import static net.kyori.adventure.text.Component.text;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.BLUE;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.DARK_AQUA;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
||||
- private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version").build();
|
||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo").build();
|
||||
|
||||
public PaperCommand(String name) {
|
||||
super(name);
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
if (args.length == 3)
|
||||
return getListMatchingLast(sender, args, Registry.ENTITY_TYPE.keySet().stream().map(ResourceLocation::toString).sorted().toArray(String[]::new));
|
||||
break;
|
||||
+ case "debug":
|
||||
+ if (args.length == 2) {
|
||||
+ return getListMatchingLast(sender, args, "help", "chunks");
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.GREEN;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class ChunkDebugCommand implements PaperSubcommand {
|
||||
+ @Override
|
||||
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ switch (subCommand) {
|
||||
+ case "debug" -> this.doDebug(sender, args);
|
||||
+ case "chunkinfo" -> this.doChunkInfo(sender, args);
|
||||
+ }
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ @Override
|
||||
+ public List<String> tabComplete(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ switch (subCommand) {
|
||||
+ case "debug" -> {
|
||||
+ if (args.length == 1) {
|
||||
+ return CommandUtil.getListMatchingLast(sender, args, "help", "chunks");
|
||||
+ }
|
||||
+ break;
|
||||
+ case "chunkinfo":
|
||||
+ }
|
||||
+ case "chunkinfo" -> {
|
||||
+ List<String> worldNames = new ArrayList<>();
|
||||
+ worldNames.add("*");
|
||||
+ for (org.bukkit.World world : Bukkit.getWorlds()) {
|
||||
+ worldNames.add(world.getName());
|
||||
+ }
|
||||
+ if (args.length == 2) {
|
||||
+ return getListMatchingLast(sender, args, worldNames);
|
||||
+ if (args.length == 1) {
|
||||
+ return CommandUtil.getListMatchingLast(sender, args, worldNames);
|
||||
+ }
|
||||
+ break;
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
case "reload":
|
||||
doReload(sender);
|
||||
break;
|
||||
+ case "debug":
|
||||
+ doDebug(sender, args);
|
||||
+ break;
|
||||
+ case "chunkinfo":
|
||||
+ doChunkInfo(sender, args);
|
||||
+ break;
|
||||
case "ver":
|
||||
if (!testPermission(sender, "version")) break; // "ver" needs a special check because it's an alias. All other commands are checked up before the switch statement (because they are present in the SUBCOMMANDS set)
|
||||
case "version":
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
return true;
|
||||
}
|
||||
|
||||
+ private void doChunkInfo(CommandSender sender, String[] args) {
|
||||
+ }
|
||||
+ }
|
||||
+ return Collections.emptyList();
|
||||
+ }
|
||||
+
|
||||
+ private void doChunkInfo(final CommandSender sender, final String[] args) {
|
||||
+ List<org.bukkit.World> worlds;
|
||||
+ if (args.length < 2 || args[1].equals("*")) {
|
||||
+ if (args.length < 1 || args[0].equals("*")) {
|
||||
+ worlds = Bukkit.getWorlds();
|
||||
+ } else {
|
||||
+ worlds = new ArrayList<>(args.length - 1);
|
||||
+ for (int i = 1; i < args.length; ++i) {
|
||||
+ org.bukkit.World world = Bukkit.getWorld(args[i]);
|
||||
+ worlds = new ArrayList<>(args.length);
|
||||
+ for (final String arg : args) {
|
||||
+ org.bukkit.@Nullable World world = Bukkit.getWorld(arg);
|
||||
+ if (world == null) {
|
||||
+ sender.sendMessage(text("World '" + args[i] + "' is invalid", RED));
|
||||
+ sender.sendMessage(text("World '" + arg + "' is invalid", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ worlds.add(world);
|
||||
|
@ -127,8 +139,8 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ int accumulatedTicking = 0;
|
||||
+ int accumulatedEntityTicking = 0;
|
||||
+
|
||||
+ for (org.bukkit.World bukkitWorld : worlds) {
|
||||
+ ServerLevel world = ((CraftWorld)bukkitWorld).getHandle();
|
||||
+ for (final org.bukkit.World bukkitWorld : worlds) {
|
||||
+ final ServerLevel world = ((CraftWorld) bukkitWorld).getHandle();
|
||||
+
|
||||
+ int total = 0;
|
||||
+ int inactive = 0;
|
||||
|
@ -136,7 +148,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ int ticking = 0;
|
||||
+ int entityTicking = 0;
|
||||
+
|
||||
+ for (ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) {
|
||||
+ for (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunkMap.values()) {
|
||||
+ if (chunk.getFullChunkNowUnchecked() == null) {
|
||||
+ continue;
|
||||
+ }
|
||||
|
@ -146,18 +158,10 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ ChunkHolder.FullChunkStatus state = ChunkHolder.getFullChunkStatus(chunk.getTicketLevel());
|
||||
+
|
||||
+ switch (state) {
|
||||
+ case INACCESSIBLE:
|
||||
+ ++inactive;
|
||||
+ continue;
|
||||
+ case BORDER:
|
||||
+ ++border;
|
||||
+ continue;
|
||||
+ case TICKING:
|
||||
+ ++ticking;
|
||||
+ continue;
|
||||
+ case ENTITY_TICKING:
|
||||
+ ++entityTicking;
|
||||
+ continue;
|
||||
+ case INACCESSIBLE -> ++inactive;
|
||||
+ case BORDER -> ++border;
|
||||
+ case TICKING -> ++ticking;
|
||||
+ case ENTITY_TICKING -> ++entityTicking;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
|
@ -188,16 +192,16 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void doDebug(CommandSender sender, String[] args) {
|
||||
+ if (args.length < 2) {
|
||||
+ private void doDebug(final CommandSender sender, final String[] args) {
|
||||
+ if (args.length < 1) {
|
||||
+ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+
|
||||
+ String debugType = args[1].toLowerCase(Locale.ENGLISH);
|
||||
+ final String debugType = args[0].toLowerCase(Locale.ENGLISH);
|
||||
+ switch (debugType) {
|
||||
+ case "chunks":
|
||||
+ if (args.length >= 3 && args[2].toLowerCase(Locale.ENGLISH).equals("help")) {
|
||||
+ case "chunks" -> {
|
||||
+ if (args.length >= 2 && args[1].toLowerCase(Locale.ENGLISH).equals("help")) {
|
||||
+ sender.sendMessage(text("Use /paper debug chunks [world] to dump loaded chunk information to a file", RED));
|
||||
+ break;
|
||||
+ }
|
||||
|
@ -211,19 +215,13 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ MinecraftServer.LOGGER.warn("Failed to dump chunk information to file " + file.toString(), thr);
|
||||
+ sender.sendMessage(text("Failed to dump chunk information, see console", RED));
|
||||
+ }
|
||||
+
|
||||
+ break;
|
||||
+ case "help":
|
||||
+ // fall through to default
|
||||
+ default:
|
||||
+ sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ // "help" & default
|
||||
+ default -> sender.sendMessage(text("Use /paper debug [chunks] help for more information on a specific command", RED));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/*
|
||||
* Ported from MinecraftForge - author: LexManos <LexManos@gmail.com> - License: LGPLv2.1
|
||||
*/
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/MCUtil.java b/src/main/java/net/minecraft/server/MCUtil.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/MCUtil.java
|
||||
|
|
|
@ -8,16 +8,16 @@ tickDistanceManager call can take up quite a bit in
|
|||
the function. I saw approximately 1/3rd of the function
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
--- a/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/ChunkDebugCommand.java
|
||||
@@ -0,0 +0,0 @@ public final class ChunkDebugCommand implements PaperSubcommand {
|
||||
int ticking = 0;
|
||||
int entityTicking = 0;
|
||||
|
||||
- for (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.updatingChunkMap.values()) {
|
||||
+ for (final ChunkHolder chunk : world.getChunkSource().chunkMap.updatingChunks.getUpdatingMap().values()) { // Paper - change updating chunks map
|
||||
if (chunk.getFullChunkNowUnchecked() == null) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -8,19 +8,19 @@ 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
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
ChunkPos chunk = e.chunkPosition();
|
||||
info.left++;
|
||||
info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
|
||||
- if (!chunkProviderServer.isPositionTicking(e)) {
|
||||
+ if (!chunkProviderServer.isPositionTicking(e) || e instanceof net.minecraft.world.entity.Marker) { // Markers aren't ticked.
|
||||
nonEntityTicking.merge(key, Integer.valueOf(1), Integer::sum);
|
||||
}
|
||||
});
|
||||
--- a/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||
@@ -0,0 +0,0 @@ public final class EntityCommand implements PaperSubcommand {
|
||||
ChunkPos chunk = e.chunkPosition();
|
||||
info.left++;
|
||||
info.right.put(chunk, info.right.getOrDefault(chunk, 0) + 1);
|
||||
- if (!chunkProviderServer.isPositionTicking(e)) {
|
||||
+ if (!chunkProviderServer.isPositionTicking(e) || e instanceof net.minecraft.world.entity.Marker) { // Markers aren't ticked.
|
||||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ServerLevel.java
|
||||
|
|
|
@ -6,76 +6,87 @@ Subject: [PATCH] Fix Light Command
|
|||
This lets you run /paper fixlight <chunkRadius> (max 5) to automatically
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ 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;
|
||||
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ package io.papermc.paper.command;
|
||||
|
||||
import io.papermc.paper.command.subcommands.ChunkDebugCommand;
|
||||
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.VersionCommand;
|
||||
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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.ThreadedLevelLightEngine;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.server.MCUtil;
|
||||
@@ -0,0 +0,0 @@ import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
+import net.minecraft.world.level.ChunkPos;
|
||||
+import net.minecraft.world.level.chunk.LevelChunk;
|
||||
+import org.bukkit.command.CommandSender;
|
||||
+import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.io.File;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
+import java.util.ArrayDeque;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
+import java.util.Deque;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -0,0 +0,0 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
||||
- private static final ImmutableSet<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);
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
case "chunkinfo":
|
||||
doChunkInfo(sender, args);
|
||||
break;
|
||||
+ case "fixlight":
|
||||
+ this.doFixLight(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":
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
|
||||
Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
||||
}
|
||||
+ private void doFixLight(CommandSender sender, String[] args) {
|
||||
+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.GREEN;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class FixLightCommand implements PaperSubcommand {
|
||||
+ @Override
|
||||
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ this.doFixLight(sender, args);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ private void doFixLight(final CommandSender sender, final String[] args) {
|
||||
+ if (!(sender instanceof Player)) {
|
||||
+ sender.sendMessage("Only players can use this command");
|
||||
+ sender.sendMessage(text("Only players can use this command", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ @Nullable Runnable post = null;
|
||||
+ int radius = 2;
|
||||
+ if (args.length > 1) {
|
||||
+ if (args.length > 0) {
|
||||
+ try {
|
||||
+ radius = Math.min(5, Integer.parseInt(args[1]));
|
||||
+ } catch (Exception e) {
|
||||
+ sender.sendMessage("Not a number");
|
||||
+ 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;
|
||||
|
@ -85,28 +96,37 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ net.minecraft.core.BlockPos center = MCUtil.toBlockPosition(player.getLocation());
|
||||
+ 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) {
|
||||
+ ChunkPos coord = queue.poll();
|
||||
+ private void updateLight(
|
||||
+ 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) {
|
||||
+ sender.sendMessage("All Chunks Light updated");
|
||||
+ sender.sendMessage(text("All Chunks Light updated", GREEN));
|
||||
+ if (done != null) {
|
||||
+ done.run();
|
||||
+ }
|
||||
+ return;
|
||||
+ }
|
||||
+ world.getChunkSource().getChunkAtAsynchronously(coord.x, coord.z, false, false).whenCompleteAsync((either, ex) -> {
|
||||
+ if (ex != null) {
|
||||
+ sender.sendMessage("Error loading chunk " + coord);
|
||||
+ updateLight(sender, world, lightengine, queue);
|
||||
+ sender.sendMessage(text("Error loading chunk " + coord, RED));
|
||||
+ updateLight(sender, world, lightengine, queue, done);
|
||||
+ 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) {
|
||||
+ updateLight(sender, world, lightengine, queue);
|
||||
+ updateLight(sender, world, lightengine, queue, done);
|
||||
+ return;
|
||||
+ }
|
||||
+ 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 cz = chunk.getPos().z << 4;
|
||||
+ for (int y = 0; y < world.getHeight(); y++) {
|
||||
|
@ -118,21 +138,21 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+ }
|
||||
+ lightengine.tryScheduleUpdate();
|
||||
+ ChunkHolder visibleChunk = world.getChunkSource().chunkMap.getVisibleChunkIfPresent(chunk.coordinateKey);
|
||||
+ @Nullable ChunkHolder visibleChunk = world.getChunkSource().chunkMap.getVisibleChunkIfPresent(chunk.coordinateKey);
|
||||
+ if (visibleChunk != null) {
|
||||
+ world.getChunkSource().chunkMap.addLightTask(visibleChunk, () -> {
|
||||
+ MinecraftServer.getServer().processQueue.add(() -> {
|
||||
+ 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 {
|
||||
+ updateLight(sender, world, lightengine, queue);
|
||||
+ updateLight(sender, world, lightengine, queue, done);
|
||||
+ }
|
||||
+ lightengine.setTaskPerBatch(world.paperConfig().misc.lightQueueSize);
|
||||
+ }, MinecraftServer.getServer());
|
||||
+ }
|
||||
}
|
||||
+}
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkMap.java b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
|
|
|
@ -4,16 +4,16 @@ Date: Sun, 18 Nov 2018 19:49:56 +0000
|
|||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
|
||||
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));
|
||||
+ commandSender.sendMessage(Bukkit.permissionMessage());
|
||||
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ public final class PaperCommand extends Command {
|
||||
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));
|
||||
+ sender.sendMessage(Bukkit.permissionMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -4,102 +4,50 @@ Date: Mon, 29 Feb 2016 21:02:09 -0600
|
|||
Subject: [PATCH] Paper command
|
||||
|
||||
|
||||
diff --git a/src/main/java/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/CommandUtil.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+package com.destroystokyo.paper;
|
||||
+package io.papermc.paper.command;
|
||||
+
|
||||
+import com.google.common.base.Functions;
|
||||
+import com.google.common.base.Joiner;
|
||||
+import com.google.common.collect.ImmutableSet;
|
||||
+import com.google.common.collect.Iterables;
|
||||
+import com.google.common.collect.Lists;
|
||||
+import com.google.common.collect.Maps;
|
||||
+import net.minecraft.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.Arrays;
|
||||
+import java.util.Collection;
|
||||
+import java.util.Collections;
|
||||
+import java.util.Iterator;
|
||||
+import java.util.List;
|
||||
+import java.util.Locale;
|
||||
+import java.util.Map;
|
||||
+import java.util.Set;
|
||||
+import java.util.stream.Collectors;
|
||||
+import net.minecraft.resources.ResourceLocation;
|
||||
+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.GREEN;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.RED;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
+
|
||||
+public class PaperCommand extends Command {
|
||||
+ private static final String BASE_PERM = "bukkit.command.paper.";
|
||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version").build();
|
||||
+
|
||||
+ 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();
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class CommandUtil {
|
||||
+ private CommandUtil() {
|
||||
+ }
|
||||
+
|
||||
+ // Code from Mojang - copyright them
|
||||
+ public static List<String> getListMatchingLast(CommandSender sender, String[] args, String... matches) {
|
||||
+ return getListMatchingLast(sender, args, (Collection) Arrays.asList(matches));
|
||||
+ public static List<String> getListMatchingLast(
|
||||
+ 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());
|
||||
+ }
|
||||
+
|
||||
+ 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];
|
||||
+ ArrayList<String> results = Lists.newArrayList();
|
||||
+
|
||||
|
@ -109,7 +57,7 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ while (iterator.hasNext()) {
|
||||
+ 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);
|
||||
+ }
|
||||
+ }
|
||||
|
@ -130,171 +78,156 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ return results;
|
||||
+ }
|
||||
+ // 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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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
|
||||
+ public boolean execute(CommandSender sender, String commandLabel, String[] args) {
|
||||
+ if (!testPermission(sender)) return true;
|
||||
+ public List<String> tabComplete(
|
||||
+ 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) {
|
||||
+ sender.sendMessage(text("Usage: " + this.usageMessage, RED));
|
||||
+ return false;
|
||||
+ }
|
||||
+ if (SUBCOMMANDS.contains(args[0].toLowerCase(Locale.ENGLISH))) {
|
||||
+ if (!testPermission(sender, args[0].toLowerCase(Locale.ENGLISH))) return true;
|
||||
+ }
|
||||
+ switch (args[0].toLowerCase(Locale.ENGLISH)) {
|
||||
+ case "heap":
|
||||
+ dumpHeap(sender);
|
||||
+ break;
|
||||
+ case "entity":
|
||||
+ listEntities(sender, args);
|
||||
+ break;
|
||||
+ case "reload":
|
||||
+ doReload(sender);
|
||||
+ break;
|
||||
+ case "ver":
|
||||
+ if (!testPermission(sender, "version")) break; // "ver" needs a special check because it's an alias. All other commands are checked up before the switch statement (because they are present in the SUBCOMMANDS set)
|
||||
+ case "version":
|
||||
+ Command ver = MinecraftServer.getServer().server.getCommandMap().getCommand("version");
|
||||
+ if (ver != null) {
|
||||
+ ver.execute(sender, commandLabel, new String[0]);
|
||||
+ break;
|
||||
+ }
|
||||
+ // else - fall through to default
|
||||
+ default:
|
||||
+ sender.sendMessage(text("Usage: " + this.usageMessage, RED));
|
||||
+ return false;
|
||||
+ final @Nullable Pair<String, PaperSubcommand> subCommand = resolveCommand(args[0]);
|
||||
+
|
||||
+ if (subCommand == null) {
|
||||
+ sender.sendMessage(text("Usage: " + this.usageMessage, RED));
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
+ return true;
|
||||
+ if (!testPermission(sender, subCommand.first())) {
|
||||
+ 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;
|
||||
+ private static @Nullable Pair<String, PaperSubcommand> resolveCommand(String label) {
|
||||
+ label = label.toLowerCase(Locale.ENGLISH);
|
||||
+ @Nullable PaperSubcommand subCommand = SUBCOMMANDS.get(label);
|
||||
+ if (subCommand == null) {
|
||||
+ final @Nullable String command = ALIASES.get(label);
|
||||
+ if (command != null) {
|
||||
+ label = command;
|
||||
+ subCommand = SUBCOMMANDS.get(command);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ switch (args[1].toLowerCase(Locale.ENGLISH)) {
|
||||
+ case "list":
|
||||
+ String filter = "*";
|
||||
+ if (args.length > 2) {
|
||||
+ if (args[2].toLowerCase(Locale.ENGLISH).equals("help")) {
|
||||
+ sender.sendMessage(text("Use /paper entity list [filter] [worldName] to get entity info that matches the optional filter.", RED));
|
||||
+ return;
|
||||
+ }
|
||||
+ filter = args[2];
|
||||
+ }
|
||||
+ final String cleanfilter = filter.replace("?", ".?").replace("*", ".*?");
|
||||
+ Set<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;
|
||||
+ if (subCommand != null) {
|
||||
+ return Pair.of(label, subCommand);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void dumpHeap(CommandSender sender) {
|
||||
+ java.nio.file.Path dir = java.nio.file.Paths.get("./dumps");
|
||||
+ String name = "heap-dump-" + DateTimeFormatter.ofPattern("yyyy-MM-dd_HH.mm.ss").format(LocalDateTime.now());
|
||||
+
|
||||
+ Command.broadcastCommandMessage(sender, text("Writing JVM heap data...", YELLOW));
|
||||
+
|
||||
+ java.nio.file.Path file = CraftServer.dumpHeap(dir, name);
|
||||
+ if (file != null) {
|
||||
+ Command.broadcastCommandMessage(sender, text("Heap dump saved to " + file, GREEN));
|
||||
+ } else {
|
||||
+ Command.broadcastCommandMessage(sender, text("Failed to write heap dump, see server log for details", RED));
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ private void doReload(CommandSender sender) {
|
||||
+ Command.broadcastCommandMessage(sender, text("Please note that this command is not supported and may cause issues.", RED));
|
||||
+ Command.broadcastCommandMessage(sender, text("If you encounter any issues please use the /stop command to restart your server.", RED));
|
||||
+
|
||||
+ MinecraftServer server = ((CraftServer) sender.getServer()).getServer();
|
||||
+ server.paperConfigurations.reloadConfigs(server);
|
||||
+ server.server.reloadCount++;
|
||||
+
|
||||
+ Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
||||
+ return null;
|
||||
+ }
|
||||
+}
|
||||
diff --git a/src/main/java/io/papermc/paper/command/PaperCommands.java b/src/main/java/io/papermc/paper/command/PaperCommands.java
|
||||
|
@ -305,13 +238,15 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
@@ -0,0 +0,0 @@
|
||||
+package io.papermc.paper.command;
|
||||
+
|
||||
+import com.destroystokyo.paper.PaperCommand;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import org.bukkit.command.Command;
|
||||
+
|
||||
+import java.util.HashMap;
|
||||
+import java.util.Map;
|
||||
+import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
+import org.checkerframework.framework.qual.DefaultQualifier;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class PaperCommands {
|
||||
+
|
||||
+ private PaperCommands() {
|
||||
|
@ -328,6 +263,289 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ });
|
||||
+ }
|
||||
+}
|
||||
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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperSubcommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/EntityCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/HeapDumpCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/ReloadCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/VersionCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java
|
||||
|
|
|
@ -5,60 +5,88 @@ Subject: [PATCH] Paper dumpitem command
|
|||
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
import org.bukkit.craftbukkit.entity.CraftPlayer;
|
||||
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
package io.papermc.paper.command;
|
||||
|
||||
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;
|
||||
@@ -0,0 +0,0 @@ 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..0000000000000000000000000000000000000000
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/DumpItemCommand.java
|
||||
@@ -0,0 +0,0 @@
|
||||
+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.entity.Player;
|
||||
+import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
@@ -0,0 +0,0 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
||||
- private static final ImmutableSet<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();
|
||||
|
||||
public PaperCommand(String name) {
|
||||
super(name);
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
case "reload":
|
||||
doReload(sender);
|
||||
break;
|
||||
+ case "dumpitem":
|
||||
+ doDumpItem(sender);
|
||||
+ break;
|
||||
case "debug":
|
||||
doDebug(sender, args);
|
||||
break;
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
|
||||
Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
||||
}
|
||||
+ private void doDumpItem(CommandSender sender) {
|
||||
+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.GRAY;
|
||||
+import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
+import static net.kyori.adventure.text.format.TextDecoration.ITALIC;
|
||||
+
|
||||
+@DefaultQualifier(NonNull.class)
|
||||
+public final class DumpItemCommand implements PaperSubcommand {
|
||||
+ @Override
|
||||
+ public boolean execute(final CommandSender sender, final String subCommand, final String[] args) {
|
||||
+ this.doDumpItem(sender);
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ private void doDumpItem(final CommandSender sender) {
|
||||
+ if (!(sender instanceof Player)) {
|
||||
+ sender.sendMessage("Only players can use this command");
|
||||
+ return;
|
||||
+ }
|
||||
+ ItemStack itemInHand = ((CraftPlayer) sender).getItemInHand();
|
||||
+ net.minecraft.world.item.ItemStack itemStack = CraftItemStack.asNMSCopy(itemInHand);
|
||||
+ net.minecraft.nbt.CompoundTag tag = itemStack.getTag();
|
||||
+ if (tag != null) {
|
||||
+ net.kyori.adventure.text.Component nbtComponent = io.papermc.paper.adventure.PaperAdventure.asAdventure(net.minecraft.nbt.NbtUtils.toPrettyComponent(tag));
|
||||
+ Bukkit.getConsoleSender().sendMessage(nbtComponent);
|
||||
+ sender.sendMessage(nbtComponent);
|
||||
+ } else {
|
||||
+ sender.sendMessage("Item does not have NBT");
|
||||
+ }
|
||||
+ final ItemStack itemStack = CraftItemStack.asNMSCopy(((CraftPlayer) sender).getItemInHand());
|
||||
+ final @Nullable CompoundTag tag = itemStack.getTag();
|
||||
+ final @Nullable Component nbtComponent = tag == null ? null : PaperAdventure.asAdventure(net.minecraft.nbt.NbtUtils.toPrettyComponent(tag));
|
||||
+ final String itemId = Objects.requireNonNull(((CraftWorld) ((CraftPlayer) sender).getWorld()).getHandle().registryAccess()
|
||||
+ .registryOrThrow(Registry.ITEM_REGISTRY).getKey(itemStack.getItem())).toString();
|
||||
+ final Component message = text()
|
||||
+ .append(text(itemId, YELLOW))
|
||||
+ .apply(b -> {
|
||||
+ 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");
|
||||
+}
|
||||
|
|
|
@ -4357,24 +4357,68 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+ }
|
||||
+
|
||||
+}
|
||||
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 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -0,0 +0,0 @@ public class PaperCommand extends Command {
|
||||
}
|
||||
--- a/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||
@@ -0,0 +0,0 @@ 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;
|
||||
@@ -0,0 +0,0 @@ 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;
|
||||
|
||||
@@ -0,0 +0,0 @@ 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));
|
||||
@@ -0,0 +0,0 @@ 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
|
||||
+ 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();
|
||||
+ 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];
|
||||
+ 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 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)) {
|
||||
+ // cannot relight this chunk
|
||||
+ iterator.remove();
|
||||
|
@ -4386,51 +4430,31 @@ index 0000000000000000000000000000000000000000..00000000000000000000000000000000
|
|||
+
|
||||
+ int[] relitChunks = new int[1];
|
||||
+ lightengine.relight(chunks,
|
||||
+ (ChunkPos chunkPos) -> {
|
||||
+ ++relitChunks[0];
|
||||
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
|
||||
+ text("Relit chunk ", BLUE), text(chunkPos.toString()),
|
||||
+ text(", progress: ", BLUE), text((int)(Math.round(100.0 * (double)(relitChunks[0])/(double)pending[0])) + "%")
|
||||
+ ));
|
||||
+ },
|
||||
+ (int totalRelit) -> {
|
||||
+ final long end = System.nanoTime();
|
||||
+ final long diff = Math.round(1.0e-6*(end - start));
|
||||
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
|
||||
+ text("Relit ", BLUE), text(totalRelit),
|
||||
+ text(" chunks. Took ", BLUE), text(diff + "ms")
|
||||
+ ));
|
||||
+ });
|
||||
+ (ChunkPos chunkPos) -> {
|
||||
+ ++relitChunks[0];
|
||||
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
|
||||
+ text("Relit chunk ", BLUE), text(chunkPos.toString()),
|
||||
+ text(", progress: ", BLUE), text((int) (Math.round(100.0 * (double) (relitChunks[0]) / (double) pending[0])) + "%")
|
||||
+ ));
|
||||
+ },
|
||||
+ (int totalRelit) -> {
|
||||
+ final long end = System.nanoTime();
|
||||
+ final long diff = Math.round(1.0e-6 * (end - start));
|
||||
+ sender.getBukkitEntity().sendMessage(text().color(DARK_AQUA).append(
|
||||
+ text("Relit ", BLUE), text(totalRelit),
|
||||
+ 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")));
|
||||
+ }
|
||||
+ // Paper end - rewrite light engine
|
||||
+
|
||||
private void doFixLight(CommandSender sender, String[] args) {
|
||||
if (!(sender instanceof Player)) {
|
||||
sender.sendMessage("Only players can use this command");
|
||||
@@ -0,0 +0,0 @@ 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;
|
||||
@@ -0,0 +0,0 @@ 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);
|
||||
private void updateLight(
|
||||
final CommandSender sender,
|
||||
final ServerLevel world,
|
||||
diff --git a/src/main/java/net/minecraft/server/level/ChunkHolder.java b/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkHolder.java
|
||||
|
|
Loading…
Reference in a new issue