mirror of
https://github.com/PaperMC/Paper.git
synced 2024-11-23 11:06:29 +01:00
e294802977
* 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
187 lines
9.8 KiB
Diff
187 lines
9.8 KiB
Diff
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
|
|
From: Aikar <aikar@aikar.co>
|
|
Date: Thu, 7 May 2020 19:17:36 -0400
|
|
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/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.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 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(text("Only players can use this command", RED));
|
|
+ return;
|
|
+ }
|
|
+ @Nullable Runnable post = null;
|
|
+ int radius = 2;
|
|
+ if (args.length > 0) {
|
|
+ try {
|
|
+ final int parsed = Integer.parseInt(args[0]);
|
|
+ if (parsed < 0) {
|
|
+ sender.sendMessage(text("Radius cannot be negative!", RED));
|
|
+ return;
|
|
+ }
|
|
+ final int maxRadius = 5;
|
|
+ radius = Math.min(maxRadius, parsed);
|
|
+ if (radius != parsed) {
|
|
+ post = () -> sender.sendMessage(text("Radius '" + parsed + "' was not in the required range [0, " + maxRadius + "], it was lowered to the maximum (" + maxRadius + " chunks).", RED));
|
|
+ }
|
|
+ } catch (final Exception e) {
|
|
+ sender.sendMessage(text("'" + args[0] + "' is not a valid number.", RED));
|
|
+ return;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ CraftPlayer player = (CraftPlayer) sender;
|
|
+ ServerPlayer handle = player.getHandle();
|
|
+ ServerLevel world = (ServerLevel) handle.level;
|
|
+ ThreadedLevelLightEngine lightengine = world.getChunkSource().getLightEngine();
|
|
+
|
|
+ net.minecraft.core.BlockPos center = MCUtil.toBlockPosition(player.getLocation());
|
|
+ Deque<ChunkPos> queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius));
|
|
+ updateLight(sender, world, lightengine, queue, post);
|
|
+ }
|
|
+
|
|
+ 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(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(text("Error loading chunk " + coord, RED));
|
|
+ updateLight(sender, world, lightengine, queue, done);
|
|
+ return;
|
|
+ }
|
|
+ @Nullable LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null);
|
|
+ if (chunk == null) {
|
|
+ updateLight(sender, world, lightengine, queue, done);
|
|
+ return;
|
|
+ }
|
|
+ lightengine.setTaskPerBatch(world.paperConfig().misc.lightQueueSize + 16 * 256); // ensure full chunk can fit into queue
|
|
+ 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++) {
|
|
+ for (int x = 0; x < 16; x++) {
|
|
+ for (int z = 0; z < 16; z++) {
|
|
+ net.minecraft.core.BlockPos pos = new net.minecraft.core.BlockPos(cx + x, y, cz + z);
|
|
+ lightengine.checkBlock(pos);
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ lightengine.tryScheduleUpdate();
|
|
+ @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, done);
|
|
+ });
|
|
+ });
|
|
+ } else {
|
|
+ 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
|
|
+++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
|
|
@@ -141,6 +141,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
private final ChunkTaskPriorityQueueSorter queueSorter;
|
|
private final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> worldgenMailbox;
|
|
public final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> mainThreadMailbox;
|
|
+ // Paper start
|
|
+ final ProcessorHandle<ChunkTaskPriorityQueueSorter.Message<Runnable>> mailboxLight;
|
|
+ public void addLightTask(ChunkHolder playerchunk, Runnable run) {
|
|
+ this.mailboxLight.tell(ChunkTaskPriorityQueueSorter.message(playerchunk, run));
|
|
+ }
|
|
+ // Paper end
|
|
public final ChunkProgressListener progressListener;
|
|
private final ChunkStatusUpdateListener chunkStatusListener;
|
|
public final ChunkMap.ChunkDistanceManager distanceManager;
|
|
@@ -264,11 +270,12 @@ public class ChunkMap extends ChunkStorage implements ChunkHolder.PlayerProvider
|
|
|
|
this.progressListener = worldGenerationProgressListener;
|
|
this.chunkStatusListener = chunkStatusChangeListener;
|
|
- ProcessorMailbox<Runnable> threadedmailbox1 = ProcessorMailbox.create(executor, "light");
|
|
+ ProcessorMailbox<Runnable> lightthreaded; ProcessorMailbox<Runnable> threadedmailbox1 = lightthreaded = ProcessorMailbox.create(executor, "light"); // Paper
|
|
|
|
this.queueSorter = new ChunkTaskPriorityQueueSorter(ImmutableList.of(threadedmailbox, mailbox, threadedmailbox1), executor, Integer.MAX_VALUE);
|
|
this.worldgenMailbox = this.queueSorter.getProcessor(threadedmailbox, false);
|
|
this.mainThreadMailbox = this.queueSorter.getProcessor(mailbox, false);
|
|
+ this.mailboxLight = this.queueSorter.getProcessor(lightthreaded, false);// Paper
|
|
this.lightEngine = new ThreadedLevelLightEngine(chunkProvider, this, this.level.dimensionType().hasSkyLight(), threadedmailbox1, this.queueSorter.getProcessor(threadedmailbox1, false));
|
|
this.distanceManager = new ChunkMap.ChunkDistanceManager(executor, mainThreadExecutor);
|
|
this.overworldDataStorage = persistentStateManagerFactory;
|