aboutsummaryrefslogtreecommitdiffhomepage
path: root/patch-remap/mache-spigotflower-stripped/net/minecraft/server/level/ChunkHolder.java.patch
blob: 3e97a8baff20ed7c752fd2d8cb5ddc63594a7665 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
--- a/net/minecraft/server/level/ChunkHolder.java
+++ b/net/minecraft/server/level/ChunkHolder.java
@@ -36,6 +36,10 @@
 import net.minecraft.world.level.chunk.ProtoChunk;
 import net.minecraft.world.level.lighting.LevelLightEngine;
 
+// CraftBukkit start
+import net.minecraft.server.MinecraftServer;
+// CraftBukkit end
+
 public class ChunkHolder {
 
     public static final Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> UNLOADED_CHUNK = Either.right(ChunkHolder.ChunkLoadingFailure.UNLOADED);
@@ -90,9 +94,23 @@
         this.changedBlocksPerSection = new ShortSet[levelheightaccessor.getSectionsCount()];
     }
 
-    public CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> getFutureIfPresentUnchecked(ChunkStatus chunkstatus) {
-        CompletableFuture<Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure>> completablefuture = (CompletableFuture) this.futures.get(chunkstatus.getIndex());
+    // CraftBukkit start
+    public LevelChunk getFullChunkNow() {
+        // Note: We use the oldTicketLevel for isLoaded checks.
+        if (!ChunkLevel.fullStatus(this.oldTicketLevel).isOrAfter(FullChunkStatus.FULL)) return null;
+        return this.getFullChunkNowUnchecked();
+    }
 
+    public LevelChunk getFullChunkNowUnchecked() {
+        CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> statusFuture = this.getFutureIfPresentUnchecked(ChunkStatus.FULL);
+        Either<ChunkAccess, ChunkHolder.Failure> either = (Either<ChunkAccess, ChunkHolder.Failure>) statusFuture.getNow(null);
+        return (either == null) ? null : (LevelChunk) either.left().orElse(null);
+    }
+    // CraftBukkit end
+
+    public CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> getFutureIfPresentUnchecked(ChunkStatus chunkStatus) {
+        CompletableFuture<Either<ChunkAccess, ChunkHolder.Failure>> completablefuture = (CompletableFuture) this.futures.get(chunkStatus.getIndex());
+
         return completablefuture == null ? ChunkHolder.UNLOADED_CHUNK_FUTURE : completablefuture;
     }
 
@@ -179,6 +197,7 @@
         if (levelchunk != null) {
             int i = this.levelHeightAccessor.getSectionIndex(blockpos.getY());
 
+            if (i < 0 || i >= this.changedBlocksPerSection.length) return; // CraftBukkit - SPIGOT-6086, SPIGOT-6296
             if (this.changedBlocksPerSection[i] == null) {
                 this.hasChangedSections = true;
                 this.changedBlocksPerSection[i] = new ShortOpenHashSet();
@@ -256,9 +275,12 @@
                                 LevelChunkSection levelchunksection = levelchunk.getSection(i);
                                 ClientboundSectionBlocksUpdatePacket clientboundsectionblocksupdatepacket = new ClientboundSectionBlocksUpdatePacket(sectionpos, shortset, levelchunksection);
 
-                                this.broadcast(list, clientboundsectionblocksupdatepacket);
-                                clientboundsectionblocksupdatepacket.runUpdates((blockpos1, blockstate1) -> {
-                                    this.broadcastBlockEntityIfNeeded(list, level, blockpos1, blockstate1);
+                                this.broadcast(list, packetplayoutmultiblockchange);
+                                // CraftBukkit start
+                                List finalList = list;
+                                packetplayoutmultiblockchange.runUpdates((blockposition1, iblockdata1) -> {
+                                    this.broadcastBlockEntityIfNeeded(finalList, world, blockposition1, iblockdata1);
+                                    // CraftBukkit end
                                 });
                             }
                         }
@@ -411,7 +433,31 @@
         boolean flag1 = ChunkLevel.isLoaded(this.ticketLevel);
         FullChunkStatus fullchunkstatus = ChunkLevel.fullStatus(this.oldTicketLevel);
         FullChunkStatus fullchunkstatus1 = ChunkLevel.fullStatus(this.ticketLevel);
+        // CraftBukkit start
+        // ChunkUnloadEvent: Called before the chunk is unloaded: isChunkLoaded is still true and chunk can still be modified by plugins.
+        if (fullchunkstatus.isOrAfter(FullChunkStatus.FULL) && !fullchunkstatus1.isOrAfter(FullChunkStatus.FULL)) {
+            this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+                LevelChunk chunk = (LevelChunk)either.left().orElse(null);
+                if (chunk != null) {
+                    chunkMap.callbackExecutor.execute(() -> {
+                        // Minecraft will apply the chunks tick lists to the world once the chunk got loaded, and then store the tick
+                        // lists again inside the chunk once the chunk becomes inaccessible and set the chunk's needsSaving flag.
+                        // These actions may however happen deferred, so we manually set the needsSaving flag already here.
+                        chunk.setUnsaved(true);
+                        chunk.unloadCallback();
+                    });
+                }
+            }).exceptionally((throwable) -> {
+                // ensure exceptions are printed, by default this is not the case
+                MinecraftServer.LOGGER.error("Failed to schedule unload callback for chunk " + ChunkHolder.this.pos, throwable);
+                return null;
+            });
 
+            // Run callback right away if the future was already done
+            chunkMap.callbackExecutor.run();
+        }
+        // CraftBukkit end
+
         if (flag) {
             Either<ChunkAccess, ChunkHolder.ChunkLoadingFailure> either = Either.right(new ChunkHolder.ChunkLoadingFailure() {
                 @Override
@@ -482,6 +527,26 @@
 
         this.onLevelChange.onLevelChange(this.pos, this::getQueueLevel, this.ticketLevel, this::setQueueLevel);
         this.oldTicketLevel = this.ticketLevel;
+        // CraftBukkit start
+        // ChunkLoadEvent: Called after the chunk is loaded: isChunkLoaded returns true and chunk is ready to be modified by plugins.
+        if (!fullchunkstatus.isOrAfter(FullChunkStatus.FULL) && fullchunkstatus1.isOrAfter(FullChunkStatus.FULL)) {
+            this.getFutureIfPresentUnchecked(ChunkStatus.FULL).thenAccept((either) -> {
+                LevelChunk chunk = (LevelChunk)either.left().orElse(null);
+                if (chunk != null) {
+                    chunkMap.callbackExecutor.execute(() -> {
+                        chunk.loadCallback();
+                    });
+                }
+            }).exceptionally((throwable) -> {
+                // ensure exceptions are printed, by default this is not the case
+                MinecraftServer.LOGGER.error("Failed to schedule load callback for chunk " + ChunkHolder.this.pos, throwable);
+                return null;
+            });
+
+            // Run callback right away if the future was already done
+            chunkMap.callbackExecutor.run();
+        }
+        // CraftBukkit end
     }
 
     public boolean wasAccessibleSinceLastSave() {