77import dev .booky .betterview .common .util .ChunkIterationUtil ;
88import dev .booky .betterview .common .util .McChunkPos ;
99import io .netty .buffer .ByteBuf ;
10+ import io .netty .util .ReferenceCountUtil ;
1011import org .checkerframework .checker .nullness .qual .MonotonicNonNull ;
1112import org .jspecify .annotations .NullMarked ;
1213import org .jspecify .annotations .Nullable ;
@@ -276,8 +277,13 @@ public void move(LevelHook newLevel, McChunkPos newPos) {
276277 }
277278
278279 public void purgeQueue (int chunkX , int chunkZ ) {
279- this .chunkQueue .removeIf (entry ->
280- entry .chunkPos .getX () == chunkX && entry .chunkPos .getZ () == chunkZ );
280+ this .chunkQueue .removeIf (entry -> {
281+ if (entry .chunkPos .getX () == chunkX && entry .chunkPos .getZ () == chunkZ ) {
282+ entry .release ();
283+ return true ;
284+ }
285+ return false ;
286+ });
281287 }
282288
283289 public boolean checkQueueEntry (ChunkQueueEntry entry ) {
@@ -290,6 +296,7 @@ public boolean checkQueueEntry(ChunkQueueEntry entry) {
290296 int chunkIndex = calcIndex (chunkPos .getX (), chunkPos .getZ (), this .storageDiameter );
291297 ChunkState state = this .chunkStates [chunkIndex ];
292298 if (state .lifecycle != BV_QUEUED ) {
299+ entry .release ();
293300 return true ; // chunk no longer queued, remove entry
294301 }
295302
@@ -299,16 +306,18 @@ public boolean checkQueueEntry(ChunkQueueEntry entry) {
299306 LOGGER .error ("Error while building chunk {} for {} in {}" ,
300307 chunkPos , this .player , this .level , exception );
301308 state .lifecycle = UNLOADED ;
309+ entry .release ();
302310 return true ;
303311 }
304312
305313 ByteBuf chunkBuf = entry .future .getNow (null );
306314 if (chunkBuf == null ) {
307- // ran into generation limit, try again later
315+ // probably ran into generation limit, try again later
308316 state .lifecycle = UNLOADED ;
309317 // reset iteration index to prevent this entry from getting skipped
310318 // until all other chunks have been loaded
311319 this .iterationIndex = 0 ;
320+ entry .release ();
312321 return true ;
313322 }
314323
@@ -319,6 +328,7 @@ public boolean checkQueueEntry(ChunkQueueEntry entry) {
319328 : this .level .getEmptyChunkBuf (chunkPos );
320329 this .player .getNettyChannel ().write (new BypassedPacket (finalChunkBuf ));
321330 state .lifecycle = BV_LOADED ; // mark chunk as loaded by BV
331+ entry .release ();
322332 return true ;
323333 }
324334
@@ -342,7 +352,7 @@ public void unloadBvChunks() {
342352 }
343353 }
344354 // clear all chunks which are currently queued for sending
345- this .chunkQueue . clear ();
355+ this .clearChunkQueue ();
346356 }
347357
348358 public void handleDimensionReset (@ Nullable Object networkDimension ) {
@@ -357,7 +367,7 @@ public void handleDimensionReset(@Nullable Object networkDimension) {
357367 for (int i = 0 , len = this .chunkStates .length ; i < len ; i ++) {
358368 this .chunkStates [i ].set (0 , 0 , UNLOADED );
359369 }
360- this .chunkQueue . clear ();
370+ this .clearChunkQueue ();
361371 // disable temporarily
362372 this .disable ();
363373 }
@@ -410,6 +420,17 @@ public void disable() {
410420 this .player .sendViewDistancePacket (this .getServerViewDistance ());
411421 }
412422
423+ public void release () {
424+ this .clearChunkQueue ();
425+ }
426+
427+ public void clearChunkQueue () {
428+ for (ChunkQueueEntry entry : this .chunkQueue ) {
429+ entry .release ();
430+ }
431+ this .chunkQueue .clear ();
432+ }
433+
413434 public enum ChunkLifecycle {
414435 UNLOADED ,
415436 SERVER_LOADED ,
@@ -437,5 +458,16 @@ public boolean hasCoords() {
437458 public record ChunkQueueEntry (
438459 McChunkPos chunkPos ,
439460 CompletableFuture <@ Nullable ByteBuf > future
440- ) {}
461+ ) {
462+
463+ public ChunkQueueEntry retain () {
464+ this .future .thenApply (ReferenceCountUtil ::retain );
465+ return this ;
466+ }
467+
468+ public ChunkQueueEntry release () {
469+ this .future .thenApply (ReferenceCountUtil ::release );
470+ return this ;
471+ }
472+ }
441473}
0 commit comments