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 index 5f43df55d..0b5b65178 100644 --- 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 @@ -1,11 +1,10 @@ package com.seibel.lod.common.wrappers.worldGeneration; -import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import com.seibel.lod.common.wrappers.world.WorldWrapper; import com.seibel.lod.common.wrappers.worldGeneration.WorldGenerationStep.Steps; @@ -15,8 +14,6 @@ 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.IVersionConstants; -import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory; import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton; import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper; import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper; @@ -27,22 +24,38 @@ 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); - private static final IWrapperFactory WRAPPER_FACTORY = SingletonHandler.get(IWrapperFactory.class); - private static final IVersionConstants VERSION_CONSTANTS = SingletonHandler.get(IVersionConstants.class); public WorldGenerationStep generationGroup; public LodDimension targetLodDim; - private boolean generatorThreadRunning = false; + public static final int generationGroupSize = 1; + public static int numberOfGenerationPoints = 8; + private int generationFutureDebugIDs = 0; + + private int estimatedSampleNeeded = 128; + + private LinkedList> futures = new LinkedList>(); + private LinkedList futuresChunkPos = new LinkedList(); public ExperimentalGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { super(newLodBuilder, newLodDimension, worldWrapper); - System.out.println("================ExperimentalGenerator_INITING============="); - generationGroup = new WorldGenerationStep(((WorldWrapper) worldWrapper).getServerWorld(), newLodBuilder, newLodDimension); + System.out.println("================ExperimentalGenerator INIT============="); + generationGroup = new WorldGenerationStep(((WorldWrapper) worldWrapper).getServerWorld(), newLodBuilder, + newLodDimension); } - + private static boolean isFarEnough(int genRange, int cax, int cay, int cbx, int cby) { - int dist = Math.min(Math.abs(cax-cbx), Math.abs(cay-cby)); + int dist = Math.min(Math.abs(cax - cbx), Math.abs(cay - cby)); // return true; - return dist > genRange*2; + // TODO: tune this value + return dist > genRange * 2 + 8; + } + + private boolean checkIfPositionIsValid(int chunkX, int chunkZ) { + for (ChunkPos pos : futuresChunkPos) { + if (!isFarEnough(generationGroupSize, pos.x, pos.z, chunkX, chunkZ)) { + return false; + } + } + return true; } @Override @@ -50,125 +63,135 @@ public class ExperimentalGenerator extends AbstractExperimentalWorldGeneratorWra ExecutorService executor = generationGroup.executors; DistanceGenerationMode mode = CONFIG.client().worldGenerator().getDistanceGenerationMode(); - if (mode != DistanceGenerationMode.NONE - && !generatorThreadRunning - && MC.hasSinglePlayerServer()) - { - generatorThreadRunning = true; - - Runnable runner = () -> { - System.out.println("================ExperimentalGenerator_Run============="); - try - { - int maxSamples = 512; - int genRange = 8; - int points = 8; - // round the player's block position down to the nearest chunk BlockPos - int playerPosX = MC.getPlayerBlockPos().getX(); - int playerPosZ = MC.getPlayerBlockPos().getZ(); - - //=======================================// - // fill in positionsWaitingToBeGenerated // - //=======================================// - - ArrayList genPos = new ArrayList(points); - - PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate( - maxSamples, playerPosX, playerPosZ); - - byte detailLevel; - int posX; - int posZ; - int nearIndex = 0; - int farIndex = 0; - - for (int i = 0; i < posToGenerate.getNumberOfPos(); i++) - { - - // add the near positions - if (nearIndex < posToGenerate.getNumberOfNearPos() && posToGenerate.getNthDetail(nearIndex, true) != 0) - { - detailLevel = (byte) (posToGenerate.getNthDetail(nearIndex, true) - 1); - posX = posToGenerate.getNthPosX(nearIndex, true); - posZ = posToGenerate.getNthPosZ(nearIndex, true); - nearIndex++; - ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ)); - - boolean tooClose = false; - for (ChunkPos pos : genPos) { - if (!isFarEnough(genRange, pos.x, pos.z, chunkPos.x, chunkPos.z)) { - tooClose = true; - break; - } - } - if (tooClose) continue; - genPos.add(chunkPos); - if (genPos.size() >= points) break; - } - - // add the far positions - if (farIndex < posToGenerate.getNumberOfFarPos() && posToGenerate.getNthDetail(farIndex, false) != 0) - { - detailLevel = (byte) (posToGenerate.getNthDetail(farIndex, false) - 1); - posX = posToGenerate.getNthPosX(farIndex, false); - posZ = posToGenerate.getNthPosZ(farIndex, false); - farIndex++; + if (mode == DistanceGenerationMode.NONE || !MC.hasSinglePlayerServer()) + return; - ChunkPos chunkPos = new ChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ)); - - boolean tooClose = false; - for (ChunkPos pos : genPos) { - if (!isFarEnough(genRange, pos.x, pos.z, chunkPos.x, chunkPos.z)) { - tooClose = true; - break; - } - } - if (tooClose) continue; - genPos.add(chunkPos); - if (genPos.size() >= points) break; - } - } - System.out.println("WorldGenerator: "+genPos.size()+" number of positions queried."); - - ArrayList> futures = new ArrayList>(); - - for (ChunkPos pos : genPos) { - futures.add(executor.submit(() -> { - generationGroup.generateLodFromList(pos, genRange, Steps.Features); - })); - } - - for (Future f : futures) { - try { - f.get(30, TimeUnit.SECONDS); - } catch (InterruptedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (ExecutionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (TimeoutException e) { - f.cancel(true); - } - } - } - catch (RuntimeException e) - { - // this shouldn't ever happen, but just in case + // Update all current out standing jobs + Iterator> iter = futures.iterator(); + Iterator posIter = futuresChunkPos.iterator(); + while (iter.hasNext()) { + posIter.next(); + Future future = iter.next(); + if (future.isDone()) { + try { + future.get(); + } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); + } finally { + iter.remove(); + posIter.remove(); } - finally - { - generatorThreadRunning = false; + } + } + + // If we still all jobs running, return. + if (futures.size() >= numberOfGenerationPoints) + return; + + final int targetToGenerate = numberOfGenerationPoints - futures.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 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)) { + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); + futuresChunkPos.add(chunkPos); + futures.add(executor.submit(() -> { + generationGroup.generateLodFromList(generationFutureDebugIDs++, chunkPos, generationGroupSize, + Steps.Features); + })); + toGenerate--; } - }; - executor.execute(runner); - }; + } + if (toGenerate <= 0) + break; + + + // We have nearPos to go though + if (i < nearCount && posToGenerate.getNthDetail(i, true) != 0) { + positionGoneThough++; + // TODO: Add comment here on why theres a '-1'. + // Not sure what's happening here. This is copied from previous codes. + byte detailLevel = (byte) (posToGenerate.getNthDetail(i, true) - 1); + int chunkX = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosX(i, true)); + int chunkZ = LevelPosUtil.getChunkPos(detailLevel, posToGenerate.getNthPosZ(i, true)); + if (checkIfPositionIsValid(chunkX, chunkZ)) { + ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ); + futuresChunkPos.add(chunkPos); + futures.add(executor.submit(() -> { + generationGroup.generateLodFromList(generationFutureDebugIDs++, chunkPos, generationGroupSize, + Steps.Features); + })); + 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 index f15830cf8..a498474e9 100644 --- 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 @@ -8,10 +8,13 @@ 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.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -35,6 +38,12 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureMana import net.minecraft.world.level.lighting.LevelLightEngine; public class WorldGenerationStep { + + private static T joinAsync(CompletableFuture f) { + //while (!f.isDone()) Thread.yield(); + return f.join(); + } + ServerLevel level; ChunkGenerator generator; StructureManager structures; @@ -42,7 +51,10 @@ public class WorldGenerationStep { LodBuilder lodBuilder; LodDimension lodDim; Registry biomes; - public ExecutorService executors = Executors.newFixedThreadPool(8, new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build()); + //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============="); @@ -143,8 +155,8 @@ public class WorldGenerationStep { return chunk; } - public void generateLodFromList(ChunkPos pos, int range, Steps step) { - System.out.println("generateLodFromList("+pos.toString()+", "+range+", "+step+")"); + public void generateLodFromList(int i, ChunkPos pos, int range, Steps step) { + System.out.println(i+": generateLodFromList("+pos.toString()+", "+range+", "+step+")"); GridList referencedChunks; DistanceGenerationMode generationMode; switch (step) { @@ -178,10 +190,10 @@ public class WorldGenerationStep { referencedChunks = generateFeatures(pos, range); generationMode = DistanceGenerationMode.FEATURES; break; - case Light: - return; case LiquidCarvers: return; + case Light: + return; default: return; } @@ -194,11 +206,15 @@ public class WorldGenerationStep { lodBuilder.generateLodNodeFromChunk(lodDim, new ChunkWrapper(target.chunk), new LodBuilderConfig(generationMode)); } } - System.out.println("EXIT: generateLodFromList("+pos.toString()+", "+range+", "+step+")"); + for (ChunkSynconizer sync : referencedChunks) { + chunks.remove(sync.chunk.getPos().toLong()); + } + + System.out.println(i+": EXIT: generateLodFromList("+pos.toString()+", "+range+", "+step+")"); } public GridList generateStructureStart(ChunkPos pos, int range) { - System.out.println("generateStructureStart("+pos.toString()+", "+range+")"); + //System.out.println("generateStructureStart("+pos.toString()+", "+range+")"); int cx = pos.x; int cy = pos.z; GridList chunks = new GridList(range); @@ -233,7 +249,7 @@ public class WorldGenerationStep { public GridList generateStructureReference(ChunkPos pos, int range) { int prestepRange = range + StepStructureReference.RANGE; GridList referencedChunks = generateStructureStart(pos, prestepRange); - System.out.println("generateStructureReference(" + pos.toString() + ", " + range + ")"); + //System.out.println("generateStructureReference(" + pos.toString() + ", " + range + ")"); int centreIndex = referencedChunks.size() / 2; for (int ox = -range; ox <= range; ox++) { @@ -267,7 +283,7 @@ public class WorldGenerationStep { public GridList generateBiomes(ChunkPos pos, int range) { int prestepRange = range + 1; GridList referencedChunks = generateStructureReference(pos, prestepRange); - System.out.println("generateBiomes("+pos.toString()+", "+range+")"); + //System.out.println("generateBiomes("+pos.toString()+", "+range+")"); int centreIndex = referencedChunks.size() / 2; for (int ox = -range; ox <= range; ox++) { @@ -300,6 +316,7 @@ public class WorldGenerationStep { // System.out.println("generateNoise("+pos.toString()+", "+range+")"); int prestepRange = range + 1; GridList referencedChunks = generateBiomes(pos, prestepRange); + //System.out.println("generateNoise("+pos.toString()+", "+range+")"); int centreIndex = referencedChunks.size() / 2; for (int ox = -range; ox <= range; ox++) { @@ -332,6 +349,7 @@ public class WorldGenerationStep { public GridList generateSurface(ChunkPos pos, int range) { int prestepRange = range + 1; GridList referencedChunks = generateNoise(pos, prestepRange); + //System.out.println("generateSurface("+pos.toString()+", "+range+")"); int centreIndex = referencedChunks.size() / 2; for (int ox = -range; ox <= range; ox++) { @@ -366,6 +384,7 @@ public class WorldGenerationStep { public GridList generateCarvers(ChunkPos pos, int range) { int prestepRange = range + 1; GridList referencedChunks = generateSurface(pos, prestepRange); + //System.out.println("generateCarvers("+pos.toString()+", "+range+")"); int centreIndex = referencedChunks.size() / 2; for (int ox = -range; ox <= range; ox++) { @@ -399,6 +418,7 @@ public class WorldGenerationStep { public GridList generateFeatures(ChunkPos pos, int range) { int prestepRange = range + 1; GridList referencedChunks = generateCarvers(pos, prestepRange); + //System.out.println("generateFeatures("+pos.toString()+", "+range+")"); int centreIndex = referencedChunks.size() / 2; for (int ox = -range; ox <= range; ox++) { @@ -499,8 +519,8 @@ public class WorldGenerationStep { public static final ChunkAccess generate(ServerLevel level, ChunkGenerator generator, List chunkList, ChunkAccess chunk, Executor worker) { WorldGenRegion worldGenRegion = new WorldGenRegion(level, chunkList, STATUS, -1); - chunk = generator.createBiomes(biomeRegistry, worker, Blender.of(worldGenRegion), - level.structureFeatureManager().forWorldGenRegion(worldGenRegion), chunk).join(); + chunk = joinAsync(generator.createBiomes(biomeRegistry, worker, Blender.of(worldGenRegion), + level.structureFeatureManager().forWorldGenRegion(worldGenRegion), chunk)); ((ProtoChunk) chunk).setStatus(STATUS); return chunk; } @@ -519,8 +539,8 @@ public class WorldGenerationStep { public static final ChunkAccess generate(ServerLevel level, ChunkGenerator generator, List chunkList, ChunkAccess chunk, Executor worker) { WorldGenRegion worldGenRegion = new WorldGenRegion(level, chunkList, STATUS, 0); - chunk = generator.fillFromNoise(worker, Blender.of(worldGenRegion), - level.structureFeatureManager().forWorldGenRegion(worldGenRegion), chunk).join(); + chunk = joinAsync(generator.fillFromNoise(worker, Blender.of(worldGenRegion), + level.structureFeatureManager().forWorldGenRegion(worldGenRegion), chunk)); ((ProtoChunk) chunk).setStatus(STATUS); return chunk; } @@ -635,12 +655,12 @@ public class WorldGenerationStep { public static final ChunkAccess generate(ChunkAccess chunk) { ((ProtoChunk) chunk).setStatus(STATUS); - return lightEngine.lightChunk(chunk, chunk.isLightCorrect()).join(); + return joinAsync(lightEngine.lightChunk(chunk, chunk.isLightCorrect())); } public static final ChunkAccess load(ChunkAccess chunk) { ((ProtoChunk) chunk).setStatus(STATUS); - return lightEngine.lightChunk(chunk, chunk.isLightCorrect()).join(); + return joinAsync(lightEngine.lightChunk(chunk, chunk.isLightCorrect())); } }