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 e04273faf..d0e9c9a9e 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 @@ -18,10 +18,10 @@ */ package com.seibel.distanthorizons.common.wrappers.chunk; +import com.google.gson.internal.NonNullElementWrapperList; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.misc.MutableBlockPosWrapper; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; @@ -98,9 +98,9 @@ public class ChunkWrapper implements IChunkWrapper private int maxNonEmptyHeight = Integer.MAX_VALUE; /** will be null if we are using MC heightmaps */ - private final int[][] solidHeightMap; + private int[][] solidHeightMap = null; /** will be null if we are using MC heightmaps */ - private final int[][] lightBlockingHeightMap; + private int[][] lightBlockingHeightMap = null; @@ -109,23 +109,18 @@ public class ChunkWrapper implements IChunkWrapper //=============// public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel) + { + this(chunk, wrappedLevel, true); + } + public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel, boolean recreateHeightmaps) { this.chunk = chunk; this.wrappedLevel = wrappedLevel; this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z); - // use DH heightmaps if requested - if (Config.Common.LodBuilding.recalculateChunkHeightmaps.get()) + if (recreateHeightmaps) { - this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH]; - this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH]; - - this.recalculateDhHeightMapsIfNeeded(); - } - else - { - this.solidHeightMap = null; - this.lightBlockingHeightMap = null; + this.createDhHeightMaps(); } } @@ -248,56 +243,66 @@ public class ChunkWrapper implements IChunkWrapper } private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getInclusiveMinBuildHeight(); } - /** Will only run if the config says the MC heightmaps shouldn't be trusted. */ - public void recalculateDhHeightMapsIfNeeded() + 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; - // recalculate heightmaps if needed - if (this.solidHeightMap != null) + this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH]; + this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH]; + + for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) { - for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++) + for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) { - for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++) + int minInclusiveBuildHeight = this.getMinNonEmptyHeight(); + // if no blocks are found the height map will be at the bottom of the world + int solidHeight = minInclusiveBuildHeight; + int lightBlockingHeight = minInclusiveBuildHeight; + + + int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight(); + IBlockStateWrapper block = this.getBlockState(x, y, z); + while (// go down until we reach the minimum build height + y > minInclusiveBuildHeight + // keep going until we find both height map values + && + ( + solidHeight == minInclusiveBuildHeight + || lightBlockingHeight == minInclusiveBuildHeight + ) + ) { - int minInclusiveBuildHeight = this.getMinNonEmptyHeight(); - // if no blocks are found the height map will be at the bottom of the world - int solidHeight = minInclusiveBuildHeight; - int lightBlockingHeight = minInclusiveBuildHeight; - - - int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight(); - IBlockStateWrapper block = this.getBlockState(x, y, z); - while (// go down until we reach the minimum build height - y > minInclusiveBuildHeight - // keep going until we find both height map values - && (solidHeight == minInclusiveBuildHeight || lightBlockingHeight == minInclusiveBuildHeight)) + // is this block solid? + if (solidHeight == minInclusiveBuildHeight + && block.isSolid()) { - // is this block solid? - if (solidHeight == minInclusiveBuildHeight - && block.isSolid()) - { - solidHeight = y; - } - - // is this block light blocking? - if (lightBlockingHeight == minInclusiveBuildHeight - && block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT) - { - lightBlockingHeight = y; - } - - // get the next block down - y--; - block = this.getBlockState(x, y, z); + solidHeight = y; } - this.solidHeightMap[x][z] = solidHeight; - this.lightBlockingHeightMap[x][z] = lightBlockingHeight; + // is this block light blocking? + if (lightBlockingHeight == minInclusiveBuildHeight + && block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT) + { + lightBlockingHeight = y; + } + + // get the next block down + y--; + block = this.getBlockState(x, y, z); } + + this.solidHeightMap[x][z] = solidHeight; + this.lightBlockingHeightMap[x][z] = lightBlockingHeight; } } } 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 881650971..06265cf61 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 @@ -34,6 +34,7 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.util.ExceptionUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.gridList.ArrayGridList; @@ -61,7 +62,6 @@ import net.minecraft.world.level.ChunkPos; import net.minecraft.world.level.chunk.*; import net.minecraft.world.level.levelgen.DebugLevelSource; import net.minecraft.world.level.levelgen.FlatLevelSource; -import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; #if MC_VER <= MC_1_17_1 @@ -309,13 +309,12 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm int refPosX = genEvent.minPos.getX() - borderSize; int refPosZ = genEvent.minPos.getZ() - borderSize; - LightGetterAdaptor lightGetterAdaptor = new LightGetterAdaptor(this.globalParams.level); + LightGetterAdaptor lightGetterAdaptor = new LightGetterAdaptor(this.globalParams.mcServerLevel); DummyLightEngine dummyLightEngine = new DummyLightEngine(lightGetterAdaptor); // reused data between each offset Map chunkSkyLightingByDhPos = Collections.synchronizedMap(new HashMap<>()); Map chunkBlockLightingByDhPos = Collections.synchronizedMap(new HashMap<>()); - Map generatedChunkByDhPos = Collections.synchronizedMap(new HashMap<>()); Map chunkWrappersByDhPos = Collections.synchronizedMap(new HashMap<>()); @@ -324,7 +323,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm // read existing chunks from file // //================================// - HashMap> readFutureByDhChunkPos = new HashMap<>(); + HashMap> readFutureByDhChunkPos = new HashMap<>(); Iterator existingChunkPosIterator = ChunkPosGenStream.getIterator( genEvent.minPos.getX(), genEvent.minPos.getZ(), @@ -336,18 +335,18 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm ChunkPos chunkPos = existingChunkPosIterator.next(); DhChunkPos dhChunkPos = new DhChunkPos(chunkPos.x, chunkPos.z); - CompletableFuture getExistingChunkFuture + CompletableFuture getExistingChunkFuture // running async allows file IO to run in parallel when C2ME is present - = this.chunkFileReader.createEmptyOrPreExistingChunkAsync( + = this.chunkFileReader.createEmptyOrPreExistingChunkWrapperAsync( chunkPos.x, chunkPos.z, - chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos); + chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, chunkWrappersByDhPos); readFutureByDhChunkPos.put(dhChunkPos, getExistingChunkFuture); } // normally DH will handle each of these futures serially // but if C2ME is present these will be completed in parallel - for (CompletableFuture readChunkFuture : readFutureByDhChunkPos.values()) + for (CompletableFuture readChunkFuture : readFutureByDhChunkPos.values()) { readChunkFuture.join(); } @@ -371,8 +370,8 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm // create empty chunks outside the generation radius if (!readFutureByDhChunkPos.containsKey(dhChunkPos)) { - ChunkAccess chunk = ChunkFileReader.CreateEmptyChunk(this.globalParams.level, chunkPos); - generatedChunkByDhPos.put(dhChunkPos, chunk); + ChunkWrapper chunkWrapper = this.chunkFileReader.CreateProtoChunkWrapper(this.globalParams.mcServerLevel, chunkPos); + chunkWrappersByDhPos.put(dhChunkPos, chunkWrapper); } } @@ -407,7 +406,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm // get/create the list of chunks we're going to generate IEmptyChunkRetrievalFunc fallbackChunkGetterFunc = (chunkPosX, chunkPosZ) -> Objects.requireNonNull( - generatedChunkByDhPos.get(new DhChunkPos(chunkPosX, chunkPosZ)), + chunkWrappersByDhPos.get(new DhChunkPos(chunkPosX, chunkPosZ)).getChunk(), () -> String.format("Requested chunk [%d, %d] unavailable during world generation", chunkPosX, chunkPosZ)); ArrayGridList regionChunks = new ArrayGridList<>( @@ -424,7 +423,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm DhLitWorldGenRegion region = new DhLitWorldGenRegion( centerX, centerZ, centerChunk, - this.globalParams.level, dummyLightEngine, regionChunks, + this.globalParams.mcServerLevel, dummyLightEngine, regionChunks, ChunkStatus.STRUCTURE_STARTS, radius, // this method shouldn't be necessary since we're passing in a pre-populated // list of chunks, but just in case @@ -436,8 +435,8 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm //=============================// - // create chunk wrappers // - // and process existing chunks // + // process existing chunks // + // //=============================// ArrayGridList chunkWrapperList = new ArrayGridList<>(regionChunks.gridSize); @@ -454,17 +453,32 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm } else if (chunk != null) { - // wrap the chunk + // ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper()); chunkWrapperList.set(relX, relZ, chunkWrapper); // try setting the wrapper's lighting if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos())) { - chunkWrapper.setBlockLightStorage(chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos())); - chunkWrapper.setSkyLightStorage(chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos())); - chunkWrapper.setIsDhBlockLightCorrect(true); - chunkWrapper.setIsDhSkyLightCorrect(true); + // block + ChunkLightStorage blockLightStorage = chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos()); + // if the light storage is empty then we should try generating the lighting + // ourselves, the light data is probably missing + if (blockLightStorage != null + && !blockLightStorage.isEmpty()) + { + chunkWrapper.setBlockLightStorage(blockLightStorage); + chunkWrapper.setIsDhBlockLightCorrect(true); + } + + // sky + ChunkLightStorage skyLightStorage = chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos()); + if (skyLightStorage != null + && !skyLightStorage.isEmpty()) + { + chunkWrapper.setSkyLightStorage(skyLightStorage); + chunkWrapper.setIsDhSkyLightCorrect(true); + } } chunkWrappersByDhPos.put(chunkPos, chunkWrapper); @@ -553,6 +567,16 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm } }); + + // if we're only working with pre-existing chunks, + // biomes need to be initialized but no other steps should be done + if (genEvent.generatorMode == EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY) + { + this.stepBiomes.generateGroup(genEvent.threadedParam, region, GetCutoutFrom(chunkWrappersToGenerate, EDhApiWorldGenerationStep.BIOMES)); + return; + } + + EDhApiWorldGenerationStep step = genEvent.targetGenerationStep; if (step == EDhApiWorldGenerationStep.EMPTY) { @@ -626,27 +650,25 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm // light each chunk in the list for (int i = 0; i < iChunkWrapperList.size(); i++) { - ChunkWrapper centerChunk = (ChunkWrapper) iChunkWrapperList.get(i); - if (centerChunk == null) + ChunkWrapper centerChunkWrapper = (ChunkWrapper) iChunkWrapperList.get(i); + if (centerChunkWrapper == null) { continue; } throwIfThreadInterrupted(); - // make sure the height maps are all properly generated - // if this isn't done everything else afterward may fail - //Heightmap.primeHeightmaps(centerChunk.getChunk(), ChunkStatus.FEATURES.heightmapsAfter()); - centerChunk.recalculateDhHeightMapsIfNeeded(); - // pre-generated chunks should have lighting but new ones won't - //if (!centerChunk.isDhBlockLightingCorrect()) - //{ - // DhLightingEngine.INSTANCE.bakeChunkBlockLighting(centerChunk, iChunkWrapperList, maxSkyLight); - //} - centerChunk.setIsDhBlockLightCorrect(true); + if (!centerChunkWrapper.isDhBlockLightingCorrect()) + { + DhLightingEngine.INSTANCE.bakeChunkBlockLighting(centerChunkWrapper, iChunkWrapperList, maxSkyLight); + } - //this.dhServerLevel.updateBeaconBeamsForChunk(centerChunk, iChunkWrapperList); + List activeBeamList = centerChunkWrapper.getAllActiveBeacons(iChunkWrapperList); + if (!activeBeamList.isEmpty()) + { + this.dhServerLevel.updateBeaconBeamsForChunkPos(centerChunkWrapper.getChunkPos(), activeBeamList); + } } } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/GlobalWorldGenParams.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/GlobalWorldGenParams.java index e1b112ec2..241e1d375 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/GlobalWorldGenParams.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/GlobalWorldGenParams.java @@ -67,8 +67,8 @@ import net.minecraft.world.level.levelgen.RandomState; public final class GlobalWorldGenParams { public final ChunkGenerator generator; - public final IDhServerLevel lodLevel; - public final ServerLevel level; + public final IDhServerLevel dhServerLevel; + public final ServerLevel mcServerLevel; public final Registry biomes; public final RegistryAccess registry; public final long worldSeed; @@ -98,12 +98,12 @@ public final class GlobalWorldGenParams // constructor // //=============// - public GlobalWorldGenParams(IDhServerLevel lodLevel) + public GlobalWorldGenParams(IDhServerLevel dhServerLevel) { - this.lodLevel = lodLevel; + this.dhServerLevel = dhServerLevel; - this.level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject(); - MinecraftServer server = this.level.getServer(); + this.mcServerLevel = ((ServerLevelWrapper) dhServerLevel.getServerLevelWrapper()).getWrappedMcObject(); + MinecraftServer server = this.mcServerLevel.getServer(); WorldData worldData = server.getWorldData(); this.registry = server.registryAccess(); @@ -122,16 +122,16 @@ public final class GlobalWorldGenParams #endif #if MC_VER >= MC_1_18_2 - this.biomeManager = new BiomeManager(this.level, BiomeManager.obfuscateSeed(this.worldSeed)); - this.chunkScanner = this.level.getChunkSource().chunkScanner(); + this.biomeManager = new BiomeManager(this.mcServerLevel, BiomeManager.obfuscateSeed(this.worldSeed)); + this.chunkScanner = this.mcServerLevel.getChunkSource().chunkScanner(); #endif this.structures = server.getStructureManager(); - this.generator = this.level.getChunkSource().getGenerator(); + this.generator = this.mcServerLevel.getChunkSource().getGenerator(); this.dataFixer = server.getFixerUpper(); #if MC_VER >= MC_1_19_2 - this.randomState = this.level.getChunkSource().randomState(); + this.randomState = this.mcServerLevel.getChunkSource().randomState(); #endif } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/ThreadWorldGenParams.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/ThreadWorldGenParams.java index 5c6b20a71..d2b50b795 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/ThreadWorldGenParams.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/ThreadWorldGenParams.java @@ -56,7 +56,7 @@ public final class ThreadWorldGenParams ThreadWorldGenParams threadParam = LOCAL_PARAM_REF.get(); if (threadParam != null && threadParam.isValid - && threadParam.level == globalParams.level) + && threadParam.level == globalParams.mcServerLevel) { return threadParam; } @@ -70,7 +70,7 @@ public final class ThreadWorldGenParams { previousGlobalWorldGenParams = param; - this.level = param.level; + this.level = param.mcServerLevel; #if MC_VER < MC_1_18_2 this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, this.level); @@ -78,7 +78,7 @@ public final class ThreadWorldGenParams this.structCheck = this.createStructureCheck(param); #else this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures, - param.level.dimension(), param.generator, param.randomState, this.level, param.generator.getBiomeSource(), param.worldSeed, + param.mcServerLevel.dimension(), param.generator, param.randomState, this.level, param.generator.getBiomeSource(), param.worldSeed, param.dataFixer); #endif } 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 826992cce..6c197356d 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 @@ -20,11 +20,10 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling; import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; -import com.mojang.serialization.Dynamic; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.level.IDhServerLevel; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.LodUtil; @@ -36,6 +35,7 @@ import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.atomic.AtomicBoolean; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import net.minecraft.core.Registry; #if MC_VER >= MC_1_19_4 import net.minecraft.core.registries.Registries; @@ -64,7 +64,6 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.levelgen.Heightmap; #if MC_VER >= MC_1_18_2 -import net.minecraft.world.level.levelgen.blending.BlendingData; #if MC_VER < MC_1_19_2 import net.minecraft.world.level.levelgen.feature.StructureFeature; #endif @@ -86,7 +85,6 @@ import net.minecraft.world.level.chunk.status.ChunkStatus; import net.minecraft.world.level.chunk.status.ChunkType; #elif MC_VER >= MC_1_21_1 import net.minecraft.world.level.chunk.status.ChunkStatus; -import net.minecraft.world.level.chunk.status.ChunkType; #endif import net.minecraft.world.level.material.Fluid; @@ -111,7 +109,9 @@ public class ChunkCompoundTagParser // read chunk // //============// - public static LevelChunk createFromTag(WorldGenLevel level, ChunkPos chunkPos, CompoundTag chunkData) + public static ChunkWrapper createFromTag( + WorldGenLevel mcWorldGenLevel, IDhServerLevel dhServerLevel, + ChunkPos chunkPos, CompoundTag chunkData) { #if MC_VER < MC_1_18_2 CompoundTag tagLevel = chunkData.getCompound("Level"); @@ -158,57 +158,27 @@ public class ChunkCompoundTagParser - //==========================// - // ignore incomplete chunks // - //==========================// - - #if MC_VER < MC_1_20_6 - ChunkStatus.ChunkType chunkType; - #else - ChunkType chunkType; - #endif - chunkType = readChunkType(tagLevel); - - #if MC_VER < MC_1_18_2 - if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK) - { - return null; - } - #elif MC_VER < MC_1_20_6 - if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK) - { - return null; - } - #else - if (chunkType == ChunkType.PROTOCHUNK) - { - return null; - } - #endif - - - //===========// // get ticks // //===========// #if MC_VER < MC_1_18_2 ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer( - level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), #if MC_VER >= MC_1_17_1 level, #endif - chunkPos, level.getLevel().getChunkSource().getGenerator().getBiomeSource(), + mcWorldGenLevel.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), #if MC_VER >= MC_1_17_1 mcWorldGenLevel, #endif + chunkPos, mcWorldGenLevel.getLevel().getChunkSource().getGenerator().getBiomeSource(), tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null); String BLOCK_TICKS_TAG_PRE18 = "TileTicks"; TickList blockTicks = tagLevel.contains(BLOCK_TICKS_TAG_PRE18, 9) ? ChunkTickList.create(tagLevel.getList(BLOCK_TICKS_TAG_PRE18, 10), Registry.BLOCK::getKey, Registry.BLOCK::get) : new ProtoTickList(block -> (block == null || block.defaultBlockState().isAir()), chunkPos, - tagLevel.getList("ToBeTicked", 9) #if MC_VER >= MC_1_17_1 , level #endif ); + tagLevel.getList("ToBeTicked", 9) #if MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif ); String FLUID_TICKS_TAG_PRE18 = "LiquidTicks"; TickList fluidTicks = tagLevel.contains(FLUID_TICKS_TAG_PRE18, 9) ? ChunkTickList.create(tagLevel.getList(FLUID_TICKS_TAG_PRE18, 10), Registry.FLUID::getKey, Registry.FLUID::get) : new ProtoTickList(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos, - tagLevel.getList("LiquidsToBeTicked", 9) #if MC_VER >= MC_1_17_1 , level #endif ); + tagLevel.getList("LiquidsToBeTicked", 9) #if MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif ); #else // ticks shouldn't be needed so ignore them for MC versions after 1.18.2 LevelChunkTicks blockTicks = new LevelChunkTicks<>(); @@ -221,7 +191,10 @@ public class ChunkCompoundTagParser // get misc properties // //=====================// - LevelChunkSection[] levelChunkSections = readSections(level, chunkPos, tagLevel); + 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); + long inhabitedTime = CompoundTagUtil.getLong(tagLevel, "InhabitedTime"); boolean isLightOn = CompoundTagUtil.getBoolean(tagLevel, "isLightOn"); @@ -232,48 +205,20 @@ public class ChunkCompoundTagParser //============// #if MC_VER < MC_1_18_2 - LevelChunk chunk = new LevelChunk((Level) level.getLevel(), chunkPos, chunkBiomeContainer, UpgradeData.EMPTY, blockTicks, - fluidTicks, inhabitedTime, levelChunkSections, null); + LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel.getLevel(), chunkPos, chunkBiomeContainer, UpgradeData.EMPTY, blockTicks, + fluidTicks, inhabitedTime, chunkSections, null); #else - LevelChunk chunk = new LevelChunk((Level) level, chunkPos, UpgradeData.EMPTY, blockTicks, - fluidTicks, inhabitedTime, levelChunkSections, null, null); + LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel, chunkPos, UpgradeData.EMPTY, blockTicks, + fluidTicks, inhabitedTime, chunkSections, null, null); #endif // Set some states after object creation chunk.setLightCorrect(isLightOn); - readHeightmaps(chunk, chunkData); + boolean hasHeightmapData = readHeightmaps(chunk, chunkData); - return chunk; - } - - - - //==========================// - // chunk type // - // (incomplete chunk check) // - //==========================// - - private static - #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType - #elif MC_VER < MC_1_21_1 ChunkType - #else ChunkType #endif - readChunkType(CompoundTag tagLevel) - { - String statusString = CompoundTagUtil.getString(tagLevel,"Status"); - if (statusString != null) - { - ChunkStatus chunkStatus = ChunkStatus.byName(statusString); - if (chunkStatus != null) - { - return chunkStatus.getChunkType(); - } - } - - #if MC_VER <= MC_1_20_4 - return ChunkStatus.ChunkType.PROTOCHUNK; - #else - return ChunkType.PROTOCHUNK; - #endif + // chunk wrapper so we can pass along extra data more easily + ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, dhServerLevel.getServerLevelWrapper(), !hasHeightmapData); + return chunkWrapper; } @@ -284,10 +229,11 @@ public class ChunkCompoundTagParser //=================// /** handles both blocks and biomes */ - private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData) + private static boolean readAndPopulateSections( + LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData, + LevelChunkSection[] chunkSections) { - int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif - LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex]; + int sectionYCount = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif ListTag tagSections = CompoundTagUtil.getListTag(chunkData, "Sections", 10); // try lower-case "sections" if capital "Sections" is missing @@ -298,6 +244,7 @@ public class ChunkCompoundTagParser } + boolean blocksFound = false; if (tagSections != null) { for (int j = 0; j < tagSections.size(); ++j) @@ -364,6 +311,8 @@ public class ChunkCompoundTagParser .promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string)) .getOrThrow((message) -> logErrorAndReturnException(message)); #endif + + blocksFound = true; } else { @@ -416,7 +365,7 @@ public class ChunkCompoundTagParser biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); #else biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, biomeTag) - .promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string)) + .promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYCount, (String) string)) .getOrThrow((message) -> logErrorAndReturnException(message)); #endif } @@ -450,7 +399,7 @@ public class ChunkCompoundTagParser } } - return chunkSections; + return blocksFound; } private static Codec> getBlockStateCodec(LevelAccessor level) @@ -511,12 +460,12 @@ public class ChunkCompoundTagParser // heightmaps // //============// - private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData) + private static boolean readHeightmaps(LevelChunk chunk, CompoundTag chunkData) { CompoundTag tagHeightmaps = CompoundTagUtil.getCompoundTag(chunkData, "Heightmaps"); if (tagHeightmaps == null) { - return; + return false; } @@ -542,6 +491,7 @@ public class ChunkCompoundTagParser } Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter()); + return true; } 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 aaede6f57..2753f81f1 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 @@ -15,7 +15,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ProtoChunk; import net.minecraft.world.level.chunk.UpgradeData; import net.minecraft.world.level.chunk.storage.IOWorker; @@ -117,64 +116,57 @@ public class ChunkFileReader implements AutoCloseable * If the given chunk pos already exists in the world, that chunk will be returned, * otherwise this will return an empty chunk. */ - public CompletableFuture createEmptyOrPreExistingChunkAsync( + public CompletableFuture createEmptyOrPreExistingChunkWrapperAsync( int chunkX, int chunkZ, Map chunkSkyLightingByDhPos, Map chunkBlockLightingByDhPos, - Map generatedChunkByDhPos) + Map generatedChunkWrapperByDhPos) { ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); DhChunkPos dhChunkPos = new DhChunkPos(chunkX, chunkZ); - if (generatedChunkByDhPos.containsKey(dhChunkPos)) + if (generatedChunkWrapperByDhPos.containsKey(dhChunkPos)) { - return CompletableFuture.completedFuture(generatedChunkByDhPos.get(dhChunkPos)); + return CompletableFuture.completedFuture(generatedChunkWrapperByDhPos.get(dhChunkPos)); } return this.getChunkNbtDataAsync(chunkPos) .thenApply((CompoundTag chunkData) -> { - ChunkAccess newChunk = this.loadOrMakeChunk(chunkPos, chunkData); + ChunkWrapper newChunkWrapper = this.loadOrMakeChunkWrapper(chunkPos, chunkData); - if (Config.Common.LodBuilding.pullLightingForPregeneratedChunks.get()) + // attempt to get chunk lighting + ChunkCompoundTagParser.CombinedChunkLightStorage combinedLights = ChunkCompoundTagParser.readLight(newChunkWrapper.getChunk(), chunkData); + if (combinedLights != null) { - // attempt to get chunk lighting - ChunkCompoundTagParser.CombinedChunkLightStorage combinedLights = ChunkCompoundTagParser.readLight(newChunk, chunkData); - if (combinedLights != null) - { - chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage); - chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage); - } + // may be empty, empty checks are handled later + chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage); + chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage); } - return newChunk; + return newChunkWrapper; }) // separate handle so we can cleanly handle missing chunks and/or thrown errors - .handle((newChunk, throwable) -> + .handle((ChunkWrapper newChunkWrapper, Throwable throwable) -> { - if (newChunk != null) + if (newChunkWrapper != null) { - return newChunk; + return newChunkWrapper; } else { - return CreateEmptyChunk(this.params.level, chunkPos); + return this.CreateProtoChunkWrapper(this.params.mcServerLevel, chunkPos); } }) - .thenApply((newChunk) -> + .thenApply((ChunkWrapper newChunkWrapper) -> { - generatedChunkByDhPos.put(dhChunkPos, newChunk); - return newChunk; + generatedChunkWrapperByDhPos.put(dhChunkPos, newChunkWrapper); + return newChunkWrapper; }); } private CompletableFuture getChunkNbtDataAsync(ChunkPos chunkPos) { - ServerLevel level = this.params.level; - - //if (true) - // return CompletableFuture.completedFuture(null); - - // TODO disabling drastically reduces GC overhead (2Gb/s -> 1GB/s) + ServerLevel level = this.params.mcServerLevel; try { @@ -190,7 +182,7 @@ public class ChunkFileReader implements AutoCloseable { try { - RegionFileStorage storage = this.params.level.getChunkSource().chunkMap.worker.storage; + RegionFileStorage storage = this.params.mcServerLevel.getChunkSource().chunkMap.worker.storage; RegionFileStorageExternalCache cache = this.getOrCreateRegionFileCache(storage); return CompletableFuture.completedFuture(cache.read(chunkPos)); } @@ -264,35 +256,24 @@ public class ChunkFileReader implements AutoCloseable return CompletableFuture.completedFuture(null); } } - private ChunkAccess loadOrMakeChunk(ChunkPos chunkPos, CompoundTag chunkTagData) + private ChunkWrapper loadOrMakeChunkWrapper(ChunkPos chunkPos, CompoundTag chunkTagData) { - ServerLevel level = this.params.level; + ServerLevel mcServerLevel = this.params.mcServerLevel; if (chunkTagData == null) { - return CreateEmptyChunk(level, chunkPos); + return this.CreateProtoChunkWrapper(mcServerLevel, chunkPos); } else { try { - @Nullable - ChunkAccess chunk = ChunkCompoundTagParser.createFromTag(level, chunkPos, chunkTagData); - if (chunk != null) + ChunkWrapper chunkWrapper = ChunkCompoundTagParser.createFromTag(mcServerLevel, this.params.dhServerLevel, chunkPos, chunkTagData); + if (chunkWrapper == null) { - if (Config.Common.LodBuilding.assumePreExistingChunksAreFinished.get()) - { - // Sometimes the chunk status is wrong - // (this might be an issue with some versions of chunky) - // which can cause issues with some world gen steps re-running and locking up - ChunkWrapper.trySetStatus(chunk, ChunkStatus.FULL); - } + chunkWrapper = this.CreateProtoChunkWrapper(mcServerLevel, chunkPos); } - else - { - chunk = CreateEmptyChunk(level, chunkPos); - } - return chunk; + return chunkWrapper; } catch (Exception e) { @@ -303,12 +284,17 @@ public class ChunkFileReader implements AutoCloseable "Error: [" + e.getMessage() + "]." , e); - return CreateEmptyChunk(level, chunkPos); + return this.CreateProtoChunkWrapper(mcServerLevel, chunkPos); } } } - public static ProtoChunk CreateEmptyChunk(ServerLevel level, ChunkPos chunkPos) + public ChunkWrapper CreateProtoChunkWrapper(ServerLevel level, ChunkPos chunkPos) + { + ProtoChunk chunk = CreateProtoChunk(level, chunkPos); + return new ChunkWrapper(chunk, this.params.dhServerLevel.getLevelWrapper()); + } + public static ProtoChunk CreateProtoChunk(ServerLevel level, ChunkPos chunkPos) { #if MC_VER <= MC_1_16_5 return new ProtoChunk(chunkPos, UpgradeData.EMPTY); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/internalServer/InternalServerGenerator.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/internalServer/InternalServerGenerator.java index bfa3805a8..d0cf3f5e8 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/internalServer/InternalServerGenerator.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/internalServer/InternalServerGenerator.java @@ -172,7 +172,6 @@ public class InternalServerGenerator for (int i = 0; i < chunkWrappers.size(); i++) { ChunkWrapper chunkWrapper = (ChunkWrapper)chunkWrappers.get(i); - chunkWrapper.recalculateDhHeightMapsIfNeeded(); // pre-generated chunks should have lighting but new ones won't if (!chunkWrapper.isDhBlockLightingCorrect()) @@ -191,7 +190,7 @@ public class InternalServerGenerator while (chunkPosIterator.hasNext()) { ChunkPos chunkPos = chunkPosIterator.next(); - this.releaseChunkFromServer(this.params.level, chunkPos); + this.releaseChunkFromServer(this.params.mcServerLevel, chunkPos); } } } @@ -233,7 +232,7 @@ public class InternalServerGenerator { return CompletableFuture.supplyAsync(() -> { - ServerLevel level = this.params.level; + ServerLevel level = this.params.mcServerLevel; // ignore chunk update events for this position SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.addPosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z)); @@ -265,7 +264,7 @@ public class InternalServerGenerator .thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.getError()))); // can throw if the server is shutting down #endif - }, this.params.level.getChunkSource().chunkMap.mainThreadExecutor) + }, this.params.mcServerLevel.getChunkSource().chunkMap.mainThreadExecutor) .thenCompose(Function.identity()); } /** diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/step/StepStructureStart.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/step/StepStructureStart.java index 70c2d1431..a22da9ab2 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/step/StepStructureStart.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/step/StepStructureStart.java @@ -106,9 +106,9 @@ public final class StepStructureStart extends AbstractWorldGenStep tParams.structFeatManager, chunk, this.environment.globalParams.structures); #else this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry, - this.environment.globalParams.level.getChunkSource().getGeneratorState(), + this.environment.globalParams.mcServerLevel.getChunkSource().getGeneratorState(), tParams.structFeatManager, chunk, this.environment.globalParams.structures, - this.environment.globalParams.level.dimension()); + this.environment.globalParams.mcServerLevel.dimension()); #endif #if MC_VER >= MC_1_18_2 diff --git a/coreSubProjects b/coreSubProjects index 4e9559f23..385bd326c 160000 --- a/coreSubProjects +++ b/coreSubProjects @@ -1 +1 @@ -Subproject commit 4e9559f23043f8f3568362374025ff7c2c77c4df +Subproject commit 385bd326cf2488bcef7be82194e55743a141e3cf