From cfed00cf54f973a2de0f2a2646ec5dbe9ef647a1 Mon Sep 17 00:00:00 2001
From: Aikar <aikar@aikar.co>
Date: Thu, 7 May 2020 19:44:33 -0400
Subject: [PATCH] Fix Light Command

This lets you run /paper fixlight <chunkRadius> (max 5) to automatically
fix all light data in the chunks.

Permission node is same "bukkit.command.paper"
---
 .../0507-Fix-Light-Command.patch              | 156 ++++++++++++++++++
 1 file changed, 156 insertions(+)
 create mode 100644 Spigot-Server-Patches/0507-Fix-Light-Command.patch

diff --git a/Spigot-Server-Patches/0507-Fix-Light-Command.patch b/Spigot-Server-Patches/0507-Fix-Light-Command.patch
new file mode 100644
index 0000000000..9e1cce1248
--- /dev/null
+++ b/Spigot-Server-Patches/0507-Fix-Light-Command.patch
@@ -0,0 +1,156 @@
+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/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
+index ddb60e9a48e5e7225ad575240b94fda24b6b78ca..bd9c56dd277c58b5f2c423f6e3e65a5086099619 100644
+--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
++++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
+@@ -19,6 +19,7 @@ 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;
+@@ -35,14 +36,14 @@ public class PaperCommand extends Command {
+     public PaperCommand(String name) {
+         super(name);
+         this.description = "Paper related commands";
+-        this.usageMessage = "/paper [heap | entity | reload | version | debug | chunkinfo | syncloadinfo]";
++        this.usageMessage = "/paper [heap | entity | reload | version | debug | chunkinfo | syncloadinfo | fixlight]";
+         this.setPermission("bukkit.command.paper");
+     }
+ 
+     @Override
+     public List<String> tabComplete(CommandSender sender, String alias, String[] args, Location location) throws IllegalArgumentException {
+         if (args.length <= 1)
+-            return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "chunkinfo", "syncloadinfo");
++            return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "chunkinfo", "syncloadinfo", "fixlight");
+ 
+         switch (args[0].toLowerCase(Locale.ENGLISH))
+         {
+@@ -140,6 +141,9 @@ public class PaperCommand extends Command {
+             case "syncloadinfo":
+                 this.doSyncLoadInfo(sender, args);
+                 break;
++            case "fixlight":
++                this.doFixLight(sender, args);
++                break;
+             case "ver":
+             case "version":
+                 Command ver = org.bukkit.Bukkit.getServer().getCommandMap().getCommand("version");
+@@ -156,6 +160,65 @@ public class PaperCommand extends Command {
+         return true;
+     }
+ 
++    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;
++        EntityPlayer handle = player.getHandle();
++        net.minecraft.server.WorldServer world = (WorldServer) handle.world;
++        LightEngineThreaded lightengine = world.getChunkProvider().getLightEngine();
++
++        BlockPosition center = MCUtil.toBlockPosition(player.getLocation());
++        Deque<ChunkCoordIntPair> queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius));
++        updateLight(sender, world, lightengine, queue);
++    }
++
++    private void updateLight(CommandSender sender, WorldServer world, LightEngineThreaded lightengine, Deque<ChunkCoordIntPair> queue) {
++        ChunkCoordIntPair coord = queue.poll();
++        if (coord == null) {
++            sender.sendMessage("All Chunks Light updated");
++            return;
++        }
++        world.getChunkProvider().getChunkAtAsynchronously(coord.x, coord.z, false, chunk -> {
++            lightengine.a(world.paperConfig.lightQueueSize + 16 * 256); // ensure full chunk can fit into queue
++            sender.sendMessage("Updating Light " + coord);
++            for (int y = 0; y < world.getHeight(); y++) {
++                for (int x = 0; x < 16; x++) {
++                    for (int z = 0; z < 16; z++) {
++                        BlockPosition pos = new BlockPosition(chunk.getPos().getBlockX() + x, y, chunk.getPos().getBlockZ() + z);
++                        lightengine.a(pos);
++                    }
++                }
++            }
++            lightengine.queueUpdate();
++            PlayerChunk visibleChunk = world.getChunkProvider().playerChunkMap.getVisibleChunk(chunk.coordinateKey);
++            if (visibleChunk != null) {
++                world.getChunkProvider().playerChunkMap.addLightTask(visibleChunk, () -> {
++                    MinecraftServer.getServer().processQueue.add(() -> {
++                        visibleChunk.sendPacketToTrackedPlayers(new PacketPlayOutLightUpdate(chunk.getPos(), lightengine), false);
++                        updateLight(sender, world, lightengine, queue);
++                    });
++                });
++            } else {
++                updateLight(sender, world, lightengine, queue);
++            }
++            lightengine.a(world.paperConfig.lightQueueSize);
++        });
++    }
++
+     private void doSyncLoadInfo(CommandSender sender, String[] args) {
+         if (!SyncLoadFinder.ENABLED) {
+             sender.sendMessage(ChatColor.RED + "This command requires the server startup flag '-Dpaper.debug-sync-loads=true' to be set.");
+diff --git a/src/main/java/net/minecraft/server/PlayerChunk.java b/src/main/java/net/minecraft/server/PlayerChunk.java
+index 9d71c4c455d68bcc82dc56b0706c7305e4897e46..709e35cd4864eb666b68c1b6666a9d3a5a5a3c29 100644
+--- a/src/main/java/net/minecraft/server/PlayerChunk.java
++++ b/src/main/java/net/minecraft/server/PlayerChunk.java
+@@ -437,6 +437,7 @@ public class PlayerChunk {
+ 
+     }
+ 
++    public void sendPacketToTrackedPlayers(Packet<?> packet, boolean flag) { a(packet, flag); } // Paper - OBFHELPER
+     private void a(Packet<?> packet, boolean flag) {
+         // Paper start - per player view distance
+         // there can be potential desync with player's last mapped section and the view distance map, so use the
+diff --git a/src/main/java/net/minecraft/server/PlayerChunkMap.java b/src/main/java/net/minecraft/server/PlayerChunkMap.java
+index ce7462283873635ad0d3bce2395346ed9c3637a9..81bb26abc520f49de2e916cf2757ac4c710d02cd 100644
+--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
++++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
+@@ -72,6 +72,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
+     private final ChunkTaskQueueSorter p;
+     private final Mailbox<ChunkTaskQueueSorter.a<Runnable>> mailboxWorldGen;
+     final Mailbox<ChunkTaskQueueSorter.a<Runnable>> mailboxMain; // Paper - private -> package private
++    // Paper start
++    final Mailbox<ChunkTaskQueueSorter.a<Runnable>> mailboxLight;
++    public void addLightTask(PlayerChunk playerchunk, Runnable run) {
++        this.mailboxLight.a(ChunkTaskQueueSorter.a(playerchunk, run));
++    }
++    // Paper end
+     public final WorldLoadListener worldLoadListener;
+     public final PlayerChunkMap.a chunkDistanceManager; public final PlayerChunkMap.a getChunkMapDistanceManager() { return this.chunkDistanceManager; } // Paper - OBFHELPER
+     private final AtomicInteger u;
+@@ -256,11 +262,12 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
+         Mailbox<Runnable> mailbox = Mailbox.a("main", iasynctaskhandler::a);
+ 
+         this.worldLoadListener = worldloadlistener;
+-        ThreadedMailbox<Runnable> threadedmailbox1 = ThreadedMailbox.a(executor, "light");
++        ThreadedMailbox<Runnable> lightthreaded; ThreadedMailbox<Runnable> threadedmailbox1 = lightthreaded = ThreadedMailbox.a(executor, "light"); // Paper
+ 
+         this.p = new ChunkTaskQueueSorter(ImmutableList.of(threadedmailbox, mailbox, threadedmailbox1), executor, Integer.MAX_VALUE);
+         this.mailboxWorldGen = this.p.a(threadedmailbox, false);
+         this.mailboxMain = this.p.a(mailbox, false);
++        this.mailboxLight = this.p.a(lightthreaded, false);// Paper
+         this.lightEngine = new LightEngineThreaded(ilightaccess, this, this.world.getWorldProvider().f(), threadedmailbox1, this.p.a(threadedmailbox1, false));
+         this.chunkDistanceManager = new PlayerChunkMap.a(executor, iasynctaskhandler); this.chunkDistanceManager.chunkMap = this; // Paper
+         this.l = supplier;