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 8e142f0c3..ba8050414 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,9 +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.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.BatchGenerationEnvironment;
import com.seibel.lod.common.wrappers.worldGeneration.WorldGeneratorWrapper;
/**
@@ -79,4 +81,10 @@ public class WrapperFactory implements IWrapperFactory {
public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) {
return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper);
}
+
+ @Override
+ 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..0a44aaeb2
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/BatchGenerationEnvironment.java
@@ -0,0 +1,552 @@
+/*
+ * 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 = false;
+ public static final boolean ENABLE_EVENT_LOGGING = false;
+ public static final boolean ENABLE_LOAD_EVENT_LOGGING = false;
+ //TODO: Make actual proper support for StarLight
+
+ public static class PrefEvent
+ {
+ long beginNano = 0;
+ long emptyNano = 0;
+ long structStartNano = 0;
+ long structRefNano = 0;
+ long biomeNano = 0;
+ long noiseNano = 0;
+ long surfaceNano = 0;
+ long carverNano = 0;
+ long featureNano = 0;
+ long lightNano = 0;
+ long endNano = 0;
+
+ @Override
+ public String toString()
+ {
+ return "beginNano: " + beginNano + ",\n" +
+ "emptyNano: " + emptyNano + ",\n" +
+ "structStartNano: " + structStartNano + ",\n" +
+ "structRefNano: " + structRefNano + ",\n" +
+ "biomeNano: " + biomeNano + ",\n" +
+ "noiseNano: " + noiseNano + ",\n" +
+ "surfaceNano: " + surfaceNano + ",\n" +
+ "carverNano: " + carverNano + ",\n" +
+ "featureNano: " + featureNano + ",\n" +
+ "lightNano: " + lightNano + ",\n" +
+ "endNano: " + endNano + "\n";
+ }
+ }
+
+ public static class PerfCalculator
+ {
+ public static final int SIZE = 50;
+ Rolling totalTime = new Rolling(SIZE);
+ Rolling emptyTime = new Rolling(SIZE);
+ Rolling structStartTime = new Rolling(SIZE);
+ Rolling structRefTime = new Rolling(SIZE);
+ Rolling biomeTime = new Rolling(SIZE);
+ Rolling noiseTime = new Rolling(SIZE);
+ Rolling surfaceTime = new Rolling(SIZE);
+ Rolling carverTime = new Rolling(SIZE);
+ Rolling featureTime = new Rolling(SIZE);
+ Rolling lightTime = new Rolling(SIZE);
+ Rolling lodTime = new Rolling(SIZE);
+
+ public void recordEvent(PrefEvent e)
+ {
+ long preTime = e.beginNano;
+ totalTime.add(e.endNano - preTime);
+ if (e.emptyNano != 0)
+ {
+ emptyTime.add(e.emptyNano - preTime);
+ preTime = e.emptyNano;
+ }
+ if (e.structStartNano != 0)
+ {
+ structStartTime.add(e.structStartNano - preTime);
+ preTime = e.structStartNano;
+ }
+ if (e.structRefNano != 0)
+ {
+ structRefTime.add(e.structRefNano - preTime);
+ preTime = e.structRefNano;
+ }
+ if (e.biomeNano != 0)
+ {
+ biomeTime.add(e.biomeNano - preTime);
+ preTime = e.biomeNano;
+ }
+ if (e.noiseNano != 0)
+ {
+ noiseTime.add(e.noiseNano - preTime);
+ preTime = e.noiseNano;
+ }
+ if (e.surfaceNano != 0)
+ {
+ surfaceTime.add(e.surfaceNano - preTime);
+ preTime = e.surfaceNano;
+ }
+ if (e.carverNano != 0)
+ {
+ carverTime.add(e.carverNano - preTime);
+ preTime = e.carverNano;
+ }
+ if (e.featureNano != 0)
+ {
+ featureTime.add(e.featureNano - preTime);
+ preTime = e.featureNano;
+ }
+ if (e.lightNano != 0)
+ {
+ lightTime.add(e.lightNano - preTime);
+ preTime = e.lightNano;
+ }
+ if (e.endNano != 0)
+ {
+ lodTime.add(e.endNano - preTime);
+ preTime = e.endNano;
+ }
+ }
+
+ public String toString()
+ {
+ return "Total: " + Duration.ofNanos((long) totalTime.getAverage()) + ", Empty/LoadChunk: "
+ + Duration.ofNanos((long) emptyTime.getAverage()) + ", StructStart: "
+ + Duration.ofNanos((long) structStartTime.getAverage()) + ", StructRef: "
+ + Duration.ofNanos((long) structRefTime.getAverage()) + ", Biome: "
+ + Duration.ofNanos((long) biomeTime.getAverage()) + ", Noise: "
+ + Duration.ofNanos((long) noiseTime.getAverage()) + ", Surface: "
+ + Duration.ofNanos((long) surfaceTime.getAverage()) + ", Carver: "
+ + Duration.ofNanos((long) carverTime.getAverage()) + ", Feature: "
+ + Duration.ofNanos((long) featureTime.getAverage()) + ", Light: "
+ + Duration.ofNanos((long) lightTime.getAverage()) + ", Lod: "
+ + Duration.ofNanos((long) lodTime.getAverage());
+ }
+ }
+
+ public static final int TIMEOUT_SECONDS = 30;
+
+ //=================Generation Step===================
+
+ public final LinkedList events = new LinkedList();
+ public final GlobalParameters params;
+ public final StepStructureStart stepStructureStart = new StepStructureStart(this);
+ public final StepStructureReference stepStructureReference = new StepStructureReference(this);
+ public final StepBiomes stepBiomes = new StepBiomes(this);
+ public final StepNoise stepNoise = new StepNoise(this);
+ public final StepSurface stepSurface = new StepSurface(this);
+ public final StepFeatures stepFeatures = new StepFeatures(this);
+ public final StepLight stepLight = new StepLight(this);
+ private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
+
+ public static final LodThreadFactory threadFactory = new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY);
+
+ public ExecutorService executors = Executors.newFixedThreadPool(
+ CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), threadFactory);
+
+ public void resizeThreadPool(int newThreadCount)
+ {
+ executors = Executors.newFixedThreadPool(newThreadCount,
+ new LodThreadFactory("Gen-Worker-Thread", Thread.MIN_PRIORITY));
+ }
+
+ public boolean tryAddPoint(int px, int pz, int range, Steps target)
+ {
+ int boxSize = range * 2 + 1;
+ int x = Math.floorDiv(px, boxSize) * boxSize + range;
+ int z = Math.floorDiv(pz, boxSize) * boxSize + range;
+
+ for (GenerationEvent event : events)
+ {
+ if (event.tooClose(x, z, range))
+ return false;
+ }
+ // System.out.println(x + ", "+z);
+ events.add(new GenerationEvent(new ChunkPos(x, z), range, this, target));
+ return true;
+ }
+
+ public void updateAllFutures()
+ {
+ // Update all current out standing jobs
+ Iterator iter = events.iterator();
+ while (iter.hasNext())
+ {
+ GenerationEvent event = iter.next();
+ if (event.isCompleted())
+ {
+ try
+ {
+ event.join();
+ }
+ catch (Throwable e)
+ {
+ e.printStackTrace();
+ while (e.getCause() != null)
+ {
+ e = e.getCause();
+ e.printStackTrace();
+ }
+ }
+ finally
+ {
+ iter.remove();
+ }
+ }
+ else if (event.hasTimeout(TIMEOUT_SECONDS, TimeUnit.SECONDS))
+ {
+ ClientApi.LOGGER.error("Batching World Generator: " + event + " timed out and terminated!");
+ ClientApi.LOGGER.info("Dump PrefEvent: " + event.pEvent);
+ try
+ {
+ if (!event.terminate())
+ ClientApi.LOGGER.error("Failed to terminate the stuck generation event!");
+ }
+ finally
+ {
+ iter.remove();
+ }
+ }
+ }
+ }
+
+ public BatchGenerationEnvironment(IWorldWrapper serverlevel, LodBuilder lodBuilder, LodDimension lodDim)
+ {
+ super(serverlevel, lodBuilder, lodDim);
+ ClientApi.LOGGER.info("================WORLD_GEN_STEP_INITING=============");
+ params = new GlobalParameters((ServerLevel) ((WorldWrapper) serverlevel).getWorld(), lodBuilder, lodDim);
+ }
+
+ public void startLoadingAllRegionsFromFile(LodDimension lodDim)
+ {
+ ServerLevel level = params.level;
+ level.getChunkSource();
+
+ }
+
+ @SuppressWarnings("resource")
+ public static ChunkAccess loadOrMakeChunk(ChunkPos chunkPos, ServerLevel level, LevelLightEngine lightEngine)
+ {
+ CompoundTag chunkData = null;
+ try
+ {
+ chunkData = level.getChunkSource().chunkMap.readChunk(chunkPos);
+ }
+ catch (IOException e)
+ {
+ ClientApi.LOGGER.error("DistantHorizons: Couldn't load chunk {}", chunkPos, e);
+ }
+ if (chunkData == null)
+ {
+ return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level);
+ }
+ else
+ {
+ return ChunkLoader.read(level, lightEngine, chunkPos, chunkData);
+ }
+
+ }
+
+
+
+ public void generateLodFromList(GenerationEvent e)
+ {
+ if (ENABLE_EVENT_LOGGING)
+ ClientApi.LOGGER.info("Lod Generate Event: " + e.pos);
+ e.pEvent.beginNano = System.nanoTime();
+ GridList referencedChunks;
+ DistanceGenerationMode generationMode;
+ LightedWorldGenRegion region;
+ WorldGenLevelLightEngine lightEngine;
+ LightGetterAdaptor adaptor;
+
+ try
+ {
+ adaptor = new LightGetterAdaptor(params.level);
+ lightEngine = new WorldGenLevelLightEngine(adaptor);
+
+ int cx = e.pos.x;
+ int cy = e.pos.z;
+ int rangeEmpty = e.range + 1;
+ GridList chunks = new GridList(rangeEmpty);
+
+ @SuppressWarnings("resource")
+ EmptyChunkGenerator generator = (int x, int z) ->
+ {
+ ChunkPos chunkPos = new ChunkPos(x, z);
+ ChunkAccess target = null;
+ try
+ {
+ target = loadOrMakeChunk(chunkPos, params.level, lightEngine);
+ }
+ catch (RuntimeException e2)
+ {
+ // Continue...
+ e2.printStackTrace();
+ }
+ if (target == null)
+ target = new ProtoChunk(chunkPos, UpgradeData.EMPTY, params.level);
+ return target;
+ };
+
+ for (int oy = -rangeEmpty; oy <= rangeEmpty; oy++)
+ {
+ for (int ox = -rangeEmpty; ox <= rangeEmpty; ox++)
+ {
+ ChunkAccess target = generator.generate(cx + ox, cy + oy);
+ chunks.add(target);
+ }
+ }
+ e.pEvent.emptyNano = System.nanoTime();
+ e.refreshTimeout();
+ region = new LightedWorldGenRegion(params.level, lightEngine, e.tParam.structFeat, chunks, ChunkStatus.STRUCTURE_STARTS, rangeEmpty, e.lightMode, generator);
+ adaptor.setRegion(region);
+ referencedChunks = chunks.subGrid(e.range);
+ referencedChunks = generateDirect(e, referencedChunks, e.target, region);
+
+ }
+ catch (StepStructureStart.StructStartCorruptedException f)
+ {
+ e.tParam.markAsInvalid();
+ return;
+ }
+
+ switch (e.target)
+ {
+ case Empty:
+ case StructureStart:
+ case StructureReference:
+ generationMode = DistanceGenerationMode.NONE;
+ break;
+ case Biomes:
+ generationMode = DistanceGenerationMode.BIOME_ONLY;
+ case Noise:
+ generationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT;
+ break;
+ case Surface:
+ case Carvers:
+ generationMode = DistanceGenerationMode.SURFACE;
+ break;
+ case Features:
+ generationMode = DistanceGenerationMode.FEATURES;
+ break;
+ case Light:
+ case LiquidCarvers:
+ default:
+ return;
+ }
+ int centreIndex = referencedChunks.size() / 2;
+
+ for (int oy = -e.range; oy <= e.range; oy++)
+ {
+ for (int ox = -e.range; ox <= e.range; ox++)
+ {
+ int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy);
+ ChunkAccess target = referencedChunks.get(targetIndex);
+ target.setLightCorrect(true);
+ //if (target instanceof LevelChunk)
+ // ((LevelChunk) target).setClientLightReady(true);
+ boolean isFull = target.getStatus() == ChunkStatus.FULL || target instanceof LevelChunk;
+ //boolean isPartial = target.isOldNoiseGeneration();
+ if (isFull)
+ {
+ if (ENABLE_LOAD_EVENT_LOGGING)
+ ClientApi.LOGGER.info("Detected full existing chunk at {}", target.getPos());
+ params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region),
+ new LodBuilderConfig(DistanceGenerationMode.FULL), true);
+ }
+ else if (target.getStatus() == ChunkStatus.EMPTY && generationMode == DistanceGenerationMode.NONE)
+ {
+ params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region),
+ LodBuilderConfig.getFillVoidConfig(), true);
+ }
+ else
+ {
+ params.lodBuilder.generateLodNodeFromChunk(params.lodDim, new ChunkWrapper(target, region),
+ new LodBuilderConfig(generationMode), true);
+ }
+ if (e.lightMode == LightGenerationMode.FANCY || isFull)
+ {
+ lightEngine.retainData(target.getPos(), false);
+ }
+
+ }
+ }
+ e.pEvent.endNano = System.nanoTime();
+ e.refreshTimeout();
+ if (ENABLE_PERF_LOGGING)
+ {
+ e.tParam.perf.recordEvent(e.pEvent);
+ ClientApi.LOGGER.info(e.tParam.perf);
+ }
+ }
+
+ public GridList generateDirect(GenerationEvent e, GridList subRange, Steps step,
+ LightedWorldGenRegion region)
+ {
+ try
+ {
+ subRange.forEach((chunk) ->
+ {
+ if (chunk instanceof ProtoChunk)
+ {
+ ((ProtoChunk) chunk).setLightEngine(region.getLightEngine());
+ region.getLightEngine().retainData(chunk.getPos(), true);
+ }
+ });
+ if (step == Steps.Empty)
+ return subRange;
+ stepStructureStart.generateGroup(e.tParam, region, subRange);
+ e.pEvent.structStartNano = System.nanoTime();
+ e.refreshTimeout();
+ if (step == Steps.StructureStart)
+ return subRange;
+ stepStructureReference.generateGroup(e.tParam, region, subRange);
+ e.pEvent.structRefNano = System.nanoTime();
+ e.refreshTimeout();
+ if (step == Steps.StructureReference)
+ return subRange;
+ stepBiomes.generateGroup(e.tParam, region, subRange);
+ e.pEvent.biomeNano = System.nanoTime();
+ e.refreshTimeout();
+ if (step == Steps.Biomes)
+ return subRange;
+ stepNoise.generateGroup(e.tParam, region, subRange);
+ e.pEvent.noiseNano = System.nanoTime();
+ e.refreshTimeout();
+ if (step == Steps.Noise)
+ return subRange;
+ stepSurface.generateGroup(e.tParam, region, subRange);
+ e.pEvent.surfaceNano = System.nanoTime();
+ e.refreshTimeout();
+ if (step == Steps.Surface)
+ return subRange;
+ if (step == Steps.Carvers)
+ return subRange;
+ stepFeatures.generateGroup(e.tParam, region, subRange);
+ e.pEvent.featureNano = System.nanoTime();
+ e.refreshTimeout();
+ return subRange;
+ }
+ finally
+ {
+ switch (region.lightMode)
+ {
+ case FANCY:
+ stepLight.generateGroup(region.getLightEngine(), subRange);
+ break;
+ case FAST:
+ subRange.forEach((p) ->
+ {
+ if (p instanceof ProtoChunk)
+ ((ProtoChunk) p).setLightCorrect(true);
+ });
+ break;
+ }
+ e.pEvent.lightNano = System.nanoTime();
+ e.refreshTimeout();
+ }
+ }
+
+ public interface EmptyChunkGenerator
+ {
+ ChunkAccess generate(int x, int z);
+ }
+
+ @Override
+ public int getEventCount() {
+ return events.size();
+ }
+
+ @Override
+ public void stop(boolean blocking) {
+ ClientApi.LOGGER.info("Batch Chunk Generator shutting down...");
+ executors.shutdownNow();
+ if (blocking) try {
+ if (!executors.awaitTermination(10, TimeUnit.SECONDS)) {
+ ClientApi.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...");
+ }
+ } catch (InterruptedException e) {
+ ClientApi.LOGGER.error("Batch Chunk Generator shutdown failed! Ignoring child threads...", e);
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GenerationEvent.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GenerationEvent.java
new file mode 100644
index 000000000..d0f95a01f
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GenerationEvent.java
@@ -0,0 +1,102 @@
+
+package com.seibel.lod.common.wrappers.worldGeneration;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+
+import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment.PrefEvent;
+import com.seibel.lod.core.api.ClientApi;
+import com.seibel.lod.core.enums.config.LightGenerationMode;
+import com.seibel.lod.core.util.SingletonHandler;
+import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
+import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper.Steps;
+
+import net.minecraft.world.level.ChunkPos;
+
+//======================= Main Event class======================
+public final class GenerationEvent
+{
+ static private final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
+
+ private static int generationFutureDebugIDs = 0;
+ final ThreadedParameters tParam;
+ final ChunkPos pos;
+ final int range;
+ final Future> future;
+ long nanotime;
+ final int id;
+ final Steps target;
+ final LightGenerationMode lightMode;
+ final PrefEvent pEvent = new PrefEvent();
+
+ public GenerationEvent(ChunkPos pos, int range, BatchGenerationEnvironment generationGroup, Steps target)
+ {
+ nanotime = System.nanoTime();
+ this.pos = pos;
+ this.range = range;
+ id = generationFutureDebugIDs++;
+ this.target = target;
+ this.tParam = ThreadedParameters.getOrMake(generationGroup.params);
+ LightGenerationMode mode = CONFIG.client().worldGenerator().getLightGenerationMode();
+
+ this.lightMode = mode;
+
+ future = generationGroup.executors.submit(() ->
+ {
+ generationGroup.generateLodFromList(this);
+ });
+ }
+
+ public boolean isCompleted()
+ {
+ return future.isDone();
+ }
+
+ public boolean hasTimeout(int duration, TimeUnit unit)
+ {
+ long currentTime = System.nanoTime();
+ long delta = currentTime - nanotime;
+ return (delta > TimeUnit.NANOSECONDS.convert(duration, unit));
+ }
+
+ public boolean terminate()
+ {
+ future.cancel(true);
+ ClientApi.LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
+ BatchGenerationEnvironment.threadFactory.dumpAllThreadStacks();
+ return future.isCancelled();
+ }
+
+ public void join()
+ {
+ try
+ {
+ future.get();
+ }
+ catch (InterruptedException | ExecutionException e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean tooClose(int cx, int cz, int cr)
+ {
+ int distX = Math.abs(cx - pos.x);
+ int distZ = Math.abs(cz - pos.z);
+ int minRange = cr + range + 1; // Need one to account for the center
+ minRange += 1 + 1; // Account for required empty chunks
+ return distX < minRange && distZ < minRange;
+ }
+
+ public void refreshTimeout()
+ {
+ nanotime = System.nanoTime();
+ }
+
+ @Override
+ public String toString()
+ {
+ return id + ":" + range + "@" + pos + "(" + target + ")";
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GlobalParameters.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GlobalParameters.java
new file mode 100644
index 000000000..8ef2d512d
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/GlobalParameters.java
@@ -0,0 +1,51 @@
+
+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 BiomeManager biomeManager;
+ public final WorldGenSettings worldGenSettings;
+ public final ThreadedLevelLightEngine lightEngine;
+ public final LodBuilder lodBuilder;
+ public final LodDimension lodDim;
+ public final Registry biomes;
+ public final RegistryAccess registry;
+ public final long worldSeed;
+ public final 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..ae16b25a4
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/Rolling.java
@@ -0,0 +1,34 @@
+
+package com.seibel.lod.common.wrappers.worldGeneration;
+
+//FIXME: Move this outside the WorldGenerationStep thingy
+public class Rolling
+{
+
+ private final int size;
+ private double total = 0d;
+ private int index = 0;
+ private final double[] samples;
+
+ public Rolling(int size)
+ {
+ this.size = size;
+ samples = new double[size];
+ for (int i = 0; i < size; i++)
+ samples[i] = 0d;
+ }
+
+ public void add(double x)
+ {
+ total -= samples[index];
+ samples[index] = x;
+ total += x;
+ if (++index == size)
+ index = 0; // cheaper than modulus
+ }
+
+ public double getAverage()
+ {
+ return total / size;
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ThreadedParameters.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ThreadedParameters.java
new file mode 100644
index 000000000..c33398ce9
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ThreadedParameters.java
@@ -0,0 +1,44 @@
+
+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/mimicObject/ChunkLoader.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/ChunkLoader.java
new file mode 100644
index 000000000..b83b70bea
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/ChunkLoader.java
@@ -0,0 +1,148 @@
+
+package com.seibel.lod.common.wrappers.worldGeneration.mimicObject;
+
+import com.seibel.lod.core.api.ClientApi;
+
+import java.util.Objects;
+
+import net.minecraft.core.Registry;
+import net.minecraft.core.SectionPos;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.ChunkTickList;
+import net.minecraft.world.level.LightLayer;
+import net.minecraft.world.level.TickList;
+import net.minecraft.world.level.WorldGenLevel;
+import net.minecraft.world.level.block.Block;
+import net.minecraft.world.level.chunk.ChunkBiomeContainer;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.DataLayer;
+import net.minecraft.world.level.chunk.LevelChunk;
+import net.minecraft.world.level.chunk.LevelChunkSection;
+import net.minecraft.world.level.chunk.ProtoTickList;
+import net.minecraft.world.level.chunk.UpgradeData;
+import net.minecraft.world.level.levelgen.Heightmap;
+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 {
+ private static final Logger LOGGER = ClientApi.LOGGER;
+
+ private static LevelChunkSection[] readSections(WorldGenLevel level, LevelLightEngine lightEngine,
+ ChunkPos chunkPos, CompoundTag tagLevel) {
+ boolean isLightOn = tagLevel.getBoolean("isLightOn");
+ ListTag listTag = tagLevel.getList("Sections", 10);
+ int i = level.getSectionsCount();
+ LevelChunkSection[] levelChunkSections = new LevelChunkSection[i];
+ boolean bl2 = level.getLevel().dimensionType().hasSkyLight();
+ if (isLightOn)
+ lightEngine.retainData(chunkPos, true);
+ for (int j = 0; j < listTag.size(); j++) {
+ CompoundTag compoundTag3 = listTag.getCompound(j);
+ int k = compoundTag3.getByte("Y");
+ if (compoundTag3.contains("Palette", 9) && compoundTag3.contains("BlockStates", 12)) {
+ LevelChunkSection levelChunkSection = new LevelChunkSection(k << 4);
+ levelChunkSection.getStates().read(compoundTag3.getList("Palette", 10),
+ compoundTag3.getLongArray("BlockStates"));
+ levelChunkSection.recalcBlockCounts();
+ if (!levelChunkSection.isEmpty())
+ levelChunkSections[level.getSectionIndexFromSectionY(k)] = levelChunkSection;
+ }
+ if (isLightOn) {
+ if (compoundTag3.contains("BlockLight", 7))
+ lightEngine.queueSectionData(LightLayer.BLOCK, SectionPos.of(chunkPos, k),
+ new DataLayer(compoundTag3.getByteArray("BlockLight")), true);
+ if (bl2 && compoundTag3.contains("SkyLight", 7))
+ lightEngine.queueSectionData(LightLayer.SKY, SectionPos.of(chunkPos, k),
+ new DataLayer(compoundTag3.getByteArray("SkyLight")), true);
+ }
+ }
+ return levelChunkSections;
+ }
+
+ private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData) {
+ CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
+ for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter()) {
+ String heightmap = type.getSerializationKey();
+ if (tagHeightmaps.contains(heightmap, 12))
+ chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
+ }
+ 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) {
+ ListTag listTag3 = tagPostProcessings.getList(n);
+ for (int o = 0; o < listTag3.size(); ++o) {
+ chunk.addPackedPostProcess(listTag3.getShort(o), n);
+ }
+ }
+ }
+
+ public static ChunkStatus.ChunkType readChunkType(CompoundTag tagLevel) {
+ 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) {
+ CompoundTag tagLevel = chunkData.getCompound("Level");
+
+ ChunkStatus.ChunkType chunkType = readChunkType(tagLevel);
+ if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
+ return null;
+
+ ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos"));
+ if (!Objects.equals(chunkPos, actualPos)) {
+ LOGGER.error("Distant Horizons: Chunk file at {} is in the wrong location; Ignoring. (Expected {}, got {})",
+ (Object) chunkPos, (Object) chunkPos, (Object) actualPos);
+ return null;
+ }
+
+ // ====================== Read params for making the LevelChunk
+ // ============================
+ ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
+ level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), level, 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"), level)
+ : 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), level);
+
+ 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), level);
+
+ long inhabitedTime = tagLevel.getLong("InhabitedTime");
+
+ LevelChunkSection[] levelChunkSections = readSections(level, lightEngine, chunkPos, tagLevel);
+
+ // ======================== Make the chunk
+ // ===========================================
+ LevelChunk chunk = new LevelChunk(level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks,
+ liquidTicks, inhabitedTime, levelChunkSections, null);
+
+ // ========================== Post setup some chunk data
+ // ==============================
+ chunk.setLightCorrect(tagLevel.getBoolean("isLightOn"));
+ readHeightmaps(chunk, tagLevel);
+ readPostPocessings(chunk, tagLevel);
+ // 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..15b06f6ef
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightGetterAdaptor.java
@@ -0,0 +1,40 @@
+package com.seibel.lod.common.wrappers.worldGeneration.mimicObject;
+
+import com.seibel.lod.core.api.ModAccessorApi;
+import com.seibel.lod.core.wrapperInterfaces.modAccessor.IStarlightAccessor;
+
+import net.minecraft.world.level.BlockGetter;
+import net.minecraft.world.level.LevelHeightAccessor;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.LightChunkGetter;
+
+public class LightGetterAdaptor implements LightChunkGetter {
+ private final BlockGetter heightGetter;
+ public LightedWorldGenRegion genRegion = null;
+ final boolean shouldReturnNull;
+
+ public LightGetterAdaptor(BlockGetter heightAccessor) {
+ this.heightGetter = heightAccessor;
+ shouldReturnNull = ModAccessorApi.get(IStarlightAccessor.class) != null;
+ }
+
+ public void setRegion(LightedWorldGenRegion region) {
+ genRegion = region;
+ }
+
+ @Override
+ public BlockGetter getChunkForLighting(int chunkX, int chunkZ) {
+ if (genRegion == null)
+ throw new IllegalStateException("World Gen region has not been set!");
+ // May be null
+ return genRegion.getChunk(chunkX, chunkZ, ChunkStatus.EMPTY, false);
+ }
+
+ @Override
+ public BlockGetter getLevel() {
+ return shouldReturnNull ? null : (genRegion != null ? genRegion : heightGetter);
+ }
+ public LevelHeightAccessor getLevelHeightAccessor() {
+ return heightGetter;
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightedWorldGenRegion.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightedWorldGenRegion.java
new file mode 100644
index 000000000..5be286eba
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/LightedWorldGenRegion.java
@@ -0,0 +1,220 @@
+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.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, chunkStatus, i);
+ this.lightMode = lightMode;
+ this.firstPos = list.get(0).getPos();
+ this.generator = generator;
+ this.structFeat = structFeat;
+ light = lightEngine;
+ writeRadius = i;
+ cache = list;
+ size = Mth.floor(Math.sqrt(list.size()));
+ }
+
+ // Bypass BCLib mixin overrides.
+ @Override
+ public boolean ensureCanWrite(BlockPos blockPos) {
+ int i = SectionPos.blockToSectionCoord(blockPos.getX());
+ int j = SectionPos.blockToSectionCoord(blockPos.getZ());
+ ChunkPos chunkPos = this.getCenter();
+ int k = Math.abs(chunkPos.x - i);
+ int l = Math.abs(chunkPos.z - j);
+ if (k > this.writeRadius || l > this.writeRadius) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public Stream extends StructureStart>> 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..12f849132
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenLevelLightEngine.java
@@ -0,0 +1,177 @@
+package com.seibel.lod.common.wrappers.worldGeneration.mimicObject;
+
+import org.jetbrains.annotations.Nullable;
+
+import net.minecraft.core.BlockPos;
+import net.minecraft.core.SectionPos;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.LevelHeightAccessor;
+import net.minecraft.world.level.LightLayer;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.DataLayer;
+import net.minecraft.world.level.chunk.LevelChunkSection;
+import net.minecraft.world.level.lighting.BlockLightEngine;
+import net.minecraft.world.level.lighting.LayerLightEventListener;
+import net.minecraft.world.level.lighting.LevelLightEngine;
+import net.minecraft.world.level.lighting.SkyLightEngine;
+
+public class WorldGenLevelLightEngine extends LevelLightEngine {
+ public static final int MAX_SOURCE_LEVEL = 15;
+ public static final int LIGHT_SECTION_PADDING = 1;
+ protected final LevelHeightAccessor levelHeightAccessor;
+ @Nullable
+ public final BlockLightEngine blockEngine;
+ @Nullable
+ public final SkyLightEngine skyEngine;
+
+ public WorldGenLevelLightEngine(LightGetterAdaptor genRegion) {
+ super(genRegion, false, false);
+ this.levelHeightAccessor = genRegion.getLevelHeightAccessor();
+ this.blockEngine = new BlockLightEngine(genRegion);
+ this.skyEngine = new SkyLightEngine(genRegion);
+ }
+
+ @Override
+ public void checkBlock(BlockPos blockPos) {
+ if (this.blockEngine != null) {
+ this.blockEngine.checkBlock(blockPos);
+ }
+ if (this.skyEngine != null) {
+ this.skyEngine.checkBlock(blockPos);
+ }
+ }
+
+ @Override
+ public void onBlockEmissionIncrease(BlockPos blockPos, int i) {
+ if (this.blockEngine != null) {
+ this.blockEngine.onBlockEmissionIncrease(blockPos, i);
+ }
+ }
+
+ @Override
+ public boolean hasLightWork() {
+ if (this.skyEngine != null && this.skyEngine.hasLightWork()) {
+ return true;
+ }
+ return this.blockEngine != null && this.blockEngine.hasLightWork();
+ }
+
+ @Override
+ public int runUpdates(int i, boolean bl, boolean bl2) {
+ if (this.blockEngine != null && this.skyEngine != null) {
+ int j = i / 2;
+ int k = this.blockEngine.runUpdates(j, bl, bl2);
+ int l = i - j + k;
+ int m = this.skyEngine.runUpdates(l, bl, bl2);
+ if (k == 0 && m > 0) {
+ return this.blockEngine.runUpdates(m, bl, bl2);
+ }
+ return m;
+ }
+ if (this.blockEngine != null) {
+ return this.blockEngine.runUpdates(i, bl, bl2);
+ }
+ if (this.skyEngine != null) {
+ return this.skyEngine.runUpdates(i, bl, bl2);
+ }
+ return i;
+ }
+
+ @Override
+ public void updateSectionStatus(SectionPos sectionPos, boolean bl) {
+ if (this.blockEngine != null) {
+ this.blockEngine.updateSectionStatus(sectionPos, bl);
+ }
+ if (this.skyEngine != null) {
+ this.skyEngine.updateSectionStatus(sectionPos, bl);
+ }
+ }
+
+ @Override
+ public void enableLightSources(ChunkPos chunkPos, boolean bl) {
+ if (this.blockEngine != null) {
+ this.blockEngine.enableLightSources(chunkPos, bl);
+ }
+ if (this.skyEngine != null) {
+ this.skyEngine.enableLightSources(chunkPos, bl);
+ }
+ }
+
+ @Override
+ public LayerLightEventListener getLayerListener(LightLayer lightLayer) {
+ if (lightLayer == LightLayer.BLOCK) {
+ if (this.blockEngine == null) {
+ return LayerLightEventListener.DummyLightLayerEventListener.INSTANCE;
+ }
+ return this.blockEngine;
+ }
+ if (this.skyEngine == null) {
+ return LayerLightEventListener.DummyLightLayerEventListener.INSTANCE;
+ }
+ return this.skyEngine;
+ }
+
+ @Override
+ public int getRawBrightness(BlockPos blockPos, int i) {
+ int j = this.skyEngine == null ? 0 : this.skyEngine.getLightValue(blockPos) - i;
+ int k = this.blockEngine == null ? 0 : this.blockEngine.getLightValue(blockPos);
+ return Math.max(k, j);
+ }
+
+ public void lightChunk(ChunkAccess chunkAccess, boolean needLightBlockUpdate) {
+ ChunkPos chunkPos = chunkAccess.getPos();
+ chunkAccess.setLightCorrect(false);
+
+ LevelChunkSection[] levelChunkSections = chunkAccess.getSections();
+ for (int i = 0; i < chunkAccess.getSectionsCount(); ++i) {
+ LevelChunkSection levelChunkSection = levelChunkSections[i];
+ if (!LevelChunkSection.isEmpty(levelChunkSection)) {
+ int j = this.levelHeightAccessor.getSectionYFromSectionIndex(i);
+ updateSectionStatus(SectionPos.of(chunkPos, j), false);
+ }
+ }
+ enableLightSources(chunkPos, true);
+ if (needLightBlockUpdate) {
+ chunkAccess.getLights().forEach(blockPos ->
+ onBlockEmissionIncrease(blockPos, chunkAccess.getLightEmission(blockPos)));
+ }
+
+ chunkAccess.setLightCorrect(true);
+ }
+
+ @Override
+ public String getDebugData(LightLayer lightLayer, SectionPos sectionPos) {
+ throw new UnsupportedOperationException("This should never be used!");
+ }
+ @Override
+ public void queueSectionData(LightLayer lightLayer, SectionPos sectionPos, @Nullable DataLayer dataLayer, boolean bl) {
+ if (lightLayer == LightLayer.BLOCK) {
+ if (this.blockEngine != null) {
+ this.blockEngine.queueSectionData(sectionPos.asLong(), dataLayer, bl);
+ }
+ } else if (this.skyEngine != null) {
+ this.skyEngine.queueSectionData(sectionPos.asLong(), dataLayer, bl);
+ }
+ }
+ @Override
+ public void retainData(ChunkPos chunkPos, boolean bl) {
+ if (this.blockEngine != null) {
+ this.blockEngine.retainData(chunkPos, bl);
+ }
+ if (this.skyEngine != null) {
+ this.skyEngine.retainData(chunkPos, bl);
+ }
+ }
+ @Override
+ public int getLightSectionCount() {
+ throw new UnsupportedOperationException("This should never be used!");
+ }
+ @Override
+ public int getMinLightSection() {
+ throw new UnsupportedOperationException("This should never be used!");
+ }
+ @Override
+ public int getMaxLightSection() {
+ throw new UnsupportedOperationException("This should never be used!");
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java
new file mode 100644
index 000000000..2e9deb4f5
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java
@@ -0,0 +1,51 @@
+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 extends StructureStart>> 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..13728799b
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepLight.java
@@ -0,0 +1,59 @@
+package com.seibel.lod.common.wrappers.worldGeneration.step;
+
+import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
+import com.seibel.lod.common.wrappers.worldGeneration.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.LightEventListener;
+
+public final class StepLight {
+ /**
+ *
+ */
+ private final BatchGenerationEnvironment environment;
+
+ /**
+ * @param batchGenerationEnvironment
+ */
+ public StepLight(BatchGenerationEnvironment batchGenerationEnvironment)
+ {
+ environment = batchGenerationEnvironment;
+ }
+
+ public final ChunkStatus STATUS = ChunkStatus.LIGHT;
+
+ public void generateGroup(LightEventListener lightEngine,
+ GridList chunks) {
+ //ArrayList chunksToDo = new ArrayList();
+
+ for (ChunkAccess chunk : chunks) {
+ if (chunk.getStatus().isOrAfter(STATUS)) continue;
+ ((ProtoChunk) chunk).setStatus(STATUS);
+ }
+
+ for (ChunkAccess chunk : chunks) {
+ boolean hasCorrectBlockLight = (chunk instanceof LevelChunk && chunk.isLightCorrect());
+ try {
+ if (lightEngine == null) {
+ // Do nothing
+ } else if (lightEngine instanceof WorldGenLevelLightEngine) {
+ ((WorldGenLevelLightEngine)lightEngine).lightChunk(chunk, !hasCorrectBlockLight);
+ } else if (lightEngine instanceof ThreadedLevelLightEngine) {
+ ((ThreadedLevelLightEngine) lightEngine).lightChunk(chunk, !hasCorrectBlockLight).join();
+ } else {
+ assert(false);
+ }
+
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ chunk.setLightCorrect(true);
+ }
+ lightEngine.runUpdates(Integer.MAX_VALUE, true, true);
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepNoise.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepNoise.java
new file mode 100644
index 000000000..96f1bcc48
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepNoise.java
@@ -0,0 +1,93 @@
+package com.seibel.lod.common.wrappers.worldGeneration.step;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+
+import com.google.common.collect.Sets;
+import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
+import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
+
+import net.minecraft.core.QuartPos;
+import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.util.Mth;
+import net.minecraft.world.level.StructureFeatureManager;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.LevelChunkSection;
+import net.minecraft.world.level.chunk.ProtoChunk;
+import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
+import net.minecraft.world.level.levelgen.NoiseSettings;
+
+public final class StepNoise {
+ /**
+ *
+ */
+ private final BatchGenerationEnvironment environment;
+
+ /**
+ * @param batchGenerationEnvironment
+ */
+ public StepNoise(BatchGenerationEnvironment batchGenerationEnvironment)
+ {
+ environment = batchGenerationEnvironment;
+ }
+
+ private static T joinSync(CompletableFuture f) {
+ if (!f.isDone()) throw new RuntimeException("The future is concurrent!");
+ return f.join();
+ }
+ public final ChunkStatus STATUS = ChunkStatus.NOISE;
+
+ private ChunkAccess NoiseBased$fillFromNoise(NoiseBasedChunkGenerator generator, StructureFeatureManager structureFeatureManager, ChunkAccess chunkAccess) {
+ NoiseSettings noiseSettings = generator.settings.get().noiseSettings();
+ int i = Math.max(noiseSettings.minY(), chunkAccess.getMinBuildHeight());
+ int j = Math.min(noiseSettings.minY() + noiseSettings.height(), chunkAccess.getMaxBuildHeight());
+ int cellHeight = QuartPos.toBlock(noiseSettings.noiseSizeVertical());
+ int k = Mth.intFloorDiv(i, cellHeight);
+ int l = Mth.intFloorDiv(j - i, cellHeight);
+ if (l <= 0) {
+ return chunkAccess;
+ }
+ int m = chunkAccess.getSectionIndex(l * cellHeight - 1 + i);
+ int n = chunkAccess.getSectionIndex(i);
+ HashSet set = Sets.newHashSet();
+ try {
+ for (int o = m; o >= n; --o) {
+ LevelChunkSection levelChunkSection = chunkAccess.getOrCreateSection(o);
+ levelChunkSection.acquire();
+ set.add(levelChunkSection);
+ }
+ chunkAccess = generator.doFill(structureFeatureManager, chunkAccess, k, l);
+ return chunkAccess;
+ } finally {
+ for (LevelChunkSection levelChunkSection : set) {
+ levelChunkSection.release();
+ };
+ }
+ }
+
+ public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
+ List chunks) {
+
+ ArrayList chunksToDo = new ArrayList();
+
+ for (ChunkAccess chunk : chunks) {
+ if (chunk.getStatus().isOrAfter(STATUS)) continue;
+ ((ProtoChunk) chunk).setStatus(STATUS);
+ chunksToDo.add(chunk);
+ }
+
+ for (ChunkAccess chunk : chunksToDo) {
+ // System.out.println("StepNoise: "+chunk.getPos());
+ if (environment.params.generator instanceof NoiseBasedChunkGenerator) {
+ chunk = NoiseBased$fillFromNoise((NoiseBasedChunkGenerator)environment.params.generator,
+ tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
+ } else {
+ chunk = joinSync(environment.params.generator.fillFromNoise(Runnable::run,
+ tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureReference.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureReference.java
new file mode 100644
index 000000000..fda9c44f0
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureReference.java
@@ -0,0 +1,92 @@
+package com.seibel.lod.common.wrappers.worldGeneration.step;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.seibel.lod.common.wrappers.worldGeneration.BatchGenerationEnvironment;
+import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
+
+import net.minecraft.CrashReport;
+import net.minecraft.CrashReportCategory;
+import net.minecraft.ReportedException;
+import net.minecraft.core.Registry;
+import net.minecraft.core.SectionPos;
+import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.world.level.ChunkPos;
+import net.minecraft.world.level.StructureFeatureManager;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.ProtoChunk;
+import net.minecraft.world.level.levelgen.structure.StructureStart;
+
+public final class StepStructureReference {
+ /**
+ *
+ */
+ private final BatchGenerationEnvironment environment;
+
+ /**
+ * @param batchGenerationEnvironment
+ */
+ public StepStructureReference(BatchGenerationEnvironment batchGenerationEnvironment)
+ {
+ environment = batchGenerationEnvironment;
+ }
+
+ public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES;
+
+ private void createReferences(WorldGenRegion worldGenLevel, StructureFeatureManager structureFeatureManager,
+ ChunkAccess chunkAccess) {
+ ChunkPos chunkPos = chunkAccess.getPos();
+ int j = chunkPos.x;
+ int k = chunkPos.z;
+ int l = chunkPos.getMinBlockX();
+ int m = chunkPos.getMinBlockZ();
+
+ SectionPos sectionPos = SectionPos.bottomOf(chunkAccess);
+
+ for (int n = j - 8; n <= j + 8; n++) {
+ for (int o = k - 8; o <= k + 8; o++) {
+ if (!worldGenLevel.hasChunk(n, o))
+ continue;
+ long p = ChunkPos.asLong(n, o);
+ for (StructureStart> structureStart : worldGenLevel.getChunk(n, o).getAllStarts().values()) {
+ try {
+ if (structureStart.isValid()
+ && structureStart.getBoundingBox().intersects(l, m, l + 15, m + 15)) {
+ structureFeatureManager.addReferenceForFeature(sectionPos, structureStart.getFeature(),
+ p, chunkAccess);
+ }
+ } catch (Exception exception) {
+ CrashReport crashReport = CrashReport.forThrowable(exception,
+ "Generating structure reference");
+ CrashReportCategory crashReportCategory = crashReport.addCategory("Structure");
+ crashReportCategory.setDetail("Id",
+ () -> Registry.STRUCTURE_FEATURE.getKey(structureStart.getFeature()).toString());
+ crashReportCategory.setDetail("Name", () -> structureStart.getFeature().getFeatureName());
+ crashReportCategory.setDetail("Class",
+ () -> structureStart.getFeature().getClass().getCanonicalName());
+ throw new ReportedException(crashReport);
+ }
+ }
+ }
+ }
+ }
+
+ public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
+ List chunks) {
+
+ ArrayList chunksToDo = new ArrayList();
+
+ for (ChunkAccess chunk : chunks) {
+ if (chunk.getStatus().isOrAfter(STATUS)) continue;
+ ((ProtoChunk) chunk).setStatus(STATUS);
+ chunksToDo.add(chunk);
+ }
+
+ for (ChunkAccess chunk : chunksToDo) {
+ // System.out.println("StepStructureReference: "+chunk.getPos());
+ createReferences(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureStart.java b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepStructureStart.java
new file mode 100644
index 000000000..1b8d1c664
--- /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.BatchGenerationEnvironment;
+import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
+
+import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.ProtoChunk;
+
+public final class StepStructureStart {
+ /**
+ *
+ */
+ private final BatchGenerationEnvironment environment;
+
+ /**
+ * @param batchGenerationEnvironment
+ */
+ public StepStructureStart(BatchGenerationEnvironment batchGenerationEnvironment)
+ {
+ environment = batchGenerationEnvironment;
+ }
+
+ public final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS;
+
+ public static class StructStartCorruptedException extends RuntimeException {
+ private static final long serialVersionUID = -8987434342051563358L;
+
+ public StructStartCorruptedException(ArrayIndexOutOfBoundsException e) {
+ super("StructStartCorruptedException");
+ super.initCause(e);
+ fillInStackTrace();
+ }
+ }
+
+ public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
+ List chunks) {
+
+ ArrayList chunksToDo = new ArrayList();
+
+ for (ChunkAccess chunk : chunks) {
+ if (chunk.getStatus().isOrAfter(STATUS)) continue;
+ ((ProtoChunk) chunk).setStatus(STATUS);
+ chunksToDo.add(chunk);
+ }
+
+ if (environment.params.worldGenSettings.generateFeatures()) {
+ for (ChunkAccess chunk : chunksToDo) {
+ // System.out.println("StepStructureStart: "+chunk.getPos());
+ environment.params.generator.createStructures(environment.params.registry, tParams.structFeat, chunk, environment.params.structures,
+ environment.params.worldSeed);
+ }
+ }
+ }
+}
\ 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..5da1a0289
--- /dev/null
+++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/step/StepSurface.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.BatchGenerationEnvironment;
+import com.seibel.lod.common.wrappers.worldGeneration.ThreadedParameters;
+
+import net.minecraft.server.level.WorldGenRegion;
+import net.minecraft.world.level.chunk.ChunkAccess;
+import net.minecraft.world.level.chunk.ChunkStatus;
+import net.minecraft.world.level.chunk.ProtoChunk;
+
+public final class StepSurface {
+ /**
+ *
+ */
+ private final BatchGenerationEnvironment environment;
+
+ /**
+ * @param batchGenerationEnvironment
+ */
+ public StepSurface(BatchGenerationEnvironment batchGenerationEnvironment)
+ {
+ environment = batchGenerationEnvironment;
+ }
+
+ public final ChunkStatus STATUS = ChunkStatus.SURFACE;
+
+ public void generateGroup(ThreadedParameters tParams, WorldGenRegion worldGenRegion,
+ List chunks) {
+ ArrayList chunksToDo = new ArrayList();
+
+ for (ChunkAccess chunk : chunks) {
+ if (chunk.getStatus().isOrAfter(STATUS)) continue;
+ ((ProtoChunk) chunk).setStatus(STATUS);
+ chunksToDo.add(chunk);
+ }
+
+ for (ChunkAccess chunk : chunksToDo) {
+ // System.out.println("StepSurface: "+chunk.getPos());
+ environment.params.generator.buildSurfaceAndBedrock(worldGenRegion, chunk);
+ }
+ }
+}
\ No newline at end of file
diff --git a/common/src/main/resources/lod.accesswidener b/common/src/main/resources/lod.accesswidener
index 594426ce4..c639800fb 100644
--- a/common/src/main/resources/lod.accesswidener
+++ b/common/src/main/resources/lod.accesswidener
@@ -26,7 +26,10 @@ accessible field net/minecraft/world/level/lighting/LevelLightEngine skyEngine L
accessible method net/minecraft/world/level/levelgen/Heightmap setHeight (III)V
accessible field net/minecraft/world/level/biome/Biome generationSettings Lnet/minecraft/world/level/biome/BiomeGenerationSettings;
accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Ljava/util/function/Supplier;
+accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doFill (Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;II)Lnet/minecraft/world/level/chunk/ChunkAccess;
+
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
+accessible field net/minecraft/world/level/chunk/ChunkGenerator biomeSource Lnet/minecraft/world/level/biome/BiomeSource;
# lod generation from save file
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
diff --git a/core b/core
index 4bcb6c0ac..069978ee1 160000
--- a/core
+++ b/core
@@ -1 +1 @@
-Subproject commit 4bcb6c0acd5e31a247d7c706e3fcf99cedb236dc
+Subproject commit 069978ee1d2c3096afef34c3bb9b9b7429ecf6f2