Improve pre-existing chunk handling

This commit is contained in:
James Seibel
2025-12-04 07:52:54 -06:00
parent c250a7408e
commit 47e97630b4
9 changed files with 197 additions and 235 deletions
@@ -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;
}
}
}
@@ -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<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = Collections.synchronizedMap(new HashMap<>());
Map<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = Collections.synchronizedMap(new HashMap<>());
Map<DhChunkPos, ChunkAccess> generatedChunkByDhPos = Collections.synchronizedMap(new HashMap<>());
Map<DhChunkPos, ChunkWrapper> chunkWrappersByDhPos = Collections.synchronizedMap(new HashMap<>());
@@ -324,7 +323,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
// read existing chunks from file //
//================================//
HashMap<DhChunkPos, CompletableFuture<ChunkAccess>> readFutureByDhChunkPos = new HashMap<>();
HashMap<DhChunkPos, CompletableFuture<ChunkWrapper>> readFutureByDhChunkPos = new HashMap<>();
Iterator<ChunkPos> 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<ChunkAccess> getExistingChunkFuture
CompletableFuture<ChunkWrapper> 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<ChunkAccess> readChunkFuture : readFutureByDhChunkPos.values())
for (CompletableFuture<ChunkWrapper> 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<ChunkAccess> 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<ChunkWrapper> 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<BeaconBeamDTO> activeBeamList = centerChunkWrapper.getAllActiveBeacons(iChunkWrapperList);
if (!activeBeamList.isEmpty())
{
this.dhServerLevel.updateBeaconBeamsForChunkPos(centerChunkWrapper.getChunkPos(), activeBeamList);
}
}
}
}
@@ -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<Biome> 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
}
@@ -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
}
@@ -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<Block> 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 -> (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<Fluid> 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 -> (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<Block> 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<PalettedContainer<BlockState>> 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;
}
@@ -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<ChunkAccess> createEmptyOrPreExistingChunkAsync(
public CompletableFuture<ChunkWrapper> createEmptyOrPreExistingChunkWrapperAsync(
int chunkX, int chunkZ,
Map<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos,
Map<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos,
Map<DhChunkPos, ChunkAccess> generatedChunkByDhPos)
Map<DhChunkPos, ChunkWrapper> 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<CompoundTag> 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);
@@ -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());
}
/**
@@ -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