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 8f2f6cdd9..e18b64739 100644 --- a/src/main/java/com/seibel/lod/core/api/EventApi.java +++ b/src/main/java/com/seibel/lod/core/api/EventApi.java @@ -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(); } - - + } diff --git a/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java b/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java new file mode 100644 index 000000000..abf2e1b2c --- /dev/null +++ b/src/main/java/com/seibel/lod/core/builders/worldGeneration/BatchGenerator.java @@ -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 . + */ + +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(); + } + +} 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 6eef1501a..af0ec3190 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 @@ -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 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.

+ * 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. + * 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); } - + } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IVersionConstants.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IVersionConstants.java index 1a04b2a62..3261afa43 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IVersionConstants.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IVersionConstants.java @@ -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.
- * 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.
+ * False if the generation must be run on Minecraft's server thread. */ boolean isWorldGeneratorSingleThreaded(DistanceGenerationMode distanceGenerationMode); - /** + /** + * @Returns True if BatchGeneration is implemented
+ * False if it is not supported + */ + boolean hasBatchGenerationImplementation(); + + /** * @Returns the number of generations call per thread. */ default int getWorldGenerationCountPerThread() { return 8; } - + } 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 13c376d3f..b56708859 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/IWrapperFactory.java @@ -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; } } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java new file mode 100644 index 000000000..3ba1421db --- /dev/null +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractBatchGenerationEnvionmentWrapper.java @@ -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(); +} diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractExperimentalWorldGeneratorWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractExperimentalWorldGeneratorWrapper.java index 3eff58d29..5fd2b2351 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractExperimentalWorldGeneratorWrapper.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractExperimentalWorldGeneratorWrapper.java @@ -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(); } diff --git a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java index 696c89e86..e09a220cb 100644 --- a/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java +++ b/src/main/java/com/seibel/lod/core/wrapperInterfaces/worldGeneration/AbstractWorldGeneratorWrapper.java @@ -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. *

- * 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); }