From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Spottedleaf <spottedleaf@spottedleaf.dev>
Date: Wed, 10 Jun 2020 15:17:15 -0700
Subject: [PATCH] Rework PlayerChunk main thread checks

These need to fail instead of continuing, as hiding these errors
the way paper has is just going to allow unexpected reordering
of callbacks.

For example, thanks to this patch incorrect future
completion (completion of the world gen future,
PlayerChunkMap#b(PlayerChunk, ChunkStatus)) was detected and fixed.

diff --git a/src/main/java/net/minecraft/server/level/PlayerChunk.java b/src/main/java/net/minecraft/server/level/PlayerChunk.java
index f7deb4fe98ef9900acb276c7680d5a2da9661e8f..6b939d35e13fdc84fd3767e74eaf78c9d9b90c69 100644
--- a/src/main/java/net/minecraft/server/level/PlayerChunk.java
+++ b/src/main/java/net/minecraft/server/level/PlayerChunk.java
@@ -590,6 +590,7 @@ public class PlayerChunk {
     }
 
     protected void a(PlayerChunkMap playerchunkmap) {
+        com.tuinity.tuinity.util.TickThread.ensureTickThread("Async ticket level update"); // Tuinity
         ChunkStatus chunkstatus = getChunkStatus(this.oldTicketLevel);
         ChunkStatus chunkstatus1 = getChunkStatus(this.ticketLevel);
         boolean flag = this.oldTicketLevel <= PlayerChunkMap.GOLDEN_TICKET;
@@ -599,7 +600,8 @@ public class PlayerChunk {
         // CraftBukkit start
         // ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins.
         if (playerchunk_state.isAtLeast(PlayerChunk.State.BORDER) && !playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER)) {
-            this.getStatusFutureUncheckedMain(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main
+            this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main // Tuinity - is always on main
+                com.tuinity.tuinity.util.TickThread.ensureTickThread("Async full status chunk future completion"); // Tuinity
                 Chunk chunk = (Chunk)either.left().orElse(null);
                 if (chunk != null) {
                     playerchunkmap.callbackExecutor.execute(() -> {
@@ -664,7 +666,8 @@ public class PlayerChunk {
         if (!flag2 && flag3) {
             // Paper start - cache ticking ready status
             int expectCreateCount = ++this.fullChunkCreateCount;
-            this.fullChunkFuture = playerchunkmap.b(this); ensureMain(this.fullChunkFuture).thenAccept((either) -> { // Paper - ensure main
+            this.fullChunkFuture = playerchunkmap.b(this); this.fullChunkFuture.thenAccept((either) -> { // Paper - ensure main // Tuinity - always fired on main
+                com.tuinity.tuinity.util.TickThread.ensureTickThread("Async full chunk future completion"); // Tuinity
                 if (either.left().isPresent() && PlayerChunk.this.fullChunkCreateCount == expectCreateCount) {
                     // note: Here is a very good place to add callbacks to logic waiting on this.
                     Chunk fullChunk = either.left().get();
@@ -695,7 +698,8 @@ public class PlayerChunk {
 
         if (!flag4 && flag5) {
             // Paper start - cache ticking ready status
-            this.tickingFuture = playerchunkmap.a(this); ensureMain(this.tickingFuture).thenAccept((either) -> { // Paper - ensure main
+            this.tickingFuture = playerchunkmap.a(this); this.tickingFuture.thenAccept((either) -> { // Paper - ensure main // Tuinity - always completed on main
+                com.tuinity.tuinity.util.TickThread.ensureTickThread("Async ticking chunk future completion"); // Tuinity
                 if (either.left().isPresent()) {
                     // note: Here is a very good place to add callbacks to logic waiting on this.
                     Chunk tickingChunk = either.left().get();
@@ -735,7 +739,8 @@ public class PlayerChunk {
             }
 
             // Paper start - cache ticking ready status
-            this.entityTickingFuture = playerchunkmap.b(this.location); ensureMain(this.entityTickingFuture).thenAccept((either) -> { // Paper ensureMain
+            this.entityTickingFuture = playerchunkmap.b(this.location); this.entityTickingFuture.thenAccept((either) -> { // Paper ensureMain // Tuinity - always completed on main
+                com.tuinity.tuinity.util.TickThread.ensureTickThread("Async entity ticking chunk future completion"); // Tuinity
                 if (either.left().isPresent()) {
                     // note: Here is a very good place to add callbacks to logic waiting on this.
                     Chunk entityTickingChunk = either.left().get();
@@ -786,7 +791,8 @@ public class PlayerChunk {
         // CraftBukkit start
         // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins.
         if (!playerchunk_state.isAtLeast(PlayerChunk.State.BORDER) && playerchunk_state1.isAtLeast(PlayerChunk.State.BORDER)) {
-            this.getStatusFutureUncheckedMain(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main
+            this.getStatusFutureUnchecked(ChunkStatus.FULL).thenAccept((either) -> { // Paper - ensure main // Tuinity - is always on main
+                com.tuinity.tuinity.util.TickThread.ensureTickThread("Async full status chunk future completion"); // Tuinity
                 Chunk chunk = (Chunk)either.left().orElse(null);
                 if (chunk != null) {
                     playerchunkmap.callbackExecutor.execute(() -> {
diff --git a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
index db271a5a1f09f8c11ac2498f8975cf7b025dd5e3..d395a694335f2ffcdfae3abf3884f09fb69b59d7 100644
--- a/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
+++ b/src/main/java/net/minecraft/server/level/PlayerChunkMap.java
@@ -1355,7 +1355,10 @@ public class PlayerChunkMap extends IChunkLoader implements PlayerChunk.d {
             }
             // Paper end
             this.mailboxWorldGen.a(ChunkTaskQueueSorter.a(playerchunk, runnable));
-        });
+        }).thenComposeAsync((either) -> { // Tuinity start - force competion on the main thread
+            return CompletableFuture.completedFuture(either);
+        }, this.mainInvokingExecutor);
+        // Tuinity end - force competion on the main thread
     }
 
     protected void c(ChunkCoordIntPair chunkcoordintpair) {
