aboutsummaryrefslogtreecommitdiffhomepage
path: root/patches/server/0361-Fix-Light-Command.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/server/0361-Fix-Light-Command.patch')
-rw-r--r--patches/server/0361-Fix-Light-Command.patch166
1 files changed, 166 insertions, 0 deletions
diff --git a/patches/server/0361-Fix-Light-Command.patch b/patches/server/0361-Fix-Light-Command.patch
new file mode 100644
index 0000000000..f6384f5895
--- /dev/null
+++ b/patches/server/0361-Fix-Light-Command.patch
@@ -0,0 +1,166 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Aikar <[email protected]>
+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/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
+index 005361c38b02713fb823d0be40954400d59f0c4d..3091c100eaf5a86ba270ef0d96de1852a2a0ac9e 100644
+--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
++++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
+@@ -10,7 +10,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;
++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.resources.ResourceLocation;
+@@ -25,15 +26,18 @@ import org.bukkit.command.Command;
+ import org.bukkit.command.CommandSender;
+ import org.bukkit.craftbukkit.CraftServer;
+ import org.bukkit.craftbukkit.CraftWorld;
++import org.bukkit.craftbukkit.entity.CraftPlayer;
+ import org.bukkit.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;
+@@ -43,7 +47,7 @@ import java.util.stream.Collectors;
+
+ 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);
+@@ -158,6 +162,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":
+@@ -413,4 +420,74 @@ public class PaperCommand extends Command {
+
+ Command.broadcastCommandMessage(sender, ChatColor.GREEN + "Paper config reload complete.");
+ }
++ private void doFixLight(CommandSender sender, String[] args) {
++ if (!(sender instanceof Player)) {
++ sender.sendMessage("Only players can use this command");
++ return;
++ }
++ int radius = 2;
++ if (args.length > 1) {
++ try {
++ radius = Math.min(5, Integer.parseInt(args[1]));
++ } catch (Exception e) {
++ sender.sendMessage("Not a number");
++ return;
++ }
++
++ }
++
++ 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);
++ }
++
++ private void updateLight(CommandSender sender, ServerLevel world, ThreadedLevelLightEngine lightengine, Deque<ChunkPos> queue) {
++ ChunkPos coord = queue.poll();
++ if (coord == null) {
++ sender.sendMessage("All Chunks Light updated");
++ 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);
++ return;
++ }
++ net.minecraft.world.level.chunk.LevelChunk chunk = (net.minecraft.world.level.chunk.LevelChunk) either.left().orElse(null);
++ if (chunk == null) {
++ updateLight(sender, world, lightengine, queue);
++ return;
++ }
++ lightengine.setTaskPerBatch(world.paperConfig.lightQueueSize + 16 * 256); // ensure full chunk can fit into queue
++ sender.sendMessage("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();
++ 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);
++ });
++ });
++ } else {
++ updateLight(sender, world, lightengine, queue);
++ }
++ lightengine.setTaskPerBatch(world.paperConfig.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 848952c9bd3d91488d964c72bdc77925f904c3fa..cb050e658c5c99feb4586c1fba9a57ee3c0d6052 100644
+--- a/src/main/java/net/minecraft/server/level/ChunkMap.java
++++ b/src/main/java/net/minecraft/server/level/ChunkMap.java
+@@ -134,6 +134,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;
+@@ -242,11 +248,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;