From e5ea86bf8f962c9603534171e8bea5e7a99d3bfe Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 6 Dec 2025 12:14:03 -0600 Subject: [PATCH] Fix handling bad heightmaps --- .../common/wrappers/chunk/ChunkWrapper.java | 7 --- .../BatchGenerationEnvironment.java | 4 ++ .../ChunkCompoundTagParser.java | 53 +++++++++++++++++-- .../chunkFileHandling/ChunkFileReader.java | 2 +- 4 files changed, 55 insertions(+), 11 deletions(-) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java index 72e8cee5c..0e4eca66b 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java @@ -244,13 +244,6 @@ public class ChunkWrapper implements IChunkWrapper public void createDhHeightMaps() { - // only continue if we haven't already generated the DH heightmaps - if (this.solidHeightMap != null) - { - return; - } - - // re-calculate the min/max heights for consistency (during world gen these may be wrong) this.minNonEmptyHeight = Integer.MIN_VALUE; this.maxNonEmptyHeight = Integer.MAX_VALUE; diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java index 6ece223cc..e58be1b81 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java @@ -668,6 +668,10 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm throwIfThreadInterrupted(); + // not always necessary, but sometimes MC heightmap is wrong + // and can cause LODs to generate incorrectly + centerChunkWrapper.createDhHeightMaps(); + // pre-generated chunks should have lighting but new ones won't if (!centerChunkWrapper.isDhBlockLightingCorrect()) { diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkCompoundTagParser.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkCompoundTagParser.java index 6c197356d..9ad4cabb3 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkCompoundTagParser.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkCompoundTagParser.java @@ -194,6 +194,11 @@ public class ChunkCompoundTagParser int sectionYCount = #if MC_VER < MC_1_17_1 16; #else mcWorldGenLevel.getSectionsCount(); #endif LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYCount]; boolean hasBlocks = readAndPopulateSections(mcWorldGenLevel, chunkPos, tagLevel, chunkSections); + if (!hasBlocks) + { + return null; + } + long inhabitedTime = CompoundTagUtil.getLong(tagLevel, "InhabitedTime"); boolean isLightOn = CompoundTagUtil.getBoolean(tagLevel, "isLightOn"); @@ -218,7 +223,49 @@ public class ChunkCompoundTagParser // chunk wrapper so we can pass along extra data more easily ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, dhServerLevel.getServerLevelWrapper(), !hasHeightmapData); - return chunkWrapper; + + + + //===========================// + // check if chunk has blocks // + //===========================// + + // in some MC versions all the NBT data will be there + // but the chunk will be totally empty, + // usually this means the chunk was only partially generated. + // If that happens we should try to generate the chunk from scratch + // otherwise we can end up with large empty holes in the world. + + // walking through the heightmap (recreated by DH if missing) + // is a fast way to check if there are any blocks in the chunk + boolean chunkHasBlocks = false; + int serverMinHeight = dhServerLevel.getServerLevelWrapper().getMinHeight(); + for (int x = 0; x < 16 && !chunkHasBlocks; x++) + { + for (int z = 0; z < 16 && !chunkHasBlocks; z++) + { + int heightMap = Math.max( + // max between both heightmaps just in case there's a discrepancy + chunkWrapper.getLightBlockingHeightMapValue(x, z), + chunkWrapper.getSolidHeightMapValue(x, z) + ); + if (heightMap != serverMinHeight) + { + chunkHasBlocks = true; + } + } + } + + + if (chunkHasBlocks) + { + return chunkWrapper; + } + else + { + // no blocks detected, this chunk should be generated from scratch + return null; + } } @@ -247,9 +294,9 @@ public class ChunkCompoundTagParser boolean blocksFound = false; if (tagSections != null) { - for (int j = 0; j < tagSections.size(); ++j) + for (int i = 0; i < tagSections.size(); ++i) { - CompoundTag tagSection = CompoundTagUtil.getCompoundTag(tagSections, j); + CompoundTag tagSection = CompoundTagUtil.getCompoundTag(tagSections, i); if (tagSection == null) { continue; diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkFileReader.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkFileReader.java index 3ea80ceec..cc51b392d 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkFileReader.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkFileReader.java @@ -293,7 +293,7 @@ public class ChunkFileReader implements AutoCloseable public ChunkWrapper CreateProtoChunkWrapper(ServerLevel level, ChunkPos chunkPos) { ProtoChunk chunk = CreateProtoChunk(level, chunkPos); - return new ChunkWrapper(chunk, this.params.dhServerLevel.getLevelWrapper()); + return new ChunkWrapper(chunk, this.params.dhServerLevel.getLevelWrapper(), false); } public static ProtoChunk CreateProtoChunk(ServerLevel level, ChunkPos chunkPos) {