diff --git a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java index 902b65050..898620df8 100644 --- a/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java +++ b/src/main/java/com/seibel/lod/builders/LodBufferBuilder.java @@ -18,21 +18,15 @@ package com.seibel.lod.builders; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; import java.util.List; -import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; -import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import org.lwjgl.opengl.GL11; -import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker; -import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.handlers.LodConfig; import com.seibel.lod.objects.LevelPos; import com.seibel.lod.objects.LodDataPoint; @@ -44,32 +38,25 @@ import com.seibel.lod.util.DetailDistanceUtil; import com.seibel.lod.util.LodThreadFactory; import com.seibel.lod.util.LodUtil; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.vertex.VertexBuffer; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.server.ServerWorld; -import net.minecraftforge.common.WorldWorkerManager; /** * This object is used to create NearFarBuffer objects. * * @author James Seibel - * @version 8-22-2021 + * @version 8-24-2021 */ public class LodBufferBuilder { - private Minecraft mc; - /** This holds the thread used to generate new LODs off the main thread. */ private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " - main")); /** This holds the threads used to generate buffers. */ private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), new LodThreadFactory(this.getClass().getSimpleName() + " - builder")); //private ExecutorService bufferBuilderThreads = Executors.newFixedThreadPool(2, new LodThreadFactory(this.getClass().getSimpleName() + " - builder")); - private LodBuilder LodQuadTreeNodeBuilder; - /** The buffers that are used to create LODs using far fog */ public volatile BufferBuilder[][] buildableBuffers; @@ -85,35 +72,12 @@ public class LodBufferBuilder */ public boolean generatingBuffers = false; - /** - * if this is true the LOD buffers are currently being - * regenerated. - */ - public Set positionWaitingToBeGenerated = new HashSet<>(); - /** * if this is true new LOD buffers have been generated * and are waiting to be swapped with the drawable buffers */ private boolean switchVbos = false; - /** - * This keeps track of how many chunk generation requests are on going. - * This is to prevent chunks from being generated for a long time in an area - * the player is no longer in. - */ - public AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0); - - - /** - * how many chunks to generate outside of the player's - * view distance at one time. (or more specifically how - * many requests to make at one time). - * I multiply by 8 to make sure there is always a buffer of chunk requests, - * to make sure the CPU is always busy and we can generate LODs as quickly as - * possible. - */ - public int maxChunkGenRequests = LodConfig.CLIENT.numberOfWorldGenerationThreads.get() * 8; /** Size of the buffer builders in bytes last time we created them */ public int previousBufferSize = 0; @@ -127,14 +91,12 @@ public class LodBufferBuilder - public LodBufferBuilder(LodBuilder newLodBuilder) + public LodBufferBuilder() { - mc = Minecraft.getInstance(); - LodQuadTreeNodeBuilder = newLodBuilder; + } - private LodDimension previousDimension = null; /** @@ -156,11 +118,6 @@ public class LodBufferBuilder if (buildableBuffers == null) throw new IllegalStateException("\"generateLodBuffersAsync\" was called before the \"setupBuffers\" method was called."); - if (previousDimension != lodDim) - { - previousDimension = lodDim; - } - generatingBuffers = true; @@ -181,19 +138,12 @@ public class LodBufferBuilder long treeEnd = System.currentTimeMillis(); long startTime = System.currentTimeMillis(); - - ArrayList chunksToGen = new ArrayList<>(maxChunkGenRequests); - // if we don't have a full number of chunks to generate in chunksToGen - // we can top it off from the reserve - ArrayList chunksToGenReserve = new ArrayList<>(maxChunkGenRequests); + + ArrayList> builderThreads = new ArrayList<>(lodDim.regions.length * lodDim.regions.length); - + startBuffers(); - // used when determining which chunks are closer when queuing distance - // generation - int minChunkDist = Integer.MAX_VALUE; - // =====================// // RENDERING PART // // =====================// @@ -286,182 +236,6 @@ public class LodBufferBuilder } } long renderEnd = System.currentTimeMillis(); - - - // =====================// - // GENERATION PART // - // =====================// - - - List posListToGenerate; - List generationRequestList = new ArrayList<>(); - - /**TODO can give a totally different generation*/ - /* - for (byte detail = LodUtil.BLOCK_DETAIL_LEVEL; detail <= LodUtil.REGION_DETAIL_LEVEL; detail++) - { - if (!posListToGenerate.isEmpty()) break; - for (byte detailGen = LodUtil.BLOCK_DETAIL_LEVEL; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++) - { - if (!posListToGenerate.isEmpty()) break; - posListToGenerate.addAll(lodDim.getDataToGenerate( - playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ(), - (int) (distancesLinear[detailGen]*1.5), - (int) (distancesLinear[detailGen+1]*1.5), - (byte) distancesGenerators[detailGen].complexity, - detail, - 16)); - System.out.println("HERE"); - } - } - */ - - long genReqStart = 0; - long genReqEnd = 0; - long genStart = 0; - long genEnd = 0; - - if (LodConfig.CLIENT.distanceGenerationMode.get() != DistanceGenerationMode.NONE) - { - int requesting = maxChunkGenRequests; - - genReqStart = System.currentTimeMillis(); - //we firstly make sure that the world is filled with half region wide block - for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++) - { - if (requesting == 0) break; - posListToGenerate = lodDim.getDataToGenerate( - playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ(), - DetailDistanceUtil.getDistanceGeneration(detailGen), - DetailDistanceUtil.getDistanceGeneration(detailGen + 1), - LodConfig.CLIENT.distanceGenerationMode.get().complexity, - (byte) 9, - requesting/2); - for(LevelPos levelPos : posListToGenerate){ - generationRequestList.add(new GenerationRequest(levelPos,LodConfig.CLIENT.distanceGenerationMode.get(), DetailDistanceUtil.getLodDetail(detailGen))); - } - requesting = maxChunkGenRequests - generationRequestList.size(); - - } - - //we then fill the world with the rest of the block - for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++) - { - if (requesting == 0) break; - posListToGenerate = lodDim.getDataToGenerate( - playerBlockPosRounded.getX(), - playerBlockPosRounded.getZ(), - DetailDistanceUtil.getDistanceGeneration(detailGen), - DetailDistanceUtil.getDistanceGeneration(detailGen + 1), - LodConfig.CLIENT.distanceGenerationMode.get().complexity, - DetailDistanceUtil.getLodDetail(detailGen).detailLevel, - maxChunkGenRequests); - for(LevelPos levelPos : posListToGenerate){ - generationRequestList.add(new GenerationRequest(levelPos,LodConfig.CLIENT.distanceGenerationMode.get(), DetailDistanceUtil.getLodDetail(detailGen))); - } - requesting = maxChunkGenRequests - generationRequestList.size(); - } - - // determine which points in the posListToGenerate - // should actually be queued up - for (GenerationRequest generationRequest : generationRequestList) - { - ChunkPos chunkPos = generationRequest.getChunkPos(); - - if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests) - { - - if (positionWaitingToBeGenerated.contains(chunkPos)) - { - //ClientProxy.LOGGER.debug(pos + " asked to be generated again."); - continue; - } - - // determine if this position is closer to the player - // than the previous - int newDistance = playerChunkPos.getChessboardDistance(chunkPos); - - if (newDistance < minChunkDist) - { - // this chunk is closer, clear any previous - // positions and update the new minimum distance - minChunkDist = newDistance; - - // move all the old chunks into the reserve - ArrayList oldReserve = new ArrayList<>(chunksToGenReserve); - chunksToGenReserve.clear(); - chunksToGenReserve.addAll(chunksToGen); - // top off reserve with whatever was in oldReerve - for (int i = 0; i < oldReserve.size(); i++) - { - if (chunksToGenReserve.size() < maxChunkGenRequests) - chunksToGenReserve.add(oldReserve.get(i)); - else - break; - } - - chunksToGen.clear(); - chunksToGen.add(generationRequest); - } - else if (newDistance == minChunkDist) - { - // this chunk position as close as the minimum distance - if (chunksToGen.size() < maxChunkGenRequests) - { - // we are still under the number of chunks to generate - // add this position to the list - chunksToGen.add(generationRequest); - } - } - else - { - // this chunk is farther away than the minimum distance, - // add it to the reserve to make sure we always have a full reserve - chunksToGenReserve.add(generationRequest); - } - - } // lod null and can generate more chunks - } // positions to generate - - genReqEnd = System.currentTimeMillis(); - - genStart = System.currentTimeMillis(); - // queue up chunks to be generated - if (mc.hasSingleplayerServer()) - { - // issue #19 - // TODO add a way for a server side mod to generate chunks requested here - ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension); - - // make sure we have as many chunks to generate as we are allowed - if (chunksToGen.size() < maxChunkGenRequests) - { - Iterator reserveIterator = chunksToGenReserve.iterator(); - while (chunksToGen.size() < maxChunkGenRequests && reserveIterator.hasNext()) - { - chunksToGen.add(reserveIterator.next()); - } - } - - // start chunk generation - for (GenerationRequest generationRequest : generationRequestList) - { - // don't add null chunkPos (which shouldn't happen anyway) - // or add more to the generation queue - ChunkPos chunkPos = generationRequest.getChunkPos(); - if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests) - continue; - - positionWaitingToBeGenerated.add(chunkPos); - numberOfChunksWaitingToGenerate.addAndGet(1); - LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos,generationRequest.generationMode, generationRequest.detail, renderer, LodQuadTreeNodeBuilder, this, lodDim, serverWorld); - WorldWorkerManager.addWorker(genWorker); - } - } - genEnd = System.currentTimeMillis(); - } // if distanceGenerationMode != DistanceGenerationMode.NONE // finish the buffer building @@ -476,18 +250,11 @@ public class LodBufferBuilder long treeTime = treeEnd - treeStart; - long renderingTime = renderEnd - renderStart; - long genReqTime = genReqEnd - genReqStart; - - long genTime = genEnd - genStart; - ClientProxy.LOGGER.info("Buffer Build time: " + buildTime + " ms" + '\n' + "Tree cutting time: " + treeTime + " ms" + '\n' + - "Rendering time: " + renderingTime + " ms" + '\n' + - "Generation request time: " + genReqTime + " ms" + '\n' + - "Generation time: " + genTime + " ms"); + "Rendering time: " + renderingTime + " ms"); // mark that the buildable buffers as ready to swap switchVbos = true; diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java index a1bb814aa..097aabb81 100644 --- a/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodNodeGenWorker.java @@ -26,7 +26,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.function.Supplier; -import com.seibel.lod.builders.LodBufferBuilder; import com.seibel.lod.builders.LodBuilder; import com.seibel.lod.builders.LodBuilderConfig; import com.seibel.lod.enums.DistanceGenerationMode; @@ -71,7 +70,7 @@ import net.minecraftforge.common.WorldWorkerManager.IWorker; * This is used to generate a LodChunk at a given ChunkPos. * * @author James Seibel - * @version 8-21-2021 + * @version 8-24-2021 */ public class LodNodeGenWorker implements IWorker { @@ -88,7 +87,7 @@ public class LodNodeGenWorker implements IWorker public LodNodeGenWorker(ChunkPos newPos, DistanceGenerationMode newGenerationMode, LodDetail newDetaillevel, LodRenderer newLodRenderer, - LodBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder, + LodBuilder newLodBuilder, LodDimension newLodDimension, ServerWorld newServerWorld) { // just a few sanity checks @@ -101,9 +100,6 @@ public class LodNodeGenWorker implements IWorker if (newLodBuilder == null) throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder"); - if (newLodBufferBuilder == null) - throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodBufferBuilder"); - if (newLodDimension == null) throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension"); @@ -113,7 +109,7 @@ public class LodNodeGenWorker implements IWorker thread = new LodChunkGenThread(newPos, newGenerationMode, newDetaillevel, newLodRenderer, - newLodBuilder, newLodBufferBuilder, + newLodBuilder, newLodDimension, newServerWorld); } @@ -165,12 +161,11 @@ public class LodNodeGenWorker implements IWorker public final LodDetail detailLevel; public final LodBuilder lodBuilder; public final LodRenderer lodRenderer; - private LodBufferBuilder lodBufferBuilder; private ChunkPos pos; public LodChunkGenThread(ChunkPos newPos, DistanceGenerationMode newGenerationMode, LodDetail newDetailLevel, LodRenderer newLodRenderer, - LodBuilder newLodBuilder, LodBufferBuilder newLodBufferBuilder, + LodBuilder newLodBuilder, LodDimension newLodDimension, ServerWorld newServerWorld) { pos = newPos; @@ -178,7 +173,6 @@ public class LodNodeGenWorker implements IWorker detailLevel = newDetailLevel; lodRenderer = newLodRenderer; lodBuilder = newLodBuilder; - lodBufferBuilder = newLodBufferBuilder; lodDim = newLodDimension; serverWorld = newServerWorld; } @@ -247,10 +241,10 @@ public class LodNodeGenWorker implements IWorker finally { // decrement how many threads are running - thread.lodBufferBuilder.numberOfChunksWaitingToGenerate.addAndGet(-1); + LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1); // this position is no longer being generated - lodBufferBuilder.positionWaitingToBeGenerated.remove(pos); + LodWorldGenerator.INSTANCE.positionWaitingToBeGenerated.remove(pos); } }// run diff --git a/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java new file mode 100644 index 000000000..3ec07fd43 --- /dev/null +++ b/src/main/java/com/seibel/lod/builders/worldGeneration/LodWorldGenerator.java @@ -0,0 +1,298 @@ +package com.seibel.lod.builders.worldGeneration; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +import com.seibel.lod.builders.GenerationRequest; +import com.seibel.lod.builders.LodBuilder; +import com.seibel.lod.enums.DistanceGenerationMode; +import com.seibel.lod.handlers.LodConfig; +import com.seibel.lod.objects.LevelPos; +import com.seibel.lod.objects.LodDimension; +import com.seibel.lod.render.LodRenderer; +import com.seibel.lod.util.DetailDistanceUtil; +import com.seibel.lod.util.LodThreadFactory; +import com.seibel.lod.util.LodUtil; + +import net.minecraft.client.Minecraft; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.ChunkPos; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.common.WorldWorkerManager; + +/** + * A singleton that handles all long distance LOD world generation. + * + * @author James Seibel + * @version 8-24-2021 + */ +public class LodWorldGenerator +{ + public Minecraft mc = Minecraft.getInstance(); + + /** This holds the thread used to generate new LODs off the main thread. */ + private ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator")); + + /** we only want to queue up one generator thread at a time */ + private boolean generatorThreadRunning = false; + + /** + * how many chunks to generate outside of the player's view distance at one + * time. (or more specifically how many requests to make at one time). I + * multiply by 8 to make sure there is always a buffer of chunk requests, to + * make sure the CPU is always busy and we can generate LODs as quickly as + * possible. + */ + public int maxChunkGenRequests; + + /** + * This keeps track of how many chunk generation requests are on going. This is + * to limit how many chunks are queued at once. To prevent chunks from being + * generated for a long time in an area the player is no longer in. + */ + public AtomicInteger numberOfChunksWaitingToGenerate = new AtomicInteger(0); + + public Set positionWaitingToBeGenerated = new HashSet<>(); + + /** Singleton copy of this object */ + public static final LodWorldGenerator INSTANCE = new LodWorldGenerator(); + + private LodWorldGenerator() + { + + } + + /** + * Queues up LodNodeGenWorkers for the given lodDimension. + * + * @param renderer needed so the LodNodeGenWorkers can flag that the + * buffers need to be rebuilt. + */ + public void queueGenerationRequests(LodDimension lodDim, LodRenderer renderer, LodBuilder lodBuilder) + { + if (LodConfig.CLIENT.distanceGenerationMode.get() != DistanceGenerationMode.NONE + && !generatorThreadRunning + && mc.hasSingleplayerServer()) + { + // the thread is now running, don't queue up another thread + generatorThreadRunning = true; + + // just in case the config is changed + maxChunkGenRequests = LodConfig.CLIENT.numberOfWorldGenerationThreads.get() * 16; + + Thread generatorThread = new Thread(() -> + { + try + { + // round the player's block position down to the nearest chunk BlockPos + ChunkPos playerChunkPos = new ChunkPos(mc.player.blockPosition()); + BlockPos playerBlockPosRounded = playerChunkPos.getWorldPosition(); + + // used when determining which chunks are closer when queuing distance + // generation + int minChunkDist = Integer.MAX_VALUE; + + List levelPosListToGen; + List generationRequestList = new ArrayList<>(); + + ArrayList chunksToGen = new ArrayList<>(maxChunkGenRequests); + // if we don't have a full number of chunks to generate in chunksToGen + // we can top it off from this reserve + ArrayList chunksToGenReserve = new ArrayList<>(maxChunkGenRequests); + + // how many level positions to + int requesting = maxChunkGenRequests; + + + /** TODO can give a totally different generation */ + /* + * for (byte detail = LodUtil.BLOCK_DETAIL_LEVEL; detail <= + * LodUtil.REGION_DETAIL_LEVEL; detail++) { if (!posListToGenerate.isEmpty()) + * break; for (byte detailGen = LodUtil.BLOCK_DETAIL_LEVEL; detailGen <= + * LodUtil.REGION_DETAIL_LEVEL; detailGen++) { if (!posListToGenerate.isEmpty()) + * break; posListToGenerate.addAll(lodDim.getDataToGenerate( + * playerBlockPosRounded.getX(), playerBlockPosRounded.getZ(), (int) + * (distancesLinear[detailGen]*1.5), (int) (distancesLinear[detailGen+1]*1.5), + * (byte) distancesGenerators[detailGen].complexity, detail, 16)); + * System.out.println("HERE"); } } + */ + + + //=======================================// + // create the generation Request objects // + //=======================================// + + // start by generating half-region sized blocks... + for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++) + { + if (requesting == 0) + break; + + levelPosListToGen = lodDim.getDataToGenerate( + playerBlockPosRounded.getX(), + playerBlockPosRounded.getZ(), + DetailDistanceUtil.getDistanceGeneration(detailGen), + DetailDistanceUtil.getDistanceGeneration(detailGen + 1), + LodConfig.CLIENT.distanceGenerationMode.get().complexity, + (byte) 9, + requesting / 2); + + for (LevelPos levelPos : levelPosListToGen) + { + generationRequestList.add(new GenerationRequest(levelPos, LodConfig.CLIENT.distanceGenerationMode.get(), DetailDistanceUtil.getLodDetail(detailGen))); + } + requesting = maxChunkGenRequests - generationRequestList.size(); + + } + + // ...then once the world is filled with half-region sized blocks + // fill in the rest + for (byte detailGen = LodConfig.CLIENT.maxGenerationDetail.get().detailLevel; detailGen <= LodUtil.REGION_DETAIL_LEVEL; detailGen++) + { + if (requesting == 0) + break; + + levelPosListToGen = lodDim.getDataToGenerate( + playerBlockPosRounded.getX(), + playerBlockPosRounded.getZ(), + DetailDistanceUtil.getDistanceGeneration(detailGen), + DetailDistanceUtil.getDistanceGeneration(detailGen + 1), + LodConfig.CLIENT.distanceGenerationMode.get().complexity, + DetailDistanceUtil.getLodDetail(detailGen).detailLevel, + maxChunkGenRequests); + + for (LevelPos levelPos : levelPosListToGen) + { + generationRequestList.add(new GenerationRequest(levelPos, LodConfig.CLIENT.distanceGenerationMode.get(), DetailDistanceUtil.getLodDetail(detailGen))); + } + + requesting = maxChunkGenRequests - generationRequestList.size(); + } + + + + //====================================// + // get the closet generation requests // + //====================================// + + // determine which points in the posListToGenerate + // should actually be queued to generate + for (GenerationRequest generationRequest : generationRequestList) + { + ChunkPos chunkPos = generationRequest.getChunkPos(); + + if (numberOfChunksWaitingToGenerate.get() < maxChunkGenRequests) + { + // prevent generating the same chunk multiple times + if (positionWaitingToBeGenerated.contains(chunkPos)) + { + // ClientProxy.LOGGER.debug(pos + " asked to be generated again."); + continue; + } + + // determine if this position is closer to the player + // than the previous + int newDistance = playerChunkPos.getChessboardDistance(chunkPos); + + if (newDistance < minChunkDist) + { + // this chunk is closer, clear any previous + // positions and update the new minimum distance + minChunkDist = newDistance; + + // move all the old chunks into the reserve + ArrayList oldReserve = new ArrayList<>(chunksToGenReserve); + chunksToGenReserve.clear(); + chunksToGenReserve.addAll(chunksToGen); + // top off reserve with whatever was in oldReerve + for (int i = 0; i < oldReserve.size(); i++) + { + if (chunksToGenReserve.size() < maxChunkGenRequests) + chunksToGenReserve.add(oldReserve.get(i)); + else + break; + } + + chunksToGen.clear(); + chunksToGen.add(generationRequest); + } + else if (newDistance == minChunkDist) + { + // this chunk position as close as the minimum distance + if (chunksToGen.size() < maxChunkGenRequests) + { + // we are still under the number of chunks to generate + // add this position to the list + chunksToGen.add(generationRequest); + } + } + else + { + // this chunk is farther away than the minimum distance, + // add it to the reserve to make sure we always have a full reserve + chunksToGenReserve.add(generationRequest); + } + + } // lod null and can generate more chunks + } // positions to generate + + // fill up chunksToGen from the reserve if it isn't full + // already + if (chunksToGen.size() < maxChunkGenRequests) + { + Iterator reserveIterator = chunksToGenReserve.iterator(); + while (chunksToGen.size() < maxChunkGenRequests && reserveIterator.hasNext()) + { + chunksToGen.add(reserveIterator.next()); + } + } + + + + + //=============================// + // start the LodNodeGenWorkers // + //=============================// + + // issue #19 + // TODO add a way for a server side mod to generate chunks requested here + ServerWorld serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension); + + // start chunk generation + for (GenerationRequest generationRequest : generationRequestList) + { + // don't add null chunkPos (which shouldn't happen anyway) + // or add more to the generation queue + ChunkPos chunkPos = generationRequest.getChunkPos(); + if (chunkPos == null || numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests) + continue; + + positionWaitingToBeGenerated.add(chunkPos); + numberOfChunksWaitingToGenerate.addAndGet(1); + LodNodeGenWorker genWorker = new LodNodeGenWorker(chunkPos, generationRequest.generationMode, generationRequest.detail, renderer, lodBuilder, lodDim, serverWorld); + WorldWorkerManager.addWorker(genWorker); + } + + } + catch (Exception e) + { + // this shouldn't ever happen, but just in case + e.printStackTrace(); + } + finally + { + generatorThreadRunning = false; + } + }); + + mainGenThread.execute(generatorThread); + } // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning + } + +} diff --git a/src/main/java/com/seibel/lod/proxy/ClientProxy.java b/src/main/java/com/seibel/lod/proxy/ClientProxy.java index 7ebb2ccca..ff0ab3bc5 100644 --- a/src/main/java/com/seibel/lod/proxy/ClientProxy.java +++ b/src/main/java/com/seibel/lod/proxy/ClientProxy.java @@ -23,6 +23,7 @@ import org.apache.logging.log4j.Logger; import com.seibel.lod.builders.LodBufferBuilder; import com.seibel.lod.builders.LodBuilder; import com.seibel.lod.builders.worldGeneration.LodNodeGenWorker; +import com.seibel.lod.builders.worldGeneration.LodWorldGenerator; import com.seibel.lod.enums.DistanceCalculatorType; import com.seibel.lod.enums.DistanceGenerationMode; import com.seibel.lod.enums.FogDistance; @@ -39,6 +40,7 @@ import com.seibel.lod.util.LodUtil; import net.minecraft.client.Minecraft; import net.minecraft.profiler.IProfiler; import net.minecraft.util.text.StringTextComponent; +import net.minecraftforge.event.TickEvent; import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.ChunkEvent; import net.minecraftforge.event.world.WorldEvent; @@ -49,7 +51,7 @@ import net.minecraftforge.eventbus.api.SubscribeEvent; * and is the starting point for most of this program. * * @author James_Seibel - * @version 8-17-2021 + * @version 8-24-2021 */ public class ClientProxy { @@ -57,8 +59,9 @@ public class ClientProxy private static LodWorld lodWorld = new LodWorld(); private static LodBuilder lodBuilder = new LodBuilder(); - private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(lodBuilder); + private static LodBufferBuilder lodBufferBuilder = new LodBufferBuilder(); private static LodRenderer renderer = new LodRenderer(lodBufferBuilder); + private static LodWorldGenerator lodWorldGenerator = LodWorldGenerator.INSTANCE; private boolean configOverrideReminderPrinted = false; @@ -141,7 +144,7 @@ public class ClientProxy } // LodConfig.CLIENT.drawLODs.set(true); - LodConfig.CLIENT.debugMode.set(false); + LodConfig.CLIENT.debugMode.set(true); LodConfig.CLIENT.maxDrawDetail.set(LodDetail.FULL); LodConfig.CLIENT.maxGenerationDetail.set(LodDetail.FULL); @@ -158,7 +161,7 @@ public class ClientProxy LodConfig.CLIENT.lodDistanceCalculatorType.set(DistanceCalculatorType.LINEAR); LodConfig.CLIENT.lodQuality.set(2); LodConfig.CLIENT.allowUnstableFeatureGeneration.set(false); - LodConfig.CLIENT.numberOfWorldGenerationThreads.set(8); + LodConfig.CLIENT.numberOfWorldGenerationThreads.set(16); // has to be set in the config file // LodConfig.CLIENT.numberOfWorldGenerationThreads.set(16); @@ -169,6 +172,19 @@ public class ClientProxy // forge events // //==============// + @SubscribeEvent + public void serverTickEvent(TickEvent.ServerTickEvent event) + { + if (mc == null || mc.player == null || !lodWorld.getIsWorldLoaded()) + return; + + LodDimension lodDim = lodWorld.getLodDimension(mc.player.level.dimensionType()); + if (lodDim == null) + return; + + lodWorldGenerator.queueGenerationRequests(lodDim, renderer, lodBuilder); + } + @SubscribeEvent public void chunkLoadEvent(ChunkEvent.Load event) { @@ -203,7 +219,7 @@ public class ClientProxy // preventing new LodChunks form being generated LodNodeGenWorker.restartExecuterService(); - lodBufferBuilder.numberOfChunksWaitingToGenerate.set(0); + LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0); // the player has disconnected from a server lodWorld.deselectWorld();