From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Mon, 3 Feb 2020 20:37:44 -0800
Subject: [PATCH] Improved oversized chunk data packet handling

Now target all TE data, except for TE's that do not have
update packets.

This patch relies upon the improve extra packet handling
patch, as we now use PacketPlayOutMapChunk as an extra packet.
See its patch notes for further details.

diff --git a/src/main/java/net/minecraft/network/protocol/game/PacketPlayOutMapChunk.java b/src/main/java/net/minecraft/network/protocol/game/PacketPlayOutMapChunk.java
index 3047cf8c4ec1b664d6b790f18d2b1657e4b00435..1295c391ae48f0864c243ca9046f31480eb768fe 100644
--- a/src/main/java/net/minecraft/network/protocol/game/PacketPlayOutMapChunk.java
+++ b/src/main/java/net/minecraft/network/protocol/game/PacketPlayOutMapChunk.java
@@ -32,7 +32,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
     @Nullable
     private int[] e;
     private byte[] f; private byte[] getData() { return this.f; } // Paper - OBFHELPER
-    private List<NBTTagCompound> g;
+    private List<NBTTagCompound> g; private List<NBTTagCompound> getTileEntityData() { return this.g; } // Tuinity - OBFHELPER
     private boolean h;
 
     // Paper start - Async-Anti-Xray - Set the ready flag to true
@@ -44,7 +44,9 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
 
     // Paper start
     private final java.util.List<Packet> extraPackets = new java.util.ArrayList<>();
-    private static final int TE_LIMIT = Integer.getInteger("Paper.excessiveTELimit", 750);
+    private static final int TE_LIMIT = Integer.getInteger("tuinity.excessive-te-limit", 750); // Tuinity - handle oversized chunk data packets more robustly
+    private static final int TE_SPLIT_LIMIT = Math.max(4096 + 1, Integer.getInteger("tuinity.te-split-limit", 15_000)); // Tuinity - handle oversized chunk data packets more robustly
+    private boolean mustSplit; // Tuinity - handle oversized chunk data packets more robustly
 
     @Override
     public java.util.List<Packet> getExtraPackets() {
@@ -53,7 +55,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
     // Paper end
     // Paper start - Anti-Xray - Add chunk packet info
     @Deprecated public PacketPlayOutMapChunk(Chunk chunk, int i) { this(chunk, i, true); } // Notice for updates: Please make sure this constructor isn't used anywhere
-    public PacketPlayOutMapChunk(Chunk chunk, int i, boolean modifyBlocks) {
+    public PacketPlayOutMapChunk(Chunk chunk, int i, boolean modifyBlocks) { final int chunkSectionBitSet = i; // Tuinity - handle oversized chunk data packets more robustly
         ChunkPacketInfo<IBlockData> chunkPacketInfo = modifyBlocks ? chunk.world.chunkPacketBlockController.getChunkPacketInfo(this, chunk, i) : null;
         // Paper end
         ChunkCoordIntPair chunkcoordintpair = chunk.getPos();
@@ -62,27 +64,12 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
         this.b = chunkcoordintpair.z;
         this.h = i == 65535;
         this.d = new NBTTagCompound();
-        Iterator iterator = chunk.f().iterator();
-
-        Entry entry;
-
-        while (iterator.hasNext()) {
-            entry = (Entry) iterator.next();
-            if (((HeightMap.Type) entry.getKey()).c()) {
-                this.d.set(((HeightMap.Type) entry.getKey()).b(), new NBTTagLongArray(((HeightMap) entry.getValue()).a()));
-            }
-        }
-
-        if (this.h) {
-            this.e = chunk.getBiomeIndex().a();
-        }
-
-        this.f = new byte[this.a(chunk, i)];
-        // Paper start - Anti-Xray - Add chunk packet info
-        if (chunkPacketInfo != null) {
-            chunkPacketInfo.setData(this.getData());
-        }
-        this.c = this.writeChunk(new PacketDataSerializer(this.j()), chunk, i, chunkPacketInfo);
+        // Tuinity - move this after the tile entity logic, we need to determine whether we're going to split
+        // Tuinity - before writing chunk block data
+        // Tuinity - note: for future maintenance, git will prefer the smallest diff, so if moving the TE code is
+        // Tuinity - a smaller diff, do that, else move the chunk writing - this makes sure the start/end is correct
+        Iterator iterator; // Tuinity - move declaration up
+        Entry entry; // Tuinity - move delcaration up
         // Paper end
         this.g = Lists.newArrayList();
         iterator = chunk.getTileEntities().entrySet().iterator();
@@ -95,8 +82,16 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
             int j = blockposition.getY() >> 4;
 
             if (this.f() || (i & 1 << j) != 0) {
+                // Tuinity start - improve oversized chunk data packet handling
+                ++totalTileEntities;
+                if (totalTileEntities > TE_SPLIT_LIMIT) {
+                    this.mustSplit = true;
+                    this.getTileEntityData().clear();
+                    this.extraPackets.clear();
+                    break;
+                }
                 // Paper start - improve oversized chunk data packet handling
-                if (++totalTileEntities > TE_LIMIT) {
+                if (totalTileEntities > TE_LIMIT) { // Tuinity end - improve oversized chunk data packet handling
                     PacketPlayOutTileEntityData updatePacket = tileentity.getUpdatePacket();
                     if (updatePacket != null) {
                         this.extraPackets.add(updatePacket);
@@ -110,7 +105,42 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
                 this.g.add(nbttagcompound);
             }
         }
+        // Tuinity start - moved after tile entity gathering
+        iterator = chunk.f().iterator(); // Declared earlier
+
+        while (iterator.hasNext()) {
+            entry = (Entry) iterator.next();
+            if (((HeightMap.Type) entry.getKey()).c()) {
+                this.d.set(((HeightMap.Type) entry.getKey()).b(), new NBTTagLongArray(((HeightMap) entry.getValue()).a()));
+            }
+        }
+
+        if (this.h) {
+            this.e = chunk.getBiomeIndex().a();
+        }
+
+        this.f = new byte[this.a(chunk, i)];
+        // Paper start - Anti-Xray - Add chunk packet info
+        if (chunkPacketInfo != null) {
+            chunkPacketInfo.setData(this.getData());
+        }
+        this.c = this.writeChunk(new PacketDataSerializer(this.j()), chunk, i, chunkPacketInfo);
+        // Tuinity end - moved after tile entity gathering
         chunk.world.chunkPacketBlockController.modifyBlocks(this, chunkPacketInfo); // Paper - Anti-Xray - Modify blocks
+        // Tuinity start - improve oversized chunk data packet handling
+        if (this.mustSplit) {
+            int chunkSectionBitSetCopy = chunkSectionBitSet;
+            for (int a = 0, len = Integer.bitCount(chunkSectionBitSet); a < len; ++a) {
+                int trailingBit = com.destroystokyo.paper.util.math.IntegerUtil.getTrailingBit(chunkSectionBitSetCopy);
+                int sectionIndex = Integer.numberOfTrailingZeros(trailingBit);
+                chunkSectionBitSetCopy ^= trailingBit; // move on to the next
+
+                if (chunk.getSections()[sectionIndex] != null) {
+                    this.extraPackets.add(new PacketPlayOutMapChunk(chunk, trailingBit));
+                }
+            }
+        }
+        // Tuinity end - improve oversized chunk data packet handling
     }
 
     // Paper start - Async-Anti-Xray - Getter and Setter for the ready flag
@@ -201,7 +231,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
         for (int l = achunksection.length; k < l; ++k) {
             ChunkSection chunksection = achunksection[k];
 
-            if (chunksection != Chunk.a && (!this.f() || !chunksection.c()) && (i & 1 << k) != 0) {
+            if ((!this.mustSplit && chunksection != Chunk.a) && (!this.f() || !chunksection.c()) && (i & 1 << k) != 0) { // Tuinity - improve oversized chunk data packet handling
                 j |= 1 << k;
                 chunksection.writeChunkSection(packetdataserializer, chunkPacketInfo); // Paper - Anti-Xray - Add chunk packet info
             }
@@ -218,7 +248,7 @@ public class PacketPlayOutMapChunk implements Packet<PacketListenerPlayOut> {
         for (int l = achunksection.length; k < l; ++k) {
             ChunkSection chunksection = achunksection[k];
 
-            if (chunksection != Chunk.a && (!this.f() || !chunksection.c()) && (i & 1 << k) != 0) {
+            if ((!this.mustSplit && chunksection != Chunk.a) && (!this.f() || !chunksection.c()) && (i & 1 << k) != 0) {
                 j += chunksection.j();
             }
         }
