Refactor paper command (#8112)
* Refactor paper command * Improve paper dumpitem output * Register paper command permissions Would be nice to add descriptions for these too, but that's an enhancement for another time * Update MobcapsCommandTest fail message * Notify on bad radius for fix light * fixup rebase
This commit is contained in:
parent
5ffeb70186
commit
e294802977
11 changed files with 1035 additions and 772 deletions
|
@ -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
|
||||
index 527b37e2740d6ca0d8d7695f069111d156c74b66..c1c95d6abfc7f1698a74387319ff90ad3a7e39af 100644
|
||||
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
|
||||
@@ -12,7 +12,8 @@ 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;
|
||||
diff --git a/src/main/java/io/papermc/paper/command/PaperCommand.java b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
index 395c43f6440c1e0e47919eef096ea8a8d552ccec..f44ab1d71210e84328661c0feb662989a5635b6d 100644
|
||||
--- a/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
+++ b/src/main/java/io/papermc/paper/command/PaperCommand.java
|
||||
@@ -2,6 +2,7 @@ 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;
|
||||
@@ -42,6 +43,7 @@ public final class PaperCommand extends Command {
|
||||
commands.put(Set.of("reload"), new ReloadCommand());
|
||||
commands.put(Set.of("version"), new VersionCommand());
|
||||
commands.put(Set.of("debug", "chunkinfo"), new ChunkDebugCommand());
|
||||
+ commands.put(Set.of("fixlight"), new FixLightCommand());
|
||||
|
||||
return commands.entrySet().stream()
|
||||
.flatMap(entry -> entry.getKey().stream().map(s -> Map.entry(s, entry.getValue())))
|
||||
diff --git a/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..190df802cb24aa360f6cf4d291e38b4b3fe4a2ac
|
||||
--- /dev/null
|
||||
+++ b/src/main/java/io/papermc/paper/command/subcommands/FixLightCommand.java
|
||||
@@ -0,0 +1,121 @@
|
||||
+package io.papermc.paper.command.subcommands;
|
||||
+
|
||||
+import io.papermc.paper.command.PaperSubcommand;
|
||||
+import java.util.ArrayDeque;
|
||||
+import java.util.Deque;
|
||||
+import net.minecraft.server.MCUtil;
|
||||
+import net.minecraft.server.MinecraftServer;
|
||||
+import net.minecraft.server.level.ChunkHolder;
|
||||
+import net.minecraft.server.level.ServerLevel;
|
||||
+import net.minecraft.server.level.ServerPlayer;
|
||||
+import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.world.entity.EntityType;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.server.MCUtil;
|
||||
@@ -25,15 +26,18 @@ import org.bukkit.command.Command;
|
||||
import org.bukkit.command.CommandSender;
|
||||
import org.bukkit.craftbukkit.CraftServer;
|
||||
import org.bukkit.craftbukkit.CraftWorld;
|
||||
+import 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;
|
||||
@@ -50,7 +54,7 @@ import static net.kyori.adventure.text.format.NamedTextColor.YELLOW;
|
||||
|
||||
public class PaperCommand extends Command {
|
||||
private static final String BASE_PERM = "bukkit.command.paper.";
|
||||
- private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo").build();
|
||||
+ private static final ImmutableSet<String> SUBCOMMANDS = ImmutableSet.<String>builder().add("heap", "entity", "reload", "version", "debug", "chunkinfo", "fixlight").build();
|
||||
|
||||
public PaperCommand(String name) {
|
||||
super(name);
|
||||
@@ -165,6 +169,9 @@ public class PaperCommand extends Command {
|
||||
case "chunkinfo":
|
||||
doChunkInfo(sender, args);
|
||||
break;
|
||||
+ case "fixlight":
|
||||
+ this.doFixLight(sender, args);
|
||||
+ 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":
|
||||
@@ -425,4 +432,74 @@ public class PaperCommand extends Command {
|
||||
|
||||
Command.broadcastCommandMessage(sender, text("Paper config reload complete.", GREEN));
|
||||
}
|
||||
+ private void doFixLight(CommandSender sender, String[] args) {
|
||||
+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 527b37e2740d6ca0d8d7695f069111d156c74b66..c1c95d6abfc7f1698a74387319ff90ad
|
|||
+
|
||||
+ 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 527b37e2740d6ca0d8d7695f069111d156c74b66..c1c95d6abfc7f1698a74387319ff90ad
|
|||
+ }
|
||||
+ }
|
||||
+ 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 375f66bf1617e255b3465b736076a65e080eb36e..d95db45e21861eb9f1623c44dd797429ae158760 100644
|
||||
--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue