From c29420b695cc08d5e0bf8068e8183139a7a5a790 Mon Sep 17 00:00:00 2001 From: tom lee Date: Thu, 3 Feb 2022 16:47:22 +0800 Subject: [PATCH] BatchGen: Completely refactor the ExperGen into now call BatchGen --- .../lod/common/wrappers/VersionConstants.java | 6 + .../lod/common/wrappers/WrapperFactory.java | 9 +- .../BatchGenerationEnvironment.java | 472 ++++++ .../ExperimentalGenerator.java | 320 ---- .../worldGeneration/GenerationEvent.java | 88 ++ .../worldGeneration/GlobalParameters.java | 48 + .../wrappers/worldGeneration/Rolling.java | 29 + .../StructStartCorruptedException.java | 11 + .../worldGeneration/ThreadedParameters.java | 38 + .../worldGeneration/WorldGenerationStep.java | 1291 ----------------- .../WorldGeneratorWrapper.java | 214 ++- .../{ => mimicObject}/ChunkLoader.java | 44 +- .../mimicObject/LightGetterAdaptor.java | 36 + .../mimicObject/LightedWorldGenRegion.java | 207 +++ .../mimicObject/WorldGenLevelLightEngine.java | 164 +++ .../WorldGenStructFeatManager.java | 52 + .../worldGeneration/step/StepBiomes.java | 45 + .../worldGeneration/step/StepFeatures.java | 78 + .../worldGeneration/step/StepLight.java | 58 + .../worldGeneration/step/StepNoise.java | 45 + .../step/StepStructureReference.java | 78 + .../step/StepStructureStart.java | 59 + .../worldGeneration/step/StepSurface.java | 44 + core | 2 +- 24 files changed, 1686 insertions(+), 1752 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/StructStartCorruptedException.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 rename common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/{ => mimicObject}/ChunkLoader.java (89%) 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 46442a340..2cd381534 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 @@ -46,4 +46,10 @@ public class VersionConstants implements IVersionConstants { 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 9940b8292..2e834551e 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; /** @@ -83,7 +83,8 @@ public class WrapperFactory implements IWrapperFactory { } @Override - public AbstractExperimentalWorldGeneratorWrapper createExperimentalWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { - return new ExperimentalGenerator(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..e6ecb93b5 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java @@ -0,0 +1,472 @@ +/* + * 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.GridList; +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.Iterator; +import java.util.LinkedList; +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.ChunkLoader; +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.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 = 10; + private int dataCount = 0; + 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) { + dataCount++; + 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() { + if (dataCount < SIZE) + return "Pref Calculator collecting samples..."; + 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; + + // =================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(); + 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); + static private 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); + + @Override + public void resizeThreadPool(int newThreadCount) { + executors = Executors.newFixedThreadPool(newThreadCount, + new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY)); + } + + @Override + 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; + } + + @Override + 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); + } + + @SuppressWarnings("resource") + private 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); + } else { + return ChunkLoader.read(level, lightEngine, chunkPos, chunkData); + } + + } + + 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); + 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); + e.tParam.makeStructFeat(region, params.worldGenSettings); + referencedChunks = chunks.subGrid(e.range); + referencedChunks = generateDirect(e, referencedChunks, e.target, region); + + } catch (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); + boolean isFull = target.getStatus() == ChunkStatus.FULL || target instanceof LevelChunk; + 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 (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); + } + } + + 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) { + } + } +} 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..dd38ed541 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GenerationEvent.java @@ -0,0 +1,88 @@ +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..51473016a --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GlobalParameters.java @@ -0,0 +1,48 @@ +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.chunk.ChunkGenerator; +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 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 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(); + 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..ed0922e4c --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/Rolling.java @@ -0,0 +1,29 @@ +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/StructStartCorruptedException.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/StructStartCorruptedException.java new file mode 100644 index 000000000..9af179cc4 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/StructStartCorruptedException.java @@ -0,0 +1,11 @@ +package com.seibel.lod.common.wrappers.worldGeneration; + +public class StructStartCorruptedException extends RuntimeException { + private static final long serialVersionUID = -8987434342051563358L; + + public StructStartCorruptedException(ArrayIndexOutOfBoundsException e) { + super("StructStartCorruptedException"); + super.initCause(e); + fillInStackTrace(); + } +} \ 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..ed7e0d189 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ThreadedParameters.java @@ -0,0 +1,38 @@ +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; + +public final class ThreadedParameters { + private static final ThreadLocal localParam = new ThreadLocal(); + final ServerLevel level; + public WorldGenStructFeatManager structFeat; + 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; + structFeat = new WorldGenStructFeatManager(level, param.worldGenSettings, null); + } + + public void makeStructFeat(WorldGenLevel genLevel, WorldGenSettings worldGenSettings) { + structFeat = new WorldGenStructFeatManager(level, worldGenSettings, genLevel); + } +} \ 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 5d3247e2e..000000000 --- a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGenerationStep.java +++ /dev/null @@ -1,1291 +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.GridList; -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.Iterator; -import java.util.LinkedList; -import java.util.List; -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 java.util.stream.Stream; - -import org.jetbrains.annotations.Nullable; - -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.LevelAccessor; -import net.minecraft.world.level.LightLayer; -import net.minecraft.world.level.StructureFeatureManager; -import net.minecraft.world.level.WorldGenLevel; -import net.minecraft.world.level.biome.Biome; -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.ChunkGenerator; -import net.minecraft.world.level.chunk.ChunkStatus; -import net.minecraft.world.level.chunk.DataLayer; -import net.minecraft.world.level.chunk.ImposterProtoChunk; -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.server.level.ThreadedLevelLightEngine; -import net.minecraft.CrashReport; -import net.minecraft.CrashReportCategory; -import net.minecraft.ReportedException; -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.WorldGenSettings; -import net.minecraft.world.level.levelgen.WorldgenRandom; -import net.minecraft.world.level.levelgen.feature.StructureFeature; -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.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 = 10; - private int dataCount = 0; - 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) { - dataCount++; - 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() { - if (dataCount biomes; - final RegistryAccess registry; - final long worldSeed; - 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(); - fixerUpper = server.getFixerUpper(); - } - } - - public static final class ThreadedParameters { - private static final ThreadLocal localParam = new ThreadLocal(); - final ServerLevel level; - public WorldGenStructFeatManager structFeat; - 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; - structFeat = new WorldGenStructFeatManager(level, param.worldGenSettings, null); - } - - public void makeStructFeat(WorldGenLevel genLevel, WorldGenSettings worldGenSettings) { - structFeat = new WorldGenStructFeatManager(level, worldGenSettings, genLevel); - } - } - - //======================= 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); - ClientApi.LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN======================="); - 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 + ")"; - } - } - - //=================Generation Step=================== - - 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 static final LodThreadFactory threadFactory = new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY); - - //public final ExecutorService executors = Executors - // .newCachedThreadPool(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 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); - } 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); - 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); - e.tParam.makeStructFeat(region, params.worldGenSettings); - referencedChunks = chunks.subGrid(e.range); - referencedChunks = generateDirect(e, referencedChunks, e.target, region); - - } catch (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); - boolean isFull = target.getStatus() == ChunkStatus.FULL || target instanceof LevelChunk; - 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 (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 static class StructStartCorruptedException extends RuntimeException { - private static final long serialVersionUID = -8987434342051563358L; - - public StructStartCorruptedException(ArrayIndexOutOfBoundsException e) { - super("StructStartCorruptedException"); - super.initCause(e); - fillInStackTrace(); - } - } - - public final class StepStructureStart { - public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS; - - - 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 { - params.generator.createStructures(params.registry, tParams.structFeat, chunk, params.structures, params.worldSeed); - //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.of(chunkAccess.getPos(), 0); - - 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; - - 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()); - params.generator.createBiomes(params.biomes, chunk); - } - } - } - - public final class StepNoise { - public final ChunkStatus STATUS = ChunkStatus.NOISE; - - 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()); - params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, 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.buildSurfaceAndBedrock(worldGenRegion, chunk); - } - } - } - - public final class StepFeatures { - public final ChunkStatus STATUS = ChunkStatus.FEATURES; - - public void applyBiomeDecoration(ChunkGenerator generator, WorldGenRegion worldGenRegion, - StructureFeatureManager structureFeatureManager, ChunkAccess chunk) { - int i = chunk.getPos().x; - int j = chunk.getPos().z; - int k = i * 16; - int l = j * 16; - BlockPos blockPos = new BlockPos(k, 0, l); - Biome biome = generator.biomeSource.getNoiseBiome((i << 2) + 2, 2, (j << 2) + 2); - WorldgenRandom worldgenRandom = new WorldgenRandom(); - long m = worldgenRandom.setDecorationSeed(worldGenRegion.getSeed(), k, l); - try { - synchronized(generator) { - biome.generate(structureFeatureManager, generator, worldGenRegion, m, worldgenRandom, blockPos); - } - } catch (Exception exception) { - CrashReport crashReport = CrashReport.forThrowable(exception, "Biome decoration"); - crashReport.addCategory("Generation") - - .setDetail("CenterX", Integer.valueOf(i)).setDetail("CenterZ", Integer.valueOf(j)) - .setDetail("Seed", Long.valueOf(m)).setDetail("Biome", biome); - throw new ReportedException(crashReport); - } - } - - 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 { - applyBiomeDecoration(params.generator, worldGenRegion, tParams.structFeat, chunk); - } catch (ReportedException e) { - e.printStackTrace(); - } - } - } - } - - public final class StepLight { - public final ChunkStatus STATUS = ChunkStatus.LIGHT; - - public void generateGroup(LevelLightEngine 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 WorldGenStructFeatManager extends StructureFeatureManager { - WorldGenLevel genLevel; - WorldGenSettings worldGenSettings; - public WorldGenStructFeatManager(LevelAccessor levelAccessor, WorldGenSettings worldGenSettings, WorldGenLevel genLevel) { - super(levelAccessor, worldGenSettings); - this.genLevel = genLevel; - this.worldGenSettings = worldGenSettings; - } - - @Override - public WorldGenStructFeatManager forWorldGenRegion(WorldGenRegion worldGenRegion) { - if (worldGenRegion == genLevel) return this; - return new WorldGenStructFeatManager(worldGenRegion, worldGenSettings, worldGenRegion); - } - - @Override - public Stream> startsForFeature(SectionPos sectionPos2, StructureFeature structureFeature) { - if (genLevel==null) return Stream.empty(); - ChunkAccess chunk = genLevel.getChunk(sectionPos2.x(), sectionPos2.z(), ChunkStatus.STRUCTURE_REFERENCES, false); - if (chunk == null) return Stream.empty(); - return chunk.getReferencesForFeature(structureFeature) - .stream() - .map(pos -> { - SectionPos sectPos = SectionPos.of(ChunkPos.getX(pos), 0, ChunkPos.getZ(pos)); - ChunkAccess startChunk = genLevel.getChunk(sectPos.x(), sectPos.z(), ChunkStatus.STRUCTURE_STARTS, false); - if (startChunk == null) return null; - return this.getStartForFeature(sectPos, structureFeature, startChunk); - }).filter(structureStart -> structureStart != null && structureStart.isValid()); - } - } - - 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; - 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); - 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())); - } - - @Override - public Stream> 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=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 static class WorldGenLevelLightEngine extends LevelLightEngine { - public static final int MAX_SOURCE_LEVEL = 15; - public static final int LIGHT_SECTION_PADDING = 1; - @Nullable - public final BlockLightEngine blockEngine; - @Nullable - public final SkyLightEngine skyEngine; - - public WorldGenLevelLightEngine(LightGetterAdaptor genRegion) { - super(genRegion, false, false); - 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 < 16; i++) { - LevelChunkSection levelChunkSection = levelChunkSections[i]; - if (!LevelChunkSection.isEmpty(levelChunkSection)) { - updateSectionStatus(SectionPos.of(chunkPos, i), 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); - } - } - } - - - - - -} 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 b5b471d78..f27cb9d40 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 @@ -24,135 +24,121 @@ import net.minecraft.world.level.chunk.*; * @author James Seibel * @version 11-13-2021 */ -public class WorldGeneratorWrapper extends AbstractWorldGeneratorWrapper -{ - public final ServerLevel serverWorld; - public final LodDimension lodDim; - public final LodBuilder lodBuilder; +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); + public WorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { + super(newLodBuilder, newLodDimension, worldWrapper); - lodBuilder = newLodBuilder; - lodDim = newLodDimension; - serverWorld = ((WorldWrapper) worldWrapper).getServerWorld(); - } + 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 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); + } - /** takes about 10 - 20 ms */ - @Override - public void generateSurface(AbstractChunkPosWrapper pos) - { - generate(pos.getX(), pos.getZ(), DistanceGenerationMode.SURFACE); - } + /** + * 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); + } - - /** - * 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) { + private void generate(int chunkX, int chunkZ, DistanceGenerationMode generationMode) { // long t = System.nanoTime(); - ChunkStatus targetStatus; - switch (generationMode) { - case NONE: - return; - case BIOME_ONLY: - targetStatus = ChunkStatus.BIOMES; - break; - case BIOME_ONLY_SIMULATE_HEIGHT: - targetStatus = ChunkStatus.NOISE; - break; - case SURFACE: - targetStatus = ChunkStatus.SURFACE; - break; - case FEATURES: - targetStatus = ChunkStatus.FEATURES; - break; - case FULL: - targetStatus = ChunkStatus.FULL; - break; - default: - return; - } + ChunkStatus targetStatus; + switch (generationMode) { + case NONE: + return; + case BIOME_ONLY: + targetStatus = ChunkStatus.BIOMES; + break; + case BIOME_ONLY_SIMULATE_HEIGHT: + targetStatus = ChunkStatus.NOISE; + break; + case SURFACE: + targetStatus = ChunkStatus.SURFACE; + break; + case FEATURES: + targetStatus = ChunkStatus.FEATURES; + break; + case FULL: + targetStatus = ChunkStatus.FULL; + break; + default: + return; + } - // The bool=true means that we wants to generate chunk, and that the returned ChunkAccess must not be null + // 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"); - lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(ca, serverWorld), new LodBuilderConfig(generationMode), false); + 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"); + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(ca, serverWorld), + new LodBuilderConfig(generationMode), false); - // long duration = System.nanoTime()-t; + // long duration = System.nanoTime()-t; - // Debug print the duration - // System.out.println("LodChunkGenFull["+chunkX+","+chunkZ+"]: "+(double)(duration)/1000.); - } + // Debug print the duration + // System.out.println("LodChunkGenFull["+chunkX+","+chunkZ+"]: + // "+(double)(duration)/1000.); + } - - - - - - - /* TODO: Ask leetom to update chart - * performance/generation tests related to + /* + * TODO: Ask leetom to update 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) */ } 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/mimicObject/ChunkLoader.java similarity index 89% rename from common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ChunkLoader.java rename to common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/ChunkLoader.java index b19278c5e..ffc69f557 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/mimicObject/ChunkLoader.java @@ -1,5 +1,5 @@ -package com.seibel.lod.common.wrappers.worldGeneration; +package com.seibel.lod.common.wrappers.worldGeneration.mimicObject; import com.seibel.lod.core.api.ClientApi; @@ -28,8 +28,6 @@ import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluids; - - import org.apache.logging.log4j.Logger; public class ChunkLoader { @@ -75,7 +73,7 @@ public class ChunkLoader { } Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter()); } - + private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData) { ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9); for (int n = 0; n < tagPostProcessings.size(); ++n) { @@ -87,17 +85,17 @@ public class ChunkLoader { } public static ChunkStatus.ChunkType readChunkType(CompoundTag tagLevel) { - ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status")); - if (chunkStatus != null) { - return chunkStatus.getChunkType(); - } - return ChunkStatus.ChunkType.PROTOCHUNK; + ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status")); + if (chunkStatus != null) { + return chunkStatus.getChunkType(); + } + return ChunkStatus.ChunkType.PROTOCHUNK; } - public static LevelChunk read(WorldGenLevel level, LevelLightEngine lightEngine, - ChunkPos chunkPos, CompoundTag chunkData) { + public static LevelChunk read(WorldGenLevel level, LevelLightEngine lightEngine, ChunkPos chunkPos, + CompoundTag chunkData) { CompoundTag tagLevel = chunkData.getCompound("Level"); - + ChunkStatus.ChunkType chunkType = readChunkType(tagLevel); if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK) return null; @@ -109,41 +107,43 @@ public class ChunkLoader { return null; } - - //====================== Read params for making the LevelChunk ============================ + // ====================== Read params for making the LevelChunk + // ============================ ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer( level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), chunkPos, level.getLevel().getChunkSource().getGenerator().getBiomeSource(), tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null); - + UpgradeData upgradeData = tagLevel.contains("UpgradeData", 10) ? new UpgradeData(tagLevel.getCompound("UpgradeData")) : UpgradeData.EMPTY; - + TickList blockTicks = tagLevel.contains("TileTicks", 9) ? ChunkTickList.create(tagLevel.getList("TileTicks", 10), Registry.BLOCK::getKey, Registry.BLOCK::get) : new ProtoTickList(block -> (block == null || block.defaultBlockState().isAir()), chunkPos, tagLevel.getList("ToBeTicked", 9)); - + TickList liquidTicks = tagLevel.contains("LiquidTicks", 9) ? ChunkTickList.create(tagLevel.getList("LiquidTicks", 10), Registry.FLUID::getKey, Registry.FLUID::get) : new ProtoTickList(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos, tagLevel.getList("LiquidsToBeTicked", 9)); - + long inhabitedTime = tagLevel.getLong("InhabitedTime"); - + LevelChunkSection[] levelChunkSections = readSections(level, lightEngine, chunkPos, tagLevel); - //======================== Make the chunk =========================================== + // ======================== Make the chunk + // =========================================== LevelChunk chunk = new LevelChunk(level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks, liquidTicks, inhabitedTime, levelChunkSections, null); - //========================== Post setup some chunk data ============================== + // ========================== Post setup some chunk data + // ============================== chunk.setLightCorrect(tagLevel.getBoolean("isLightOn")); readHeightmaps(chunk, tagLevel); readPostPocessings(chunk, tagLevel); - //ClientApi.LOGGER.info("Loaded chunk @ "+chunk.getPos()); + // ClientApi.LOGGER.info("Loaded chunk @ "+chunk.getPos()); return chunk; } } 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..6451ffbc8 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightGetterAdaptor.java @@ -0,0 +1,36 @@ +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.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); + } +} \ 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..089f71e64 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightedWorldGenRegion.java @@ -0,0 +1,207 @@ +package com.seibel.lod.common.wrappers.worldGeneration.mimicObject; + +import java.util.List; +import java.util.stream.Stream; + +import org.jetbrains.annotations.Nullable; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +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.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); + 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())); + } + + @Override + public Stream> 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); + } + 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..488f5788a --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenLevelLightEngine.java @@ -0,0 +1,164 @@ +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.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; + @Nullable + public final BlockLightEngine blockEngine; + @Nullable + public final SkyLightEngine skyEngine; + + public WorldGenLevelLightEngine(LightGetterAdaptor genRegion) { + super(genRegion, false, false); + 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 < 16; i++) { + LevelChunkSection levelChunkSection = levelChunkSections[i]; + if (!LevelChunkSection.isEmpty(levelChunkSection)) { + updateSectionStatus(SectionPos.of(chunkPos, i), 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); + } + } +} \ 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..25355b57f --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java @@ -0,0 +1,52 @@ +package com.seibel.lod.common.wrappers.worldGeneration.mimicObject; + +import java.util.stream.Stream; + +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.StructureStart; + +public class WorldGenStructFeatManager extends StructureFeatureManager { + WorldGenLevel genLevel; + WorldGenSettings worldGenSettings; + + public WorldGenStructFeatManager(LevelAccessor levelAccessor, WorldGenSettings worldGenSettings, + WorldGenLevel genLevel) { + super(levelAccessor, worldGenSettings); + this.genLevel = genLevel; + this.worldGenSettings = worldGenSettings; + } + + @Override + public WorldGenStructFeatManager forWorldGenRegion(WorldGenRegion worldGenRegion) { + if (worldGenRegion == genLevel) + return this; + return new WorldGenStructFeatManager(worldGenRegion, worldGenSettings, worldGenRegion); + } + + @Override + public Stream> startsForFeature(SectionPos sectionPos2, + StructureFeature structureFeature) { + if (genLevel == null) + return Stream.empty(); + ChunkAccess chunk = genLevel.getChunk(sectionPos2.x(), sectionPos2.z(), ChunkStatus.STRUCTURE_REFERENCES, + false); + if (chunk == null) + return Stream.empty(); + return chunk.getReferencesForFeature(structureFeature).stream().map(pos -> { + SectionPos sectPos = SectionPos.of(ChunkPos.getX(pos), 0, ChunkPos.getZ(pos)); + ChunkAccess startChunk = genLevel.getChunk(sectPos.x(), sectPos.z(), ChunkStatus.STRUCTURE_STARTS, false); + if (startChunk == null) + return null; + return this.getStartForFeature(sectPos, structureFeature, startChunk); + }).filter(structureStart -> structureStart != null && structureStart.isValid()); + } +} \ 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..df6cfdf47 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepBiomes.java @@ -0,0 +1,45 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.List; + +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; + +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 StepBiomes { + /** + * + */ + private final BatchGenerationEnvironment envionment; + + /** + * @param worldGenerationEnvironment + */ + public StepBiomes(BatchGenerationEnvironment worldGenerationEnvironment) { + envionment = worldGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.BIOMES; + + 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()); + envionment.params.generator.createBiomes(envionment.params.biomes, 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..24f571d7d --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepFeatures.java @@ -0,0 +1,78 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; + +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.lod.core.util.GridList; + +import net.minecraft.CrashReport; +import net.minecraft.ReportedException; +import net.minecraft.core.BlockPos; +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.WorldgenRandom; + +public final class StepFeatures { + /** + * + */ + private final BatchGenerationEnvironment envionment; + + /** + * @param worldGenerationEnvironment + */ + public StepFeatures(BatchGenerationEnvironment worldGenerationEnvironment) { + envionment = worldGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.FEATURES; + + public void applyBiomeDecoration(ChunkGenerator generator, WorldGenRegion worldGenRegion, + StructureFeatureManager structureFeatureManager, ChunkAccess chunk) { + int i = chunk.getPos().x; + int j = chunk.getPos().z; + int k = i * 16; + int l = j * 16; + BlockPos blockPos = new BlockPos(k, 0, l); + Biome biome = generator.biomeSource.getNoiseBiome((i << 2) + 2, 2, (j << 2) + 2); + WorldgenRandom worldgenRandom = new WorldgenRandom(); + long m = worldgenRandom.setDecorationSeed(worldGenRegion.getSeed(), k, l); + try { + synchronized (generator) { + biome.generate(structureFeatureManager, generator, worldGenRegion, m, worldgenRandom, blockPos); + } + } catch (Exception exception) { + CrashReport crashReport = CrashReport.forThrowable(exception, "Biome decoration"); + crashReport.addCategory("Generation") + + .setDetail("CenterX", Integer.valueOf(i)).setDetail("CenterZ", Integer.valueOf(j)) + .setDetail("Seed", Long.valueOf(m)).setDetail("Biome", biome); + throw new ReportedException(crashReport); + } + } + + 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 { + applyBiomeDecoration(envionment.params.generator, worldGenRegion, tParams.structFeat, chunk); + } catch (ReportedException e) { + e.printStackTrace(); + } + } + } +} \ 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..39fbc057d --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepLight.java @@ -0,0 +1,58 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; +import com.seibel.lod.common.wrappers.worldGeneration.mimicObject.WorldGenLevelLightEngine; +import com.seibel.lod.core.util.GridList; + +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.LevelLightEngine; + +public final class StepLight { + /** + * + */ + private final BatchGenerationEnvironment envionment; + + /** + * @param worldGenerationEnvironment + */ + public StepLight(BatchGenerationEnvironment worldGenerationEnvironment) { + envionment = worldGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.LIGHT; + + public void generateGroup(LevelLightEngine 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..ff4282649 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepNoise.java @@ -0,0 +1,45 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.List; + +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; + +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 StepNoise { + /** + * + */ + private final BatchGenerationEnvironment envionment; + + /** + * @param worldGenerationEnvironment + */ + public StepNoise(BatchGenerationEnvironment worldGenerationEnvironment) { + envionment = worldGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.NOISE; + + 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()); + envionment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, 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..6c7696e3a --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureReference.java @@ -0,0 +1,78 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.List; + +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; + +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 { + 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.of(chunkAccess.getPos(), 0); + + 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..ab6e7c071 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureStart.java @@ -0,0 +1,59 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.List; + +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; + +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 envionment; + + /** + * @param worldGenerationEnvironment + */ + public StepStructureStart(BatchGenerationEnvironment worldGenerationEnvironment) { + envionment = worldGenerationEnvironment; + } + + public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS; + + 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 (envionment.params.worldGenSettings.generateFeatures()) { + for (ChunkAccess chunk : chunksToDo) { + // System.out.println("StepStructureStart: "+chunk.getPos()); + envionment.params.generator.createStructures(envionment.params.registry, tParams.structFeat, chunk, + envionment.params.structures, envionment.params.worldSeed); + // try { + envionment.params.generator.createStructures(envionment.params.registry, tParams.structFeat, chunk, + envionment.params.structures, envionment.params.worldSeed); + // 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); } + */ + } + } + } +} \ 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..2932ad4a0 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepSurface.java @@ -0,0 +1,44 @@ +package com.seibel.lod.common.wrappers.worldGeneration.step; + +import java.util.ArrayList; +import java.util.List; + +import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters; +import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment; + +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 envionment; + + /** + * @param worldGenerationEnvironment + */ + public StepSurface(BatchGenerationEnvironment worldGenerationEnvironment) { + envionment = worldGenerationEnvironment; + } + + 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()); + envionment.params.generator.buildSurfaceAndBedrock(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