diff --git a/src/main/java/com/seibel/lod/core/api/EventApi.java b/src/main/java/com/seibel/lod/core/api/EventApi.java
index c1aa845de..d5c9ea638 100644
--- a/src/main/java/com/seibel/lod/core/api/EventApi.java
+++ b/src/main/java/com/seibel/lod/core/api/EventApi.java
@@ -21,7 +21,6 @@ package com.seibel.lod.core.api;
import org.lwjgl.glfw.GLFW;
-import com.seibel.lod.core.builders.worldGeneration.LodGenWorker;
import com.seibel.lod.core.builders.worldGeneration.LodWorldGenerator;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.objects.lod.LodDimension;
@@ -81,7 +80,8 @@ public class EventApi
if (lodDim == null)
return;
- LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ClientApi.renderer, ApiShared.lodBuilder);
+ // FIXME: This is in server thread. We shouldn't be accessing the client's renderer!
+ LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ApiShared.lodBuilder);
}
@@ -147,7 +147,7 @@ public class EventApi
// if this isn't done unfinished tasks may be left in the queue
// preventing new LodChunks form being generated
- LodGenWorker.restartExecutorService();
+ LodWorldGenerator.INSTANCE.restartExecutorService();
LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.set(0);
ApiShared.lodWorld.deselectWorld();
diff --git a/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodGenWorker.java b/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodGenWorker.java
deleted file mode 100644
index 55f74dd8d..000000000
--- a/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodGenWorker.java
+++ /dev/null
@@ -1,212 +0,0 @@
-/*
- * This file is part of the Distant Horizon mod (formerly the LOD Mod),
- * licensed under the GNU GPL v3 License.
- *
- * Copyright (C) 2020 James Seibel
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, version 3.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see
- *
- * This is done to clear any outstanding tasks
- * that may exist after the player leaves their current world.
- * If this isn't done unfinished tasks may be left in the queue
- * preventing new LodChunks form being generated.
- */
- public static void restartExecutorService()
- {
- if (genThreads != null && !genThreads.isShutdown())
- {
- genThreads.shutdownNow();
- }
- genThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
- }
-
-}
diff --git a/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java b/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java
index e97498c7c..176d8ba1e 100644
--- a/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java
+++ b/src/main/java/com/seibel/lod/core/builders/worldGeneration/LodWorldGenerator.java
@@ -25,11 +25,12 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
+import com.google.common.util.concurrent.ThreadFactoryBuilder;
+import com.seibel.lod.core.api.ClientApi;
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
import com.seibel.lod.core.enums.config.DistanceGenerationMode;
import com.seibel.lod.core.objects.PosToGenerateContainer;
import com.seibel.lod.core.objects.lod.LodDimension;
-import com.seibel.lod.core.render.LodRenderer;
import com.seibel.lod.core.util.DetailDistanceUtil;
import com.seibel.lod.core.util.LevelPosUtil;
import com.seibel.lod.core.util.LodThreadFactory;
@@ -56,6 +57,9 @@ public class LodWorldGenerator
/** This holds the thread used to create LOD generation requests off the main thread. */
private final ExecutorService mainGenThread = Executors.newSingleThreadExecutor(new LodThreadFactory(this.getClass().getSimpleName() + " world generator"));
+ private ExecutorService genSubThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(),
+ new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
+
/** we only want to queue up one generator thread at a time */
private boolean generatorThreadRunning = false;
@@ -83,19 +87,14 @@ public class LodWorldGenerator
*/
public static final LodWorldGenerator INSTANCE = new LodWorldGenerator();
-
-
- private 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)
+ public void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder)
{
if (CONFIG.client().worldGenerator().getDistanceGenerationMode() != DistanceGenerationMode.NONE
&& !generatorThreadRunning
@@ -107,7 +106,7 @@ public class LodWorldGenerator
// just in case the config changed
maxChunkGenRequests = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads() * 8;
- Thread generatorThread = new Thread(() ->
+ Runnable generatorFunc = (() ->
{
try
{
@@ -159,8 +158,7 @@ public class LodWorldGenerator
positionsWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
- LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
- genWorker.queueWork();
+ queueWork(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
}
@@ -185,8 +183,7 @@ public class LodWorldGenerator
positionsWaitingToBeGenerated.add(chunkPos);
numberOfChunksWaitingToGenerate.addAndGet(1);
- LodGenWorker genWorker = new LodGenWorker(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
- genWorker.queueWork();
+ queueWork(chunkPos, DetailDistanceUtil.getDistanceGenerationMode(detailLevel), lodBuilder, lodDim, serverWorld);
}
}
@@ -201,9 +198,138 @@ public class LodWorldGenerator
generatorThreadRunning = false;
}
});
-
- mainGenThread.execute(generatorThread);
+ if (WRAPPER_FACTORY.isWorldGeneratorSingleThreaded()) {
+ generatorFunc.run();
+ } else {
+ mainGenThread.execute(generatorFunc);
+ }
} // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning
} // queueGenerationRequests
+ private void queueWork(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
+ LodBuilder newLodBuilder,
+ LodDimension newLodDimension, IWorldWrapper serverWorld)
+ {
+ // just a few sanity checks
+ if (newPos == null)
+ throw new IllegalArgumentException("LodChunkGenWorker must have a non-null ChunkPos");
+
+ if (newLodBuilder == null)
+ throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodChunkBuilder");
+
+ if (newLodDimension == null)
+ throw new IllegalArgumentException("LodChunkGenThread requires a non-null LodDimension");
+
+ if (serverWorld == null)
+ throw new IllegalArgumentException("LodChunkGenThread requires a non-null ServerWorld");
+
+ Runnable method = (() -> {generateChunk(newPos, newGenerationMode,
+ newLodBuilder, newLodDimension, serverWorld);});
+
+ if (CONFIG.client().worldGenerator().getDistanceGenerationMode() == DistanceGenerationMode.FULL
+ || WRAPPER_FACTORY.isWorldGeneratorSingleThreaded())
+ {
+ // if we are using FULL generation there is no reason
+ // to queue up a bunch of generation requests,
+ // because MC's internal server (as of 1.16.5) only
+ // responds with a single thread. And we don't
+ // want to cause more lag than necessary or queue up
+ // requests that may end up being unneeded.
+ // In 1.17+, world generation becomes completely single
+ // threaded. So to allow that, we check the boolean for
+ // whether the wrapper requires single thread
+ method.run();
+ }
+ else
+ {
+ // Every other method can
+ // be done asynchronously
+ genSubThreads.execute(method);
+ }
+
+ // useful for debugging
+// ClientProxy.LOGGER.info(thread.lodDim.getNumberOfLods());
+// ClientProxy.LOGGER.info(genThreads.toString());
+ }
+
+ private void generateChunk(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode,
+ LodBuilder newLodBuilder, LodDimension lodDim, IWorldWrapper worldWrapper)
+ {
+ // try
+ {
+ var worldGenWrapper = WRAPPER_FACTORY.createWorldGenerator(newLodBuilder, lodDim, worldWrapper);
+ // only generate LodChunks if they can
+ // be added to the current LodDimension
+
+ if (lodDim.regionIsInRange(pos.getX() / LodUtil.REGION_WIDTH_IN_CHUNKS, pos.getZ() / LodUtil.REGION_WIDTH_IN_CHUNKS))
+ {
+ switch (generationMode)
+ {
+ case NONE:
+ // don't generate
+ break;
+ case BIOME_ONLY:
+ case BIOME_ONLY_SIMULATE_HEIGHT:
+ // fastest
+ worldGenWrapper.generateBiomesOnly(pos, generationMode);
+ break;
+ case SURFACE:
+ // faster
+ worldGenWrapper.generateSurface(pos);
+ break;
+ case FEATURES:
+ // fast
+ worldGenWrapper.generateFeatures(pos);
+ break;
+ case FULL:
+ // very slow
+ worldGenWrapper.generateFull(pos);
+ break;
+ }
+
+// boolean dataExistence = lodDim.doesDataExist(new LevelPos((byte) 3, pos.x, pos.z));
+// if (dataExistence)
+// ClientProxy.LOGGER.info(pos.x + " " + pos.z + " Success!");
+// else
+// ClientProxy.LOGGER.info(pos.x + " " + pos.z);
+
+ // shows the pool size, active threads, queued tasks and completed tasks
+// ClientProxy.LOGGER.info(genThreads.toString());
+
+ }// if in range
+ }
+ // catch (Exception e)
+ // {
+ // ClientApi.LOGGER.error(LodWorldGenerator.class.getSimpleName() + ": ran into an error: " + e.getMessage());
+ // e.printStackTrace();
+ // }
+ // finally
+ {
+ // decrement how many threads are running
+ LodWorldGenerator.INSTANCE.numberOfChunksWaitingToGenerate.addAndGet(-1);
+
+ // this position is no longer being generated
+ LodWorldGenerator.INSTANCE.positionsWaitingToBeGenerated.remove(pos);
+ }
+ }// run
+
+ /**
+ * Stops the current genThreads if they are running
+ * and then recreates the Executor service.
+ *
+ * This is done to clear any outstanding tasks + * that may exist after the player leaves their current world. + * If this isn't done unfinished tasks may be left in the queue + * preventing new LodChunks form being generated. + */ + public void restartExecutorService() + { + if (genSubThreads != null && !genSubThreads.isShutdown()) + { + genSubThreads.shutdownNow(); + } + genSubThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(), + new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build()); + } + } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java index 123aa33f7..980cdcf31 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java @@ -45,4 +45,7 @@ public interface IWrapperFactory AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper); + + // FIXME: A temp getter for chunk gen mutli thread settings. Using default for backward compatibility + default boolean isWorldGeneratorSingleThreaded() {return false;} }