From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: MeFisto94 <MeFisto94@users.noreply.github.com>
Date: Tue, 12 May 2020 23:02:43 +0200
Subject: [PATCH] Workaround for Client Lag Spikes (MC-162253)

When crossing certain chunk boundaries, the client needlessly
calculates light maps for chunk neighbours. In some specific map
configurations, these calculations cause a 500ms+ freeze on the Client.

This patch basically serves as a workaround by sending light maps
to the client, so that it doesn't attempt to calculate them.
This mitigates the frametime impact to a minimum (but it's still there).

diff --git a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
index ede47aaaace80280756fe4463def1ea26792c9e4..d5c939281df21683efc63937a9146b0dfeb22e2c 100644
--- a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
@@ -85,6 +85,7 @@ import net.minecraft.world.level.World;
 import net.minecraft.world.level.chunk.Chunk;
 import net.minecraft.world.level.chunk.ChunkConverter;
 import net.minecraft.world.level.chunk.ChunkGenerator;
+import net.minecraft.world.level.chunk.ChunkSection;
 import net.minecraft.world.level.chunk.ChunkStatus;
 import net.minecraft.world.level.chunk.IChunkAccess;
 import net.minecraft.world.level.chunk.ILightAccess;
@@ -2055,9 +2056,68 @@ Sections go from 0..16. Now whenever a section is not empty, it can potentially
     public final void sendChunk(EntityPlayer entityplayer, Packet<?>[] apacket, Chunk chunk) { this.a(entityplayer, apacket, chunk); } // Paper - OBFHELPER
     private void a(EntityPlayer entityplayer, Packet<?>[] apacket, Chunk chunk) {
         if (apacket[0] == null) {
+            // Paper start - add 8 for light fix workaround
+            if (apacket.length != 10) { // in case Plugins call sendChunk, resize
+                apacket = new Packet[10];
+            }
+            // Paper end
             apacket[0] = new PacketPlayOutMapChunk(chunk, 65535, chunk.world.chunkPacketBlockController.shouldModify(entityplayer, chunk, 65535)); // Paper - Anti-Xray - Bypass
             apacket[1] = new PacketPlayOutLightUpdate(chunk.getPos(), this.lightEngine, true);
+
+            // Paper start - Fix MC-162253
+            final int lightMask = getLightMask(chunk);
+            int i = 1;
+            for (int x = -1; x <= 1; x++) {
+                for (int z = -1; z <= 1; z++) {
+                    if (x == 0 && z == 0) {
+                        continue;
+                    }
+
+                    ++i;
+
+                    if (!chunk.isNeighbourLoaded(x, z)) {
+                        continue;
+                    }
+
+                    final Chunk neighbor = chunk.getRelativeNeighbourIfLoaded(x, z);
+                    final int updateLightMask = lightMask & ~getCeilingLightMask(neighbor);
+
+                    if (updateLightMask == 0) {
+                        continue;
+                    }
+
+                    apacket[i] = new PacketPlayOutLightUpdate(new ChunkCoordIntPair(chunk.getPos().x + x, chunk.getPos().z + z), lightEngine, updateLightMask, 0, true);
+                }
+            }
+        }
+
+        final int viewDistance = playerViewDistanceBroadcastMap.getLastViewDistance(entityplayer);
+        final long lastPosition = playerViewDistanceBroadcastMap.getLastCoordinate(entityplayer);
+
+        int j = 1;
+        for (int x = -1; x <= 1; x++) {
+            for (int z = -1; z <= 1; z++) {
+                if (x == 0 && z == 0) {
+                    continue;
+                }
+
+                ++j;
+
+                Packet<?> packet = apacket[j];
+                if (packet == null) {
+                    continue;
+                }
+
+                final int distX = Math.abs(MCUtil.getCoordinateX(lastPosition) - (chunk.getPos().x + x));
+                final int distZ = Math.abs(MCUtil.getCoordinateZ(lastPosition) - (chunk.getPos().z + z));
+
+                if (Math.max(distX, distZ) > viewDistance) {
+                    continue;
+                }
+                entityplayer.playerConnection.sendPacket(packet);
+            }
         }
+        // Paper end - Fix MC-162253
 
         entityplayer.a(chunk.getPos(), apacket[0], apacket[1]);
         PacketDebug.a(this.world, chunk.getPos());
diff --git a/src/main/java/net/minecraft/world/level/chunk/Chunk.java b/src/main/java/net/minecraft/world/level/chunk/Chunk.java
index 90e895e9eac6158a28de4a30589bf7538e5ec9cc..34a9f7b2f998f77b1279516cd09397ab6c2ac1cc 100644
--- a/src/main/java/net/minecraft/world/level/chunk/Chunk.java
+++ b/src/main/java/net/minecraft/world/level/chunk/Chunk.java
@@ -280,7 +280,7 @@ public class Chunk implements IChunkAccess {
 
                     // broadcast
                     Object[] backingSet = inRange.getBackingSet();
-                    Packet[] chunkPackets = new Packet[2];
+                    Packet[] chunkPackets = new Packet[10];
                     for (int index = 0, len = backingSet.length; index < len; ++index) {
                         Object temp = backingSet[index];
                         if (!(temp instanceof EntityPlayer)) {
diff --git a/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java b/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java
index 973aa060d6964c7d470bc7aff89b879daf1df153..8fe060c3b2ad0873f96218eb7d02cdff3279224e 100644
--- a/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java
+++ b/src/main/java/net/minecraft/world/level/chunk/ChunkSection.java
@@ -107,6 +107,7 @@ public class ChunkSection {
         return this.nonEmptyBlockCount == 0;
     }
 
+    public static boolean isEmpty(@Nullable ChunkSection chunksection) { return a(chunksection) ; } // Paper - OBFHELPER
     public static boolean a(@Nullable ChunkSection chunksection) {
         return chunksection == Chunk.a || chunksection.c();
     }