BatchGen: Refactor the experGen into proper generator
This commit is contained in:
@@ -23,6 +23,7 @@ import org.lwjgl.glfw.GLFW;
|
||||
|
||||
import com.seibel.lod.core.api.ClientApi.LagSpikeCatcher;
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.builders.worldGeneration.BatchGenerator;
|
||||
import com.seibel.lod.core.builders.worldGeneration.LodWorldGenerator;
|
||||
import com.seibel.lod.core.enums.WorldType;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
@@ -33,6 +34,7 @@ import com.seibel.lod.core.util.DataPointUtil;
|
||||
import com.seibel.lod.core.util.DetailDistanceUtil;
|
||||
import com.seibel.lod.core.util.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
import com.seibel.lod.core.wrapperInterfaces.IVersionConstants;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.config.ILodConfigWrapperSingleton;
|
||||
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftWrapper;
|
||||
@@ -40,197 +42,206 @@ import com.seibel.lod.core.wrapperInterfaces.world.IDimensionTypeWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* This holds the methods that should be called
|
||||
* by the host mod loader (Fabric, Forge, etc.).
|
||||
* Specifically server and client events.
|
||||
* This holds the methods that should be called by the host mod loader (Fabric,
|
||||
* Forge, etc.). Specifically server and client events.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-12-2021
|
||||
*/
|
||||
public class EventApi
|
||||
{
|
||||
public class EventApi {
|
||||
public static final boolean ENABLE_STACK_DUMP_LOGGING = false;
|
||||
public static final EventApi INSTANCE = new EventApi();
|
||||
|
||||
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
|
||||
private static final IVersionConstants VERSION_CONSTANTS = SingletonHandler.get(IVersionConstants.class);
|
||||
|
||||
/**
|
||||
* can be set if we want to recalculate variables related
|
||||
* to the LOD view distance
|
||||
* can be set if we want to recalculate variables related to the LOD view
|
||||
* distance
|
||||
*/
|
||||
private boolean recalculateWidths = false;
|
||||
|
||||
|
||||
private EventApi()
|
||||
{
|
||||
|
||||
|
||||
private EventApi() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
//=============//
|
||||
|
||||
// =============//
|
||||
// tick events //
|
||||
//=============//
|
||||
|
||||
public void serverTickEvent()
|
||||
{
|
||||
// =============//
|
||||
public BatchGenerator batchGenerator = null;
|
||||
|
||||
public void serverTickEvent() {
|
||||
if (!MC.playerExists() || ApiShared.lodWorld.getIsWorldNotLoaded())
|
||||
return;
|
||||
|
||||
|
||||
LodDimension lodDim = ApiShared.lodWorld.getLodDimension(MC.getCurrentDimension());
|
||||
if (lodDim == null)
|
||||
return;
|
||||
if (ApiShared.isShuttingDown) return;
|
||||
|
||||
LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ApiShared.lodBuilder);
|
||||
if (ApiShared.isShuttingDown)
|
||||
return;
|
||||
|
||||
try {
|
||||
if (VERSION_CONSTANTS.hasBatchGenerationImplementation()) {
|
||||
if (batchGenerator == null)
|
||||
batchGenerator = new BatchGenerator(ApiShared.lodBuilder, lodDim);
|
||||
batchGenerator.queueGenerationRequests(lodDim, ApiShared.lodBuilder);
|
||||
} else {
|
||||
LodWorldGenerator.INSTANCE.queueGenerationRequests(lodDim, ApiShared.lodBuilder);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// Exception may happen if world got unloaded unorderly
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
|
||||
// ==============//
|
||||
// world events //
|
||||
//==============//
|
||||
|
||||
public void worldSaveEvent()
|
||||
{
|
||||
// ==============//
|
||||
|
||||
public void worldSaveEvent() {
|
||||
ApiShared.lodWorld.saveAllDimensions(false); // Do an async save.
|
||||
}
|
||||
|
||||
|
||||
private boolean isCurrentlyOnSinglePlayerServer = false;
|
||||
|
||||
|
||||
/** This is also called when a new dimension loads */
|
||||
public void worldLoadEvent(IWorldWrapper world)
|
||||
{
|
||||
public void worldLoadEvent(IWorldWrapper world) {
|
||||
if (ENABLE_STACK_DUMP_LOGGING)
|
||||
ClientApi.LOGGER.info("WorldLoadEvent called here for "+ (world.getWorldType() == WorldType.ClientWorld ?
|
||||
"clientLevel" : "serverLevel"), new RuntimeException());
|
||||
ClientApi.LOGGER.info(
|
||||
"WorldLoadEvent called here for "
|
||||
+ (world.getWorldType() == WorldType.ClientWorld ? "clientLevel" : "serverLevel"),
|
||||
new RuntimeException());
|
||||
// Always ignore ServerWorld event
|
||||
if (world.getWorldType() == WorldType.ServerWorld) return;
|
||||
if (world.getWorldType() == WorldType.ServerWorld)
|
||||
return;
|
||||
isCurrentlyOnSinglePlayerServer = MC.hasSinglePlayerServer();
|
||||
ApiShared.isShuttingDown = false;
|
||||
DataPointUtil.WORLD_HEIGHT = world.getHeight();
|
||||
LodBuilder.MIN_WORLD_HEIGHT = world.getMinHeight(); // This updates the World height
|
||||
|
||||
//LodNodeGenWorker.restartExecutorService();
|
||||
//ThreadMapUtil.clearMaps();
|
||||
|
||||
|
||||
// LodNodeGenWorker.restartExecutorService();
|
||||
// ThreadMapUtil.clearMaps();
|
||||
|
||||
// the player just loaded a new world/dimension
|
||||
ApiShared.lodWorld.selectWorld(LodUtil.getWorldID(world));
|
||||
|
||||
|
||||
|
||||
// make sure the correct LODs are being rendered
|
||||
// (if this isn't done the previous world's LODs may be drawn)
|
||||
ClientApi.renderer.regenerateLODsNextFrame();
|
||||
}
|
||||
|
||||
|
||||
/** This is also called when the user disconnects from a server+ */
|
||||
public void worldUnloadEvent(IWorldWrapper world)
|
||||
{
|
||||
public void worldUnloadEvent(IWorldWrapper world) {
|
||||
if (ENABLE_STACK_DUMP_LOGGING)
|
||||
ClientApi.LOGGER.info("WorldUnloadEvent called here for "+ (world.getWorldType() == WorldType.ClientWorld ? "clientLevel" : "serverLevel"), new RuntimeException());
|
||||
ClientApi.LOGGER.info(
|
||||
"WorldUnloadEvent called here for "
|
||||
+ (world.getWorldType() == WorldType.ClientWorld ? "clientLevel" : "serverLevel"),
|
||||
new RuntimeException());
|
||||
// If it's single player, ignore the client side world unload event
|
||||
// Note: using isCurrentlyOnSinglePlayerServer as often API call unload event AFTER setting MC to not be in a singlePlayerServer
|
||||
if (isCurrentlyOnSinglePlayerServer && world.getWorldType() == WorldType.ClientWorld) return;
|
||||
|
||||
// Note: using isCurrentlyOnSinglePlayerServer as often API call unload event
|
||||
// AFTER setting MC to not be in a singlePlayerServer
|
||||
if (isCurrentlyOnSinglePlayerServer && world.getWorldType() == WorldType.ClientWorld)
|
||||
return;
|
||||
|
||||
// TODO should "resetMod()" be called here? -James
|
||||
|
||||
|
||||
// if this isn't done unfinished tasks may be left in the queue
|
||||
// preventing new LodChunks form being generated
|
||||
ApiShared.isShuttingDown = true;
|
||||
|
||||
|
||||
// TODO Better report on when world gen is stuck and timeout
|
||||
LodWorldGenerator.INSTANCE.restartExecutorService();
|
||||
|
||||
if (VERSION_CONSTANTS.hasBatchGenerationImplementation()) {
|
||||
if (batchGenerator != null)
|
||||
batchGenerator.stop();
|
||||
batchGenerator = null;
|
||||
} else {
|
||||
LodWorldGenerator.INSTANCE.restartExecutorService();
|
||||
}
|
||||
|
||||
ApiShared.lodWorld.deselectWorld(); // This force a save and shutdown lodDim properly
|
||||
|
||||
|
||||
// prevent issues related to the buffer builder
|
||||
// breaking or retaining previous data when changing worlds.
|
||||
ClientApi.renderer.destroyBuffers();
|
||||
ClientApi.renderer.requestCleanup();
|
||||
GLProxy.ensureAllGLJobCompleted();
|
||||
recalculateWidths = true;
|
||||
|
||||
|
||||
// TODO: Check if after the refactoring, is this still needed
|
||||
ClientApi.renderer = new LodRenderer(ApiShared.lodBufferBuilderFactory);
|
||||
ClientApi.INSTANCE.rendererDisabledBecauseOfExceptions = false;
|
||||
}
|
||||
|
||||
public void blockChangeEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType)
|
||||
{
|
||||
if (dimType != MC.getCurrentDimension()) return;
|
||||
|
||||
public void blockChangeEvent(IChunkWrapper chunk, IDimensionTypeWrapper dimType) {
|
||||
if (dimType != MC.getCurrentDimension())
|
||||
return;
|
||||
// recreate the LOD where the blocks were changed
|
||||
LagSpikeCatcher blockChangeUpdate = new LagSpikeCatcher();
|
||||
ClientApi.INSTANCE.toBeLoaded.add(chunk.getLongChunkPos());
|
||||
blockChangeUpdate.end("clientChunkLoad");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
|
||||
// =============//
|
||||
// Misc Events //
|
||||
//=============//
|
||||
|
||||
public void onKeyInput(int key, int keyAction)
|
||||
{
|
||||
if (CONFIG.client().advanced().debugging().getDebugKeybindingsEnabled())
|
||||
{
|
||||
if (key == GLFW.GLFW_KEY_F8 && keyAction == GLFW.GLFW_PRESS)
|
||||
{
|
||||
CONFIG.client().advanced().debugging().setDebugMode(CONFIG.client().advanced().debugging().getDebugMode().getNext());
|
||||
// =============//
|
||||
|
||||
public void onKeyInput(int key, int keyAction) {
|
||||
if (CONFIG.client().advanced().debugging().getDebugKeybindingsEnabled()) {
|
||||
if (key == GLFW.GLFW_KEY_F8 && keyAction == GLFW.GLFW_PRESS) {
|
||||
CONFIG.client().advanced().debugging()
|
||||
.setDebugMode(CONFIG.client().advanced().debugging().getDebugMode().getNext());
|
||||
MC.sendChatMessage("F8: Set debug mode " + CONFIG.client().advanced().debugging().getDebugMode());
|
||||
}
|
||||
|
||||
if (key == GLFW.GLFW_KEY_F6 && keyAction == GLFW.GLFW_PRESS)
|
||||
{
|
||||
CONFIG.client().advanced().debugging().setDrawLods(!CONFIG.client().advanced().debugging().getDrawLods());
|
||||
|
||||
if (key == GLFW.GLFW_KEY_F6 && keyAction == GLFW.GLFW_PRESS) {
|
||||
CONFIG.client().advanced().debugging()
|
||||
.setDrawLods(!CONFIG.client().advanced().debugging().getDrawLods());
|
||||
MC.sendChatMessage("F6: Set rendering " + CONFIG.client().advanced().debugging().getDrawLods());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// NOTE: This is being called from Render Thread.
|
||||
/** Re-centers the given LodDimension if it needs to be. */
|
||||
public void playerMoveEvent(LodDimension lodDim)
|
||||
{
|
||||
public void playerMoveEvent(LodDimension lodDim) {
|
||||
// make sure the dimension is centered
|
||||
RegionPos playerRegionPos = new RegionPos(MC.getPlayerBlockPos());
|
||||
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - lodDim.getCenterRegionPosX(), playerRegionPos.z - lodDim.getCenterRegionPosZ());
|
||||
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0)
|
||||
{
|
||||
RegionPos center = lodDim.getCenterRegionPos();
|
||||
RegionPos worldRegionOffset = new RegionPos(playerRegionPos.x - center.x, playerRegionPos.z - center.z);
|
||||
if (worldRegionOffset.x != 0 || worldRegionOffset.z != 0) {
|
||||
lodDim.move(worldRegionOffset);
|
||||
//LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z + "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
|
||||
// LOGGER.info("offset: " + worldRegionOffset.x + "," + worldRegionOffset.z +
|
||||
// "\t center: " + lodDim.getCenterX() + "," + lodDim.getCenterZ());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/** Re-sizes all LodDimensions if they need to be. */
|
||||
public void viewDistanceChangedEvent()
|
||||
{
|
||||
public void viewDistanceChangedEvent() {
|
||||
// calculate how wide the dimension(s) should be in regions
|
||||
int chunksWide;
|
||||
if (MC.getWrappedClientWorld().getDimensionType().hasCeiling())
|
||||
chunksWide = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(), LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1;
|
||||
chunksWide = Math.min(CONFIG.client().graphics().quality().getLodChunkRenderDistance(),
|
||||
LodUtil.CEILED_DIMENSION_MAX_RENDER_DISTANCE) * 2 + 1;
|
||||
else
|
||||
chunksWide = CONFIG.client().graphics().quality().getLodChunkRenderDistance() * 2 + 1;
|
||||
|
||||
|
||||
int newWidth = (int) Math.ceil(chunksWide / (float) LodUtil.REGION_WIDTH_IN_CHUNKS);
|
||||
// make sure we have an odd number of regions
|
||||
newWidth += (newWidth & 1) == 0 ? 1 : 0;
|
||||
|
||||
|
||||
// do the dimensions need to change in size?
|
||||
if (ApiShared.lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths)
|
||||
{
|
||||
if (ApiShared.lodBuilder.defaultDimensionWidthInRegions != newWidth || recalculateWidths) {
|
||||
// update the dimensions to fit the new width
|
||||
ApiShared.lodWorld.resizeDimensionRegionWidth(newWidth);
|
||||
ApiShared.lodBuilder.defaultDimensionWidthInRegions = newWidth;
|
||||
ClientApi.renderer.setupBuffers();
|
||||
|
||||
|
||||
recalculateWidths = false;
|
||||
//LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: " + newWidth );
|
||||
// LOGGER.info("new dimension width in regions: " + newWidth + "\t potential: "
|
||||
// + newWidth );
|
||||
}
|
||||
DetailDistanceUtil.updateSettings();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizon mod (formerly the LOD Mod),
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2021 Tom Lee (TomTheFurry) & James Seibel (Original code)
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.lod.core.builders.worldGeneration;
|
||||
|
||||
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.enums.config.GenerationPriority;
|
||||
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.LodUtil;
|
||||
import com.seibel.lod.core.util.SingletonHandler;
|
||||
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;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper.Steps;
|
||||
|
||||
public class BatchGenerator {
|
||||
|
||||
public static final boolean ENABLE_GENERATOR_STATS_LOGGING = false;
|
||||
|
||||
private static final IMinecraftWrapper MC = SingletonHandler.get(IMinecraftWrapper.class);
|
||||
private static final ILodConfigWrapperSingleton CONFIG = SingletonHandler.get(ILodConfigWrapperSingleton.class);
|
||||
private static final IWrapperFactory FACTORY = SingletonHandler.get(IWrapperFactory.class);
|
||||
public AbstractBatchGenerationEnvionmentWrapper generationGroup;
|
||||
public LodDimension targetLodDim;
|
||||
public static final int generationGroupSize = 4;
|
||||
public static int previousThreadCount = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads();
|
||||
|
||||
private int estimatedSampleNeeded = 128;
|
||||
private int estimatedPointsToQueue = 1;
|
||||
|
||||
public BatchGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension) {
|
||||
IWorldWrapper world = LodUtil.getServerWorldFromDimension(newLodDimension.dimension);
|
||||
generationGroup = FACTORY.createBatchGenerator(newLodBuilder, newLodDimension, world);
|
||||
MC.sendChatMessage("NOTE: You are currently using Distant Horizon's Batch Chunk Pre-Generator.");
|
||||
ClientApi.LOGGER.info("1.18 Experimental Chunk Generator initialized");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder) {
|
||||
if (lodDim != targetLodDim) {
|
||||
stop();
|
||||
IWorldWrapper dim = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
generationGroup = FACTORY.createBatchGenerator(lodBuilder, lodDim, dim);
|
||||
targetLodDim = lodDim;
|
||||
ClientApi.LOGGER.info("1.18 Experimental Chunk Generator reinitialized");
|
||||
}
|
||||
|
||||
DistanceGenerationMode mode = CONFIG.client().worldGenerator().getDistanceGenerationMode();
|
||||
int newThreadCount = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads();
|
||||
if (newThreadCount != previousThreadCount) {
|
||||
generationGroup.resizeThreadPool(newThreadCount);
|
||||
previousThreadCount = newThreadCount;
|
||||
}
|
||||
if (estimatedPointsToQueue < newThreadCount)
|
||||
estimatedPointsToQueue = newThreadCount;
|
||||
|
||||
GenerationPriority priority = CONFIG.client().worldGenerator().getGenerationPriority();
|
||||
if (priority == GenerationPriority.AUTO)
|
||||
priority = MC.hasSinglePlayerServer() ? GenerationPriority.FAR_FIRST : GenerationPriority.NEAR_FIRST;
|
||||
|
||||
generationGroup.updateAllFutures();
|
||||
if (!MC.hasSinglePlayerServer())
|
||||
return;
|
||||
int eventsCount = generationGroup.getEventCount();
|
||||
// If we still all jobs running, return.
|
||||
if (eventsCount >= estimatedPointsToQueue) {
|
||||
estimatedPointsToQueue--;
|
||||
if (estimatedPointsToQueue < newThreadCount)
|
||||
estimatedPointsToQueue = newThreadCount;
|
||||
return;
|
||||
}
|
||||
|
||||
final int targetToGenerate = estimatedPointsToQueue - eventsCount;
|
||||
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();
|
||||
|
||||
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(estimatedSampleNeeded, playerPosX, playerPosZ,
|
||||
priority, mode);
|
||||
|
||||
if (eventsCount == 0 && posToGenerate.getNumberOfPos() >= estimatedSampleNeeded) {
|
||||
estimatedPointsToQueue++;
|
||||
if (estimatedPointsToQueue > newThreadCount * 10)
|
||||
estimatedPointsToQueue = newThreadCount * 10;
|
||||
}
|
||||
|
||||
// ClientApi.LOGGER.info("PosToGenerate: {}", posToGenerate);
|
||||
|
||||
// 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.
|
||||
Steps targetStep;
|
||||
switch (mode) {
|
||||
case NONE:
|
||||
targetStep = Steps.Empty; // NOTE: Only load in existing chunks. No new chunk generation
|
||||
break;
|
||||
case BIOME_ONLY:
|
||||
targetStep = Steps.Biomes; // NOTE: No block. Require fake height in LodBuilder
|
||||
break;
|
||||
case BIOME_ONLY_SIMULATE_HEIGHT:
|
||||
targetStep = Steps.Noise; // NOTE: Stone only. Require fake surface
|
||||
break;
|
||||
case SURFACE:
|
||||
targetStep = Steps.Surface; // Carvers or Surface???
|
||||
break;
|
||||
case FEATURES:
|
||||
case FULL:
|
||||
targetStep = Steps.Features;
|
||||
break;
|
||||
default:
|
||||
assert false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (ENABLE_GENERATOR_STATS_LOGGING)
|
||||
ClientApi.LOGGER.info("WorldGen. Near:" + posToGenerate.getNumberOfNearPos() + " Far:"
|
||||
+ posToGenerate.getNumberOfFarPos());
|
||||
if (priority == GenerationPriority.FAR_FIRST || priority == GenerationPriority.BALANCED) {
|
||||
|
||||
int nearCount = posToGenerate.getNumberOfNearPos();
|
||||
int farCount = posToGenerate.getNumberOfFarPos();
|
||||
if (ENABLE_GENERATOR_STATS_LOGGING)
|
||||
ClientApi.LOGGER.info("WorldGen. Near:" + nearCount + " Far:" + farCount);
|
||||
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++;
|
||||
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));
|
||||
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
|
||||
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) {
|
||||
toGenerate--;
|
||||
}
|
||||
}
|
||||
if (toGenerate <= 0)
|
||||
break;
|
||||
|
||||
// We have nearPos to go though
|
||||
if (i < nearCount && posToGenerate.getNthDetail(i, true) != 0) {
|
||||
positionGoneThough++;
|
||||
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));
|
||||
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
|
||||
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) {
|
||||
toGenerate--;
|
||||
}
|
||||
}
|
||||
|
||||
if (toGenerate <= 0)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
int nearCount = posToGenerate.getNumberOfNearPos();
|
||||
for (int i = 0; i < nearCount; i++) {
|
||||
|
||||
// We have nearPos to go though
|
||||
if (posToGenerate.getNthDetail(i, true) != 0) {
|
||||
positionGoneThough++;
|
||||
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));
|
||||
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
|
||||
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) {
|
||||
toGenerate--;
|
||||
}
|
||||
if (toGenerate <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Only do far gen if toGenerate is non 0 and that we have requested all samples
|
||||
// we can get.
|
||||
if (toGenerate > 0 && estimatedSampleNeeded > posToGenerate.getNumberOfPos()) {
|
||||
int farCount = posToGenerate.getNumberOfFarPos();
|
||||
for (int i = 0; i < farCount; i++) {
|
||||
// We have farPos to go though
|
||||
if (posToGenerate.getNthDetail(i, false) != 0) {
|
||||
positionGoneThough++;
|
||||
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));
|
||||
int genSize = detailLevel > LodUtil.CHUNK_DETAIL_LEVEL ? 0 : generationGroupSize;
|
||||
if (generationGroup.tryAddPoint(chunkX, chunkZ, genSize, targetStep)) {
|
||||
toGenerate--;
|
||||
}
|
||||
}
|
||||
if (toGenerate <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (targetToGenerate != toGenerate && ENABLE_GENERATOR_STATS_LOGGING) {
|
||||
if (toGenerate <= 0) {
|
||||
ClientApi.LOGGER.info(
|
||||
"WorldGenerator: Sampled " + posToGenerate.getNumberOfPos() + " out of " + estimatedSampleNeeded
|
||||
+ " points, started all targeted " + targetToGenerate + " generations.");
|
||||
} else {
|
||||
ClientApi.LOGGER.info("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;
|
||||
if (ENABLE_GENERATOR_STATS_LOGGING)
|
||||
ClientApi.LOGGER.info("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;
|
||||
if (ENABLE_GENERATOR_STATS_LOGGING)
|
||||
ClientApi.LOGGER.info("WorldGenerator: Decreasing estimatedSampleNeeeded to " + estimatedSampleNeeded);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
ClientApi.LOGGER.info("1.18 Experimental Chunk Generator shutting down...");
|
||||
generationGroup.stop();
|
||||
}
|
||||
|
||||
}
|
||||
+133
-161
@@ -43,230 +43,206 @@ import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
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 com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||
|
||||
/**
|
||||
* A singleton that handles all long distance LOD world generation.
|
||||
*
|
||||
* @author Leonardo Amato
|
||||
* @author James Seibel
|
||||
* @version 12-11-2021
|
||||
*/
|
||||
public class LodWorldGenerator
|
||||
{
|
||||
public class LodWorldGenerator {
|
||||
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);
|
||||
|
||||
|
||||
/** 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", 1));
|
||||
private ExecutorService genSubThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(),
|
||||
|
||||
/**
|
||||
* 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", 1));
|
||||
private ExecutorService genSubThreads = Executors.newFixedThreadPool(
|
||||
CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(),
|
||||
new LodThreadFactory("Gen-Worker-Thread", 1));
|
||||
|
||||
|
||||
|
||||
/** we only want to queue up one generator thread at a time */
|
||||
private boolean generatorThreadRunning = false;
|
||||
|
||||
|
||||
/**
|
||||
* 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 final Set<AbstractChunkPosWrapper> positionsWaitingToBeGenerated = new HashSet<>();
|
||||
|
||||
|
||||
/**
|
||||
* Singleton copy of this object
|
||||
*/
|
||||
public static final LodWorldGenerator INSTANCE = new LodWorldGenerator();
|
||||
public AbstractExperimentalWorldGeneratorWrapper experimentalWorldGenerator;
|
||||
|
||||
private LodWorldGenerator() {}
|
||||
|
||||
|
||||
private LodWorldGenerator() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues up LodNodeGenWorkers for the given lodDimension.
|
||||
* renderer needed so the LodNodeGenWorkers can flag that the
|
||||
* buffers need to be rebuilt.
|
||||
* Queues up LodNodeGenWorkers for the given lodDimension. renderer needed so
|
||||
* the LodNodeGenWorkers can flag that the buffers need to be rebuilt.
|
||||
*/
|
||||
public void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder)
|
||||
{
|
||||
if (!CONFIG.client().worldGenerator().getEnableDistantGeneration()) return;
|
||||
|
||||
IWorldWrapper world = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
|
||||
// TODO: Rename the config option
|
||||
if (experimentalWorldGenerator == null) {
|
||||
try {
|
||||
experimentalWorldGenerator = WRAPPER_FACTORY.createExperimentalWorldGenerator(lodBuilder, lodDim, world);
|
||||
} catch (RuntimeException e) {
|
||||
// Exception may happen if world got unloaded unorderly
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (experimentalWorldGenerator != null) {
|
||||
experimentalWorldGenerator.queueGenerationRequests(lodDim, lodBuilder);
|
||||
public void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder) {
|
||||
if (!CONFIG.client().worldGenerator().getEnableDistantGeneration())
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: This currently doesn't use the DetailDistanceUtil.getDistanceGenerationMode(int detail) to get the mode.
|
||||
// This is fine currently since DistanceGenerationMode doesn't care about the detail level for now.
|
||||
|
||||
// TODO: This currently doesn't use the
|
||||
// DetailDistanceUtil.getDistanceGenerationMode(int detail) to get the mode.
|
||||
// This is fine currently since DistanceGenerationMode doesn't care about the
|
||||
// detail level for now.
|
||||
// However, If that was to be changed, This will need to be fixed.
|
||||
DistanceGenerationMode mode = CONFIG.client().worldGenerator().getDistanceGenerationMode();
|
||||
final GenerationPriority priority;
|
||||
if (CONFIG.client().worldGenerator().getGenerationPriority() == GenerationPriority.AUTO)
|
||||
priority = MC.hasSinglePlayerServer() ? GenerationPriority.FAR_FIRST : GenerationPriority.NEAR_FIRST;
|
||||
else priority = CONFIG.client().worldGenerator().getGenerationPriority();
|
||||
|
||||
if (mode != DistanceGenerationMode.NONE
|
||||
&& !generatorThreadRunning
|
||||
&& MC.hasSinglePlayerServer())
|
||||
{
|
||||
else
|
||||
priority = CONFIG.client().worldGenerator().getGenerationPriority();
|
||||
|
||||
if (mode != DistanceGenerationMode.NONE && !generatorThreadRunning && MC.hasSinglePlayerServer()) {
|
||||
// the thread is now running, don't queue up another thread
|
||||
generatorThreadRunning = true;
|
||||
|
||||
|
||||
/**
|
||||
* How many chunks to generate outside 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.
|
||||
* How many chunks to generate outside 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.
|
||||
*/
|
||||
int genRequestPerThread = VERSION_CONSTANTS.getWorldGenerationCountPerThread();
|
||||
int maxChunkGenRequests;
|
||||
if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(mode))
|
||||
maxChunkGenRequests = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads() * genRequestPerThread;
|
||||
else maxChunkGenRequests = genRequestPerThread;
|
||||
|
||||
Runnable generatorFunc = (() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
maxChunkGenRequests = CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads()
|
||||
* genRequestPerThread;
|
||||
else
|
||||
maxChunkGenRequests = genRequestPerThread;
|
||||
|
||||
Runnable generatorFunc = (() -> {
|
||||
try {
|
||||
// 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 //
|
||||
//=======================================//
|
||||
|
||||
// =======================================//
|
||||
|
||||
IWorldWrapper serverWorld = LodUtil.getServerWorldFromDimension(lodDim.dimension);
|
||||
|
||||
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(
|
||||
maxChunkGenRequests, playerPosX, playerPosZ, priority, mode);
|
||||
|
||||
|
||||
PosToGenerateContainer posToGenerate = lodDim.getPosToGenerate(maxChunkGenRequests, playerPosX,
|
||||
playerPosZ, priority, mode);
|
||||
|
||||
byte detailLevel;
|
||||
int posX;
|
||||
int posZ;
|
||||
int nearIndex = 0;
|
||||
int farIndex = 0;
|
||||
|
||||
for (int i = 0; i < posToGenerate.getNumberOfPos(); i++)
|
||||
{
|
||||
// I wish there was a way to compress this code, but I'm not aware of
|
||||
|
||||
for (int i = 0; i < posToGenerate.getNumberOfPos(); i++) {
|
||||
// I wish there was a way to compress this code, but I'm not aware of
|
||||
// an easy way to do so.
|
||||
|
||||
|
||||
// add the near positions
|
||||
if (nearIndex < posToGenerate.getNumberOfNearPos() && posToGenerate.getNthDetail(nearIndex, true) != 0)
|
||||
{
|
||||
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++;
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(
|
||||
LevelPosUtil.getChunkPos(detailLevel, posX),
|
||||
LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||
|
||||
// prevent generating the same chunk multiple times
|
||||
if (positionsWaitingToBeGenerated.contains(chunkPos))
|
||||
continue;
|
||||
|
||||
|
||||
// don't add more to the generation queue then allowed
|
||||
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
|
||||
break;
|
||||
|
||||
|
||||
positionsWaitingToBeGenerated.add(chunkPos);
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
queueWork(chunkPos, mode, lodBuilder, lodDim, serverWorld);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// add the far positions
|
||||
// But if priority is NEAR_FIRST, we only do that if near pos has ran out.
|
||||
if ((nearIndex >= posToGenerate.getNumberOfNearPos() || priority != GenerationPriority.NEAR_FIRST) &&
|
||||
farIndex < posToGenerate.getNumberOfFarPos() && posToGenerate.getNthDetail(farIndex, false) != 0)
|
||||
{
|
||||
if ((nearIndex >= posToGenerate.getNumberOfNearPos()
|
||||
|| priority != GenerationPriority.NEAR_FIRST)
|
||||
&& 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++;
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(LevelPosUtil.getChunkPos(detailLevel, posX), LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||
|
||||
|
||||
AbstractChunkPosWrapper chunkPos = WRAPPER_FACTORY.createChunkPos(
|
||||
LevelPosUtil.getChunkPos(detailLevel, posX),
|
||||
LevelPosUtil.getChunkPos(detailLevel, posZ));
|
||||
|
||||
// don't add more to the generation queue then allowed
|
||||
if (numberOfChunksWaitingToGenerate.get() >= maxChunkGenRequests)
|
||||
continue;
|
||||
//break;
|
||||
|
||||
// break;
|
||||
|
||||
// prevent generating the same chunk multiple times
|
||||
if (positionsWaitingToBeGenerated.contains(chunkPos))
|
||||
continue;
|
||||
|
||||
|
||||
positionsWaitingToBeGenerated.add(chunkPos);
|
||||
numberOfChunksWaitingToGenerate.addAndGet(1);
|
||||
queueWork(chunkPos, mode, lodBuilder, lodDim, serverWorld);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
|
||||
} catch (RuntimeException e) {
|
||||
// this shouldn't ever happen, but just in case
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
} finally {
|
||||
generatorThreadRunning = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(mode))
|
||||
{
|
||||
|
||||
if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(mode)) {
|
||||
generatorFunc.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
mainGenThread.execute(generatorFunc);
|
||||
}
|
||||
} // if distanceGenerationMode != DistanceGenerationMode.NONE && !generatorThreadRunning
|
||||
} // if distanceGenerationMode != DistanceGenerationMode.NONE &&
|
||||
// !generatorThreadRunning
|
||||
} // queueGenerationRequests
|
||||
|
||||
|
||||
private void queueWork(AbstractChunkPosWrapper newPos, DistanceGenerationMode newGenerationMode,
|
||||
LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, IWorldWrapper serverWorld)
|
||||
{
|
||||
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 (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(newGenerationMode))
|
||||
{
|
||||
|
||||
Runnable method = (() -> {
|
||||
generateChunk(newPos, newGenerationMode, newLodBuilder, newLodDimension, serverWorld);
|
||||
});
|
||||
|
||||
if (VERSION_CONSTANTS.isWorldGeneratorSingleThreaded(newGenerationMode)) {
|
||||
// --Note: This is now using version constants--
|
||||
// if we are using FULL generation there is no reason
|
||||
// to queue up a bunch of generation requests,
|
||||
@@ -278,32 +254,29 @@ public class LodWorldGenerator
|
||||
// threaded. So to allow that, we check the boolean for
|
||||
// whether the wrapper requires single thread
|
||||
method.run();
|
||||
}
|
||||
else
|
||||
{
|
||||
} 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)
|
||||
{
|
||||
|
||||
private void generateChunk(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode,
|
||||
LodBuilder newLodBuilder, LodDimension lodDim, IWorldWrapper worldWrapper) {
|
||||
// try
|
||||
{
|
||||
AbstractWorldGeneratorWrapper worldGenWrapper = WRAPPER_FACTORY.createWorldGenerator(newLodBuilder, lodDim, worldWrapper);
|
||||
AbstractWorldGeneratorWrapper 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)
|
||||
{
|
||||
|
||||
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;
|
||||
@@ -334,59 +307,58 @@ public class LodWorldGenerator
|
||||
|
||||
// shows the pool size, active threads, queued tasks and completed tasks
|
||||
// ClientProxy.LOGGER.info(genThreads.toString());
|
||||
|
||||
}// if in range
|
||||
|
||||
} // if in range
|
||||
}
|
||||
// catch (Exception e)
|
||||
// {
|
||||
// ClientApi.LOGGER.error(LodWorldGenerator.class.getSimpleName() + ": ran into an error: " + e.getMessage());
|
||||
// e.printStackTrace();
|
||||
// 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. <br><br>
|
||||
* Stops the current genThreads if they are running and then recreates the
|
||||
* Executor service. <br>
|
||||
* <br>
|
||||
* <p>
|
||||
* 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.
|
||||
* 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 (experimentalWorldGenerator != null) {
|
||||
experimentalWorldGenerator.stop();
|
||||
experimentalWorldGenerator = null;
|
||||
}
|
||||
|
||||
if (genSubThreads != null && !genSubThreads.isShutdown())
|
||||
{
|
||||
public void restartExecutorService() {
|
||||
|
||||
if (genSubThreads != null && !genSubThreads.isShutdown()) {
|
||||
ClientApi.LOGGER.info("Blocking until generator sub threads terminated!!");
|
||||
try {
|
||||
mainGenThread.shutdownNow();
|
||||
genSubThreads.shutdownNow();
|
||||
boolean worked = genSubThreads.awaitTermination(30, TimeUnit.SECONDS);
|
||||
if (!worked)
|
||||
ClientApi.LOGGER.error("Generator sub threads timed out! May cause crash on game exit due to cleanup failure.");
|
||||
ClientApi.LOGGER.error(
|
||||
"Generator sub threads timed out! May cause crash on game exit due to cleanup failure.");
|
||||
} catch (InterruptedException e) {
|
||||
ClientApi.LOGGER.error("Generator sub threads shutdown is interrupted! May cause crash on game exit due to cleanup failure.");
|
||||
ClientApi.LOGGER.error(
|
||||
"Generator sub threads shutdown is interrupted! May cause crash on game exit due to cleanup failure.");
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
genSubThreads = Executors.newFixedThreadPool(CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(),
|
||||
genSubThreads = Executors.newFixedThreadPool(
|
||||
CONFIG.client().advanced().threading().getNumberOfWorldGenerationThreads(),
|
||||
new ThreadFactoryBuilder().setNameFormat("Gen-Worker-Thread-%d").build());
|
||||
}
|
||||
}
|
||||
// Doing this instead of setting it to 0 because even if shutdown fail, it won't cause the int to underflow below 0 afterwards
|
||||
// Doing this instead of setting it to 0 because even if shutdown fail, it won't
|
||||
// cause the int to underflow below 0 afterwards
|
||||
numberOfChunksWaitingToGenerate = new AtomicInteger(0);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,29 +4,34 @@ import com.seibel.lod.core.enums.config.DistanceGenerationMode;
|
||||
|
||||
/**
|
||||
* A singleton that contains variables specific to each version of Minecraft
|
||||
* which can be used to change how DH-Core runs.
|
||||
* For example: After MC 1.17 blocks can be negative, which changes how we generate LODs.
|
||||
* which can be used to change how DH-Core runs. For example: After MC 1.17
|
||||
* blocks can be negative, which changes how we generate LODs.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 12-11-2021
|
||||
*/
|
||||
public interface IVersionConstants
|
||||
{
|
||||
public interface IVersionConstants {
|
||||
/** @returns the minimum height blocks can be generated */
|
||||
int getMinimumWorldHeight();
|
||||
|
||||
|
||||
/**
|
||||
* @Returns True if the given DistanceGenerationMode can be run on our own thread. <br>
|
||||
* False if the generation must be run on Minecraft's server thread.
|
||||
|
||||
/**
|
||||
* @Returns True if the given DistanceGenerationMode can be run on our own
|
||||
* thread. <br>
|
||||
* False if the generation must be run on Minecraft's server thread.
|
||||
*/
|
||||
boolean isWorldGeneratorSingleThreaded(DistanceGenerationMode distanceGenerationMode);
|
||||
|
||||
/**
|
||||
/**
|
||||
* @Returns True if BatchGeneration is implemented <br>
|
||||
* False if it is not supported
|
||||
*/
|
||||
boolean hasBatchGenerationImplementation();
|
||||
|
||||
/**
|
||||
* @Returns the number of generations call per thread.
|
||||
*/
|
||||
default int getWorldGenerationCountPerThread() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.wrapperInterfaces.block.AbstractBlockPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvionmentWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractExperimentalWorldGeneratorWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGeneratorWrapper;
|
||||
|
||||
@@ -33,22 +34,26 @@ import com.seibel.lod.core.wrapperInterfaces.worldGeneration.AbstractWorldGenera
|
||||
* @author James Seibel
|
||||
* @version 12-14-2021
|
||||
*/
|
||||
public interface IWrapperFactory
|
||||
{
|
||||
public interface IWrapperFactory {
|
||||
AbstractBlockPosWrapper createBlockPos();
|
||||
|
||||
AbstractBlockPosWrapper createBlockPos(int x, int y, int z);
|
||||
|
||||
|
||||
|
||||
AbstractChunkPosWrapper createChunkPos();
|
||||
|
||||
AbstractChunkPosWrapper createChunkPos(long xAndZPositionCombined);
|
||||
|
||||
AbstractChunkPosWrapper createChunkPos(int x, int z);
|
||||
|
||||
AbstractChunkPosWrapper createChunkPos(AbstractChunkPosWrapper newChunkPos);
|
||||
|
||||
AbstractChunkPosWrapper createChunkPos(AbstractBlockPosWrapper blockPos);
|
||||
|
||||
|
||||
AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper);
|
||||
// Return null to signal that there is no AbstractWorldGenerator
|
||||
default AbstractExperimentalWorldGeneratorWrapper createExperimentalWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) {
|
||||
|
||||
AbstractWorldGeneratorWrapper createWorldGenerator(LodBuilder newLodBuilder, LodDimension newLodDimension,
|
||||
IWorldWrapper worldWrapper);
|
||||
|
||||
default AbstractBatchGenerationEnvionmentWrapper createBatchGenerator(LodBuilder newLodBuilder,
|
||||
LodDimension newLodDimension, IWorldWrapper worldWrapper) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.seibel.lod.core.wrapperInterfaces.worldGeneration;
|
||||
|
||||
import com.seibel.lod.core.builders.lodBuilding.LodBuilder;
|
||||
import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
public abstract class AbstractBatchGenerationEnvionmentWrapper {
|
||||
public static enum Steps {
|
||||
Empty, StructureStart, StructureReference, Biomes, Noise, Surface, Carvers, LiquidCarvers, Features, Light,
|
||||
}
|
||||
|
||||
public AbstractBatchGenerationEnvionmentWrapper(IWorldWrapper serverLevel, LodBuilder lodBuilder,
|
||||
LodDimension lodDim) {
|
||||
}
|
||||
|
||||
public abstract void resizeThreadPool(int newThreadCount);
|
||||
|
||||
public abstract void updateAllFutures();
|
||||
|
||||
public abstract int getEventCount();
|
||||
|
||||
public abstract boolean tryAddPoint(int chunkX, int chunkZ, int genSize, Steps targetStep);
|
||||
|
||||
public abstract void stop();
|
||||
}
|
||||
+5
-1
@@ -5,7 +5,11 @@ import com.seibel.lod.core.objects.lod.LodDimension;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
public abstract class AbstractExperimentalWorldGeneratorWrapper {
|
||||
public AbstractExperimentalWorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { }
|
||||
public AbstractExperimentalWorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension,
|
||||
IWorldWrapper worldWrapper) {
|
||||
}
|
||||
|
||||
public abstract void queueGenerationRequests(LodDimension lodDim, LodBuilder lodBuilder);
|
||||
|
||||
public abstract void stop();
|
||||
}
|
||||
|
||||
+11
-12
@@ -26,25 +26,24 @@ import com.seibel.lod.core.wrapperInterfaces.chunk.AbstractChunkPosWrapper;
|
||||
import com.seibel.lod.core.wrapperInterfaces.world.IWorldWrapper;
|
||||
|
||||
/**
|
||||
* This is used for generating chunks
|
||||
* in a variety of detail and threading levels.
|
||||
* This is used for generating chunks in a variety of detail and threading
|
||||
* levels.
|
||||
* <p>
|
||||
* Abstract instead of an interface, so
|
||||
* we can define its constructors.
|
||||
* Abstract instead of an interface, so we can define its constructors.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 11-20-2021
|
||||
*/
|
||||
public abstract class AbstractWorldGeneratorWrapper
|
||||
{
|
||||
public AbstractWorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension, IWorldWrapper worldWrapper) { }
|
||||
|
||||
|
||||
public abstract class AbstractWorldGeneratorWrapper {
|
||||
public AbstractWorldGeneratorWrapper(LodBuilder newLodBuilder, LodDimension newLodDimension,
|
||||
IWorldWrapper worldWrapper) {
|
||||
}
|
||||
|
||||
public abstract void generateBiomesOnly(AbstractChunkPosWrapper pos, DistanceGenerationMode generationMode);
|
||||
|
||||
|
||||
public abstract void generateSurface(AbstractChunkPosWrapper pos);
|
||||
|
||||
|
||||
public abstract void generateFeatures(AbstractChunkPosWrapper pos);
|
||||
|
||||
|
||||
public abstract void generateFull(AbstractChunkPosWrapper pos);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user