Fix potential crash during worldgen with release_protochunks enabled

The crash can occur if a protochunk next to a FULL chunk is dropped,
and then later re-requested. If it was not persisted to disk for any
reason, it starts regeneration from scratch. At FEATURES stage, it may
try to place blocks into the adjacent LevelChunk already in the world.

The fix is to prevent this situation from even happening by pinning
protochunks directly next to FULL chunks, and preventing them from
unloading.
This commit is contained in:
embeddedt 2026-05-24 19:45:24 -04:00
parent 74f76f7305
commit 494203ef5a
No known key found for this signature in database
GPG Key ID: A69433EC199B5613
3 changed files with 10 additions and 4 deletions

View File

@ -4,7 +4,6 @@ import com.mojang.datafixers.util.Either;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkLevel;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.embeddedt.modernfix.duck.release_protochunks.IClearableChunkHolder;
@ -85,7 +84,7 @@ public class ChunkHolderMixin implements IClearableChunkHolder {
}
private void mfix$markAsNeedingProtoChunkDrop() {
if (!ChunkLevel.fullStatus(this.ticketLevel).isOrAfter(FullChunkStatus.FULL)
if (this.ticketLevel >= LOWEST_DROPPABLE_TICKET_LEVEL
&& ChunkLevel.isLoaded(this.ticketLevel)) {
// register for suspension check when chain completes
var map = ((ISuspendedHolderTrackingChunkMap)this.playerProvider);

View File

@ -8,7 +8,6 @@ import it.unimi.dsi.fastutil.longs.Long2ObjectLinkedOpenHashMap;
import net.minecraft.server.level.ChunkHolder;
import net.minecraft.server.level.ChunkLevel;
import net.minecraft.server.level.ChunkMap;
import net.minecraft.server.level.FullChunkStatus;
import net.minecraft.util.thread.BlockableEventLoop;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.ChunkAccess;
@ -68,7 +67,7 @@ public abstract class ChunkMapMixin implements ISuspendedHolderTrackingChunkMap
long pos = entry.getLongKey();
ChunkHolder holder = this.updatingChunkMap.get(pos);
if (holder == null // already removed
|| ChunkLevel.fullStatus(holder.getTicketLevel()).isOrAfter(FullChunkStatus.FULL) // promoted to FULL
|| holder.getTicketLevel() < IClearableChunkHolder.LOWEST_DROPPABLE_TICKET_LEVEL // promoted to FULL or adjacent to FULL chunk
|| !ChunkLevel.isLoaded(holder.getTicketLevel()) // is going to be dropped through normal code path
) {
dropIterator.remove();

View File

@ -1,8 +1,16 @@
package org.embeddedt.modernfix.duck.release_protochunks;
import net.minecraft.server.level.ChunkLevel;
import net.minecraft.server.level.FullChunkStatus;
import java.util.concurrent.atomic.AtomicInteger;
public interface IClearableChunkHolder {
/**
* We don't want to drop FULL chunks, or chunks immediately surrouding FULL. So + 2 is the minimum we can drop.
*/
int LOWEST_DROPPABLE_TICKET_LEVEL = ChunkLevel.byStatus(FullChunkStatus.FULL) + 2;
void mfix$resetProtoChunkFutures();
AtomicInteger mfix$getGenerationRefCount();