papermc/Spigot-Server-Patches/0507-Fix-Light-Command.patch
Aikar edd6b6a2ba
Protect the visible chunk map from plugins touching it, trim Timing Errors
Blow up if a plugin tries to mutate visibleChunks directly and prevent them
from doing so.

Also provide a safe get call if any plugins directly call get on it so
that it uses the special logic to check pending.

Also restores ABI for the visibleChunks field back to what it was too.

Additionally, remove the stack trace from Timings Stack Corruption for any
error thrown on Minecraft Timings, and tell them to get the error ABOVE this
instead, so people stop giving us useless error reports.

Also fixes a memory leak when the source map down sizes but dest map didn't,
which resulted in lingering references to old chunk holders.

Fixes #3414
2020-05-22 00:39:16 -04:00

166 lines
8.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/com/destroystokyo/paper/PaperCommand.java b/src/main/java/com/destroystokyo/paper/PaperCommand.java
index 182b440ba4802d199b8e44f7779b3401ace495d5..10b72083322b7f8e3e14525b3e834f5374ec369d 100644
--- a/src/main/java/com/destroystokyo/paper/PaperCommand.java
+++ b/src/main/java/com/destroystokyo/paper/PaperCommand.java
@@ -20,6 +20,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;
@@ -36,14 +37,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 | dumpwaiting | chunkinfo | syncloadinfo]";
+ this.usageMessage = "/paper [heap | entity | reload | version | debug | dumpwaiting | 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", "dumpwaiting", "chunkinfo", "syncloadinfo");
+ return getListMatchingLast(args, "heap", "entity", "reload", "version", "debug", "dumpwaiting", "chunkinfo", "syncloadinfo", "fixlight");
switch (args[0].toLowerCase(Locale.ENGLISH))
{
@@ -144,6 +145,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");
@@ -160,6 +164,75 @@ 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, false).whenCompleteAsync((either, ex) -> {
+ if (ex != null) {
+ sender.sendMessage("Error loading chunk " + coord);
+ updateLight(sender, world, lightengine, queue);
+ return;
+ }
+ Chunk chunk = (Chunk) either.left().orElse(null);
+ if (chunk == null) {
+ updateLight(sender, world, lightengine, queue);
+ return;
+ }
+ 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);
+ }, MinecraftServer.getServer());
+ }
+
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 03fb688fe4bdc19b4bc36b1f1d5b40c61e7bef9b..aeca6b2b9d5d73aeb6dc639b5cad2f2533a2de44 100644
--- a/src/main/java/net/minecraft/server/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/PlayerChunk.java
@@ -327,6 +327,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 3b72b2c88575d19ec51f46226ddcc8a15c169721..a280f4af4f997f29e81a877455a2765d6751e842 100644
--- a/src/main/java/net/minecraft/server/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/PlayerChunkMap.java
@@ -95,6 +95,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;
@@ -282,11 +288,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;