From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Aikar Date: Thu, 7 May 2020 19:17:36 -0400 Subject: [PATCH] Fix Light Command This lets you run /paper fixlight (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 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 queue = new ArrayDeque<>(MCUtil.getSpiralOutChunks(center, radius)); + updateLight(sender, world, lightengine, queue); + } + + private void updateLight(CommandSender sender, WorldServer world, LightEngineThreaded lightengine, Deque 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> mailboxWorldGen; final Mailbox> mailboxMain; // Paper - private -> package private + // Paper start + final Mailbox> 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 mailbox = Mailbox.a("main", iasynctaskhandler::a); this.worldLoadListener = worldloadlistener; - ThreadedMailbox threadedmailbox1 = ThreadedMailbox.a(executor, "light"); + ThreadedMailbox lightthreaded; ThreadedMailbox 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;