diff --git a/common/src/main/java/com/seibel/lod/common/Config.java b/common/src/main/java/com/seibel/lod/common/Config.java index ae527b910..715b1c7b2 100644 --- a/common/src/main/java/com/seibel/lod/common/Config.java +++ b/common/src/main/java/com/seibel/lod/common/Config.java @@ -170,8 +170,6 @@ public class Config extends ConfigGui @Category("client.worldGenerator") @Entry public static BlocksToAvoid blocksToAvoid = IWorldGenerator.BLOCKS_TO_AVOID_DEFAULT; - -// public static boolean useExperimentalPreGenLoading = false; } public static class Advanced 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 9105a9d78..3efa77836 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 @@ -19,12 +19,14 @@ package com.seibel.lod.common.wrappers; +import com.seibel.lod.common.wrappers.worldGeneration.ExperimentalGenerator; import com.seibel.lod.core.builders.lodBuilding.LodBuilder; import com.seibel.lod.core.objects.lod.LodDimension; 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.AbstractWorldGeneratorWrapper; import com.seibel.lod.common.wrappers.block.BlockPosWrapper; import com.seibel.lod.common.wrappers.chunk.ChunkPosWrapper; @@ -36,32 +38,27 @@ import com.seibel.lod.common.wrappers.worldGeneration.WorldGeneratorWrapper; * @author James Seibel * @version 11-20-2021 */ -public class WrapperFactory implements IWrapperFactory -{ +public class WrapperFactory implements IWrapperFactory { public static final WrapperFactory INSTANCE = new WrapperFactory(); @Override - public AbstractBlockPosWrapper createBlockPos() - { + public AbstractBlockPosWrapper createBlockPos() { return new BlockPosWrapper(); } @Override - public AbstractBlockPosWrapper createBlockPos(int x, int y, int z) - { - return new BlockPosWrapper(x,y,z); + public AbstractBlockPosWrapper createBlockPos(int x, int y, int z) { + return new BlockPosWrapper(x, y, z); } @Override - public AbstractChunkPosWrapper createChunkPos() - { + public AbstractChunkPosWrapper createChunkPos() { return new ChunkPosWrapper(); } @Override - public AbstractChunkPosWrapper createChunkPos(long xAndZPositionCombined) - { + public AbstractChunkPosWrapper createChunkPos(long xAndZPositionCombined) { int x = (int) (xAndZPositionCombined & Integer.MAX_VALUE); int z = (int) (xAndZPositionCombined >> Long.SIZE / 2) & Integer.MAX_VALUE; @@ -69,26 +66,27 @@ public class WrapperFactory implements IWrapperFactory } @Override - public AbstractChunkPosWrapper createChunkPos(int x, int z) - { + public AbstractChunkPosWrapper createChunkPos(int x, int z) { return new ChunkPosWrapper(x, z); } @Override - public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos) - { + public AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos) { return new ChunkPosWrapper(newChunkPos); } @Override - public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos) - { + public AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos) { return new ChunkPosWrapper(blockPos); } @Override - public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) - { + public AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { return new WorldGeneratorWrapper(newLodBuilder, newLodDimension, worldWrapper); } + + @Override + public AbstractExperimentalWorldGeneratorWrapper createExperimentalWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { + return new ExperimentalGenerator(newLodBuilder, newLodDimension, worldWrapper); + } } diff --git a/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java index 670a7e5b7..26fbcc546 100644 --- a/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java +++ b/common/src/main/java/com/seibel/lod/common/wrappers/minecraft/MinecraftRenderWrapper.java @@ -4,6 +4,7 @@ import java.awt.*; import java.util.HashSet; import com.mojang.blaze3d.platform.NativeImage; +import com.mojang.blaze3d.systems.RenderSystem; import com.seibel.lod.common.wrappers.misc.LightMapWrapper; import com.seibel.lod.core.handlers.IReflectionHandler; import com.seibel.lod.core.handlers.ReflectionHandler; @@ -44,8 +45,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper { public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper(); - private static final MinecraftWrapper MC_WRAPPER = MinecraftWrapper.INSTANCE; - private static final Minecraft MC = Minecraft.getInstance(); private static final GameRenderer GAME_RENDERER = MC.gameRenderer; @@ -96,8 +95,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public Color getFogColor() { - float[] colorValues = new float[4]; - GL20.glGetFloatv(GL20.GL_FOG_COLOR, colorValues); + float[] colorValues = RenderSystem.getShaderFogColor(); return new Color(colorValues[0], colorValues[1], colorValues[2], colorValues[3]); } 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 new file mode 100644 index 000000000..9d5e29b64 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/ExperimentalGenerator.java @@ -0,0 +1,175 @@ +package com.seibel.lod.common.wrappers.worldGeneration; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import com.seibel.lod.common.wrappers.world.WorldWrapper; +import com.seibel.lod.common.wrappers.worldGeneration.WorldGenerationStep.GenerationEvent; +import com.seibel.lod.common.wrappers.worldGeneration.WorldGenerationStep.Steps; +import com.seibel.lod.core.builders.lodBuilding.LodBuilder; +import com.seibel.lod.core.enums.config.DistanceGenerationMode; +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.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; + +import net.minecraft.world.level.ChunkPos; + +public class ExperimentalGenerator extends AbstractExperimentalWorldGeneratorWrapper { + + 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 final int generationGroupSizeFar = 0; + public static int numberOfGenerationPoints = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads()*2; + + private int estimatedSampleNeeded = 128; + + private LinkedList events = new LinkedList(); + + public ExperimentalGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { + super(newLodBuilder, newLodDimension, worldWrapper); + System.out.println("================ExperimentalGenerator INIT============="); + generationGroup = new WorldGenerationStep(((WorldWrapper) worldWrapper).getServerWorld(), newLodBuilder, + newLodDimension); + } + + private boolean checkIfPositionIsValid(int chunkX, int chunkZ, int range) { + for (GenerationEvent event : events) { + if (event.tooClose(chunkX, chunkZ, range)) return false; + } + return true; + } + + @Override + public void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder) { + DistanceGenerationMode mode = CONFIG.client().worldGenerator().getDistanceGenerationMode(); + numberOfGenerationPoints = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(); + + if (mode == DistanceGenerationMode.NONE || !MC.hasSinglePlayerServer()) + return; + + // Update all current out standing jobs + Iterator iter = events.iterator(); + while (iter.hasNext()) { + GenerationEvent event = iter.next(); + if (event.isCompleted()) { + event.join(); + iter.remove(); + } else if (event.hasTimeout(5, TimeUnit.SECONDS)) { + System.err.println(event.id+": Timed out and terminated!"); + event.terminate(); + iter.remove(); + } + } + + // If we still all jobs running, return. + if (events.size() >= numberOfGenerationPoints) + return; + + final int targetToGenerate = numberOfGenerationPoints - events.size(); + 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); + + // 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. + int nearCount = posToGenerate.getNumberOfNearPos(); + int farCount = posToGenerate.getNumberOfFarPos(); + int maxIteration = Math.max(nearCount, farCount); + + for (int i = 0; i < maxIteration; i++) { + + // 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)); + if (checkIfPositionIsValid(chunkX, chunkZ, generationGroupSize)) { + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); + events.add(new GenerationEvent(chunkPos, generationGroupSize, generationGroup, Steps.Surface)); + toGenerate--; + } + } + //if (toGenerate <= 0) + // break; + + // 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)); + if (checkIfPositionIsValid(chunkX, chunkZ, generationGroupSizeFar)) { + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); + events.add(new GenerationEvent(chunkPos, generationGroupSizeFar, generationGroup, Steps.Surface)); + toGenerate--; + } + } + if (toGenerate <= 0) + break; + + } + if (targetToGenerate != toGenerate) { + if (toGenerate <= 0) { + System.out.println( + "WorldGenerator: Sampled " + posToGenerate.getNumberOfPos() + " out of " + estimatedSampleNeeded + + " points, started all targeted " + targetToGenerate + " generations."); + } else { + System.out.println("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; + System.out.println("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; + System.out.println("WorldGenerator: Decreasing estimatedSampleNeeeded to " + estimatedSampleNeeded); + } + + } + + @Override + public void stop() { + System.out.println("================ExperimentalGenerator SHUTDOWN============="); + generationGroup.executors.shutdownNow(); + } + +} 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 new file mode 100644 index 000000000..7e46d9d74 --- /dev/null +++ b/common/src/main/java/com/seibel/lod/common/wrappers/worldGeneration/WorldGenerationStep.java @@ -0,0 +1,902 @@ +package com.seibel.lod.common.wrappers.worldGeneration; + +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.objects.lod.LodDimension; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import com.google.common.collect.ImmutableList; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import com.seibel.lod.common.wrappers.chunk.ChunkWrapper; +import com.seibel.lod.common.wrappers.worldGeneration.WorldGenerationStep.Steps; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.WorldGenRegion; +import net.minecraft.world.entity.ai.village.VillageSiege; +import net.minecraft.world.entity.npc.CatSpawner; +import net.minecraft.world.entity.npc.WanderingTraderSpawner; +import net.minecraft.world.level.ChunkPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.StructureFeatureManager; +import net.minecraft.world.level.WorldGenLevel; +import net.minecraft.world.level.biome.Biome; +import net.minecraft.world.level.biome.BiomeManager; +import net.minecraft.world.level.chunk.*; +import net.minecraft.world.level.dimension.DimensionType; +import net.minecraft.world.level.dimension.LevelStem; +import net.minecraft.server.level.ThreadedLevelLightEngine; +import net.minecraft.core.MappedRegistry; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.world.level.levelgen.GenerationStep; +import net.minecraft.world.level.levelgen.Heightmap; +import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator; +import net.minecraft.world.level.levelgen.PatrolSpawner; +import net.minecraft.world.level.levelgen.PhantomSpawner; +import net.minecraft.world.level.levelgen.WorldGenSettings; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager; +import net.minecraft.world.level.lighting.LevelLightEngine; +import net.minecraft.world.level.storage.ServerLevelData; +import net.minecraft.world.level.storage.WorldData; + +public class WorldGenerationStep { + + /* + public static class ChunkScanner implements ChunkScanAccess { + + @Override + public CompletableFuture scanChunk(ChunkPos paramChunkPos, StreamTagVisitor paramStreamTagVisitor) { + // TODO Auto-generated method stub + return null; + } + + }*/ + + + public static class GenerationEvent { + private static int generationFutureDebugIDs = 0; + ChunkPos pos; + int range; + Future future; + long nanotime; + int id; + Steps target; + + public GenerationEvent(ChunkPos pos, int range, WorldGenerationStep generationGroup, Steps target) { + nanotime = System.nanoTime(); + this.pos = pos; + this.range = range; + id = generationFutureDebugIDs++; + this.target = target; + 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 void terminate() { + future.cancel(true); + } + public void join() { + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + } + public boolean tooClose(int cx, int cz, int cr) { + int dist = Math.min(Math.abs(cx - pos.x), Math.abs(cz - pos.z)); + return dist T joinAsync(CompletableFuture f) { + //while (!f.isDone()) Thread.yield(); + return f.join(); + } + + ServerLevel level; + ChunkGenerator generator; + StructureManager structures; + BiomeManager biomeManager; + WorldGenSettings worldGenSettings; + ThreadedLevelLightEngine lightEngine; + LodBuilder lodBuilder; + LodDimension lodDim; + StructureFeatureManager structureFeatureManager; +// StructureCheck structureCheck; + Registry biomes; + RegistryAccess registry; + long worldSeed; + //public ExecutorService executors = Executors.newWorkStealingPool(); + public ExecutorService executors = Executors.newCachedThreadPool(new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build()); + + //public ExecutorService executors = Executors.newFixedThreadPool(8, new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build()); + + public WorldGenerationStep(ServerLevel level, LodBuilder lodBuilder, LodDimension lodDim) { + System.out.println("================WORLD_GEN_STEP_INITING============="); + this.level = level; + this.lodBuilder = lodBuilder; + this.lodDim = lodDim; + setupStuff(); + + StepStructureStart.onLevelLoad(generator, worldGenSettings, registry, structureFeatureManager, structures, worldSeed); + StepStructureReference.onLevelLoad(level, generator, structureFeatureManager); + StepBiomes.onLevelLoad(level, generator, biomes, structureFeatureManager); + StepNoise.onLevelLoad(level, generator, structureFeatureManager); + StepSurface.onLevelLoad(level, generator, structureFeatureManager); + StepCarvers.onLevelLoad(level, generator, structureFeatureManager, worldSeed, biomeManager); + StepFeatures.onLevelLoad(level, generator, structureFeatureManager, lightEngine); + StepLight.onLevelLoad(lightEngine); + + } + + + private void setupStuff() { + 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(); + long biomeSeed = BiomeManager.obfuscateSeed(worldSeed); + // FIXME: broken in 1.17.1 +// biomeManager = new BiomeManager(level, biomeSeed); + structures = server.getStructureManager(); + // TODO: Get the current level dimension + MappedRegistry mappedRegistry = worldGenSettings.dimensions(); + LevelStem levelStem = mappedRegistry.get(LevelStem.OVERWORLD); + if (levelStem == null) { + throw new RuntimeException("There should already be a level.... Right???"); + } else { + generator = levelStem.generator(); + } + structureFeatureManager = new StructureFeatureManager(level, worldGenSettings); + } + + public static final class GridList extends ArrayList implements List { + + private static final long serialVersionUID = 1585978374811888116L; + public final int gridCentreToEdge; + public final int gridSize; + + public GridList(int gridCentreToEdge) { + super((gridCentreToEdge * 2 + 1) * (gridCentreToEdge * 2 + 1)); + gridSize = gridCentreToEdge * 2 + 1; + this.gridCentreToEdge = gridCentreToEdge; + } + + public final int offsetOf(int index, int x, int y) { + return index + x + y * gridSize; + } + + public GridList subGrid(int centreIndex, int gridCentreToEdge) { + GridList subGrid = new GridList(gridCentreToEdge); + for (int oy = -gridCentreToEdge; oy <= gridCentreToEdge; oy++) { + int begin = offsetOf(centreIndex, -gridCentreToEdge, oy); + int end = offsetOf(centreIndex, gridCentreToEdge, oy); + subGrid.addAll(this.subList(begin, end+1)); + } + + //System.out.println("========================================\n"+ + //this.toDetailString() + "\nTOOOOOOOOOOOOO\n"+subGrid.toDetailString()+ + //"==========================================\n"); + return subGrid; + } + + @Override + public String toString() { + return "GridList "+gridSize+"*"+gridSize+"["+size()+"]"; + } + public String toDetailString() { + StringBuilder str = new StringBuilder("\n"); + int i = 0; + for (T t : this) { + str.append(t.toString()); + str.append(", "); + i++; + if (i%gridSize == 0) { + str.append("\n"); + } + } + return str.toString(); + } + } + + public static class ChunkSynconizer { + + private ReentrantLock uniqueOwnerLock = new ReentrantLock(); + ChunkAccess chunk; + Steps completedStep = Steps.Empty; + + public ChunkSynconizer(ChunkPos pos, ServerLevel level) { + chunk = new ProtoChunk(pos, UpgradeData.EMPTY, level); + } + + public boolean tryClaimOwnerLock() { + return uniqueOwnerLock.tryLock(); + } + + public void releaseOwnerLock() { + uniqueOwnerLock.unlock(); + } + + public boolean hasCompletedStep(Steps step) { + return step.compareTo(completedStep) <= 0; + } + + public void set(ChunkAccess newChunk, Steps newStep) { + chunk = newChunk; + completedStep = newStep; + } + + public void set(Steps newStep) { + completedStep = newStep; + } + + @Override + public String toString() { + return chunk.getPos().toString(); + } + } + + ConcurrentHashMap chunks = new ConcurrentHashMap(); + // No longer using Long2ObjectLinkedOpenHashMap as I doubt it is multithread + // safe. + + private static final long toLongPos(int cx, int cy) { + return ChunkPos.asLong(cx, cy); + } + + private final ChunkSynconizer getChunkSynconizer(long pos) { + ChunkSynconizer chunk = chunks.get(pos); + if (chunk != null) + return chunk; + chunk = new ChunkSynconizer(new ChunkPos(pos), level); + ChunkSynconizer oldVal = chunks.putIfAbsent(pos, chunk); + if (oldVal != null) + return oldVal; + return chunk; + } + + public void generateLodFromList(GenerationEvent event) { + try { + System.out.println("Started event: "+event); + GridList referencedChunks; + DistanceGenerationMode generationMode; + Runnable lambda = () -> {event.refreshTimeout();}; + switch (event.target) { + case Empty: + return; + case StructureStart: + referencedChunks = generateStructureStart(lambda, event.pos, event.range); + generationMode = DistanceGenerationMode.NONE; + break; + case StructureReference: + referencedChunks = generateStructureReference(lambda, event.pos, event.range); + generationMode = DistanceGenerationMode.NONE; + break; + case Biomes: + referencedChunks = generateBiomes(lambda, event.pos, event.range); + generationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; + break; + case Noise: + referencedChunks = generateNoise(lambda, event.pos, event.range); + generationMode = DistanceGenerationMode.BIOME_ONLY_SIMULATE_HEIGHT; + break; + case Surface: + referencedChunks = generateSurface(lambda, event.pos, event.range); + generationMode = DistanceGenerationMode.SURFACE; + break; + case Carvers: + referencedChunks = generateCarvers(lambda, event.pos, event.range); + generationMode = DistanceGenerationMode.SURFACE; + break; + case Features: + referencedChunks = generateFeatures(lambda, event.pos, event.range); + generationMode = DistanceGenerationMode.FEATURES; + break; + case LiquidCarvers: + return; + case Light: + return; + default: + return; + } + int centreIndex = referencedChunks.size() / 2; + + for (int ox = -event.range; ox <= event.range; ox++) { + for (int oy = -event.range; oy <= event.range; oy++) { + int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); + ChunkSynconizer target = referencedChunks.get(targetIndex); + lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(target.chunk), new LodBuilderConfig(generationMode)); + } + } + lambda.run(); + for (ChunkSynconizer sync : referencedChunks) { + chunks.remove(sync.chunk.getPos().toLong()); + } + System.out.println("Ended event: "+event); + } catch (RuntimeException e) { + e.printStackTrace(); + throw e; + } + } + + public GridList generateStructureStart(Runnable r, ChunkPos pos, int range) { + int cx = pos.x; + int cy = pos.z; + GridList chunks = new GridList(range); + + for (int oy = -range; oy <= range; oy++) { + for (int ox = -range; ox <= range; ox++) { + ChunkSynconizer target = getChunkSynconizer(toLongPos(cx + ox, cy + oy)); + chunks.add(target); + if (!target.hasCompletedStep(Steps.StructureStart)) { + + boolean owned = target.tryClaimOwnerLock(); + if (owned) { + try { + ChunkAccess access = target.chunk; + target.set(StepStructureStart.generate(access), + Steps.StructureStart); + } finally { + target.releaseOwnerLock(); + } + } + + + + } + } + } + r.run(); + return chunks; + } + + public GridList generateStructureReference(Runnable r, ChunkPos pos, int range) { + int prestepRange = range + StepStructureReference.RANGE; + GridList referencedChunks = generateStructureStart(r, pos, prestepRange); + int centreIndex = referencedChunks.size() / 2; + + for (int oy = -range; oy <= range; oy++) { + for (int ox = -range; ox <= range; ox++) { + int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); + ChunkSynconizer target = referencedChunks.get(targetIndex); + if (!target.hasCompletedStep(Steps.StructureReference)) { + boolean owned = target.tryClaimOwnerLock(); + if (owned) { + try { + GridList reference = referencedChunks.subGrid(targetIndex, + StepStructureReference.RANGE); + ArrayList referenceAccess = new ArrayList(reference.size()); + for (ChunkSynconizer ref : reference) { + referenceAccess.add(ref.chunk); + } + StepStructureReference.generate(referenceAccess, target.chunk); + target.set(Steps.StructureReference); + } finally { + target.releaseOwnerLock(); + } + } + } + } + } + r.run(); + return referencedChunks; + } + + public GridList generateBiomes(Runnable r, ChunkPos pos, int range) { + int prestepRange = range + 1; + GridList referencedChunks = generateStructureReference(r, pos, prestepRange); + int centreIndex = referencedChunks.size() / 2; + + for (int oy = -range; oy <= range; oy++) { + for (int ox = -range; ox <= range; ox++) { + int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); + ChunkSynconizer target = referencedChunks.get(targetIndex); + if (!target.hasCompletedStep(Steps.Biomes)) { + boolean owned = target.tryClaimOwnerLock(); + if (owned) { + try { + GridList reference = referencedChunks.subGrid(targetIndex, + StepBiomes.RANGE); + ArrayList referenceAccess = new ArrayList(reference.size()); + for (ChunkSynconizer ref : reference) { + referenceAccess.add(ref.chunk); + } + target.set(StepBiomes.generate(referenceAccess, target.chunk, executors), Steps.Biomes); + } finally { + target.releaseOwnerLock(); + } + } + } + } + } + r.run(); + return referencedChunks; + } + + public GridList generateNoise(Runnable r, ChunkPos pos, int range) { + int prestepRange = range + 1; + GridList referencedChunks = generateBiomes(r, pos, prestepRange); + int centreIndex = referencedChunks.size() / 2; + + for (int oy = -range; oy <= range; oy++) { + for (int ox = -range; ox <= range; ox++) { + int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); + ChunkSynconizer target = referencedChunks.get(targetIndex); + if (!target.hasCompletedStep(Steps.Noise)) { + boolean owned = target.tryClaimOwnerLock(); + if (owned) { + try { + GridList reference = referencedChunks.subGrid(targetIndex, + StepNoise.RANGE); + ArrayList referenceAccess = new ArrayList(reference.size()); + for (ChunkSynconizer ref : reference) { + referenceAccess.add(ref.chunk); + } + target.set(StepNoise.generate(referenceAccess, target.chunk, executors), + Steps.Noise); + } finally { + target.releaseOwnerLock(); + } + } + } + } + } + r.run(); + return referencedChunks; + } + + public GridList generateSurface(Runnable r, ChunkPos pos, int range) { + int prestepRange = range + 1; + GridList referencedChunks = generateNoise(r, pos, prestepRange); + int centreIndex = referencedChunks.size() / 2; + + for (int oy = -range; oy <= range; oy++) { + for (int ox = -range; ox <= range; ox++) { + int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); + ChunkSynconizer target = referencedChunks.get(targetIndex); + if (!target.hasCompletedStep(Steps.Surface)) { + boolean owned = target.tryClaimOwnerLock(); + if (owned) { + try { + GridList reference = referencedChunks.subGrid(targetIndex, + StepSurface.RANGE); + ArrayList referenceAccess = new ArrayList(reference.size()); + for (ChunkSynconizer ref : reference) { + referenceAccess.add(ref.chunk); + } + + target.set(StepSurface.generate(referenceAccess, target.chunk), + Steps.Surface); + } finally { + target.releaseOwnerLock(); + } + } + } + } + } + r.run(); + return referencedChunks; + } + + + public GridList generateCarvers(Runnable r, ChunkPos pos, int range) { + int prestepRange = range + 1; + GridList referencedChunks = generateSurface(r, pos, prestepRange); + int centreIndex = referencedChunks.size() / 2; + + for (int oy = -range; oy <= range; oy++) { + for (int ox = -range; ox <= range; ox++) { + int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); + ChunkSynconizer target = referencedChunks.get(targetIndex); + if (!target.hasCompletedStep(Steps.Carvers)) { + boolean owned = target.tryClaimOwnerLock(); + if (owned) { + try { + GridList reference = referencedChunks.subGrid(targetIndex, + StepCarvers.RANGE); + ArrayList referenceAccess = new ArrayList(reference.size()); + for (ChunkSynconizer ref : reference) { + referenceAccess.add(ref.chunk); + } + target.set(StepCarvers.generate(referenceAccess, target.chunk), + Steps.Carvers); + } finally { + target.releaseOwnerLock(); + } + } + } + } + } + r.run(); + return referencedChunks; + } + + + public GridList generateFeatures(Runnable r, ChunkPos pos, int range) { + int prestepRange = range + 1; + GridList referencedChunks = generateCarvers(r, pos, prestepRange); + int centreIndex = referencedChunks.size() / 2; + + for (int oy = -range; oy <= range; oy++) { + for (int ox = -range; ox <= range; ox++) { + int targetIndex = referencedChunks.offsetOf(centreIndex, ox, oy); + ChunkSynconizer target = referencedChunks.get(targetIndex); + if (!target.hasCompletedStep(Steps.Features)) { + boolean owned = target.tryClaimOwnerLock(); + if (owned) { + try { + GridList reference = referencedChunks.subGrid(targetIndex, + StepFeatures.RANGE); + ArrayList referenceAccess = new ArrayList(reference.size()); + for (ChunkSynconizer ref : reference) { + referenceAccess.add(ref.chunk); + } + target.set(StepFeatures.generate(referenceAccess, target.chunk), + Steps.Features); + } finally { + target.releaseOwnerLock(); + } + } + } + } + } + r.run(); + return referencedChunks; + } + + + + + enum Steps { + Empty, StructureStart, StructureReference, Biomes, Noise, Surface, Carvers, LiquidCarvers, Features, Light, + } + + public static class StepStructureStart { + public static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS; + public static final int RANGE = STATUS.getRange(); + public static final EnumSet HEIGHTMAP_TYPES = STATUS.heightmapsAfter(); + private static ChunkGenerator gen; + private static boolean doGenerateFeatures = true; + private static RegistryAccess registry; + private static StructureFeatureManager structFeat; + private static StructureManager struct; + private static long seed; + + public final static void onLevelLoad(ChunkGenerator generator, WorldGenSettings genSettings, RegistryAccess registryAccess, + StructureFeatureManager structureFeature, StructureManager structures, long worldSeed) { + gen = generator; + doGenerateFeatures = genSettings.generateFeatures(); + registry = registryAccess; + structFeat = structureFeature; + struct = structures; + seed = worldSeed; + } + + public final static ChunkAccess generate(ChunkAccess chunk) { + if (doGenerateFeatures) { + // Should be thread safe + gen.createStructures(registry, structFeat, chunk, struct, seed); + } + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + + static ChunkAccess load(ServerLevel level, ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + } + + public static class StepStructureReference { + public static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES; + public static final int RANGE = STATUS.getRange(); + public static final EnumSet HEIGHTMAP_TYPES = STATUS.heightmapsAfter(); + private static ChunkGenerator gen; + private static StructureFeatureManager structFeat; + private static ServerLevel level; + + public final static void onLevelLoad(ServerLevel serverLevel, ChunkGenerator generator, StructureFeatureManager structureFeature) { + gen = generator; + structFeat = structureFeature; + level = serverLevel; + } + + public static final void generate(List chunkList, ChunkAccess chunk) { + WorldGenRegion worldGenRegion = new WorldGenRegion(level, chunkList, STATUS, -1); + // Note: Not certain StructureFeatureManager.forWorldGenRegion(...) is thread safe + gen.createReferences(worldGenRegion, structFeat.forWorldGenRegion(worldGenRegion), chunk); + ((ProtoChunk) chunk).setStatus(STATUS); + } + + static ChunkAccess load(ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + } + + public static class StepBiomes { + public static final ChunkStatus STATUS = ChunkStatus.BIOMES; + public static final int RANGE = STATUS.getRange(); + public static final EnumSet HEIGHTMAP_TYPES = STATUS.heightmapsAfter(); + public static Registry biomeRegistry; + private static ChunkGenerator gen; + private static StructureFeatureManager structFeat; + private static ServerLevel level; + + public final static void onLevelLoad(ServerLevel serverLevel, ChunkGenerator generator, Registry registry, StructureFeatureManager structureFeature) { + biomeRegistry = registry; + gen = generator; + structFeat = structureFeature; + level = serverLevel; + } + + public static final ChunkAccess generate(List chunkList, ChunkAccess chunk, Executor worker) { + WorldGenRegion worldGenRegion = new WorldGenRegion(level, chunkList, STATUS, -1); + chunk = joinAsync(gen.fillFromNoise(worker, structFeat.forWorldGenRegion(worldGenRegion), chunk)); + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + + static ChunkAccess load(ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + } + + public static class StepNoise { + public static final ChunkStatus STATUS = ChunkStatus.NOISE; + public static final int RANGE = STATUS.getRange(); + public static final EnumSet HEIGHTMAP_TYPES = STATUS.heightmapsAfter(); + private static ChunkGenerator gen; + private static StructureFeatureManager structFeat; + private static ServerLevel level; + + public final static void onLevelLoad(ServerLevel serverLevel, ChunkGenerator generator, StructureFeatureManager structureFeature) { + gen = generator; + structFeat = structureFeature; + level = serverLevel; + } + + public static final ChunkAccess generate(List chunkList, ChunkAccess chunk, Executor worker) { + WorldGenRegion worldGenRegion = new WorldGenRegion(level, chunkList, STATUS, 0); + chunk = joinAsync(gen.fillFromNoise(worker, structFeat.forWorldGenRegion(worldGenRegion), chunk)); + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + + static ChunkAccess load(ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + } + + public static class StepSurface { + public static final ChunkStatus STATUS = ChunkStatus.SURFACE; + public static final int RANGE = STATUS.getRange(); + public static final EnumSet HEIGHTMAP_TYPES = STATUS.heightmapsAfter(); + private static ChunkGenerator gen; + private static StructureFeatureManager structFeat; + private static ServerLevel level; + + public final static void onLevelLoad(ServerLevel serverLevel, ChunkGenerator generator, StructureFeatureManager structureFeature) { + gen = generator; + structFeat = structureFeature; + level = serverLevel; + } + + public static final ChunkAccess generate(List chunkList, ChunkAccess chunk) { + WorldGenRegion worldGenRegion = new WorldGenRegion(level, chunkList, STATUS, 0); + gen.buildSurfaceAndBedrock(worldGenRegion, chunk); + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + + static ChunkAccess load(ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + } + + public static class StepCarvers { + public static final ChunkStatus STATUS = ChunkStatus.CARVERS; + public static final int RANGE = STATUS.getRange(); + public static final EnumSet HEIGHTMAP_TYPES = STATUS.heightmapsAfter(); + private static ChunkGenerator gen; + private static StructureFeatureManager structFeat; + private static ServerLevel level; + private static long seed; + private static BiomeManager biomes; + + public final static void onLevelLoad(ServerLevel serverLevel, ChunkGenerator generator, StructureFeatureManager structureFeature, + long worldSeed, BiomeManager biomeManger) { + gen = generator; + structFeat = structureFeature; + level = serverLevel; + seed = worldSeed; + biomes = biomeManger; + } + + + public static final ChunkAccess generate(List chunkList, ChunkAccess chunk) { + gen.applyCarvers(seed, biomes, chunk, GenerationStep.Carving.AIR); + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + + static ChunkAccess load(ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + } + + public static class StepLiquidCarvers { + public static final ChunkStatus STATUS = ChunkStatus.LIQUID_CARVERS; + public static final int RANGE = STATUS.getRange(); + public static final EnumSet HEIGHTMAP_TYPES = STATUS.heightmapsAfter(); + + public static final ChunkAccess generate(List chunkList, ChunkAccess chunk, Executor worker) { + // FIXME: I think the decompiler failed on this one. Find the actual body and + // put it here. + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + + static ChunkAccess load(ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + } + + public static class StepFeatures { + public static final ChunkStatus STATUS = ChunkStatus.FEATURES; + public static final int RANGE = STATUS.getRange(); + public static final EnumSet HEIGHTMAP_TYPES = STATUS.heightmapsAfter(); + private static ChunkGenerator gen; + private static StructureFeatureManager structFeat; + private static ServerLevel level; + private static LevelLightEngine lights; + + public final static void onLevelLoad(ServerLevel serverLevel, ChunkGenerator generator, StructureFeatureManager structureFeature, + LevelLightEngine lightEngine) { + gen = generator; + structFeat = structureFeature; + level = serverLevel; + lights = lightEngine; + } + + private static ReentrantLock testLock = new ReentrantLock(); + public static final ChunkAccess generate(List chunkList, ChunkAccess chunk) { + ProtoChunk protoChunk = (ProtoChunk) chunk; + if (chunk.getStatus() == STATUS) return chunk; + testLock.lock(); + try { + if (chunk.getStatus() != STATUS) { + + protoChunk.setLightEngine(lights); + + Heightmap.primeHeightmaps(chunk, + EnumSet.of(Heightmap.Types.MOTION_BLOCKING, Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, + Heightmap.Types.OCEAN_FLOOR, Heightmap.Types.WORLD_SURFACE)); + + // This could be problematic. May need to lock the 8 surrounding chunks then. + WorldGenRegion worldGenRegion = new WorldGenRegion(level, chunkList, STATUS, 1); + + gen.applyBiomeDecoration(worldGenRegion, structFeat.forWorldGenRegion(worldGenRegion)); + //Blender.generateBorderTicks(worldGenRegion, chunk); + + protoChunk.setStatus(STATUS); + } + } finally { + testLock.unlock(); + } + return chunk; + } + + static ChunkAccess load(ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return chunk; + } + } + + public static class StepLight { + public static final ChunkStatus STATUS = ChunkStatus.LIGHT; + public static final int RANGE = STATUS.getRange(); + public static final EnumSet HEIGHTMAP_TYPES = STATUS.heightmapsAfter(); + private static ThreadedLevelLightEngine lightEngine; + + public final static void onLevelLoad(ThreadedLevelLightEngine engine) { + lightEngine = engine; + } + + public static final ChunkAccess generate(ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return joinAsync(lightEngine.lightChunk(chunk, chunk.isLightCorrect())); + } + + public static final ChunkAccess load(ChunkAccess chunk) { + ((ProtoChunk) chunk).setStatus(STATUS); + return joinAsync(lightEngine.lightChunk(chunk, chunk.isLightCorrect())); + } + } + + // The following may not be needed + /* + * public static class Spawn implements SimpleGen { + * + * @Override public EnumSet getHeightmapTypes() { return POST_FEATURES; } + * + * @Override public int getDependencyRange() { return 0; } + * + * @Override public final void doSimpleWork(ChunkStatus targetStatus, + * ServerLevel level, ChunkGenerator generator, List chunkList, + * ChunkAccess chunk) { if (!chunk.isUpgrading()) + * generator.spawnOriginalMobs(new WorldGenRegion(level, chunkList, + * targetStatus, -1)); } } public static class Heightmaps implements SimpleGen { + * + * @Override public EnumSet getHeightmapTypes() { return POST_FEATURES; } + * + * @Override public int getDependencyRange() { return 0; } + * + * @Override public final void doSimpleWork(ChunkStatus targetStatus, + * ServerLevel level, ChunkGenerator generator, List chunkList, + * ChunkAccess chunk) { // Apearently nothing again??? Decompiler Error? } } + * + * public static class Full implements Gen { + * + * @Override public EnumSet getHeightmapTypes() { return POST_FEATURES; } + * + * @Override public int getDependencyRange() { return 0; } + * + * @Override public final ChunkAccess doWork(ChunkStatus targetStatus, Executor + * worker, ServerLevel level, ChunkGenerator generator, StructureManager + * structures, ThreadedLevelLightEngine lightEngine, Mutator function, + * List chunkList, ChunkAccess chunk, boolean alwaysRegenerate) { + * return function.call(chunk); } + * + * @Override public final ChunkAccess load(ChunkStatus targetStatus, ServerLevel + * level, StructureManager structures, ThreadedLevelLightEngine lightEngine, + * Mutator function, ChunkAccess chunk) { return function.call(chunk); } } + * + * + */ + +}