From 01545dc29535e7c07adb30134c8238c71f7a6ded Mon Sep 17 00:00:00 2001 From: tom lee Date: Thu, 3 Feb 2022 18:00:31 +0800 Subject: [PATCH] Update the generator with 1.16 changes --- .../lod/common/wrappers/VersionConstants.java | 89 +- .../lod/common/wrappers/WrapperFactory.java | 123 +- .../BatchGenerationEnvironment.java | 650 ++++++++ .../wrappers/worldGeneration/ChunkLoader.java | 28 +- .../ExperimentalGenerator.java | 320 ---- .../worldGeneration/GenerationEvent.java | 102 ++ .../worldGeneration/GlobalParameters.java | 55 + .../wrappers/worldGeneration/Rolling.java | 34 + .../worldGeneration/ThreadedParameters.java | 49 + .../worldGeneration/WorldGenerationStep.java | 1432 ----------------- .../WorldGeneratorWrapper.java | 172 +- .../mimicObject/LightGetterAdaptor.java | 40 + .../mimicObject/LightedWorldGenRegion.java | 227 +++ .../mimicObject/WorldGenLevelLightEngine.java | 176 ++ .../WorldGenStructFeatManager.java | 75 + .../worldGeneration/step/StepBiomes.java | 63 + .../worldGeneration/step/StepFeatures.java | 60 + .../worldGeneration/step/StepLight.java | 59 + .../worldGeneration/step/StepNoise.java | 91 ++ .../step/StepStructureReference.java | 92 ++ .../step/StepStructureStart.java | 67 + .../worldGeneration/step/StepSurface.java | 46 + core | 2 +- 23 files changed, 2106 insertions(+), 1946 deletions(-) create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java delete mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ExperimentalGenerator.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GenerationEvent.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GlobalParameters.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/Rolling.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ThreadedParameters.java delete mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGenerationStep.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightGetterAdaptor.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightedWorldGenRegion.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenLevelLightEngine.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepBiomes.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepFeatures.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepLight.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepNoise.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureReference.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureStart.java create mode 100644 common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepSurface.java diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/VersionConstants.java b/common/src/main/java/com/seibel/lod/common/wrappers/VersionConstants.java index 204d71634..f78a26654 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/VersionConstants.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/VersionConstants.java @@ -1,3 +1,4 @@ + package com.seibel.lod.common.wrappers; import com.seibel.lod.core.enums.config.DistanceGenerationMode; @@ -7,42 +8,54 @@ import com.seibel.lod.core.wrapperInterfaces.IVersionConstants; * @author James Seibel * @version 12-11-2021 */ -public class VersionConstants implements IVersionConstants { - public static final VersionConstants INSTANCE = new VersionConstants(); - - - private VersionConstants() { - - } - - - @Override - public int getMinimumWorldHeight() { - return 0; - } - - @Override - public boolean isWorldGeneratorSingleThreaded(DistanceGenerationMode distanceGenerationMode) { - // We are always asking the server to generate the chunk, - // so no use running this stuff multithreaded. - return true; - /* - switch (distanceGenerationMode) { - default: - case NONE: - case BIOME_ONLY: - case BIOME_ONLY_SIMULATE_HEIGHT: - case SURFACE: - case FEATURES: - return false; - - case FULL: - return true; - }*/ - } - - @Override - public int getWorldGenerationCountPerThread() { - return 1; - } +public class VersionConstants implements IVersionConstants +{ + public static final VersionConstants INSTANCE = new VersionConstants(); + + + private VersionConstants() + { + + } + + + @Override + public int getMinimumWorldHeight() + { + return 0; + } + + @Override + public boolean isWorldGeneratorSingleThreaded(DistanceGenerationMode distanceGenerationMode) + { + // We are always asking the server to generate the chunk, + // so no use running this stuff multithreaded. + return true; + /* + switch (distanceGenerationMode) { + default: + case NONE: + case BIOME_ONLY: + case BIOME_ONLY_SIMULATE_HEIGHT: + case SURFACE: + case FEATURES: + return false; + + case FULL: + return true; + }*/ + } + + @Override + public int getWorldGenerationCountPerThread() + { + return 1; + } + + + @Override + public boolean hasBatchGenerationImplementation() + { + return true; + } } \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java b/common/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java index 931f6812b..77b145c6e 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/WrapperFactory.java @@ -25,11 +25,11 @@ import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper; import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; -import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractExperimentalWorldGeneratorWrapper; +import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper; import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper; import com.seibel.lod.common.wrappers.block.BlockPosWrapper; import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper; -import com.seibel.lod.common.wrappers.worldGeneration.ExperimentalGenerator; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.lod.common.wrappers.worldGeneration.WorldGeneratorWrapper; /** @@ -40,64 +40,65 @@ import com.seibel.lod.common.wrappers.worldGeneration.WorldGeneratorWrapper; */ public class WrapperFactory implements IWrapperFactory { - public static final WrapperFactory INSTANCE = new WrapperFactory(); - - - @Override - public AbstractBlockPosWrapper createBlockPos() - { - return new BlockPosWrapper(); - } - - @Override - public AbstractBlockPosWrapper createBlockPos(int x, int y, int z) - { - return new BlockPosWrapper(x,y,z); - } - - - - - @Override - public AbstractChunkPosWrapper createChunkPos() - { - return new ChunkPosWrapper(); - } - - @Override - public AbstractChunkPosWrapper createChunkPos(long xAndZPositionCombined) - { - return new ChunkPosWrapper(xAndZPositionCombined); - } - - @Override - public AbstractChunkPosWrapper createChunkPos(int x, int z) - { - return new ChunkPosWrapper(x, z); - } - - @Override - public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos) - { - return new ChunkPosWrapper(newChunkPos); - } - - @Override - public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos) - { - return new ChunkPosWrapper(blockPos); - } - - - - @Override - public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) - { - return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper); - } - - @Override - public AbstractExperimentalWorldGeneratorWrapper createExperimentalWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { - return new ExperimentalGenerator(newLodBuilder, newLodDimension, worldWrapper); + public static final WrapperFactory INSTANCE = new WrapperFactory(); + + + @Override + public AbstractBlockPosWrapper createBlockPos() + { + return new BlockPosWrapper(); + } + + @Override + public AbstractBlockPosWrapper createBlockPos(int x, int y, int z) + { + return new BlockPosWrapper(x, y, z); + } + + + + + @Override + public AbstractChunkPosWrapper createChunkPos() + { + return new ChunkPosWrapper(); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(long xAndZPositionCombined) + { + return new ChunkPosWrapper(xAndZPositionCombined); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(int x, int z) + { + return new ChunkPosWrapper(x, z); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos) + { + return new ChunkPosWrapper(newChunkPos); + } + + @Override + public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos) + { + return new ChunkPosWrapper(blockPos); + } + + + + @Override + public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) + { + return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper); + } + + public AbstractBatchGenerationEnvionmentWrapper createBatchGenerator(LodBuilder newLodBuilder, + LodDimension newLodDimension, IWorldWrapper worldWrapper) + { + return new BatchGenerationEnvironment(worldWrapper, newLodBuilder, newLodDimension); } } diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java new file mode 100644 index 000000000..852b6ebe2 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java @@ -0,0 +1,650 @@ +/* + * This file is part of the Distant Horizon mod (formerly the LOD Mod), + * licensed under the GNU GPL v3 License. + * + * Copyright (C) 2021 Tom Lee (TomTheFurry) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package com.seibel.lod.common.wrappers.worldGeneration; + +import com.seibel.lod.core.api.ClientApi; +import com.seibel.lod.core.builders.lodBuilding.LodBuilder; +import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig; +import com.seibel.lod.core.enums.config.DistanceGenerationMode; +import com.seibel.lod.core.enums.config.LightGenerationMode; +import com.seibel.lod.core.objects.lod.LodDimension; +import com.seibel.lod.core.util.LodThreadFactory; +import com.seibel.lod.core.util.SingletonHandler; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; +import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; +import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper; + +import java.io.IOException; +import java.time.Duration; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +import com.seibel.lod.common.wrappers.chunk.ChunkWrapper; +import com.seibel.lod.common.wrappers.world.WorldWrapper; +import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.LightGetterAdaptor; +import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.LightedWorldGenRegion; +import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.WorldGenLevelLightEngine; +import com.seibel.lod.common.wrappers.worldGeneration.step.StepBiomes; +import com.seibel.lod.common.wrappers.worldGeneration.step.StepFeatures; +import com.seibel.lod.common.wrappers.worldGeneration.step.StepLight; +import com.seibel.lod.common.wrappers.worldGeneration.step.StepNoise; +import com.seibel.lod.common.wrappers.worldGeneration.step.StepStructureReference; +import com.seibel.lod.common.wrappers.worldGeneration.step.StepStructureStart; +import com.seibel.lod.common.wrappers.worldGeneration.step.StepSurface; + +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.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.chunk.UpgradeData; +import net.minecraft.core.Registry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.world.level.lighting.LevelLightEngine; + +/* +Total: 3.135214124s +===================================== +Empty Chunks: 0.000558328s +StructureStart Step: 0.025177207s +StructureReference Step: 0.00189559s +Biome Step: 0.13789155s +Noise Step: 1.570347555s +Surface Step: 0.741238194s +Carver Step: 0.000009923s +Feature Step: 0.389072425s +Lod Generation: 0.269023348s +*/ + +public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnvionmentWrapper +{ + public static final boolean ENABLE_PERF_LOGGING = true; + public static final boolean ENABLE_EVENT_LOGGING = false; + public static final boolean ENABLE_LOAD_EVENT_LOGGING = false; + //TODO: Make actual proper support for StarLight + + public static class PrefEvent + { + long beginNano = 0; + long emptyNano = 0; + long structStartNano = 0; + long structRefNano = 0; + long biomeNano = 0; + long noiseNano = 0; + long surfaceNano = 0; + long carverNano = 0; + long featureNano = 0; + long lightNano = 0; + long endNano = 0; + + @Override + public String toString() + { + return "beginNano: " + beginNano + ",\n" + + "emptyNano: " + emptyNano + ",\n" + + "structStartNano: " + structStartNano + ",\n" + + "structRefNano: " + structRefNano + ",\n" + + "biomeNano: " + biomeNano + ",\n" + + "noiseNano: " + noiseNano + ",\n" + + "surfaceNano: " + surfaceNano + ",\n" + + "carverNano: " + carverNano + ",\n" + + "featureNano: " + featureNano + ",\n" + + "lightNano: " + lightNano + ",\n" + + "endNano: " + endNano + "\n"; + } + } + + public static class PerfCalculator + { + public static final int SIZE = 50; + Rolling totalTime = new Rolling(SIZE); + Rolling emptyTime = new Rolling(SIZE); + Rolling structStartTime = new Rolling(SIZE); + Rolling structRefTime = new Rolling(SIZE); + Rolling biomeTime = new Rolling(SIZE); + Rolling noiseTime = new Rolling(SIZE); + Rolling surfaceTime = new Rolling(SIZE); + Rolling carverTime = new Rolling(SIZE); + Rolling featureTime = new Rolling(SIZE); + Rolling lightTime = new Rolling(SIZE); + Rolling lodTime = new Rolling(SIZE); + + public void recordEvent(PrefEvent e) + { + long preTime = e.beginNano; + totalTime.add(e.endNano - preTime); + if (e.emptyNano != 0) + { + emptyTime.add(e.emptyNano - preTime); + preTime = e.emptyNano; + } + if (e.structStartNano != 0) + { + structStartTime.add(e.structStartNano - preTime); + preTime = e.structStartNano; + } + if (e.structRefNano != 0) + { + structRefTime.add(e.structRefNano - preTime); + preTime = e.structRefNano; + } + if (e.biomeNano != 0) + { + biomeTime.add(e.biomeNano - preTime); + preTime = e.biomeNano; + } + if (e.noiseNano != 0) + { + noiseTime.add(e.noiseNano - preTime); + preTime = e.noiseNano; + } + if (e.surfaceNano != 0) + { + surfaceTime.add(e.surfaceNano - preTime); + preTime = e.surfaceNano; + } + if (e.carverNano != 0) + { + carverTime.add(e.carverNano - preTime); + preTime = e.carverNano; + } + if (e.featureNano != 0) + { + featureTime.add(e.featureNano - preTime); + preTime = e.featureNano; + } + if (e.lightNano != 0) + { + lightTime.add(e.lightNano - preTime); + preTime = e.lightNano; + } + if (e.endNano != 0) + { + lodTime.add(e.endNano - preTime); + preTime = e.endNano; + } + } + + public String toString() + { + return "Total: " + Duration.ofNanos((long) totalTime.getAverage()) + ", Empty/LoadChunk: " + + Duration.ofNanos((long) emptyTime.getAverage()) + ", StructStart: " + + Duration.ofNanos((long) structStartTime.getAverage()) + ", StructRef: " + + Duration.ofNanos((long) structRefTime.getAverage()) + ", Biome: " + + Duration.ofNanos((long) biomeTime.getAverage()) + ", Noise: " + + Duration.ofNanos((long) noiseTime.getAverage()) + ", Surface: " + + Duration.ofNanos((long) surfaceTime.getAverage()) + ", Carver: " + + Duration.ofNanos((long) carverTime.getAverage()) + ", Feature: " + + Duration.ofNanos((long) featureTime.getAverage()) + ", Light: " + + Duration.ofNanos((long) lightTime.getAverage()) + ", Lod: " + + Duration.ofNanos((long) lodTime.getAverage()); + } + } + + public static final int TIMEOUT_SECONDS = 30; + + //FIXME: Remove this and use the Utils one + public static final class GridList extends ArrayList implements List + { + + public static class Pos + { + public int x; + public int y; + + public Pos(int xx, int yy) + { + x = xx; + y = yy; + } + } + + private static final long serialVersionUID = 1585978374811888116L; + public final int gridCentreToEdge; + public final int gridSize; + + public GridList(int gridCentreToEdge) + { + super((gridCentreToEdge * 2 + 1) * (gridCentreToEdge * 2 + 1)); + gridSize = gridCentreToEdge * 2 + 1; + this.gridCentreToEdge = gridCentreToEdge; + } + + public T getOffsetOf(int index, int x, int y) + { + return get(index + x + y * gridSize); + } + + public int offsetOf(int index, int x, int y) + { + return index + x + y * gridSize; + } + + public Pos posOf(int index) + { + return new Pos(index % gridSize, index / gridSize); + } + + public int calculateOffset(int x, int y) + { + return x + y * gridSize; + } + + public GridList subGrid(int gridCentreToEdge) + { + int centreIndex = size() / 2; + GridList subGrid = new GridList(gridCentreToEdge); + for (int oy = -gridCentreToEdge; oy <= gridCentreToEdge; oy++) + { + int begin = offsetOf(centreIndex, -gridCentreToEdge, oy); + int end = offsetOf(centreIndex, gridCentreToEdge, oy); + subGrid.addAll(this.subList(begin, end + 1)); + } + // System.out.println("========================================\n"+ + // this.toDetailString() + "\nTOOOOOOOOOOOOO\n"+subGrid.toDetailString()+ + // "==========================================\n"); + return subGrid; + } + + @Override + public String toString() + { + return "GridList " + gridSize + "*" + gridSize + "[" + size() + "]"; + } + + public String toDetailString() + { + StringBuilder str = new StringBuilder("\n"); + int i = 0; + for (T t : this) + { + str.append(t.toString()); + str.append(", "); + i++; + if (i % gridSize == 0) + { + str.append("\n"); + } + } + return str.toString(); + } + } + + + //=================Generation Step=================== + + public final LinkedList events = new LinkedList(); + public final GlobalParameters params; + public final StepStructureStart stepStructureStart = new StepStructureStart(this); + public final StepStructureReference stepStructureReference = new StepStructureReference(this); + public final StepBiomes stepBiomes = new StepBiomes(this); + public final StepNoise stepNoise = new StepNoise(this); + public final StepSurface stepSurface = new StepSurface(this); + public final StepFeatures stepFeatures = new StepFeatures(this); + public final StepLight stepLight = new StepLight(this); + private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); + + public static final LodThreadFactory threadFactory = new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY); + + public ExecutorService executors = Executors.newFixedThreadPool( + CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), threadFactory); + + public void resizeThreadPool(int newThreadCount) + { + executors = Executors.newFixedThreadPool(newThreadCount, + new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY)); + } + + public boolean tryAddPoint(int px, int pz, int range, Steps target) + { + int boxSize = range * 2 + 1; + int x = Math.floorDiv(px, boxSize) * boxSize + range; + int z = Math.floorDiv(pz, boxSize) * boxSize + range; + + for (GenerationEvent event : events) + { + if (event.tooClose(x, z, range)) + return false; + } + // System.out.println(x + ", "+z); + events.add(new GenerationEvent(new ChunkPos(x, z), range, this, target)); + return true; + } + + public void updateAllFutures() + { + // Update all current out standing jobs + Iterator iter = events.iterator(); + while (iter.hasNext()) + { + GenerationEvent event = iter.next(); + if (event.isCompleted()) + { + try + { + event.join(); + } + catch (Throwable e) + { + e.printStackTrace(); + while (e.getCause() != null) + { + e = e.getCause(); + e.printStackTrace(); + } + } + finally + { + iter.remove(); + } + } + else if (event.hasTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)) + { + ClientApi.LOGGER.error("Batching World Generator: " + event + " timed out and terminated!"); + ClientApi.LOGGER.info("Dump PrefEvent: " + event.pEvent); + try + { + if (!event.terminate()) + ClientApi.LOGGER.error("Failed to terminate the stuck generation event!"); + } + finally + { + iter.remove(); + } + } + } + } + + public BatchGenerationEnvironment(IWorldWrapper serverlevel, LodBuilder lodBuilder, LodDimension lodDim) + { + super(serverlevel, lodBuilder, lodDim); + ClientApi.LOGGER.info("================WORLD_GEN_STEP_INITING============="); + params = new GlobalParameters((ServerLevel) ((WorldWrapper) serverlevel).getWorld(), lodBuilder, lodDim); + } + + public void startLoadingAllRegionsFromFile(LodDimension lodDim) + { + ServerLevel level = params.level; + level.getChunkSource(); + + } + + @SuppressWarnings("resource") + public static ChunkAccess loadOrMakeChunk(ChunkPos chunkPos, ServerLevel level, LevelLightEngine lightEngine) + { + CompoundTag chunkData = null; + try + { + chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos); + } + catch (IOException e) + { + ClientApi.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e); + } + if (chunkData == null) + { + return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null); + } + else + { + return ChunkLoader.read(level, lightEngine, chunkPos, chunkData); + } + + } + + + + public void generateLodFromList(GenerationEvent e) + { + if (ENABLE_EVENT_LOGGING) + ClientApi.LOGGER.info("Lod Generate Event: " + e.pos); + e.pEvent.beginNano = System.nanoTime(); + GridList referencedChunks; + DistanceGenerationMode generationMode; + LightedWorldGenRegion region; + WorldGenLevelLightEngine lightEngine; + LightGetterAdaptor adaptor; + + try + { + adaptor = new LightGetterAdaptor(params.level); + lightEngine = new WorldGenLevelLightEngine(adaptor); + + int cx = e.pos.x; + int cy = e.pos.z; + int rangeEmpty = e.range + 1; + GridList chunks = new GridList(rangeEmpty); + + @SuppressWarnings("resource") + EmptyChunkGenerator generator = (int x, int z) -> + { + ChunkPos chunkPos = new ChunkPos(x, z); + ChunkAccess target = null; + try + { + target = loadOrMakeChunk(chunkPos, params.level, lightEngine); + } + catch (RuntimeException e2) + { + // Continue... + e2.printStackTrace(); + } + if (target == null) + target = new ProtoChunk(chunkPos, UpgradeData.EMPTY, params.level, + params.biomes, null); + return target; + }; + + for (int oy = -rangeEmpty; oy <= rangeEmpty; oy++) + { + for (int ox = -rangeEmpty; ox <= rangeEmpty; ox++) + { + ChunkAccess target = generator.generate(cx + ox, cy + oy); + chunks.add(target); + } + } + e.pEvent.emptyNano = System.nanoTime(); + e.refreshTimeout(); + region = new LightedWorldGenRegion(params.level, lightEngine, e.tParam.structFeat, chunks, ChunkStatus.STRUCTURE_STARTS, rangeEmpty, e.lightMode, generator); + adaptor.setRegion(region); + referencedChunks = chunks.subGrid(e.range); + referencedChunks = generateDirect(e, referencedChunks, e.target, region); + + } + catch (StepStructureStart.StructStartCorruptedException f) + { + e.tParam.markAsInvalid(); + return; + } + + switch (e.target) + { + case Empty: + case StructureStart: + case StructureReference: + generationMode = DistanceGenerationMode.NONE; + break; + case Biomes: + generationMode = DistanceGenerationMode.BIOME_ONLY; + case Noise: + generationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; + break; + case Surface: + case Carvers: + generationMode = DistanceGenerationMode.SURFACE; + break; + case Features: + generationMode = DistanceGenerationMode.FEATURES; + break; + case Light: + case LiquidCarvers: + default: + return; + } + int centreIndex = referencedChunks.size() / 2; + + for (int oy = -e.range; oy <= e.range; oy++) + { + for (int ox = -e.range; ox <= e.range; ox++) + { + int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); + ChunkAccess target = referencedChunks.get(targetIndex); + target.setLightCorrect(true); + if (target instanceof LevelChunk) + ((LevelChunk) target).setClientLightReady(true); + boolean isFull = target.getStatus() == ChunkStatus.FULL || target instanceof LevelChunk; + boolean isPartial = target.isOldNoiseGeneration(); + if (isFull) + { + if (ENABLE_LOAD_EVENT_LOGGING) + ClientApi.LOGGER.info("Detected full existing chunk at {}", target.getPos()); + params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region), + new LodBuilderConfig(DistanceGenerationMode.FULL), true); + } + else if (isPartial) + { + if (ENABLE_LOAD_EVENT_LOGGING) + ClientApi.LOGGER.info("Detected old existing chunk at {}", target.getPos()); + params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region), + new LodBuilderConfig(generationMode), true); + } + else if (target.getStatus() == ChunkStatus.EMPTY && generationMode == DistanceGenerationMode.NONE) + { + params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region), + LodBuilderConfig.getFillVoidConfig(), true); + } + else + { + params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region), + new LodBuilderConfig(generationMode), true); + } + if (e.lightMode == LightGenerationMode.FANCY || isFull) + { + lightEngine.retainData(target.getPos(), false); + } + + } + } + e.pEvent.endNano = System.nanoTime(); + e.refreshTimeout(); + if (ENABLE_PERF_LOGGING) + { + e.tParam.perf.recordEvent(e.pEvent); + ClientApi.LOGGER.info(e.tParam.perf); + } + } + + public GridList generateDirect(GenerationEvent e, GridList subRange, Steps step, + LightedWorldGenRegion region) + { + try + { + subRange.forEach((chunk) -> + { + if (chunk instanceof ProtoChunk) + { + ((ProtoChunk) chunk).setLightEngine(region.getLightEngine()); + region.getLightEngine().retainData(chunk.getPos(), true); + } + }); + if (step == Steps.Empty) + return subRange; + stepStructureStart.generateGroup(e.tParam, region, subRange); + e.pEvent.structStartNano = System.nanoTime(); + e.refreshTimeout(); + if (step == Steps.StructureStart) + return subRange; + stepStructureReference.generateGroup(e.tParam, region, subRange); + e.pEvent.structRefNano = System.nanoTime(); + e.refreshTimeout(); + if (step == Steps.StructureReference) + return subRange; + stepBiomes.generateGroup(e.tParam, region, subRange); + e.pEvent.biomeNano = System.nanoTime(); + e.refreshTimeout(); + if (step == Steps.Biomes) + return subRange; + stepNoise.generateGroup(e.tParam, region, subRange); + e.pEvent.noiseNano = System.nanoTime(); + e.refreshTimeout(); + if (step == Steps.Noise) + return subRange; + stepSurface.generateGroup(e.tParam, region, subRange); + e.pEvent.surfaceNano = System.nanoTime(); + e.refreshTimeout(); + if (step == Steps.Surface) + return subRange; + if (step == Steps.Carvers) + return subRange; + stepFeatures.generateGroup(e.tParam, region, subRange); + e.pEvent.featureNano = System.nanoTime(); + e.refreshTimeout(); + return subRange; + } + finally + { + switch (region.lightMode) + { + case FANCY: + stepLight.generateGroup(region.getLightEngine(), subRange); + break; + case FAST: + subRange.forEach((p) -> + { + if (p instanceof ProtoChunk) + ((ProtoChunk) p).setLightCorrect(true); + }); + break; + } + e.pEvent.lightNano = System.nanoTime(); + e.refreshTimeout(); + } + } + + public interface EmptyChunkGenerator + { + ChunkAccess generate(int x, int z); + } + + @Override + public int getEventCount() { + return events.size(); + } + + @Override + public void stop() { + ClientApi.LOGGER.info("Batch Chunk Generator shutting down..."); + executors.shutdownNow(); + try { + if (!executors.awaitTermination(10, TimeUnit.SECONDS)) { + ClientApi.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads..."); + } + } catch (InterruptedException e) { + ClientApi.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...", e); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ChunkLoader.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ChunkLoader.java index d6cc70089..1443cb671 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ChunkLoader.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ChunkLoader.java @@ -54,7 +54,8 @@ public class ChunkLoader private static final String BLOCK_TICKS_TAG = "block_ticks"; private static final String FLUID_TICKS_TAG = "fluid_ticks"; - private static BlendingData readBlendingData(CompoundTag chunkData) { + private static BlendingData readBlendingData(CompoundTag chunkData) + { BlendingData blendingData = null; if (chunkData.contains("blending_data", 10)) { @@ -65,7 +66,8 @@ public class ChunkLoader return blendingData; } - private static LevelChunkSection[] readSections(LevelAccessor level, LevelLightEngine lightEngine, ChunkPos chunkPos, CompoundTag chunkData) { + private static LevelChunkSection[] readSections(LevelAccessor level, LevelLightEngine lightEngine, ChunkPos chunkPos, CompoundTag chunkData) + { Registry biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY); Codec> biomeCodec = PalettedContainer.codec( biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS)); @@ -98,7 +100,8 @@ public class ChunkLoader chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer); } - if (!isLightOn) continue; + if (!isLightOn) + continue; if (tagSection.contains("BlockLight", 7)) lightEngine.queueSectionData(LightLayer.BLOCK, SectionPos.of(chunkPos, sectionYPos), new DataLayer(tagSection.getByteArray("BlockLight")), true); @@ -108,7 +111,8 @@ public class ChunkLoader return chunkSections; } - private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData) { + private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData) + { CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps"); for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter()) { @@ -147,7 +151,7 @@ public class ChunkLoader for (String string : compoundTag2.getAllKeys()) { String string2 = string.toLowerCase(Locale.ROOT); - StructureFeature structureFeature = StructureFeature.STRUCTURES_REGISTRY.get(string2); + StructureFeature structureFeature = StructureFeature.STRUCTURES_REGISTRY.get(string2); if (structureFeature == null) { LOGGER.warn("Found reference to unknown structure '{}' in chunk {}, discarding", (Object) string2, (Object) chunkPos); @@ -167,14 +171,16 @@ public class ChunkLoader return map; } - private static void readStructures(WorldGenLevel level, LevelChunk chunk, CompoundTag chunkData) { + private static void readStructures(WorldGenLevel level, LevelChunk chunk, CompoundTag chunkData) + { CompoundTag tagStructures = chunkData.getCompound("structures"); chunk.setAllStarts( unpackStructureStart(StructurePieceSerializationContext.fromLevel(level.getLevel()), tagStructures, level.getSeed())); chunk.setAllReferences(unpackStructureReferences(chunk.getPos(), tagStructures)); } - private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData) { + private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData) + { ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9); for (int n = 0; n < tagPostProcessings.size(); ++n) { @@ -185,7 +191,7 @@ public class ChunkLoader } } } - + public static ChunkStatus.ChunkType readChunkType(CompoundTag compoundTag) { return ChunkStatus.byName(compoundTag.getString("Status")).getChunkType(); @@ -203,11 +209,13 @@ public class ChunkLoader ChunkStatus.ChunkType chunkType = readChunkType(chunkData); BlendingData blendingData = readBlendingData(chunkData); - if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise())) return null; + if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise())) + return null; // Prepare the light engine boolean isLightOn = chunkData.getBoolean("isLightOn"); - if (isLightOn) level.getLightEngine().retainData(chunkPos, true); + if (isLightOn) + level.getLightEngine().retainData(chunkPos, true); // Read params for making the LevelChunk UpgradeData upgradeData = chunkData.contains(TAG_UPGRADE_DATA, 10) diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ExperimentalGenerator.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ExperimentalGenerator.java deleted file mode 100644 index d4f2aa20d..000000000 --- a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ExperimentalGenerator.java +++ /dev/null @@ -1,320 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL v3 License. - * - * Copyright (C) 2021 Tom Lee (TomTheFurry) & James Seibel (Original code) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.common.wrappers.worldGeneration; - -import java.util.concurrent.TimeUnit; - -import com.seibel.lod.common.wrappers.world.WorldWrapper; -import com.seibel.lod.common.wrappers.worldGeneration.WorldGenerationStep.Steps; -import com.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.enums.config.GenerationPriority; -import com.seibel.lod.core.objects.PosToGenerateContainer; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.util.LevelPosUtil; -import com.seibel.lod.core.util.LodUtil; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; -import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; -import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractExperimentalWorldGeneratorWrapper; - -public class ExperimentalGenerator extends AbstractExperimentalWorldGeneratorWrapper { - - public static final boolean ENABLE_GENERATOR_STATS_LOGGING = false; - - private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - public WorldGenerationStep generationGroup; - public LodDimension targetLodDim; - public static final int generationGroupSize = 4; - public static int previousThreadCount = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(); - - private int estimatedSampleNeeded = 128; - private int estimatedPointsToQueue = 1; - - public ExperimentalGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { - super(newLodBuilder, newLodDimension, worldWrapper); - MC.sendChatMessage("WARNING: You are currently using Distant Horizon's Experimental Chunk Pre-Generator!"); - MC.sendChatMessage("The generation mode: Feature mode is not recommended for < 8GB RAM"); - MC.sendChatMessage("Stuff may break at any time!"); - generationGroup = new WorldGenerationStep(((WorldWrapper) worldWrapper).getServerWorld(), newLodBuilder, - newLodDimension); - ClientApi.LOGGER.info("1.18 Experimental Chunk Generator initialized"); - } - - @SuppressWarnings("unused") - @Override - public void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder) { - if (lodDim != targetLodDim) { - stop(); - WorldWrapper dim = (WorldWrapper) LodUtil.getServerWorldFromDimension(lodDim.dimension); - generationGroup = new WorldGenerationStep(dim.getServerWorld(), lodBuilder, lodDim); - targetLodDim = lodDim; - ClientApi.LOGGER.info("1.18 Experimental Chunk Generator reinitialized"); - } - - DistanceGenerationMode mode = CONFIG.client().worldGenerator().getDistanceGenerationMode(); - int newThreadCount = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(); - if (newThreadCount != previousThreadCount) { - generationGroup.resizeThreadPool(newThreadCount); - previousThreadCount = newThreadCount; - } - if (estimatedPointsToQueue < newThreadCount) estimatedPointsToQueue = newThreadCount; - - GenerationPriority priority = CONFIG.client().worldGenerator().getGenerationPriority(); - if (priority == GenerationPriority.AUTO) - priority = MC.hasSinglePlayerServer() ? GenerationPriority.FAR_FIRST : GenerationPriority.NEAR_FIRST; - - generationGroup.updateAllFutures(); - if (!MC.hasSinglePlayerServer()) - return; - int eventsCount = generationGroup.events.size(); - // If we still all jobs running, return. - if (eventsCount >= estimatedPointsToQueue) { - estimatedPointsToQueue--; - if (estimatedPointsToQueue < newThreadCount) estimatedPointsToQueue = newThreadCount; - return; - } - - - final int targetToGenerate = estimatedPointsToQueue - eventsCount; - int toGenerate = targetToGenerate; - int positionGoneThough = 0; - - // round the player's block position down to the nearest chunk BlockPos - int playerPosX = MC.getPlayerBlockPos().getX(); - int playerPosZ = MC.getPlayerBlockPos().getZ(); - - // TODO: Make it so that lodDim allows feeding in a function to fast halt if - // position generation is completed. - PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(estimatedSampleNeeded, playerPosX, playerPosZ, priority, mode); - - if (eventsCount == 0 && posToGenerate.getNumberOfPos()>=estimatedSampleNeeded) { - estimatedPointsToQueue++; - if (estimatedPointsToQueue > newThreadCount*10) estimatedPointsToQueue = newThreadCount*10; - } - - //ClientApi.LOGGER.info("PosToGenerate: {}", posToGenerate); - - // Find the max number of iterations we need to go though. - // We are checking one FarPos, and one NearPos per iterations. This ensure we - // aren't just - // always picking one or the other. - Steps targetStep; - switch (mode) { - case NONE: - targetStep = Steps.Empty; //NOTE: Only load in existing chunks. No new chunk generation - break; - case BIOME_ONLY: - targetStep = Steps.Biomes; //NOTE: No block. Require fake height in LodBuilder - break; - case BIOME_ONLY_SIMULATE_HEIGHT: - targetStep = Steps.Noise; //NOTE: Stone only. Require fake surface - break; - case SURFACE: - targetStep = Steps.Surface; //Carvers or Surface??? - break; - case FEATURES: - case FULL: - targetStep = Steps.Features; - break; - // TODO! - default: - assert false; - return; - } - - if (ENABLE_GENERATOR_STATS_LOGGING) - ClientApi.LOGGER.info("WorldGen. Near:"+posToGenerate.getNumberOfNearPos()+" Far:"+posToGenerate.getNumberOfFarPos()); - if (priority == GenerationPriority.FAR_FIRST || priority == GenerationPriority.BALANCED) { - - int nearCount = posToGenerate.getNumberOfNearPos(); - int farCount = posToGenerate.getNumberOfFarPos(); - if (ENABLE_GENERATOR_STATS_LOGGING) - ClientApi.LOGGER.info("WorldGen. Near:"+nearCount+" Far:"+farCount); - int maxIteration = Math.max(nearCount, farCount); - for (int i = 0; i < maxIteration; i++) { - - // We have farPos to go though - if (i < farCount && posToGenerate.getNthDetail(i, false) != 0) { - positionGoneThough++; - // TODO: Add comment here on why theres a '-1'. - // Not sure what's happening here. This is copied from previous codes. - byte detailLevel = (byte) (posToGenerate.getNthDetail(i, false) - 1); - int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, false)); - int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, false)); - int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize; - if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) { - toGenerate--; - } - } - if (toGenerate <= 0) - break; - - // We have nearPos to go though - if (i < nearCount && posToGenerate.getNthDetail(i, true) != 0) { - positionGoneThough++; - // TODO: Add comment here on why theres a '-1'. - // Not sure what's happening here. This is copied from previous codes. - byte detailLevel = (byte) (posToGenerate.getNthDetail(i, true) - 1); - int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, true)); - int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, true)); - int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize; - if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) { - toGenerate--; - } - } - - if (toGenerate <= 0) - break; - } - /* - int farCount = posToGenerate.getNumberOfFarPos(); - for (int i = 0; i < farCount; i++) { - - // We have nearPos to go though - if (posToGenerate.getNthDetail(i, false) != 0) { - positionGoneThough++; - // TODO: Add comment here on why theres a '-1'. - // Not sure what's happening here. This is copied from previous codes. - byte detailLevel = (byte) (posToGenerate.getNthDetail(i, false) - 1); - int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, false)); - int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, false)); - int genSize = detailLevel != 0 ? 0 : generationGroupSize; - if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) { - toGenerate--; - } - if (toGenerate <= 0) - break; - } - } - // Only do near gen if toGenerate is non 0 and that we have requested all samples we can get. - if (toGenerate > 0 && estimatedSampleNeeded > posToGenerate.getNumberOfPos()) { - int nearCount = posToGenerate.getNumberOfNearPos(); - for (int i = 0; i < nearCount; i++) { - // We have farPos to go though - if (posToGenerate.getNthDetail(i, true) != 0) { - positionGoneThough++; - // TODO: Add comment here on why theres a '-1'. - // Not sure what's happening here. This is copied from previous codes. - byte detailLevel = (byte) (posToGenerate.getNthDetail(i, true) - 1); - int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, true)); - int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, true)); - int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize; - if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) { - toGenerate--; - } - } - if (toGenerate <= 0) - break; - } - }*/ - } else { - int nearCount = posToGenerate.getNumberOfNearPos(); - for (int i = 0; i < nearCount; i++) { - - // We have nearPos to go though - if (posToGenerate.getNthDetail(i, true) != 0) { - positionGoneThough++; - // TODO: Add comment here on why theres a '-1'. - // Not sure what's happening here. This is copied from previous codes. - byte detailLevel = (byte) (posToGenerate.getNthDetail(i, true) - 1); - int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, true)); - int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, true)); - int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize; - if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) { - toGenerate--; - } - if (toGenerate <= 0) - break; - } - } - // Only do far gen if toGenerate is non 0 and that we have requested all samples we can get. - if (toGenerate > 0 && estimatedSampleNeeded > posToGenerate.getNumberOfPos()) { - int farCount = posToGenerate.getNumberOfFarPos(); - for (int i = 0; i < farCount; i++) { - // We have farPos to go though - if (posToGenerate.getNthDetail(i, false) != 0) { - positionGoneThough++; - // TODO: Add comment here on why theres a '-1'. - // Not sure what's happening here. This is copied from previous codes. - byte detailLevel = (byte) (posToGenerate.getNthDetail(i, false) - 1); - int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, false)); - int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, false)); - int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize; - if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) { - toGenerate--; - } - } - if (toGenerate <= 0) - break; - } - } - } - - if (targetToGenerate != toGenerate && ENABLE_GENERATOR_STATS_LOGGING) { - if (toGenerate <= 0) { - ClientApi.LOGGER.info( - "WorldGenerator: Sampled " + posToGenerate.getNumberOfPos() + " out of " + estimatedSampleNeeded - + " points, started all targeted " + targetToGenerate + " generations."); - } else { - ClientApi.LOGGER.info("WorldGenerator: Sampled " + posToGenerate.getNumberOfPos() + " out of " - + estimatedSampleNeeded + " points, started " + (targetToGenerate - toGenerate) - + " out of targeted " + targetToGenerate + " generations."); - } - } - - if (toGenerate > 0 && estimatedSampleNeeded <= posToGenerate.getNumberOfPos()) { - // We failed to generate enough points from the samples. - // Let's increase the estimatedSampleNeeded. - estimatedSampleNeeded *= 1.3; - // Ensure wee don't go to basically infinity - if (estimatedSampleNeeded > 32768) - estimatedSampleNeeded = 32768; - if (ENABLE_GENERATOR_STATS_LOGGING) - ClientApi.LOGGER.info("WorldGenerator: Increasing estimatedSampleNeeeded to " + estimatedSampleNeeded); - - } else if (toGenerate <= 0 && positionGoneThough * 1.5 < posToGenerate.getNumberOfPos()) { - // We haven't gone though half of them and it's already enough. - // Let's shink the estimatedSampleNeeded. - estimatedSampleNeeded /= 1.2; - // Ensure we don't go to near zero. - if (estimatedSampleNeeded < 4) - estimatedSampleNeeded = 4; - if (ENABLE_GENERATOR_STATS_LOGGING) - ClientApi.LOGGER.info("WorldGenerator: Decreasing estimatedSampleNeeeded to " + estimatedSampleNeeded); - } - - } - - @Override - public void stop() { - ClientApi.LOGGER.info("1.18 Experimental Chunk Generator shutting down..."); - generationGroup.executors.shutdownNow(); - try { - if (!generationGroup.executors.awaitTermination(10, TimeUnit.SECONDS)) { - ClientApi.LOGGER.error("1.18 Experimental Chunk Generator shutdown failed! Ignoring child threads..."); - } - } catch (InterruptedException e) {} - } - -} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GenerationEvent.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GenerationEvent.java new file mode 100644 index 000000000..d0f95a01f --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GenerationEvent.java @@ -0,0 +1,102 @@ + +package com.seibel.lod.common.wrappers.worldGeneration; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.PrefEvent; +import com.seibel.lod.core.api.ClientApi; +import com.seibel.lod.core.enums.config.LightGenerationMode; +import com.seibel.lod.core.util.SingletonHandler; +import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; +import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper.Steps; + +import net.minecraft.world.level.ChunkPos; + +//======================= Main Event class====================== +public final class GenerationEvent +{ + static private final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); + + private static int generationFutureDebugIDs = 0; + final ThreadedParameters tParam; + final ChunkPos pos; + final int range; + final Future future; + long nanotime; + final int id; + final Steps target; + final LightGenerationMode lightMode; + final PrefEvent pEvent = new PrefEvent(); + + public GenerationEvent(ChunkPos pos, int range, BatchGenerationEnvironment generationGroup, Steps target) + { + nanotime = System.nanoTime(); + this.pos = pos; + this.range = range; + id = generationFutureDebugIDs++; + this.target = target; + this.tParam = ThreadedParameters.getOrMake(generationGroup.params); + LightGenerationMode mode = CONFIG.client().worldGenerator().getLightGenerationMode(); + + this.lightMode = mode; + + future = generationGroup.executors.submit(() -> + { + generationGroup.generateLodFromList(this); + }); + } + + public boolean isCompleted() + { + return future.isDone(); + } + + public boolean hasTimeout(int duration, TimeUnit unit) + { + long currentTime = System.nanoTime(); + long delta = currentTime - nanotime; + return (delta > TimeUnit.NANOSECONDS.convert(duration, unit)); + } + + public boolean terminate() + { + future.cancel(true); + ClientApi.LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN======================="); + BatchGenerationEnvironment.threadFactory.dumpAllThreadStacks(); + return future.isCancelled(); + } + + public void join() + { + try + { + future.get(); + } + catch (InterruptedException | ExecutionException e) + { + e.printStackTrace(); + } + } + + public boolean tooClose(int cx, int cz, int cr) + { + int distX = Math.abs(cx - pos.x); + int distZ = Math.abs(cz - pos.z); + int minRange = cr + range + 1; // Need one to account for the center + minRange += 1 + 1; // Account for required empty chunks + return distX < minRange && distZ < minRange; + } + + public void refreshTimeout() + { + nanotime = System.nanoTime(); + } + + @Override + public String toString() + { + return id + ":" + range + "@" + pos + "(" + target + ")"; + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GlobalParameters.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GlobalParameters.java new file mode 100644 index 000000000..9d604028b --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GlobalParameters.java @@ -0,0 +1,55 @@ + +package com.seibel.lod.common.wrappers.worldGeneration; + +import com.mojang.datafixers.DataFixer; +import com.seibel.lod.core.builders.lodBuilding.LodBuilder; +import com.seibel.lod.core.objects.lod.LodDimension; + +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.storage.ChunkScanAccess; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; +import net.minecraft.world.level.storage.WorldData; + +public final class GlobalParameters +{ + public final ChunkGenerator generator; + public final StructureManager structures; + public final BiomeManager biomeManager; + public final WorldGenSettings worldGenSettings; + public final ThreadedLevelLightEngine lightEngine; + public final LodBuilder lodBuilder; + public final LodDimension lodDim; + public final Registry biomes; + public final RegistryAccess registry; + public final long worldSeed; + public final ChunkScanAccess chunkScanner; + public final ServerLevel level; // TODO: Figure out a way to remove this. Maybe ClientLevel also works? + public final DataFixer fixerUpper; + + public GlobalParameters(ServerLevel level, LodBuilder lodBuilder, LodDimension lodDim) + { + this.lodBuilder = lodBuilder; + this.lodDim = lodDim; + this.level = level; + lightEngine = (ThreadedLevelLightEngine) level.getLightEngine(); + MinecraftServer server = level.getServer(); + WorldData worldData = server.getWorldData(); + worldGenSettings = worldData.worldGenSettings(); + registry = server.registryAccess(); + biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY); + worldSeed = worldGenSettings.seed(); + biomeManager = new BiomeManager(level, BiomeManager.obfuscateSeed(worldSeed)); + structures = server.getStructureManager(); + generator = level.getChunkSource().getGenerator(); + chunkScanner = level.getChunkSource().chunkScanner(); + fixerUpper = server.getFixerUpper(); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/Rolling.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/Rolling.java new file mode 100644 index 000000000..ae16b25a4 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/Rolling.java @@ -0,0 +1,34 @@ + +package com.seibel.lod.common.wrappers.worldGeneration; + +//FIXME: Move this outside the WorldGenerationStep thingy +public class Rolling +{ + + private final int size; + private double total = 0d; + private int index = 0; + private final double[] samples; + + public Rolling(int size) + { + this.size = size; + samples = new double[size]; + for (int i = 0; i < size; i++) + samples[i] = 0d; + } + + public void add(double x) + { + total -= samples[index]; + samples[index] = x; + total += x; + if (++index == size) + index = 0; // cheaper than modulus + } + + public double getAverage() + { + return total / size; + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ThreadedParameters.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ThreadedParameters.java new file mode 100644 index 000000000..6255a5185 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ThreadedParameters.java @@ -0,0 +1,49 @@ + +package com.seibel.lod.common.wrappers.worldGeneration; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.PerfCalculator; +import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.WorldGenStructFeatManager; + +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.levelgen.structure.StructureCheck; + +public final class ThreadedParameters +{ + private static final ThreadLocal localParam = new ThreadLocal(); + final ServerLevel level; + public WorldGenStructFeatManager structFeat; + public final StructureCheck structCheck; + boolean isValid = true; + public final PerfCalculator perf = new PerfCalculator(); + + public static ThreadedParameters getOrMake(GlobalParameters param) + { + ThreadedParameters tParam = localParam.get(); + if (tParam != null && tParam.isValid && tParam.level == param.level) + return tParam; + tParam = new ThreadedParameters(param); + localParam.set(tParam); + return tParam; + } + + public void markAsInvalid() + { + isValid = false; + } + + private ThreadedParameters(GlobalParameters param) + { + level = param.level; + structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures, + param.level.dimension(), param.generator, level, param.generator.getBiomeSource(), param.worldSeed, + param.fixerUpper); + structFeat = new WorldGenStructFeatManager(level, param.worldGenSettings, null, structCheck); + } + + public void makeStructFeat(WorldGenLevel genLevel, WorldGenSettings worldGenSettings) + { + structFeat = new WorldGenStructFeatManager(level, worldGenSettings, genLevel, structCheck); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGenerationStep.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGenerationStep.java deleted file mode 100644 index 755aaf206..000000000 --- a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGenerationStep.java +++ /dev/null @@ -1,1432 +0,0 @@ -/* - * This file is part of the Distant Horizon mod (formerly the LOD Mod), - * licensed under the GNU GPL v3 License. - * - * Copyright (C) 2021 Tom Lee (TomTheFurry) - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -package com.seibel.lod.common.wrappers.worldGeneration; - -import com.seibel.lod.core.api.ClientApi; -import com.seibel.lod.core.api.ModAccessorApi; -import com.seibel.lod.core.builders.lodBuilding.LodBuilder; -import com.seibel.lod.core.builders.lodBuilding.LodBuilderConfig; -import com.seibel.lod.core.enums.config.DistanceGenerationMode; -import com.seibel.lod.core.enums.config.LightGenerationMode; -import com.seibel.lod.core.objects.lod.LodDimension; -import com.seibel.lod.core.util.LodThreadFactory; -import com.seibel.lod.core.util.SingletonHandler; -import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; -import com.seibel.lod.core.wrapperInterfaces.modAccessor.IStarlightAccessor; - -import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; - -import java.io.IOException; -import java.time.Duration; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - -import org.jetbrains.annotations.Nullable; - -import com.google.common.collect.Sets; -import com.mojang.datafixers.DataFixer; -import com.seibel.lod.common.wrappers.chunk.ChunkWrapper; - -import net.minecraft.server.MinecraftServer; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.server.level.WorldGenRegion; -import net.minecraft.util.Mth; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.level.BlockGetter; -import net.minecraft.world.level.ChunkPos; -import net.minecraft.world.level.LevelHeightAccessor; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.StructureFeatureManager; -import net.minecraft.world.level.biome.Biome; -import net.minecraft.world.level.biome.BiomeManager; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.EntityBlock; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.chunk.ChunkAccess; -import net.minecraft.world.level.chunk.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.chunk.DataLayer; -import net.minecraft.world.level.chunk.LevelChunk; -import net.minecraft.world.level.chunk.LevelChunkSection; -import net.minecraft.world.level.chunk.LightChunkGetter; -import net.minecraft.world.level.chunk.ProtoChunk; -import net.minecraft.world.level.chunk.UpgradeData; -import net.minecraft.world.level.chunk.storage.ChunkScanAccess; -import net.minecraft.server.level.ThreadedLevelLightEngine; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; -import net.minecraft.Util; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.SectionPos; -import net.minecraft.nbt.CompoundTag; -import net.minecraft.world.level.levelgen.Heightmap; -import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; -import net.minecraft.world.level.levelgen.NoiseSettings; -import net.minecraft.world.level.levelgen.WorldGenSettings; -import net.minecraft.world.level.levelgen.blending.Blender; -import net.minecraft.world.level.levelgen.structure.StructureCheck; -import net.minecraft.world.level.levelgen.structure.StructureStart; -import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; -import net.minecraft.world.level.lighting.BlockLightEngine; -import net.minecraft.world.level.lighting.LayerLightEventListener; -import net.minecraft.world.level.lighting.LevelLightEngine; -import net.minecraft.world.level.lighting.LightEventListener; -import net.minecraft.world.level.lighting.SkyLightEngine; -import net.minecraft.world.level.storage.WorldData; - -/* -Total: 3.135214124s -===================================== -Empty Chunks: 0.000558328s -StructureStart Step: 0.025177207s -StructureReference Step: 0.00189559s -Biome Step: 0.13789155s -Noise Step: 1.570347555s -Surface Step: 0.741238194s -Carver Step: 0.000009923s -Feature Step: 0.389072425s -Lod Generation: 0.269023348s -*/ - -public final class WorldGenerationStep { - public static final boolean ENABLE_PERF_LOGGING = true; - public static final boolean ENABLE_EVENT_LOGGING = false; - public static final boolean ENABLE_LOAD_EVENT_LOGGING = false; - //TODO: Make actual proper support for StarLight - - //FIXME: Move this outside the WorldGenerationStep thingy - public static class Rolling { - - private final int size; - private double total = 0d; - private int index = 0; - private final double[] samples; - - public Rolling(int size) { - this.size = size; - samples = new double[size]; - for (int i = 0; i < size; i++) - samples[i] = 0d; - } - - public void add(double x) { - total -= samples[index]; - samples[index] = x; - total += x; - if (++index == size) - index = 0; // cheaper than modulus - } - - public double getAverage() { - return total / size; - } - } - - public static class PrefEvent { - long beginNano = 0; - long emptyNano = 0; - long structStartNano = 0; - long structRefNano = 0; - long biomeNano = 0; - long noiseNano = 0; - long surfaceNano = 0; - long carverNano = 0; - long featureNano = 0; - long lightNano = 0; - long endNano = 0; - - @Override - public String toString() { - return - "beginNano: "+beginNano+",\n"+ - "emptyNano: "+emptyNano+",\n"+ - "structStartNano: "+structStartNano+",\n"+ - "structRefNano: "+structRefNano+",\n"+ - "biomeNano: "+biomeNano+",\n"+ - "noiseNano: "+noiseNano+",\n"+ - "surfaceNano: "+surfaceNano+",\n"+ - "carverNano: "+carverNano+",\n"+ - "featureNano: "+featureNano+",\n"+ - "lightNano: "+lightNano+",\n"+ - "endNano: "+endNano+"\n"; - } - } - - public static class PerfCalculator { - public static final int SIZE = 50; - Rolling totalTime = new Rolling(SIZE); - Rolling emptyTime = new Rolling(SIZE); - Rolling structStartTime = new Rolling(SIZE); - Rolling structRefTime = new Rolling(SIZE); - Rolling biomeTime = new Rolling(SIZE); - Rolling noiseTime = new Rolling(SIZE); - Rolling surfaceTime = new Rolling(SIZE); - Rolling carverTime = new Rolling(SIZE); - Rolling featureTime = new Rolling(SIZE); - Rolling lightTime = new Rolling(SIZE); - Rolling lodTime = new Rolling(SIZE); - - public void recordEvent(PrefEvent e) { - long preTime = e.beginNano; - totalTime.add(e.endNano - preTime); - if (e.emptyNano!=0) { - emptyTime.add(e.emptyNano - preTime); - preTime = e.emptyNano; - } - if (e.structStartNano!=0) { - structStartTime.add(e.structStartNano - preTime); - preTime = e.structStartNano; - } - if (e.structRefNano!=0) { - structRefTime.add(e.structRefNano - preTime); - preTime = e.structRefNano; - } - if (e.biomeNano!=0) { - biomeTime.add(e.biomeNano - preTime); - preTime = e.biomeNano; - } - if (e.noiseNano!=0) { - noiseTime.add(e.noiseNano - preTime); - preTime = e.noiseNano; - } - if (e.surfaceNano!=0) { - surfaceTime.add(e.surfaceNano - preTime); - preTime = e.surfaceNano; - } - if (e.carverNano!=0) { - carverTime.add(e.carverNano - preTime); - preTime = e.carverNano; - } - if (e.featureNano!=0) { - featureTime.add(e.featureNano - preTime); - preTime = e.featureNano; - } - if (e.lightNano!=0) { - lightTime.add(e.lightNano - preTime); - preTime = e.lightNano; - } - if (e.endNano!=0) { - lodTime.add(e.endNano - preTime); - preTime = e.endNano; - } - } - - public String toString() { - return "Total: " + Duration.ofNanos((long) totalTime.getAverage()) + ", Empty/LoadChunk: " - + Duration.ofNanos((long) emptyTime.getAverage()) + ", StructStart: " - + Duration.ofNanos((long) structStartTime.getAverage()) + ", StructRef: " - + Duration.ofNanos((long) structRefTime.getAverage()) + ", Biome: " - + Duration.ofNanos((long) biomeTime.getAverage()) + ", Noise: " - + Duration.ofNanos((long) noiseTime.getAverage()) + ", Surface: " - + Duration.ofNanos((long) surfaceTime.getAverage()) + ", Carver: " - + Duration.ofNanos((long) carverTime.getAverage()) + ", Feature: " - + Duration.ofNanos((long) featureTime.getAverage()) + ", Light: " - + Duration.ofNanos((long) lightTime.getAverage()) + ", Lod: " - + Duration.ofNanos((long) lodTime.getAverage()); - } - } - - public static final int TIMEOUT_SECONDS = 30; - - enum Steps { - Empty, StructureStart, StructureReference, Biomes, Noise, Surface, Carvers, LiquidCarvers, Features, Light, - } - - //FIXME: Remove this and use the Utils one - public static final class GridList extends ArrayList implements List { - - public static class Pos { - public int x; - public int y; - - public Pos(int xx, int yy) { - x = xx; - y = yy; - } - } - - private static final long serialVersionUID = 1585978374811888116L; - public final int gridCentreToEdge; - public final int gridSize; - - public GridList(int gridCentreToEdge) { - super((gridCentreToEdge * 2 + 1) * (gridCentreToEdge * 2 + 1)); - gridSize = gridCentreToEdge * 2 + 1; - this.gridCentreToEdge = gridCentreToEdge; - } - - public T getOffsetOf(int index, int x, int y) { - return get(index + x + y * gridSize); - } - - public int offsetOf(int index, int x, int y) { - return index + x + y * gridSize; - } - - public Pos posOf(int index) { - return new Pos(index % gridSize, index / gridSize); - } - - public int calculateOffset(int x, int y) { - return x + y * gridSize; - } - - public GridList subGrid(int gridCentreToEdge) { - int centreIndex = size() / 2; - GridList subGrid = new GridList(gridCentreToEdge); - for (int oy = -gridCentreToEdge; oy <= gridCentreToEdge; oy++) { - int begin = offsetOf(centreIndex, -gridCentreToEdge, oy); - int end = offsetOf(centreIndex, gridCentreToEdge, oy); - subGrid.addAll(this.subList(begin, end + 1)); - } - // System.out.println("========================================\n"+ - // this.toDetailString() + "\nTOOOOOOOOOOOOO\n"+subGrid.toDetailString()+ - // "==========================================\n"); - return subGrid; - } - - @Override - public String toString() { - return "GridList " + gridSize + "*" + gridSize + "[" + size() + "]"; - } - - public String toDetailString() { - StringBuilder str = new StringBuilder("\n"); - int i = 0; - for (T t : this) { - str.append(t.toString()); - str.append(", "); - i++; - if (i % gridSize == 0) { - str.append("\n"); - } - } - return str.toString(); - } - } - - public static final class GlobalParameters { - final ChunkGenerator generator; - final StructureManager structures; - final BiomeManager biomeManager; - final WorldGenSettings worldGenSettings; - final ThreadedLevelLightEngine lightEngine; - final LodBuilder lodBuilder; - final LodDimension lodDim; - final Registry biomes; - final RegistryAccess registry; - final long worldSeed; - final ChunkScanAccess chunkScanner; - final ServerLevel level; // TODO: Figure out a way to remove this. Maybe ClientLevel also works? - final DataFixer fixerUpper; - - public GlobalParameters(ServerLevel level, LodBuilder lodBuilder, LodDimension lodDim) { - this.lodBuilder = lodBuilder; - this.lodDim = lodDim; - this.level = level; - lightEngine = (ThreadedLevelLightEngine) level.getLightEngine(); - MinecraftServer server = level.getServer(); - WorldData worldData = server.getWorldData(); - worldGenSettings = worldData.worldGenSettings(); - registry = server.registryAccess(); - biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY); - worldSeed = worldGenSettings.seed(); - biomeManager = new BiomeManager(level, BiomeManager.obfuscateSeed(worldSeed)); - structures = server.getStructureManager(); - generator = level.getChunkSource().getGenerator(); - chunkScanner = level.getChunkSource().chunkScanner(); - fixerUpper = server.getFixerUpper(); - } - } - - public static final class ThreadedParameters { - private static final ThreadLocal localParam = new ThreadLocal(); - final ServerLevel level; - final StructureFeatureManager structFeat; - final StructureCheck structCheck; - boolean isValid = true; - public final PerfCalculator perf = new PerfCalculator(); - - public static ThreadedParameters getOrMake(GlobalParameters param) { - ThreadedParameters tParam = localParam.get(); - if (tParam != null && tParam.isValid && tParam.level == param.level) - return tParam; - tParam = new ThreadedParameters(param); - localParam.set(tParam); - return tParam; - } - public void markAsInvalid() { - isValid = false; - } - - private ThreadedParameters(GlobalParameters param) { - level = param.level; - structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures, - param.level.dimension(), param.generator, level, param.generator.getBiomeSource(), param.worldSeed, - param.fixerUpper); - structFeat = new StructureFeatureManager(level, param.worldGenSettings, structCheck); - } - } - - //======================= Main Event class====================== - public static final class GenerationEvent { - private static int generationFutureDebugIDs = 0; - final ThreadedParameters tParam; - final ChunkPos pos; - final int range; - final Future future; - long nanotime; - final int id; - final Steps target; - final LightGenerationMode lightMode; - final PrefEvent pEvent = new PrefEvent(); - - public GenerationEvent(ChunkPos pos, int range, WorldGenerationStep generationGroup, Steps target) { - nanotime = System.nanoTime(); - this.pos = pos; - this.range = range; - id = generationFutureDebugIDs++; - this.target = target; - this.tParam = ThreadedParameters.getOrMake(generationGroup.params); - LightGenerationMode mode = CONFIG.client().worldGenerator().getLightGenerationMode(); - - - this.lightMode = mode; - - future = generationGroup.executors.submit(() -> { - generationGroup.generateLodFromList(this); - }); - } - - public boolean isCompleted() { - return future.isDone(); - } - - public boolean hasTimeout(int duration, TimeUnit unit) { - long currentTime = System.nanoTime(); - long delta = currentTime - nanotime; - return (delta > TimeUnit.NANOSECONDS.convert(duration, unit)); - } - - public boolean terminate() { - future.cancel(true); - return future.isCancelled(); - } - - public void join() { - try { - future.get(); - } catch (InterruptedException | ExecutionException e) { - e.printStackTrace(); - } - } - - public boolean tooClose(int cx, int cz, int cr) { - int distX = Math.abs(cx - pos.x); - int distZ = Math.abs(cz - pos.z); - int minRange = cr+range+1; //Need one to account for the center - minRange += 1+1; // Account for required empty chunks - return distX < minRange && distZ < minRange; - } - - public void refreshTimeout() { - nanotime = System.nanoTime(); - } - - @Override - public String toString() { - return id + ":" + range + "@" + pos + "(" + target + ")"; - } - } - - - - - - - - //=================Generation Step=================== - - private static T joinSync(CompletableFuture f) { - if (!f.isDone()) throw new RuntimeException("The future is concurrent!"); - return f.join(); - } - - final LinkedList events = new LinkedList(); - final GlobalParameters params; - final StepStructureStart stepStructureStart = new StepStructureStart(); - final StepStructureReference stepStructureReference = new StepStructureReference(); - final StepBiomes stepBiomes = new StepBiomes(); - final StepNoise stepNoise = new StepNoise(); - final StepSurface stepSurface = new StepSurface(); - final StepCarvers stepCarvers = new StepCarvers(); - final StepFeatures stepFeatures = new StepFeatures(); - final StepLight stepLight = new StepLight(); - private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class); - - //public final ExecutorService executors = Executors - // .newCachedThreadPool(new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY)); - public ExecutorService executors = Executors - .newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), - new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY)); - - public void resizeThreadPool(int newThreadCount) - { - executors = Executors.newFixedThreadPool(newThreadCount, - new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY)); - } - - public boolean tryAddPoint(int px, int pz, int range, Steps target) { - int boxSize = range * 2 + 1; - int x = Math.floorDiv(px, boxSize) * boxSize + range; - int z = Math.floorDiv(pz, boxSize) * boxSize + range; - - for (GenerationEvent event : events) { - if (event.tooClose(x, z, range)) - return false; - } - // System.out.println(x + ", "+z); - events.add(new GenerationEvent(new ChunkPos(x, z), range, this, target)); - return true; - } - - public void updateAllFutures() { - // Update all current out standing jobs - Iterator iter = events.iterator(); - while (iter.hasNext()) { - GenerationEvent event = iter.next(); - if (event.isCompleted()) { - try { - event.join(); - } catch (Throwable e) { - e.printStackTrace(); - while (e.getCause() != null) { - e = e.getCause(); - e.printStackTrace(); - } - } finally { - iter.remove(); - } - } else if (event.hasTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS)) { - ClientApi.LOGGER.error("Batching World Generator: " + event + " timed out and terminated!"); - ClientApi.LOGGER.info("Dump PrefEvent: "+event.pEvent); - try { - if (!event.terminate()) ClientApi.LOGGER.error("Failed to terminate the stuck generation event!"); - } finally { - iter.remove(); - } - } - } - } - - public WorldGenerationStep(ServerLevel level, LodBuilder lodBuilder, LodDimension lodDim) { - ClientApi.LOGGER.info("================WORLD_GEN_STEP_INITING============="); - params = new GlobalParameters(level, lodBuilder, lodDim); - } - - public void startLoadingAllRegionsFromFile(LodDimension lodDim) { - ServerLevel level = params.level; - level.getChunkSource(); - - } - - @SuppressWarnings("resource") - public static ChunkAccess loadOrMakeChunk(ChunkPos chunkPos, ServerLevel level, LevelLightEngine lightEngine) { - CompoundTag chunkData = null; - try - { - chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos); - } - catch (IOException e) - { - ClientApi.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e); - } - if (chunkData == null) { - return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null); - } else { - return ChunkLoader.read(level, lightEngine, chunkPos, chunkData); - } - - } - - - - public void generateLodFromList(GenerationEvent e) { - if (ENABLE_EVENT_LOGGING) - ClientApi.LOGGER.info("Lod Generate Event: "+e.pos); - e.pEvent.beginNano = System.nanoTime(); - GridList referencedChunks; - DistanceGenerationMode generationMode; - LightedWorldGenRegion region; - WorldGenLevelLightEngine lightEngine; - LightGetterAdaptor adaptor; - - try { - adaptor = new LightGetterAdaptor(params.level); - lightEngine = new WorldGenLevelLightEngine(adaptor); - - int cx = e.pos.x; - int cy = e.pos.z; - int rangeEmpty = e.range + 1; - GridList chunks = new GridList(rangeEmpty); - - @SuppressWarnings("resource") - EmptyChunkGenerator generator = (int x, int z) -> { - ChunkPos chunkPos = new ChunkPos(x, z); - ChunkAccess target = null; - try { - target = loadOrMakeChunk(chunkPos, params.level, lightEngine); - } catch (RuntimeException e2) { - // Continue... - e2.printStackTrace(); - } - if (target == null) - target = new ProtoChunk(chunkPos, UpgradeData.EMPTY, params.level, - params.biomes, null); - return target; - }; - - for (int oy = -rangeEmpty; oy <= rangeEmpty; oy++) { - for (int ox = -rangeEmpty; ox <= rangeEmpty; ox++) { - ChunkAccess target = generator.generate(cx + ox, cy + oy); - chunks.add(target); - } - } - e.pEvent.emptyNano = System.nanoTime(); - e.refreshTimeout(); - region = new LightedWorldGenRegion(params.level, lightEngine, chunks, ChunkStatus.STRUCTURE_STARTS, rangeEmpty, e.lightMode, generator); - adaptor.setRegion(region); - referencedChunks = chunks.subGrid(e.range); - referencedChunks = generateDirect(e, referencedChunks, e.target, region); - - } catch (StepStructureStart.StructStartCorruptedException f) { - e.tParam.markAsInvalid(); - return; - } - - switch (e.target) { - case Empty: - case StructureStart: - case StructureReference: - generationMode = DistanceGenerationMode.NONE; - break; - case Biomes: - generationMode = DistanceGenerationMode.BIOME_ONLY; - case Noise: - generationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; - break; - case Surface: - case Carvers: - generationMode = DistanceGenerationMode.SURFACE; - break; - case Features: - generationMode = DistanceGenerationMode.FEATURES; - break; - case Light: - case LiquidCarvers: - default: - return; - } - int centreIndex = referencedChunks.size() / 2; - - for (int oy = -e.range; oy <= e.range; oy++) - { - for (int ox = -e.range; ox <= e.range; ox++) - { - int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); - ChunkAccess target = referencedChunks.get(targetIndex); - target.setLightCorrect(true); - if (target instanceof LevelChunk) ((LevelChunk)target).setClientLightReady(true); - boolean isFull = target.getStatus() == ChunkStatus.FULL || target instanceof LevelChunk; - boolean isPartial = target.isOldNoiseGeneration(); - if (isFull) { - if (ENABLE_LOAD_EVENT_LOGGING) ClientApi.LOGGER.info("Detected full existing chunk at {}", target.getPos()); - params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region), - new LodBuilderConfig(DistanceGenerationMode.FULL), true); - } else if (isPartial) { - if (ENABLE_LOAD_EVENT_LOGGING) ClientApi.LOGGER.info("Detected old existing chunk at {}", target.getPos()); - params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region), - new LodBuilderConfig(generationMode), true); - } else if (target.getStatus() == ChunkStatus.EMPTY && generationMode == DistanceGenerationMode.NONE) { - params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region), - LodBuilderConfig.getFillVoidConfig(), true); - } else { - params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region), - new LodBuilderConfig(generationMode), true); - } - if (e.lightMode == LightGenerationMode.FANCY || isFull) { - lightEngine.retainData(target.getPos(), false); - } - - } - } - e.pEvent.endNano = System.nanoTime(); - e.refreshTimeout(); - if (ENABLE_PERF_LOGGING) { - e.tParam.perf.recordEvent(e.pEvent); - ClientApi.LOGGER.info(e.tParam.perf); - } - } - - public GridList generateDirect(GenerationEvent e, GridList subRange, Steps step, - LightedWorldGenRegion region) { - try { - subRange.forEach((chunk) -> { - if (chunk instanceof ProtoChunk) { - ((ProtoChunk) chunk).setLightEngine(region.getLightEngine()); - region.getLightEngine().retainData(chunk.getPos(), true); - } - }); - if (step == Steps.Empty) - return subRange; - stepStructureStart.generateGroup(e.tParam, region, subRange); - e.pEvent.structStartNano = System.nanoTime(); - e.refreshTimeout(); - if (step == Steps.StructureStart) - return subRange; - stepStructureReference.generateGroup(e.tParam, region, subRange); - e.pEvent.structRefNano = System.nanoTime(); - e.refreshTimeout(); - if (step == Steps.StructureReference) - return subRange; - stepBiomes.generateGroup(e.tParam, region, subRange); - e.pEvent.biomeNano = System.nanoTime(); - e.refreshTimeout(); - if (step == Steps.Biomes) - return subRange; - stepNoise.generateGroup(e.tParam, region, subRange); - e.pEvent.noiseNano = System.nanoTime(); - e.refreshTimeout(); - if (step == Steps.Noise) - return subRange; - stepSurface.generateGroup(e.tParam, region, subRange); - e.pEvent.surfaceNano = System.nanoTime(); - e.refreshTimeout(); - if (step == Steps.Surface) - return subRange; - stepCarvers.generateGroup(e.tParam, region, subRange); - e.pEvent.carverNano = System.nanoTime(); - e.refreshTimeout(); - if (step == Steps.Carvers) - return subRange; - stepFeatures.generateGroup(e.tParam, region, subRange); - e.pEvent.featureNano = System.nanoTime(); - e.refreshTimeout(); - return subRange; - } finally { - switch (region.lightMode) { - case FANCY: - stepLight.generateGroup(region.getLightEngine(), subRange); - break; - case FAST: - subRange.forEach((p) -> { - if (p instanceof ProtoChunk) ((ProtoChunk)p).setLightCorrect(true); - }); - break; - } - e.pEvent.lightNano = System.nanoTime(); - e.refreshTimeout(); - } - } - - - - - public final class StepStructureStart { - public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS; - - public static class StructStartCorruptedException extends RuntimeException { - private static final long serialVersionUID = -8987434342051563358L; - - public StructStartCorruptedException(ArrayIndexOutOfBoundsException e) { - super("StructStartCorruptedException"); - super.initCause(e); - fillInStackTrace(); - } - } - - public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, - List chunks) { - - ArrayList chunksToDo = new ArrayList(); - - for (ChunkAccess chunk : chunks) { - if (chunk.getStatus().isOrAfter(STATUS)) continue; - ((ProtoChunk) chunk).setStatus(STATUS); - chunksToDo.add(chunk); - } - - if (params.worldGenSettings.generateFeatures()) { - for (ChunkAccess chunk : chunksToDo) { - // System.out.println("StepStructureStart: "+chunk.getPos()); - params.generator.createStructures(params.registry, tParams.structFeat, chunk, params.structures, - params.worldSeed); - try { - tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts()); - } catch (ArrayIndexOutOfBoundsException e) { - // There's a rare issue with StructStart where it throws ArrayIndexOutOfBounds - // This means the structFeat is corrupted (For some reason) and I need to reset it. - // TODO: Figure out in the future why this happens even though I am using new structFeat - throw new StructStartCorruptedException(e); - } - } - } - } - } - - public final class StepStructureReference { - public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES; - - private void createReferences(WorldGenRegion worldGenLevel, StructureFeatureManager structureFeatureManager, - ChunkAccess chunkAccess) { - ChunkPos chunkPos = chunkAccess.getPos(); - int j = chunkPos.x; - int k = chunkPos.z; - int l = chunkPos.getMinBlockX(); - int m = chunkPos.getMinBlockZ(); - - SectionPos sectionPos = SectionPos.bottomOf(chunkAccess); - - for (int n = j - 8; n <= j + 8; n++) { - for (int o = k - 8; o <= k + 8; o++) { - if (!worldGenLevel.hasChunk(n, o)) - continue; - long p = ChunkPos.asLong(n, o); - for (StructureStart structureStart : worldGenLevel.getChunk(n, o).getAllStarts().values()) { - try { - if (structureStart.isValid() - && structureStart.getBoundingBox().intersects(l, m, l + 15, m + 15)) { - structureFeatureManager.addReferenceForFeature(sectionPos, structureStart.getFeature(), - p, chunkAccess); - } - } catch (Exception exception) { - CrashReport crashReport = CrashReport.forThrowable(exception, - "Generating structure reference"); - CrashReportCategory crashReportCategory = crashReport.addCategory("Structure"); - crashReportCategory.setDetail("Id", - () -> Registry.STRUCTURE_FEATURE.getKey(structureStart.getFeature()).toString()); - crashReportCategory.setDetail("Name", () -> structureStart.getFeature().getFeatureName()); - crashReportCategory.setDetail("Class", - () -> structureStart.getFeature().getClass().getCanonicalName()); - throw new ReportedException(crashReport); - } - } - } - } - } - - public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, - List chunks) { - - ArrayList chunksToDo = new ArrayList(); - - for (ChunkAccess chunk : chunks) { - if (chunk.getStatus().isOrAfter(STATUS)) continue; - ((ProtoChunk) chunk).setStatus(STATUS); - chunksToDo.add(chunk); - } - - for (ChunkAccess chunk : chunksToDo) { - // System.out.println("StepStructureReference: "+chunk.getPos()); - createReferences(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk); - } - } - } - - public final class StepBiomes { - public final ChunkStatus STATUS = ChunkStatus.BIOMES; - - private ChunkAccess createBiomes(ChunkGenerator generator, Registry registry, Blender blender, StructureFeatureManager structureFeatureManager, ChunkAccess chunkAccess) { - if (generator instanceof NoiseBasedChunkGenerator) { - ((NoiseBasedChunkGenerator) generator).doCreateBiomes(registry, blender, structureFeatureManager, chunkAccess); - return chunkAccess; - } else { - chunkAccess.fillBiomesFromNoise(generator.getBiomeSource()::getNoiseBiome, generator.climateSampler()); - return chunkAccess; - } - } - - public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, - List chunks) { - - ArrayList chunksToDo = new ArrayList(); - - for (ChunkAccess chunk : chunks) { - if (chunk.getStatus().isOrAfter(STATUS)) continue; - ((ProtoChunk) chunk).setStatus(STATUS); - chunksToDo.add(chunk); - } - - for (ChunkAccess chunk : chunksToDo) { - // System.out.println("StepBiomes: "+chunk.getPos()); - chunk = createBiomes(params.generator, params.biomes, Blender.of(worldGenRegion), - tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk); - } - } - } - - public final class StepNoise { - public final ChunkStatus STATUS = ChunkStatus.NOISE; - - private ChunkAccess NoiseBased$fillFromNoise(NoiseBasedChunkGenerator generator, Blender blender, StructureFeatureManager structureFeatureManager, ChunkAccess chunkAccess) { - NoiseSettings noiseSettings = generator.settings.get().noiseSettings(); - LevelHeightAccessor levelHeightAccessor = chunkAccess.getHeightAccessorForGeneration(); - int i = Math.max(noiseSettings.minY(), levelHeightAccessor.getMinBuildHeight()); - int j = Math.min(noiseSettings.minY() + noiseSettings.height(), levelHeightAccessor.getMaxBuildHeight()); - int k = Mth.intFloorDiv(i, noiseSettings.getCellHeight()); - int l = Mth.intFloorDiv(j - i, noiseSettings.getCellHeight()); - if (l <= 0) { - return chunkAccess; - } - int m = chunkAccess.getSectionIndex(l * noiseSettings.getCellHeight() - 1 + i); - int n = chunkAccess.getSectionIndex(i); - HashSet set = Sets.newHashSet(); - for (int o = m; o >= n; --o) { - LevelChunkSection levelChunkSection = chunkAccess.getSection(o); - levelChunkSection.acquire(); - set.add(levelChunkSection); - } - chunkAccess = generator.doFill(blender, structureFeatureManager, chunkAccess, k, l); - for (LevelChunkSection levelChunkSection : set) { - levelChunkSection.release(); - }; - return chunkAccess; - } - - public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, - List chunks) { - - ArrayList chunksToDo = new ArrayList(); - - for (ChunkAccess chunk : chunks) { - if (chunk.getStatus().isOrAfter(STATUS)) continue; - ((ProtoChunk) chunk).setStatus(STATUS); - chunksToDo.add(chunk); - } - - for (ChunkAccess chunk : chunksToDo) { - // System.out.println("StepNoise: "+chunk.getPos()); - if (params.generator instanceof NoiseBasedChunkGenerator) { - chunk = NoiseBased$fillFromNoise((NoiseBasedChunkGenerator)params.generator,Blender.of(worldGenRegion), - tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk); - } else { - chunk = joinSync(params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), - tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); - } - } - } - } - - public final class StepSurface { - public final ChunkStatus STATUS = ChunkStatus.SURFACE; - - public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, - List chunks) { - ArrayList chunksToDo = new ArrayList(); - - for (ChunkAccess chunk : chunks) { - if (chunk.getStatus().isOrAfter(STATUS)) continue; - ((ProtoChunk) chunk).setStatus(STATUS); - chunksToDo.add(chunk); - } - - for (ChunkAccess chunk : chunksToDo) { - // System.out.println("StepSurface: "+chunk.getPos()); - params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), - chunk); - } - } - } - - public final class StepCarvers { - public final ChunkStatus STATUS = ChunkStatus.CARVERS; - - public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, - List chunks) { - ArrayList chunksToDo = new ArrayList(); - - for (ChunkAccess chunk : chunks) { - if (chunk.getStatus().isOrAfter(STATUS)) continue; - ((ProtoChunk) chunk).setStatus(STATUS); - chunksToDo.add(chunk); - } - - for (ChunkAccess chunk : chunksToDo) { - // DISABLED CURRENTLY! - // System.out.println("StepCarvers: "+chunk.getPos()); - // Blender.addAroundOldChunksCarvingMaskFilter((WorldGenLevel) worldGenRegion, - // (ProtoChunk) chunk); - // params.generator.applyCarvers(worldGenRegion, params.worldSeed, - // params.biomeManager, tParams.structFeat.forWorldGenRegion(worldGenRegion), - // chunk, - // GenerationStep.Carving.AIR); - ((ProtoChunk) chunk).setStatus(STATUS); - } - } - } - - public final class StepFeatures { - public final ChunkStatus STATUS = ChunkStatus.FEATURES; - - public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, - GridList chunks) { - ArrayList chunksToDo = new ArrayList(); - - for (ChunkAccess chunk : chunks) { - if (chunk.getStatus().isOrAfter(STATUS)) continue; - ((ProtoChunk) chunk).setStatus(STATUS); - chunksToDo.add(chunk); - } - - for (ChunkAccess chunk : chunksToDo) { - try { - params.generator.applyBiomeDecoration(worldGenRegion, chunk, - tParams.structFeat.forWorldGenRegion(worldGenRegion)); - Blender.generateBorderTicks(worldGenRegion, chunk); - } catch (ReportedException e) { - e.printStackTrace(); - // FIXME: Features concurrent modification issue. Something about cocobeans just - // aren't happy - // For now just retry. - } - }/* - for (ChunkAccess chunk : chunks) { - Heightmap.primeHeightmaps(chunk, - EnumSet.of(Heightmap.Types.MOTION_BLOCKING, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, - Heightmap.Types.OCEAN_FLOOR, Heightmap.Types.WORLD_SURFACE)); - }*/ - } - } - - public final class StepLight { - public final ChunkStatus STATUS = ChunkStatus.LIGHT; - - public void generateGroup(LightEventListener lightEngine, - GridList chunks) { - //ArrayList chunksToDo = new ArrayList(); - - for (ChunkAccess chunk : chunks) { - if (chunk.getStatus().isOrAfter(STATUS)) continue; - ((ProtoChunk) chunk).setStatus(STATUS); - } - - for (ChunkAccess chunk : chunks) { - boolean hasCorrectBlockLight = (chunk instanceof LevelChunk && chunk.isLightCorrect()); - try { - if (lightEngine == null) { - // Do nothing - } else if (lightEngine instanceof WorldGenLevelLightEngine) { - ((WorldGenLevelLightEngine)lightEngine).lightChunk(chunk, !hasCorrectBlockLight); - } else if (lightEngine instanceof ThreadedLevelLightEngine) { - ((ThreadedLevelLightEngine) lightEngine).lightChunk(chunk, !hasCorrectBlockLight).join(); - } else { - assert(false); - } - - } catch (Exception e) { - e.printStackTrace(); - } - chunk.setLightCorrect(true); - } - lightEngine.runUpdates(Integer.MAX_VALUE, true, true); - } - } - - public interface EmptyChunkGenerator { - ChunkAccess generate(int x, int z); - } - - public static class LightedWorldGenRegion extends WorldGenRegion { - final WorldGenLevelLightEngine light; - final LightGenerationMode lightMode; - final EmptyChunkGenerator generator; - final int writeRadius; - final int size; - private final ChunkPos firstPos; - private final List cache; - Long2ObjectOpenHashMap chunkMap = new Long2ObjectOpenHashMap(); - public LightedWorldGenRegion(ServerLevel serverLevel, WorldGenLevelLightEngine lightEngine, List list, ChunkStatus chunkStatus, int i, - LightGenerationMode lightMode, EmptyChunkGenerator generator) { - super(serverLevel, list, chunkStatus, i); - this.lightMode = lightMode; - this.firstPos = list.get(0).getPos(); - this.generator = generator; - light = lightEngine; - writeRadius = i; - cache = list; - size = Mth.floor(Math.sqrt(list.size())); - } - - // Bypass BCLib mixin overrides. - @Override - public boolean ensureCanWrite(BlockPos blockPos) { - int i = SectionPos.blockToSectionCoord(blockPos.getX()); - int j = SectionPos.blockToSectionCoord(blockPos.getZ()); - ChunkPos chunkPos = this.getCenter(); - ChunkAccess center = this.getChunk(chunkPos.x, chunkPos.z); - int k = Math.abs(chunkPos.x - i); - int l = Math.abs(chunkPos.z - j); - if (k > this.writeRadius || l > this.writeRadius) { - return false; - } - if (center.isUpgrading()) { - LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration(); - if (blockPos.getY() < levelHeightAccessor.getMinBuildHeight() || blockPos.getY() >= levelHeightAccessor.getMaxBuildHeight()) { - return false; - } - } - return true; - } - - // Skip updating the related tile entities - @Override - public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j) { - if (!this.ensureCanWrite(blockPos)) { - return false; - } - ChunkAccess chunkAccess = this.getChunk(blockPos); - if (chunkAccess instanceof LevelChunk) return true; - chunkAccess.setBlockState(blockPos, blockState, false); - //This is for post ticking for water on gen and stuff like that. Not enabled for now. - //if (blockState.hasPostProcess(this, blockPos)) this.getChunk(blockPos).markPosForPostprocessing(blockPos); - return true; - } - - // Skip Dropping the item on destroy - @Override - public boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { - BlockState blockState = this.getBlockState(blockPos); - if (blockState.isAir()) { - return false; - } - return this.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3, i); - } - - // Skip BlockEntity stuff. It aren't really needed - @Override - public BlockEntity getBlockEntity(BlockPos blockPos) { - return null; - } - - // Skip BlockEntity stuff. It aren't really needed - @Override - public boolean addFreshEntity(Entity entity) { - return true; - } - - // Allays have empty chunks even if it's outside the worldGenRegion - @Override - public boolean hasChunk(int i, int j) { - return true; - } - - // Override to ensure no other mod mixins cause skipping the overrided getChunk(...) - @Override - public ChunkAccess getChunk(int i, int j) { - return this.getChunk(i, j, ChunkStatus.EMPTY); - } - - // Override to ensure no other mod mixins cause skipping the overrided getChunk(...) - @Override - public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus) { - return this.getChunk(i, j, chunkStatus, true); - } - - // Use this instead of super.getChunk() to bypass C2ME concurrency checks - private ChunkAccess superGetChunk(int x, int z, ChunkStatus cs) { - int k = x - firstPos.x; - int l = z - firstPos.z; - return cache.get(k + l * size); - } - // Use this instead of super.hasChunk() to bypass C2ME concurrency checks - private boolean superHasChunk(int x, int z) { - int k = x - firstPos.x; - int l = z - firstPos.z; - return l>=0 && l=0 && k= getMaxLightLevel()); - } - - - - } - - public static class LightGetterAdaptor implements LightChunkGetter { - private final BlockGetter heightGetter; - public LightedWorldGenRegion genRegion = null; - final boolean shouldReturnNull; - public LightGetterAdaptor(BlockGetter heightAccessor) { - this.heightGetter = heightAccessor; - shouldReturnNull = ModAccessorApi.get(IStarlightAccessor.class)!=null; - } - public void setRegion(LightedWorldGenRegion region) { - genRegion = region; - } - - @Override - public BlockGetter getChunkForLighting(int chunkX, int chunkZ) { - if (genRegion == null) throw new IllegalStateException("World Gen region has not been set!"); - // May be null - return genRegion.getChunk(chunkX, chunkZ, ChunkStatus.EMPTY, false); - } - @Override - public BlockGetter getLevel() { - return shouldReturnNull ? null : (genRegion!=null ? genRegion : heightGetter); - } - public LevelHeightAccessor getLevelHeightAccessor() { - return heightGetter; - } - } - - public static class WorldGenLevelLightEngine extends LevelLightEngine { - public static final int MAX_SOURCE_LEVEL = 15; - public static final int LIGHT_SECTION_PADDING = 1; - protected final LevelHeightAccessor levelHeightAccessor; - @Nullable - public final BlockLightEngine blockEngine; - @Nullable - public final SkyLightEngine skyEngine; - - public WorldGenLevelLightEngine(LightGetterAdaptor genRegion) { - super(genRegion, false, false); - this.levelHeightAccessor = genRegion.getLevelHeightAccessor(); - this.blockEngine = new BlockLightEngine(genRegion); - this.skyEngine = new SkyLightEngine(genRegion); - } - - @Override - public void checkBlock(BlockPos blockPos) { - if (this.blockEngine != null) { - this.blockEngine.checkBlock(blockPos); - } - if (this.skyEngine != null) { - this.skyEngine.checkBlock(blockPos); - } - } - - @Override - public void onBlockEmissionIncrease(BlockPos blockPos, int i) { - if (this.blockEngine != null) { - this.blockEngine.onBlockEmissionIncrease(blockPos, i); - } - } - - @Override - public boolean hasLightWork() { - if (this.skyEngine != null && this.skyEngine.hasLightWork()) { - return true; - } - return this.blockEngine != null && this.blockEngine.hasLightWork(); - } - - @Override - public int runUpdates(int i, boolean bl, boolean bl2) { - if (this.blockEngine != null && this.skyEngine != null) { - int j = i / 2; - int k = this.blockEngine.runUpdates(j, bl, bl2); - int l = i - j + k; - int m = this.skyEngine.runUpdates(l, bl, bl2); - if (k == 0 && m > 0) { - return this.blockEngine.runUpdates(m, bl, bl2); - } - return m; - } - if (this.blockEngine != null) { - return this.blockEngine.runUpdates(i, bl, bl2); - } - if (this.skyEngine != null) { - return this.skyEngine.runUpdates(i, bl, bl2); - } - return i; - } - - @Override - public void updateSectionStatus(SectionPos sectionPos, boolean bl) { - if (this.blockEngine != null) { - this.blockEngine.updateSectionStatus(sectionPos, bl); - } - if (this.skyEngine != null) { - this.skyEngine.updateSectionStatus(sectionPos, bl); - } - } - - @Override - public void enableLightSources(ChunkPos chunkPos, boolean bl) { - if (this.blockEngine != null) { - this.blockEngine.enableLightSources(chunkPos, bl); - } - if (this.skyEngine != null) { - this.skyEngine.enableLightSources(chunkPos, bl); - } - } - - @Override - public LayerLightEventListener getLayerListener(LightLayer lightLayer) { - if (lightLayer == LightLayer.BLOCK) { - if (this.blockEngine == null) { - return LayerLightEventListener.DummyLightLayerEventListener.INSTANCE; - } - return this.blockEngine; - } - if (this.skyEngine == null) { - return LayerLightEventListener.DummyLightLayerEventListener.INSTANCE; - } - return this.skyEngine; - } - - @Override - public int getRawBrightness(BlockPos blockPos, int i) { - int j = this.skyEngine == null ? 0 : this.skyEngine.getLightValue(blockPos) - i; - int k = this.blockEngine == null ? 0 : this.blockEngine.getLightValue(blockPos); - return Math.max(k, j); - } - - public void lightChunk(ChunkAccess chunkAccess, boolean needLightBlockUpdate) { - ChunkPos chunkPos = chunkAccess.getPos(); - chunkAccess.setLightCorrect(false); - - LevelChunkSection[] levelChunkSections = chunkAccess.getSections(); - for (int i = 0; i < chunkAccess.getSectionsCount(); ++i) { - LevelChunkSection levelChunkSection = levelChunkSections[i]; - if (levelChunkSection.hasOnlyAir()) continue; - int j = this.levelHeightAccessor.getSectionYFromSectionIndex(i); - updateSectionStatus(SectionPos.of(chunkPos, j), false); - } - enableLightSources(chunkPos, true); - if (needLightBlockUpdate) { - chunkAccess.getLights().forEach(blockPos -> - onBlockEmissionIncrease(blockPos, chunkAccess.getLightEmission(blockPos))); - } - - chunkAccess.setLightCorrect(true); - } - - @Override - public String getDebugData(LightLayer lightLayer, SectionPos sectionPos) { - throw new UnsupportedOperationException("This should never be used!"); - } - @Override - public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer, boolean bl) { - if (lightLayer == LightLayer.BLOCK) { - if (this.blockEngine != null) { - this.blockEngine.queueSectionData(sectionPos.asLong(), dataLayer, bl); - } - } else if (this.skyEngine != null) { - this.skyEngine.queueSectionData(sectionPos.asLong(), dataLayer, bl); - } - } - @Override - public void retainData(ChunkPos chunkPos, boolean bl) { - if (this.blockEngine != null) { - this.blockEngine.retainData(chunkPos, bl); - } - if (this.skyEngine != null) { - this.skyEngine.retainData(chunkPos, bl); - } - } - @Override - public int getLightSectionCount() { - throw new UnsupportedOperationException("This should never be used!"); - } - @Override - public int getMinLightSection() { - throw new UnsupportedOperationException("This should never be used!"); - } - @Override - public int getMaxLightSection() { - throw new UnsupportedOperationException("This should never be used!"); - } - } - - - - - -} diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java index 9cbce4549..40945b67f 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGeneratorWrapper.java @@ -1,3 +1,4 @@ + package com.seibel.lod.common.wrappers.worldGeneration; import com.seibel.lod.core.builders.lodBuilding.LodBuilder; @@ -30,65 +31,67 @@ import net.minecraft.world.level.chunk.*; */ public class WorldGeneratorWrapper extends AbstractWorldGeneratorWrapper { - public final ServerLevel serverWorld; - public final LodDimension lodDim; - public final LodBuilder lodBuilder; - - public WorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) - { - super(newLodBuilder, newLodDimension, worldWrapper); - - lodBuilder = newLodBuilder; - lodDim = newLodDimension; - serverWorld = ((WorldWrapper) worldWrapper).getServerWorld(); - } - - /** takes about 2-5 ms */ - @Override - public void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode) - { - generate(pos.getX(), pos.getZ(), generationMode); - } - - /** takes about 10 - 20 ms */ - @Override - public void generateSurface(AbstractChunkPosWrapper pos) - { - generate(pos.getX(), pos.getZ(), DistanceGenerationMode.SURFACE); - } - - /** - * takes about 15 - 20 ms - */ - @Override - public void generateFeatures(AbstractChunkPosWrapper pos) - { - generate(pos.getX(), pos.getZ(), DistanceGenerationMode.FEATURES); - } - - /** - * Generates using MC's ServerWorld. - *

- * on pre generated chunks 0 - 1 ms
- * on un generated chunks 0 - 50 ms
- * with the median seeming to hover around 15 - 30 ms
- * and outliers in the 100 - 200 ms range
- *

- * Note this should not be multithreaded and does cause server/simulation lag - * (Higher lag for generating than loading) - */ - @Override - public void generateFull(AbstractChunkPosWrapper pos) - { - generate(pos.getX(), pos.getZ(), DistanceGenerationMode.FULL); - } - - private void generate(int chunkX, int chunkZ, DistanceGenerationMode generationMode) { - - // long t = System.nanoTime(); - - ChunkStatus targetStatus; - switch (generationMode) { + public final ServerLevel serverWorld; + public final LodDimension lodDim; + public final LodBuilder lodBuilder; + + public WorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) + { + super(newLodBuilder, newLodDimension, worldWrapper); + + lodBuilder = newLodBuilder; + lodDim = newLodDimension; + serverWorld = ((WorldWrapper) worldWrapper).getServerWorld(); + } + + /** takes about 2-5 ms */ + @Override + public void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode) + { + generate(pos.getX(), pos.getZ(), generationMode); + } + + /** takes about 10 - 20 ms */ + @Override + public void generateSurface(AbstractChunkPosWrapper pos) + { + generate(pos.getX(), pos.getZ(), DistanceGenerationMode.SURFACE); + } + + /** + * takes about 15 - 20 ms + */ + @Override + public void generateFeatures(AbstractChunkPosWrapper pos) + { + generate(pos.getX(), pos.getZ(), DistanceGenerationMode.FEATURES); + } + + /** + * Generates using MC's ServerWorld. + *

+ * on pre generated chunks 0 - 1 ms
+ * on un generated chunks 0 - 50 ms
+ * with the median seeming to hover around 15 - 30 ms
+ * and outliers in the 100 - 200 ms range
+ *

+ * Note this should not be multithreaded and does cause server/simulation lag + * (Higher lag for generating than loading) + */ + @Override + public void generateFull(AbstractChunkPosWrapper pos) + { + generate(pos.getX(), pos.getZ(), DistanceGenerationMode.FULL); + } + + private void generate(int chunkX, int chunkZ, DistanceGenerationMode generationMode) + { + + // long t = System.nanoTime(); + + ChunkStatus targetStatus; + switch (generationMode) + { case BIOME_ONLY: targetStatus = ChunkStatus.BIOMES; break; @@ -111,39 +114,40 @@ public class WorldGeneratorWrapper extends AbstractWorldGeneratorWrapper // The bool=true means that we wants to generate chunk, and that the returned ChunkAccess must not be null ChunkAccess ca = serverWorld.getChunkSource().getChunk(chunkX, chunkZ, targetStatus, true); - if (ca == null) throw new RuntimeException("This should NEVER be null due to bool being true"); + if (ca == null) + throw new RuntimeException("This should NEVER be null due to bool being true"); lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(ca, serverWorld), new LodBuilderConfig(generationMode), false); // long duration = System.nanoTime()-t; // Debug print the duration // System.out.println("LodChunkGenFull["+chunkX+","+chunkZ+"]: "+(double)(duration)/1000.); - - } - + + } + /* TODO: Update this chart * performance/generation tests related to * serverWorld.getChunk(x, z, ChunkStatus. *** ) - - true/false is whether they generated blocks or not - the time is how long it took to generate - - ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P) - ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks) - ChunkStatus.BIOMES 1 - 10 ms false (no height) - ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone) - ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass) - ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass) - ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass) - ChunkStatus.FEATURES 7 - 25 ms true - ChunkStatus.HEIGHTMAPS 20 - 40 ms true - ChunkStatus.LIGHT 20 - 40 ms true - ChunkStatus.FULL 30 - 50 ms true - ChunkStatus.SPAWN 50 - 80 ms true - - At this point I would suggest using FEATURES, as it generates snow and trees - (and any other object that are needed to make biomes distinct) - - Otherwise, if snow/trees aren't necessary SURFACE is the next fastest (although not by much) + + true/false is whether they generated blocks or not + the time is how long it took to generate + + ChunkStatus.EMPTY 0 - 1 ms false (empty, what did you expect? :P) + ChunkStatus.STRUCTURE_REFERENCES 1 - 2 ms false (no height, only generates some chunks) + ChunkStatus.BIOMES 1 - 10 ms false (no height) + ChunkStatus.NOISE 4 - 15 ms true (all blocks are stone) + ChunkStatus.LIQUID_CARVERS 6 - 12 ms true (no snow/trees, just grass) + ChunkStatus.SURFACE 5 - 15 ms true (no snow/trees, just grass) + ChunkStatus.CARVERS 5 - 30 ms true (no snow/trees, just grass) + ChunkStatus.FEATURES 7 - 25 ms true + ChunkStatus.HEIGHTMAPS 20 - 40 ms true + ChunkStatus.LIGHT 20 - 40 ms true + ChunkStatus.FULL 30 - 50 ms true + ChunkStatus.SPAWN 50 - 80 ms true + + At this point I would suggest using FEATURES, as it generates snow and trees + (and any other object that are needed to make biomes distinct) + + Otherwise, if snow/trees aren't necessary SURFACE is the next fastest (although not by much) */ } \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightGetterAdaptor.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightGetterAdaptor.java new file mode 100644 index 000000000..15b06f6ef --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightGetterAdaptor.java @@ -0,0 +1,40 @@ +package com.seibel.lod.common.wrappers.worldGeneration.mimicObject; + +import com.seibel.lod.core.api.ModAccessorApi; +import com.seibel.lod.core.wrapperInterfaces.modAccessor.IStarlightAccessor; + +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LightChunkGetter; + +public class LightGetterAdaptor implements LightChunkGetter { + private final BlockGetter heightGetter; + public LightedWorldGenRegion genRegion = null; + final boolean shouldReturnNull; + + public LightGetterAdaptor(BlockGetter heightAccessor) { + this.heightGetter = heightAccessor; + shouldReturnNull = ModAccessorApi.get(IStarlightAccessor.class) != null; + } + + public void setRegion(LightedWorldGenRegion region) { + genRegion = region; + } + + @Override + public BlockGetter getChunkForLighting(int chunkX, int chunkZ) { + if (genRegion == null) + throw new IllegalStateException("World Gen region has not been set!"); + // May be null + return genRegion.getChunk(chunkX, chunkZ, ChunkStatus.EMPTY, false); + } + + @Override + public BlockGetter getLevel() { + return shouldReturnNull ? null : (genRegion != null ? genRegion : heightGetter); + } + public LevelHeightAccessor getLevelHeightAccessor() { + return heightGetter; + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightedWorldGenRegion.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightedWorldGenRegion.java new file mode 100644 index 000000000..509063a8f --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightedWorldGenRegion.java @@ -0,0 +1,227 @@ +package com.seibel.lod.common.wrappers.worldGeneration.mimicObject; + +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.EmptyChunkGenerator; +import com.seibel.lod.core.api.ClientApi; +import com.seibel.lod.core.enums.config.LightGenerationMode; + +import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.StructureFeatureManager; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.ImposterProtoChunk; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.feature.StructureFeature; +import net.minecraft.world.level.levelgen.structure.StructureStart; +import net.minecraft.world.level.lighting.LevelLightEngine; + +public class LightedWorldGenRegion extends WorldGenRegion { + public final WorldGenLevelLightEngine light; + public final LightGenerationMode lightMode; + public final EmptyChunkGenerator generator; + public final int writeRadius; + public final int size; + private final ChunkPos firstPos; + private final List cache; + private final StructureFeatureManager structFeat; + Long2ObjectOpenHashMap chunkMap = new Long2ObjectOpenHashMap(); + + public LightedWorldGenRegion(ServerLevel serverLevel, WorldGenLevelLightEngine lightEngine, + StructureFeatureManager structFeat, List list, ChunkStatus chunkStatus, int i, + LightGenerationMode lightMode, EmptyChunkGenerator generator) { + super(serverLevel, list, chunkStatus, i); + this.lightMode = lightMode; + this.firstPos = list.get(0).getPos(); + this.generator = generator; + this.structFeat = structFeat; + light = lightEngine; + writeRadius = i; + cache = list; + size = Mth.floor(Math.sqrt(list.size())); + } + + // Bypass BCLib mixin overrides. + @Override + public boolean ensureCanWrite(BlockPos blockPos) { + int i = SectionPos.blockToSectionCoord(blockPos.getX()); + int j = SectionPos.blockToSectionCoord(blockPos.getZ()); + ChunkPos chunkPos = this.getCenter(); + ChunkAccess center = this.getChunk(chunkPos.x, chunkPos.z); + int k = Math.abs(chunkPos.x - i); + int l = Math.abs(chunkPos.z - j); + if (k > this.writeRadius || l > this.writeRadius) { + return false; + } + if (center.isUpgrading()) { + LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration(); + if (blockPos.getY() < levelHeightAccessor.getMinBuildHeight() || blockPos.getY() >= levelHeightAccessor.getMaxBuildHeight()) { + return false; + } + } + return true; + } + + @Override + public List> startsForFeature(SectionPos sectionPos, + StructureFeature structureFeature) { + return structFeat.startsForFeature(sectionPos, structureFeature); + } + + // Skip updating the related tile entities + @Override + public boolean setBlock(BlockPos blockPos, BlockState blockState, int i, int j) { + ChunkAccess chunkAccess = this.getChunk(blockPos); + if (chunkAccess instanceof LevelChunk) + return true; + chunkAccess.setBlockState(blockPos, blockState, false); + // This is for post ticking for water on gen and stuff like that. Not enabled + // for now. + // if (blockState.hasPostProcess(this, blockPos)) + // this.getChunk(blockPos).markPosForPostprocessing(blockPos); + return true; + } + + // Skip Dropping the item on destroy + @Override + public boolean destroyBlock(BlockPos blockPos, boolean bl, @Nullable Entity entity, int i) { + BlockState blockState = this.getBlockState(blockPos); + if (blockState.isAir()) { + return false; + } + return this.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3, i); + } + + // Skip BlockEntity stuff. It aren't really needed + @Override + public BlockEntity getBlockEntity(BlockPos blockPos) { + return null; + } + + // Skip BlockEntity stuff. It aren't really needed + @Override + public boolean addFreshEntity(Entity entity) { + return true; + } + + // Allays have empty chunks even if it's outside the worldGenRegion + // @Override + // public boolean hasChunk(int i, int j) { + // return true; + // } + + // Override to ensure no other mod mixins cause skipping the overrided + // getChunk(...) + @Override + public ChunkAccess getChunk(int i, int j) { + return this.getChunk(i, j, ChunkStatus.EMPTY); + } + + // Override to ensure no other mod mixins cause skipping the overrided + // getChunk(...) + @Override + public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus) { + return this.getChunk(i, j, chunkStatus, true); + } + + // Use this instead of super.getChunk() to bypass C2ME concurrency checks + private ChunkAccess superGetChunk(int x, int z, ChunkStatus cs) { + int k = x - firstPos.x; + int l = z - firstPos.z; + return cache.get(k + l * size); + } + + // Use this instead of super.hasChunk() to bypass C2ME concurrency checks + private boolean superHasChunk(int x, int z) { + int k = x - firstPos.x; + int l = z - firstPos.z; + return l >= 0 && l < size && k >= 0 && k < size; + } + + // Allow creating empty chunks even if it's outside the worldGenRegion + @Override + @Nullable + public ChunkAccess getChunk(int i, int j, ChunkStatus chunkStatus, boolean bl) { + ChunkAccess chunk = getChunkAccess(i, j, chunkStatus, bl); + if (chunk instanceof LevelChunk) { + chunk = new ImposterProtoChunk((LevelChunk) chunk, true); + } + return chunk; + } + + private static ChunkStatus debugTriggeredForStatus = null; + + private ChunkAccess getChunkAccess(int i, int j, ChunkStatus chunkStatus, boolean bl) { + ChunkAccess chunk = superHasChunk(i, j) ? superGetChunk(i, j, ChunkStatus.EMPTY) : null; + if (chunk != null && chunk.getStatus().isOrAfter(chunkStatus)) { + return chunk; + } + if (!bl) + return null; + if (chunk == null) { + chunk = chunkMap.get(ChunkPos.asLong(i, j)); + if (chunk == null) { + chunk = generator.generate(i, j); + if (chunk == null) + throw new NullPointerException("The provided generator should not return null!"); + chunkMap.put(ChunkPos.asLong(i, j), chunk); + } + } + if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus) { + ClientApi.LOGGER.info("WorldGen requiring " + chunkStatus + + " outside expected range detected. Force passing EMPTY chunk and seeing if it works."); + debugTriggeredForStatus = chunkStatus; + } + return chunk; + } + + // Override force use of my own light engine + @Override + public LevelLightEngine getLightEngine() { + return light; + } + + // Override force use of my own light engine + @Override + public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { + if (lightMode != LightGenerationMode.FAST) { + return light.getLayerListener(lightLayer).getLightValue(blockPos); + } + if (lightLayer == LightLayer.BLOCK) + return 0; + BlockPos p = super.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, blockPos); + return (p.getY() <= blockPos.getY()) ? getMaxLightLevel() : 0; + } + + // Override force use of my own light engine + @Override + public int getRawBrightness(BlockPos blockPos, int i) { + if (lightMode != LightGenerationMode.FAST) { + return light.getRawBrightness(blockPos, i); + } + BlockPos p = super.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, blockPos); + return (p.getY() <= blockPos.getY()) ? getMaxLightLevel() : 0; + } + + // Override force use of my own light engine + @Override + public boolean canSeeSky(BlockPos blockPos) { + return (getBrightness(LightLayer.SKY, blockPos) >= getMaxLightLevel()); + } + +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenLevelLightEngine.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenLevelLightEngine.java new file mode 100644 index 000000000..a08564854 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenLevelLightEngine.java @@ -0,0 +1,176 @@ +package com.seibel.lod.common.wrappers.worldGeneration.mimicObject; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.LightLayer; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.DataLayer; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.lighting.BlockLightEngine; +import net.minecraft.world.level.lighting.LayerLightEventListener; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.lighting.SkyLightEngine; + +public class WorldGenLevelLightEngine extends LevelLightEngine { + public static final int MAX_SOURCE_LEVEL = 15; + public static final int LIGHT_SECTION_PADDING = 1; + protected final LevelHeightAccessor levelHeightAccessor; + @Nullable + public final BlockLightEngine blockEngine; + @Nullable + public final SkyLightEngine skyEngine; + + public WorldGenLevelLightEngine(LightGetterAdaptor genRegion) { + super(genRegion, false, false); + this.levelHeightAccessor = genRegion.getLevelHeightAccessor(); + this.blockEngine = new BlockLightEngine(genRegion); + this.skyEngine = new SkyLightEngine(genRegion); + } + + @Override + public void checkBlock(BlockPos blockPos) { + if (this.blockEngine != null) { + this.blockEngine.checkBlock(blockPos); + } + if (this.skyEngine != null) { + this.skyEngine.checkBlock(blockPos); + } + } + + @Override + public void onBlockEmissionIncrease(BlockPos blockPos, int i) { + if (this.blockEngine != null) { + this.blockEngine.onBlockEmissionIncrease(blockPos, i); + } + } + + @Override + public boolean hasLightWork() { + if (this.skyEngine != null && this.skyEngine.hasLightWork()) { + return true; + } + return this.blockEngine != null && this.blockEngine.hasLightWork(); + } + + @Override + public int runUpdates(int i, boolean bl, boolean bl2) { + if (this.blockEngine != null && this.skyEngine != null) { + int j = i / 2; + int k = this.blockEngine.runUpdates(j, bl, bl2); + int l = i - j + k; + int m = this.skyEngine.runUpdates(l, bl, bl2); + if (k == 0 && m > 0) { + return this.blockEngine.runUpdates(m, bl, bl2); + } + return m; + } + if (this.blockEngine != null) { + return this.blockEngine.runUpdates(i, bl, bl2); + } + if (this.skyEngine != null) { + return this.skyEngine.runUpdates(i, bl, bl2); + } + return i; + } + + @Override + public void updateSectionStatus(SectionPos sectionPos, boolean bl) { + if (this.blockEngine != null) { + this.blockEngine.updateSectionStatus(sectionPos, bl); + } + if (this.skyEngine != null) { + this.skyEngine.updateSectionStatus(sectionPos, bl); + } + } + + @Override + public void enableLightSources(ChunkPos chunkPos, boolean bl) { + if (this.blockEngine != null) { + this.blockEngine.enableLightSources(chunkPos, bl); + } + if (this.skyEngine != null) { + this.skyEngine.enableLightSources(chunkPos, bl); + } + } + + @Override + public LayerLightEventListener getLayerListener(LightLayer lightLayer) { + if (lightLayer == LightLayer.BLOCK) { + if (this.blockEngine == null) { + return LayerLightEventListener.DummyLightLayerEventListener.INSTANCE; + } + return this.blockEngine; + } + if (this.skyEngine == null) { + return LayerLightEventListener.DummyLightLayerEventListener.INSTANCE; + } + return this.skyEngine; + } + + @Override + public int getRawBrightness(BlockPos blockPos, int i) { + int j = this.skyEngine == null ? 0 : this.skyEngine.getLightValue(blockPos) - i; + int k = this.blockEngine == null ? 0 : this.blockEngine.getLightValue(blockPos); + return Math.max(k, j); + } + + public void lightChunk(ChunkAccess chunkAccess, boolean needLightBlockUpdate) { + ChunkPos chunkPos = chunkAccess.getPos(); + chunkAccess.setLightCorrect(false); + + LevelChunkSection[] levelChunkSections = chunkAccess.getSections(); + for (int i = 0; i < chunkAccess.getSectionsCount(); ++i) { + LevelChunkSection levelChunkSection = levelChunkSections[i]; + if (levelChunkSection.hasOnlyAir()) continue; + int j = this.levelHeightAccessor.getSectionYFromSectionIndex(i); + updateSectionStatus(SectionPos.of(chunkPos, j), false); + } + enableLightSources(chunkPos, true); + if (needLightBlockUpdate) { + chunkAccess.getLights().forEach(blockPos -> + onBlockEmissionIncrease(blockPos, chunkAccess.getLightEmission(blockPos))); + } + + chunkAccess.setLightCorrect(true); + } + + @Override + public String getDebugData(LightLayer lightLayer, SectionPos sectionPos) { + throw new UnsupportedOperationException("This should never be used!"); + } + @Override + public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer, boolean bl) { + if (lightLayer == LightLayer.BLOCK) { + if (this.blockEngine != null) { + this.blockEngine.queueSectionData(sectionPos.asLong(), dataLayer, bl); + } + } else if (this.skyEngine != null) { + this.skyEngine.queueSectionData(sectionPos.asLong(), dataLayer, bl); + } + } + @Override + public void retainData(ChunkPos chunkPos, boolean bl) { + if (this.blockEngine != null) { + this.blockEngine.retainData(chunkPos, bl); + } + if (this.skyEngine != null) { + this.skyEngine.retainData(chunkPos, bl); + } + } + @Override + public int getLightSectionCount() { + throw new UnsupportedOperationException("This should never be used!"); + } + @Override + public int getMinLightSection() { + throw new UnsupportedOperationException("This should never be used!"); + } + @Override + public int getMaxLightSection() { + throw new UnsupportedOperationException("This should never be used!"); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java new file mode 100644 index 000000000..dae4dfe95 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java @@ -0,0 +1,75 @@ +package com.seibel.lod.common.wrappers.worldGeneration.mimicObject; + +import java.util.List; +import java.util.stream.Stream; + +import com.google.common.collect.ImmutableList; + +import it.unimi.dsi.fastutil.longs.LongIterator; +import it.unimi.dsi.fastutil.longs.LongSet; +import net.minecraft.core.BlockPos; +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.StructureFeatureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.levelgen.feature.StructureFeature; +import net.minecraft.world.level.levelgen.structure.StructureCheck; +import net.minecraft.world.level.levelgen.structure.StructureStart; + +public class WorldGenStructFeatManager extends StructureFeatureManager { + WorldGenLevel genLevel; + WorldGenSettings worldGenSettings; + StructureCheck structureCheck; + public WorldGenStructFeatManager(LevelAccessor levelAccessor, WorldGenSettings worldGenSettings, + WorldGenLevel genLevel, StructureCheck structureCheck) { + super(levelAccessor, worldGenSettings, structureCheck); + this.genLevel = genLevel; + this.worldGenSettings = worldGenSettings; + } + + @Override + public WorldGenStructFeatManager forWorldGenRegion(WorldGenRegion worldGenRegion) { + if (worldGenRegion == genLevel) + return this; + return new WorldGenStructFeatManager(worldGenRegion, worldGenSettings, worldGenRegion, structureCheck); + } + + @Override + public boolean hasAnyStructureAt(BlockPos blockPos) { + SectionPos sectionPos = SectionPos.of(blockPos); + ChunkAccess chunk = genLevel.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES, + false); + if (chunk == null) return false; + return chunk.hasAnyStructureReferences(); + } + + @Override + @SuppressWarnings({ "rawtypes", "unchecked" }) + public List> startsForFeature(SectionPos sectionPos, + StructureFeature structureFeature) { + if (genLevel == null) + return List.of(); + ChunkAccess chunk = genLevel.getChunk(sectionPos.x(), sectionPos.z(), ChunkStatus.STRUCTURE_REFERENCES, + false); + if (chunk == null) + return List.of(); + LongSet longSet = chunk.getReferencesForFeature(structureFeature); + ImmutableList.Builder builder = ImmutableList.builder(); + LongIterator longIterator = longSet.iterator(); + while (longIterator.hasNext()) { + long l = (Long)longIterator.next(); + SectionPos sectPos = SectionPos.of(new ChunkPos(l), genLevel.getMinSection()); + ChunkAccess startChunk = genLevel.getChunk(sectPos.x(), sectPos.z(), ChunkStatus.STRUCTURE_STARTS, false); + if (startChunk == null) continue; + StructureStart structureStart = this.getStartForFeature(sectPos, structureFeature, startChunk); + if (structureStart == null || !structureStart.isValid()) continue; + builder.add(structureStart); + } + return builder.build(); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepBiomes.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepBiomes.java new file mode 100644 index 000000000..e674e0e31 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepBiomes.java @@ -0,0 +1,63 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.List; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; + +import net.minecraft.core.Registry; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.StructureFeatureManager; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkGenerator; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +import net.minecraft.world.level.levelgen.blending.Blender; + +public final class StepBiomes { + /** + * + */ + private final BatchGenerationEnvironment environment; + + /** + * @param batchGenerationEnvironment + */ + public StepBiomes(BatchGenerationEnvironment batchGenerationEnvironment) + { + environment = batchGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.BIOMES; + + private ChunkAccess createBiomes(ChunkGenerator generator, Registry registry, Blender blender, StructureFeatureManager structureFeatureManager, ChunkAccess chunkAccess) { + if (generator instanceof NoiseBasedChunkGenerator) { + ((NoiseBasedChunkGenerator) generator).doCreateBiomes(registry, blender, structureFeatureManager, chunkAccess); + return chunkAccess; + } else { + chunkAccess.fillBiomesFromNoise(generator.getBiomeSource()::getNoiseBiome, generator.climateSampler()); + return chunkAccess; + } + } + + public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, + List chunks) { + + ArrayList chunksToDo = new ArrayList(); + + for (ChunkAccess chunk : chunks) { + if (chunk.getStatus().isOrAfter(STATUS)) continue; + ((ProtoChunk) chunk).setStatus(STATUS); + chunksToDo.add(chunk); + } + + for (ChunkAccess chunk : chunksToDo) { + // System.out.println("StepBiomes: "+chunk.getPos()); + chunk = createBiomes(environment.params.generator, environment.params.biomes, Blender.of(worldGenRegion), + tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepFeatures.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepFeatures.java new file mode 100644 index 000000000..0a65788cd --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepFeatures.java @@ -0,0 +1,60 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.GridList; + +import net.minecraft.ReportedException; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.levelgen.blending.Blender; + +public final class StepFeatures { + /** + * + */ + private final BatchGenerationEnvironment environment; + + /** + * @param batchGenerationEnvironment + */ + public StepFeatures(BatchGenerationEnvironment batchGenerationEnvironment) + { + environment = batchGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.FEATURES; + + public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, + GridList chunks) { + ArrayList chunksToDo = new ArrayList(); + + for (ChunkAccess chunk : chunks) { + if (chunk.getStatus().isOrAfter(STATUS)) continue; + ((ProtoChunk) chunk).setStatus(STATUS); + chunksToDo.add(chunk); + } + + for (ChunkAccess chunk : chunksToDo) { + try { + environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, + tParams.structFeat.forWorldGenRegion(worldGenRegion)); + Blender.generateBorderTicks(worldGenRegion, chunk); + } catch (ReportedException e) { + e.printStackTrace(); + // FIXME: Features concurrent modification issue. Something about cocobeans just + // aren't happy + // For now just retry. + } + }/* + for (ChunkAccess chunk : chunks) { + Heightmap.primeHeightmaps(chunk, + EnumSet.of(Heightmap.Types.MOTION_BLOCKING, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, + Heightmap.Types.OCEAN_FLOOR, Heightmap.Types.WORLD_SURFACE)); + }*/ + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepLight.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepLight.java new file mode 100644 index 000000000..dddb2ed4a --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepLight.java @@ -0,0 +1,59 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.GridList; +import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.WorldGenLevelLightEngine; + +import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunk; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.lighting.LightEventListener; + +public final class StepLight { + /** + * + */ + private final BatchGenerationEnvironment environment; + + /** + * @param batchGenerationEnvironment + */ + public StepLight(BatchGenerationEnvironment batchGenerationEnvironment) + { + environment = batchGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.LIGHT; + + public void generateGroup(LightEventListener lightEngine, + GridList chunks) { + //ArrayList chunksToDo = new ArrayList(); + + for (ChunkAccess chunk : chunks) { + if (chunk.getStatus().isOrAfter(STATUS)) continue; + ((ProtoChunk) chunk).setStatus(STATUS); + } + + for (ChunkAccess chunk : chunks) { + boolean hasCorrectBlockLight = (chunk instanceof LevelChunk && chunk.isLightCorrect()); + try { + if (lightEngine == null) { + // Do nothing + } else if (lightEngine instanceof WorldGenLevelLightEngine) { + ((WorldGenLevelLightEngine)lightEngine).lightChunk(chunk, !hasCorrectBlockLight); + } else if (lightEngine instanceof ThreadedLevelLightEngine) { + ((ThreadedLevelLightEngine) lightEngine).lightChunk(chunk, !hasCorrectBlockLight).join(); + } else { + assert(false); + } + + } catch (Exception e) { + e.printStackTrace(); + } + chunk.setLightCorrect(true); + } + lightEngine.runUpdates(Integer.MAX_VALUE, true, true); + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepNoise.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepNoise.java new file mode 100644 index 000000000..d319e57fa --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepNoise.java @@ -0,0 +1,91 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import com.google.common.collect.Sets; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; + +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.util.Mth; +import net.minecraft.world.level.LevelHeightAccessor; +import net.minecraft.world.level.StructureFeatureManager; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.LevelChunkSection; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +import net.minecraft.world.level.levelgen.NoiseSettings; +import net.minecraft.world.level.levelgen.blending.Blender; + +public final class StepNoise { + /** + * + */ + private final BatchGenerationEnvironment environment; + + /** + * @param batchGenerationEnvironment + */ + public StepNoise(BatchGenerationEnvironment batchGenerationEnvironment) + { + environment = batchGenerationEnvironment; + } + + private static T joinSync(CompletableFuture f) { + if (!f.isDone()) throw new RuntimeException("The future is concurrent!"); + return f.join(); + } + public final ChunkStatus STATUS = ChunkStatus.NOISE; + + private ChunkAccess NoiseBased$fillFromNoise(NoiseBasedChunkGenerator generator, Blender blender, StructureFeatureManager structureFeatureManager, ChunkAccess chunkAccess) { + NoiseSettings noiseSettings = generator.settings.get().noiseSettings(); + LevelHeightAccessor levelHeightAccessor = chunkAccess.getHeightAccessorForGeneration(); + int i = Math.max(noiseSettings.minY(), levelHeightAccessor.getMinBuildHeight()); + int j = Math.min(noiseSettings.minY() + noiseSettings.height(), levelHeightAccessor.getMaxBuildHeight()); + int k = Mth.intFloorDiv(i, noiseSettings.getCellHeight()); + int l = Mth.intFloorDiv(j - i, noiseSettings.getCellHeight()); + if (l <= 0) { + return chunkAccess; + } + int m = chunkAccess.getSectionIndex(l * noiseSettings.getCellHeight() - 1 + i); + int n = chunkAccess.getSectionIndex(i); + HashSet set = Sets.newHashSet(); + for (int o = m; o >= n; --o) { + LevelChunkSection levelChunkSection = chunkAccess.getSection(o); + levelChunkSection.acquire(); + set.add(levelChunkSection); + } + chunkAccess = generator.doFill(blender, structureFeatureManager, chunkAccess, k, l); + for (LevelChunkSection levelChunkSection : set) { + levelChunkSection.release(); + }; + return chunkAccess; + } + + public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, + List chunks) { + + ArrayList chunksToDo = new ArrayList(); + + for (ChunkAccess chunk : chunks) { + if (chunk.getStatus().isOrAfter(STATUS)) continue; + ((ProtoChunk) chunk).setStatus(STATUS); + chunksToDo.add(chunk); + } + + for (ChunkAccess chunk : chunksToDo) { + // System.out.println("StepNoise: "+chunk.getPos()); + if (environment.params.generator instanceof NoiseBasedChunkGenerator) { + chunk = NoiseBased$fillFromNoise((NoiseBasedChunkGenerator)environment.params.generator,Blender.of(worldGenRegion), + tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk); + } else { + chunk = joinSync(environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), + tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk)); + } + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureReference.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureReference.java new file mode 100644 index 000000000..fda9c44f0 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureReference.java @@ -0,0 +1,92 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.List; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; + +import net.minecraft.CrashReport; +import net.minecraft.CrashReportCategory; +import net.minecraft.ReportedException; +import net.minecraft.core.Registry; +import net.minecraft.core.SectionPos; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.StructureFeatureManager; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.ProtoChunk; +import net.minecraft.world.level.levelgen.structure.StructureStart; + +public final class StepStructureReference { + /** + * + */ + private final BatchGenerationEnvironment environment; + + /** + * @param batchGenerationEnvironment + */ + public StepStructureReference(BatchGenerationEnvironment batchGenerationEnvironment) + { + environment = batchGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES; + + private void createReferences(WorldGenRegion worldGenLevel, StructureFeatureManager structureFeatureManager, + ChunkAccess chunkAccess) { + ChunkPos chunkPos = chunkAccess.getPos(); + int j = chunkPos.x; + int k = chunkPos.z; + int l = chunkPos.getMinBlockX(); + int m = chunkPos.getMinBlockZ(); + + SectionPos sectionPos = SectionPos.bottomOf(chunkAccess); + + for (int n = j - 8; n <= j + 8; n++) { + for (int o = k - 8; o <= k + 8; o++) { + if (!worldGenLevel.hasChunk(n, o)) + continue; + long p = ChunkPos.asLong(n, o); + for (StructureStart structureStart : worldGenLevel.getChunk(n, o).getAllStarts().values()) { + try { + if (structureStart.isValid() + && structureStart.getBoundingBox().intersects(l, m, l + 15, m + 15)) { + structureFeatureManager.addReferenceForFeature(sectionPos, structureStart.getFeature(), + p, chunkAccess); + } + } catch (Exception exception) { + CrashReport crashReport = CrashReport.forThrowable(exception, + "Generating structure reference"); + CrashReportCategory crashReportCategory = crashReport.addCategory("Structure"); + crashReportCategory.setDetail("Id", + () -> Registry.STRUCTURE_FEATURE.getKey(structureStart.getFeature()).toString()); + crashReportCategory.setDetail("Name", () -> structureStart.getFeature().getFeatureName()); + crashReportCategory.setDetail("Class", + () -> structureStart.getFeature().getClass().getCanonicalName()); + throw new ReportedException(crashReport); + } + } + } + } + } + + public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, + List chunks) { + + ArrayList chunksToDo = new ArrayList(); + + for (ChunkAccess chunk : chunks) { + if (chunk.getStatus().isOrAfter(STATUS)) continue; + ((ProtoChunk) chunk).setStatus(STATUS); + chunksToDo.add(chunk); + } + + for (ChunkAccess chunk : chunksToDo) { + // System.out.println("StepStructureReference: "+chunk.getPos()); + createReferences(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk); + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureStart.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureStart.java new file mode 100644 index 000000000..9a06f7a97 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureStart.java @@ -0,0 +1,67 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.List; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; + +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.ProtoChunk; + +public final class StepStructureStart { + /** + * + */ + private final BatchGenerationEnvironment environment; + + /** + * @param batchGenerationEnvironment + */ + public StepStructureStart(BatchGenerationEnvironment batchGenerationEnvironment) + { + environment = batchGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS; + + public static class StructStartCorruptedException extends RuntimeException { + private static final long serialVersionUID = -8987434342051563358L; + + public StructStartCorruptedException(ArrayIndexOutOfBoundsException e) { + super("StructStartCorruptedException"); + super.initCause(e); + fillInStackTrace(); + } + } + + public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, + List chunks) { + + ArrayList chunksToDo = new ArrayList(); + + for (ChunkAccess chunk : chunks) { + if (chunk.getStatus().isOrAfter(STATUS)) continue; + ((ProtoChunk) chunk).setStatus(STATUS); + chunksToDo.add(chunk); + } + + if (environment.params.worldGenSettings.generateFeatures()) { + for (ChunkAccess chunk : chunksToDo) { + // System.out.println("StepStructureStart: "+chunk.getPos()); + environment.params.generator.createStructures(environment.params.registry, tParams.structFeat, chunk, environment.params.structures, + environment.params.worldSeed); + try { + tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts()); + } catch (ArrayIndexOutOfBoundsException e) { + // There's a rare issue with StructStart where it throws ArrayIndexOutOfBounds + // This means the structFeat is corrupted (For some reason) and I need to reset it. + // TODO: Figure out in the future why this happens even though I am using new structFeat + throw new StepStructureStart.StructStartCorruptedException(e); + } + } + } + } +} \ No newline at end of file diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepSurface.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepSurface.java new file mode 100644 index 000000000..aee2e2eb0 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepSurface.java @@ -0,0 +1,46 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.List; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; + +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.level.chunk.ChunkAccess; +import net.minecraft.world.level.chunk.ChunkStatus; +import net.minecraft.world.level.chunk.ProtoChunk; + +public final class StepSurface { + /** + * + */ + private final BatchGenerationEnvironment environment; + + /** + * @param batchGenerationEnvironment + */ + public StepSurface(BatchGenerationEnvironment batchGenerationEnvironment) + { + environment = batchGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.SURFACE; + + public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion, + List chunks) { + ArrayList chunksToDo = new ArrayList(); + + for (ChunkAccess chunk : chunks) { + if (chunk.getStatus().isOrAfter(STATUS)) continue; + ((ProtoChunk) chunk).setStatus(STATUS); + chunksToDo.add(chunk); + } + + for (ChunkAccess chunk : chunksToDo) { + // System.out.println("StepSurface: "+chunk.getPos()); + environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), + chunk); + } + } +} \ No newline at end of file diff --git a/core b/core index ca81ed1ef..068622895 160000 --- a/core +++ b/core @@ -1 +1 @@ -Subproject commit ca81ed1efef9b117ff848124b06bca8c27d182b4 +Subproject commit 068622895fab70dd7b7eef17c51c0ea753b0889b