Add world gen progress updates to the overlay
This commit is contained in:
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020-2023 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser 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 Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.api.enums.worldGeneration;
|
||||
|
||||
/**
|
||||
* OVERLAY <br>
|
||||
* CHAT <br>
|
||||
* LOG <br>
|
||||
* DISABLED <br><br>
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 2025-1-7
|
||||
* @since API 4.0.0
|
||||
*/
|
||||
public enum EDhApiDistantGeneratorProgressDisplayLocation
|
||||
{
|
||||
// Reminder:
|
||||
// when adding items up the API minor version
|
||||
// when removing items up the API major version
|
||||
|
||||
|
||||
OVERLAY,
|
||||
CHAT,
|
||||
LOG,
|
||||
DISABLED;
|
||||
|
||||
}
|
||||
@@ -99,6 +99,7 @@ public class ClientApi
|
||||
private long lastStaticWarningMessageSentMsTime = 0L;
|
||||
|
||||
private final Queue<String> chatMessageQueueForNextFrame = new LinkedBlockingQueue<>();
|
||||
private final Queue<String> overlayMessageQueueForNextFrame = new LinkedBlockingQueue<>();
|
||||
|
||||
public boolean rendererDisabledBecauseOfExceptions = false;
|
||||
|
||||
@@ -624,7 +625,7 @@ public class ClientApi
|
||||
}
|
||||
|
||||
|
||||
// generic messages
|
||||
// chat messages
|
||||
while (!this.chatMessageQueueForNextFrame.isEmpty())
|
||||
{
|
||||
String message = this.chatMessageQueueForNextFrame.poll();
|
||||
@@ -635,6 +636,18 @@ public class ClientApi
|
||||
}
|
||||
MC_CLIENT.sendChatMessage(message);
|
||||
}
|
||||
|
||||
// overlay messages
|
||||
while (!this.overlayMessageQueueForNextFrame.isEmpty())
|
||||
{
|
||||
String message = this.overlayMessageQueueForNextFrame.poll();
|
||||
if (message == null)
|
||||
{
|
||||
// done to prevent potential null pointers
|
||||
message = "";
|
||||
}
|
||||
MC_CLIENT.sendOverlayMessage(message);
|
||||
}
|
||||
}
|
||||
private void detectAndSendBootTimeWarnings()
|
||||
{
|
||||
@@ -722,4 +735,9 @@ public class ClientApi
|
||||
*/
|
||||
public void showChatMessageNextFrame(String chatMessage) { this.chatMessageQueueForNextFrame.add(chatMessage); }
|
||||
|
||||
/**
|
||||
* Similar to {@link ClientApi#showChatMessageNextFrame(String)} but appears above the toolbar.
|
||||
*/
|
||||
public void showOverlayMessageNextFrame(String message) { this.overlayMessageQueueForNextFrame.add(message); }
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import com.seibel.distanthorizons.api.enums.config.*;
|
||||
import com.seibel.distanthorizons.api.enums.config.quickOptions.*;
|
||||
import com.seibel.distanthorizons.api.enums.rendering.*;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorProgressDisplayLocation;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.*;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.presets.*;
|
||||
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
|
||||
@@ -1193,7 +1194,10 @@ public class Config
|
||||
.set(true)
|
||||
.comment(""
|
||||
+ " Should Distant Horizons slowly generate LODs \n"
|
||||
+ " outside the vanilla render distance?")
|
||||
+ " outside the vanilla render distance? \n"
|
||||
+ "Depending on the generator mode, this will import existing chunks \n"
|
||||
+ "and/or generating missing chunks."
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiDistantGeneratorMode> distantGeneratorMode = new ConfigEntry.Builder<EDhApiDistantGeneratorMode>()
|
||||
@@ -1237,6 +1241,25 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<EDhApiDistantGeneratorProgressDisplayLocation> showGenerationProgress = new ConfigEntry.Builder<EDhApiDistantGeneratorProgressDisplayLocation>()
|
||||
.set(EDhApiDistantGeneratorProgressDisplayLocation.OVERLAY)
|
||||
.comment(""
|
||||
+ "How should distant generator progress be displayed? \n"
|
||||
+ "\n"
|
||||
+ EDhApiDistantGeneratorProgressDisplayLocation.OVERLAY + ": may be the same as "+EDhApiDistantGeneratorProgressDisplayLocation.CHAT+" for some Minecraft versions \n"
|
||||
+ EDhApiDistantGeneratorProgressDisplayLocation.CHAT + " \n"
|
||||
+ EDhApiDistantGeneratorProgressDisplayLocation.LOG + " \n"
|
||||
+ EDhApiDistantGeneratorProgressDisplayLocation.DISABLED + " \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Integer> generationProgressDisplayIntervalInSeconds = new ConfigEntry.Builder<Integer>()
|
||||
.setMinDefaultMax(1, 2, 60 * 60 * 4) // max = 4 hours
|
||||
.comment(""
|
||||
+ "How often should the distant generator progress be displayed? \n"
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
}
|
||||
|
||||
public static class LodBuilding
|
||||
|
||||
+3
-7
@@ -40,7 +40,6 @@ import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo;
|
||||
import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.threading.PrioritySemaphore;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
@@ -611,12 +610,7 @@ public class FullDataSourceProviderV2
|
||||
*/
|
||||
@Nullable
|
||||
public LongArrayList getPositionsToRetrieve(Long pos) { return null; }
|
||||
/**
|
||||
* Returns how many positions could potentially be generated for this position assuming the position is empty.
|
||||
* Used when estimating the total number of retrieval requests.
|
||||
*/
|
||||
public int getMaxPossibleRetrievalPositionCountForPos(Long pos) { return -1; }
|
||||
|
||||
|
||||
/** @return true if the position was queued, false if not */
|
||||
@Nullable
|
||||
public CompletableFuture<WorldGenResult> queuePositionForRetrieval(Long genPos) { return null; }
|
||||
@@ -628,6 +622,8 @@ public class FullDataSourceProviderV2
|
||||
|
||||
/** Can be used to display how many total retrieval requests might be available. */
|
||||
public void setTotalRetrievalPositionCount(int newCount) { }
|
||||
/** Can be used to display how many total chunk retrieval requests should be available. */
|
||||
public void setEstimatedRemainingRetrievalChunkCount(int newCount) { }
|
||||
|
||||
/**
|
||||
* Returns how many data sources are currently in memory and haven't
|
||||
|
||||
+2
-18
@@ -37,7 +37,6 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.coreapi.util.BitShiftUtil;
|
||||
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -168,12 +167,12 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
public boolean canRetrieveMissingDataSources() { return true; }
|
||||
|
||||
@Override
|
||||
public void setTotalRetrievalPositionCount(int newCount)
|
||||
public void setEstimatedRemainingRetrievalChunkCount(int newCount)
|
||||
{
|
||||
IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||
if (worldGenQueue != null)
|
||||
{
|
||||
worldGenQueue.setEstimatedTotalTaskCount(newCount);
|
||||
worldGenQueue.setRetrievalEstimatedRemainingChunkCount(newCount);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -411,21 +410,6 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
|
||||
return generationList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxPossibleRetrievalPositionCountForPos(Long pos)
|
||||
{
|
||||
IFullDataSourceRetrievalQueue worldGenQueue = this.worldGenQueueRef.get();
|
||||
if (worldGenQueue == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int minGeneratorSectionDetailLevel = worldGenQueue.highestDataDetail() + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL;
|
||||
int detailLevelDiff = DhSectionPos.getDetailLevel(pos) - minGeneratorSectionDetailLevel;
|
||||
|
||||
return BitShiftUtil.powerOfTwo(detailLevelDiff);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=======//
|
||||
|
||||
+15
-2
@@ -24,6 +24,8 @@ import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.render.LodQuadTree;
|
||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.List;
|
||||
@@ -113,11 +115,22 @@ public interface IFullDataSourceRetrievalQueue extends Closeable
|
||||
int getWaitingTaskCount();
|
||||
int getInProgressTaskCount();
|
||||
|
||||
/** returns how many chunks are currently queued for retrieval */
|
||||
int getQueuedChunkCount();
|
||||
|
||||
/** used for rendering to the F3 menu */
|
||||
int getEstimatedTotalTaskCount();
|
||||
void setEstimatedTotalTaskCount(int newEstimate);
|
||||
int getEstimatedRemainingTaskCount();
|
||||
void setEstimatedRemainingTaskCount(int newEstimate);
|
||||
|
||||
/** used for displaying a progress update to the user */
|
||||
int getRetrievalEstimatedRemainingChunkCount();
|
||||
void setRetrievalEstimatedRemainingChunkCount(int newEstimate);
|
||||
|
||||
void addDebugMenuStringsToList(List<String> messageList);
|
||||
|
||||
/** Can be used to determine roughly how fast the world generator is running. */
|
||||
RollingAverage getRollingAverageChunkGenTimeInMs();
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+24
-3
@@ -11,6 +11,7 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -21,7 +22,11 @@ public class RemoteWorldRetrievalQueue extends AbstractFullDataNetworkRequestQue
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
private int estimatedTotalTaskCount;
|
||||
private int estimatedRemainingTaskCount;
|
||||
private int estimatedTotalChunkCount;
|
||||
|
||||
private final RollingAverage rollingAverageChunkGenTimeInMs = new RollingAverage(1_000);
|
||||
public RollingAverage getRollingAverageChunkGenTimeInMs() { return this.rollingAverageChunkGenTimeInMs; }
|
||||
|
||||
|
||||
|
||||
@@ -49,9 +54,18 @@ public class RemoteWorldRetrievalQueue extends AbstractFullDataNetworkRequestQue
|
||||
@Override
|
||||
public CompletableFuture<WorldGenResult> submitRetrievalTask(long sectionPos, byte requiredDataDetail, IWorldGenTaskTracker tracker)
|
||||
{
|
||||
long generationStartMsTime = System.currentTimeMillis();
|
||||
|
||||
return super.submitRequest(sectionPos, tracker.getDataSourceConsumer())
|
||||
.thenApply(requestResult ->
|
||||
{
|
||||
long totalGenTimeInMs = System.currentTimeMillis() - generationStartMsTime;
|
||||
|
||||
int chunkWidth = DhSectionPos.getChunkWidth(sectionPos);
|
||||
int chunkCount = chunkWidth * chunkWidth;
|
||||
double timePerChunk = (double)totalGenTimeInMs / (double)chunkCount;
|
||||
this.rollingAverageChunkGenTimeInMs.addValue(timePerChunk);
|
||||
|
||||
switch (requestResult)
|
||||
{
|
||||
case SUCCEEDED:
|
||||
@@ -94,10 +108,17 @@ public class RemoteWorldRetrievalQueue extends AbstractFullDataNetworkRequestQue
|
||||
//===============//
|
||||
|
||||
@Override
|
||||
public int getEstimatedTotalTaskCount() { return this.estimatedTotalTaskCount; }
|
||||
public int getEstimatedRemainingTaskCount() { return this.estimatedRemainingTaskCount; }
|
||||
@Override
|
||||
public void setEstimatedTotalTaskCount(int newEstimate) { this.estimatedTotalTaskCount = newEstimate; }
|
||||
public void setEstimatedRemainingTaskCount(int newEstimate) { this.estimatedRemainingTaskCount = newEstimate; }
|
||||
|
||||
@Override
|
||||
public int getRetrievalEstimatedRemainingChunkCount() { return this.estimatedTotalChunkCount; }
|
||||
@Override
|
||||
public void setRetrievalEstimatedRemainingChunkCount(int newEstimate) { this.estimatedTotalChunkCount = newEstimate; }
|
||||
|
||||
@Override
|
||||
public int getQueuedChunkCount() { return 0; }
|
||||
|
||||
|
||||
}
|
||||
+43
-7
@@ -42,6 +42,7 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil.AssertFailureException;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.DataCorruptedException;
|
||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
@@ -98,7 +99,11 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
private DhBlockPos2D generationTargetPos = DhBlockPos2D.ZERO;
|
||||
|
||||
/** just used for rendering to the F3 menu */
|
||||
private int estimatedTotalTaskCount = 0;
|
||||
private int estimatedRemainingTaskCount = 0;
|
||||
private int estimatedRemainingChunkCount = 0;
|
||||
|
||||
private final RollingAverage rollingAverageChunkGenTimeInMs = new RollingAverage(1_000);
|
||||
public RollingAverage getRollingAverageChunkGenTimeInMs() { return this.rollingAverageChunkGenTimeInMs; }
|
||||
|
||||
|
||||
|
||||
@@ -247,7 +252,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
*/
|
||||
private boolean startNextWorldGenTask(DhBlockPos2D targetPos)
|
||||
{
|
||||
if (this.waitingTasks.size() == 0)
|
||||
if (this.waitingTasks.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -333,7 +338,19 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
long taskPos = newTaskGroup.group.pos;
|
||||
LodUtil.assertTrue(taskDetailLevel >= this.highestDataDetail && taskDetailLevel <= this.lowestDataDetail);
|
||||
|
||||
newTaskGroup.genFuture = this.startGenerationEvent(taskPos, taskDetailLevel, newTaskGroup.group::consumeDataSource);
|
||||
int generationRequestChunkWidthCount = BitShiftUtil.powerOfTwo(DhSectionPos.getDetailLevel(taskPos) - taskDetailLevel - 4); // minus 4 is equal to dividing by 16 to convert to chunk scale
|
||||
|
||||
long generationStartMsTime = System.currentTimeMillis();
|
||||
CompletableFuture<Void> generationFuture = this.startGenerationEvent(taskPos, taskDetailLevel, generationRequestChunkWidthCount, newTaskGroup.group::consumeDataSource);
|
||||
generationFuture.thenRun(() ->
|
||||
{
|
||||
long totalGenTimeInMs = System.currentTimeMillis() - generationStartMsTime;
|
||||
int chunkCount = generationRequestChunkWidthCount * generationRequestChunkWidthCount;
|
||||
double timePerChunk = (double)totalGenTimeInMs / (double)chunkCount;
|
||||
this.rollingAverageChunkGenTimeInMs.addValue(timePerChunk);
|
||||
});
|
||||
|
||||
newTaskGroup.genFuture = generationFuture;
|
||||
LodUtil.assertTrue(newTaskGroup.genFuture != null);
|
||||
|
||||
newTaskGroup.genFuture.whenComplete((voidObj, exception) ->
|
||||
@@ -369,11 +386,11 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
private CompletableFuture<Void> startGenerationEvent(
|
||||
long requestPos,
|
||||
byte targetDataDetail,
|
||||
int generationRequestChunkWidthCount,
|
||||
Consumer<FullDataSourceV2> dataSourceConsumer
|
||||
)
|
||||
{
|
||||
DhChunkPos chunkPosMin = new DhChunkPos(DhSectionPos.getSectionBBoxPos(requestPos).getCornerBlockPos());
|
||||
int generationRequestChunkWidthCount = BitShiftUtil.powerOfTwo(DhSectionPos.getDetailLevel(requestPos) - targetDataDetail - 4); // minus 4 is equal to dividing by 16 to convert to chunk scale
|
||||
|
||||
EDhApiDistantGeneratorMode generatorMode = Config.Common.WorldGenerator.distantGeneratorMode.get();
|
||||
EDhApiWorldGeneratorReturnType returnType = this.generator.getReturnType();
|
||||
@@ -495,11 +512,30 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
|
||||
public byte highestDataDetail() { return this.highestDataDetail; }
|
||||
|
||||
@Override
|
||||
public int getEstimatedTotalTaskCount() { return this.estimatedTotalTaskCount; }
|
||||
public int getEstimatedRemainingTaskCount() { return this.estimatedRemainingTaskCount; }
|
||||
@Override
|
||||
public void setEstimatedTotalTaskCount(int newEstimate) { this.estimatedTotalTaskCount = newEstimate; }
|
||||
public void setEstimatedRemainingTaskCount(int newEstimate) { this.estimatedRemainingTaskCount = newEstimate; }
|
||||
|
||||
@Override public void addDebugMenuStringsToList(List<String> messageList) { }
|
||||
@Override
|
||||
public int getRetrievalEstimatedRemainingChunkCount() { return this.estimatedRemainingChunkCount; }
|
||||
@Override
|
||||
public void setRetrievalEstimatedRemainingChunkCount(int newEstimate) { this.estimatedRemainingChunkCount = newEstimate; }
|
||||
|
||||
@Override
|
||||
public void addDebugMenuStringsToList(List<String> messageList) { }
|
||||
|
||||
@Override
|
||||
public int getQueuedChunkCount()
|
||||
{
|
||||
int chunkCount = 0;
|
||||
for (long pos : this.waitingTasks.keySet())
|
||||
{
|
||||
int chunkWidth = DhSectionPos.getBlockWidth(pos) / LodUtil.CHUNK_WIDTH;
|
||||
chunkCount += (chunkWidth * chunkWidth);
|
||||
}
|
||||
|
||||
return chunkCount;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -19,21 +19,25 @@
|
||||
|
||||
package com.seibel.distanthorizons.core.level;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorProgressDisplayLocation;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataSourceProvider;
|
||||
import com.seibel.distanthorizons.core.generation.IFullDataSourceRetrievalQueue;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import com.seibel.distanthorizons.core.util.ThreadUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.RollingAverage;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.BooleanSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
@@ -188,10 +192,20 @@ public class WorldGenModule implements Closeable
|
||||
}
|
||||
|
||||
|
||||
// estimated tasks
|
||||
String waitingCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getWaitingTaskCount());
|
||||
String inProgressCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getInProgressTaskCount());
|
||||
String totalCountEstimateStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getEstimatedTotalTaskCount());
|
||||
messageList.add("World Gen/Pull Chunk Tasks: "+waitingCountStr+"/"+totalCountEstimateStr+" (in progress "+inProgressCountStr+")");
|
||||
String totalCountEstimateStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getRetrievalEstimatedRemainingChunkCount());
|
||||
String message = "World Gen/Import Tasks: "+waitingCountStr+"/"+totalCountEstimateStr+" (in progress "+inProgressCountStr+")";
|
||||
|
||||
// estimated chunks/sec
|
||||
double chunksPerSec = worldGenState.getEstimatedChunksPerSecond();
|
||||
if (chunksPerSec > -1)
|
||||
{
|
||||
message += ", " + F3Screen.NUMBER_FORMAT.format(chunksPerSec) + " chunks/sec";
|
||||
}
|
||||
|
||||
messageList.add(message);
|
||||
|
||||
worldGenState.worldGenerationQueue.addDebugMenuStringsToList(messageList);
|
||||
}
|
||||
@@ -207,8 +221,15 @@ public class WorldGenModule implements Closeable
|
||||
{
|
||||
public IFullDataSourceRetrievalQueue worldGenerationQueue;
|
||||
|
||||
private static final ThreadPoolExecutor PROGRESS_UPDATER_THREAD = ThreadUtil.makeSingleThreadPool("World Gen Progress Updater");
|
||||
private boolean progressUpdateThreadRunning = false;
|
||||
|
||||
|
||||
CompletableFuture<Void> closeAsync(boolean doInterrupt)
|
||||
{
|
||||
// this should stop the updater thread
|
||||
this.progressUpdateThreadRunning = false;
|
||||
|
||||
return this.worldGenerationQueue.startClosingAsync(true, doInterrupt)
|
||||
.exceptionally(e ->
|
||||
{
|
||||
@@ -225,7 +246,119 @@ public class WorldGenModule implements Closeable
|
||||
|
||||
/** @param targetPosForGeneration the position that world generation should be centered around */
|
||||
public void startGenerationQueueAndSetTargetPos(DhBlockPos2D targetPosForGeneration)
|
||||
{ this.worldGenerationQueue.startAndSetTargetPos(targetPosForGeneration); }
|
||||
{
|
||||
this.worldGenerationQueue.startAndSetTargetPos(targetPosForGeneration);
|
||||
this.startProgressUpdateThread();
|
||||
}
|
||||
private void startProgressUpdateThread()
|
||||
{
|
||||
// only start the thread once
|
||||
if (!this.progressUpdateThreadRunning)
|
||||
{
|
||||
this.progressUpdateThreadRunning = true;
|
||||
|
||||
PROGRESS_UPDATER_THREAD.execute(() ->
|
||||
{
|
||||
while (this.progressUpdateThreadRunning)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.sendRetrievalProgress();
|
||||
|
||||
// sleep so we only see an update once in a while
|
||||
int sleepTimeInSec = Config.Common.WorldGenerator.generationProgressDisplayIntervalInSeconds.get();
|
||||
Thread.sleep(sleepTimeInSec * 1_000L);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Unexpected issue displaying chunk retrieval progress [" + e.getMessage() + "].", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
private void sendRetrievalProgress()
|
||||
{
|
||||
int remainingChunkCount = this.worldGenerationQueue.getRetrievalEstimatedRemainingChunkCount();
|
||||
remainingChunkCount += this.worldGenerationQueue.getQueuedChunkCount();
|
||||
String remainingChunkCountStr = F3Screen.NUMBER_FORMAT.format(remainingChunkCount);
|
||||
|
||||
String message = "DH Gen/Import: " + remainingChunkCountStr + " chunks";
|
||||
|
||||
|
||||
double chunksPerSec = this.getEstimatedChunksPerSecond();
|
||||
if (chunksPerSec > 0)
|
||||
{
|
||||
long estimatedRemainingTime = (long) (remainingChunkCount / chunksPerSec);
|
||||
message += " Estimated Time: " + formatSeconds(estimatedRemainingTime); // at " + F3Screen.NUMBER_FORMAT.format(chunksPerSec) + " chunks/sec";
|
||||
}
|
||||
|
||||
|
||||
if (remainingChunkCount != 0)
|
||||
{
|
||||
EDhApiDistantGeneratorProgressDisplayLocation displayLocation = Config.Common.WorldGenerator.showGenerationProgress.get();
|
||||
if (displayLocation == EDhApiDistantGeneratorProgressDisplayLocation.OVERLAY)
|
||||
{
|
||||
ClientApi.INSTANCE.showOverlayMessageNextFrame(message);
|
||||
}
|
||||
else if (displayLocation == EDhApiDistantGeneratorProgressDisplayLocation.CHAT)
|
||||
{
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
else if (displayLocation == EDhApiDistantGeneratorProgressDisplayLocation.LOG)
|
||||
{
|
||||
LOGGER.info(message);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
private static String formatSeconds(long totalSeconds)
|
||||
{
|
||||
long days = totalSeconds / (24 * 3600); // 24 hours in a day
|
||||
long hours = (totalSeconds % (24 * 3600)) / 3600; // Hours
|
||||
long minutes = (totalSeconds % 3600) / 60; // Minutes
|
||||
long seconds = totalSeconds % 60; // Seconds
|
||||
|
||||
|
||||
String timeString = "";
|
||||
if (days > 0)
|
||||
{
|
||||
timeString += days+" ";
|
||||
}
|
||||
if (hours > 0)
|
||||
{
|
||||
timeString += hours+":";
|
||||
}
|
||||
if (minutes > 0)
|
||||
{
|
||||
timeString += String.format("%02d", minutes)+":";
|
||||
}
|
||||
timeString += String.format("%02d", seconds);
|
||||
|
||||
return timeString;
|
||||
}
|
||||
|
||||
/** @return -1 if this method isn't supported or available */
|
||||
public double getEstimatedChunksPerSecond()
|
||||
{
|
||||
RollingAverage avg = this.worldGenerationQueue.getRollingAverageChunkGenTimeInMs();
|
||||
if (avg == null)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
// convert chunk generation time in milliseconds to chunks per second
|
||||
double chunksPerSecond = (1 / avg.getAverage()) * 1_000;
|
||||
// estimate the number of chunks that can be processed per second by all threads
|
||||
// Note: this is probably higher than the actual number, we might want to drop this by 1 or 2 to give a more realistic estimate
|
||||
chunksPerSecond = ThreadPoolUtil.getThreadCount() * chunksPerSecond;
|
||||
|
||||
return chunksPerSecond;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ public class F3Screen
|
||||
if (Config.Client.Advanced.Debugging.F3Screen.showThreadPools.get())
|
||||
{
|
||||
// multi thread pools
|
||||
messageList.add(getThreadPoolStatString("World Gen/Pull Chunk", worldGenPool));//"World Gen Tasks: 40/5304, (in progress: 7)");
|
||||
messageList.add(getThreadPoolStatString("World Gen/Import", worldGenPool));
|
||||
messageList.add(getThreadPoolStatString("File Handler", fileHandlerPool));
|
||||
messageList.add(getThreadPoolStatString("Update Propagator", updatePool));
|
||||
messageList.add(getThreadPoolStatString("LOD Builder", lodBuilderPool));
|
||||
|
||||
@@ -224,7 +224,9 @@ public class DhSectionPos
|
||||
|
||||
/** @return how wide this section is in blocks */
|
||||
public static int getBlockWidth(long pos) { return BitShiftUtil.powerOfTwo(getDetailLevel(pos)); }
|
||||
|
||||
/** @return how wide this section is in chunks */
|
||||
public static int getChunkWidth(long pos) { return DhSectionPos.getBlockWidth(pos) / LodUtil.CHUNK_WIDTH; }
|
||||
|
||||
|
||||
public static DhBlockPos2D getCenterBlockPos(long pos) { return new DhBlockPos2D(getCenterBlockPosX(pos), getCenterBlockPosZ(pos)); }
|
||||
|
||||
|
||||
@@ -628,22 +628,33 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
|
||||
renderSection.tryQueuingMissingLodRetrieval();
|
||||
}
|
||||
|
||||
// calculate an estimate for the max number of tasks for the queue
|
||||
int totalWorldGenCount = 0;
|
||||
// calculate an estimate for the max number of chunks for the queue
|
||||
int totalWorldGenChunkCount = 0;
|
||||
int totalWorldGenTaskCount = 0;
|
||||
for (int i = 0; i < nodeList.size(); i++)
|
||||
{
|
||||
LodRenderSection renderSection = nodeList.get(i);
|
||||
if (!renderSection.missingPositionsCalculated())
|
||||
{
|
||||
// may be higher than the actual amount
|
||||
totalWorldGenCount += this.fullDataSourceProvider.getMaxPossibleRetrievalPositionCountForPos(renderSection.pos);
|
||||
// chunk count
|
||||
int sectionWidthInChunks = DhSectionPos.getChunkWidth(renderSection.pos);
|
||||
totalWorldGenChunkCount += sectionWidthInChunks * sectionWidthInChunks;
|
||||
|
||||
// task count
|
||||
totalWorldGenTaskCount += renderSection.ungeneratedPositionCount();
|
||||
}
|
||||
else
|
||||
{
|
||||
totalWorldGenCount += renderSection.ungeneratedPositionCount();
|
||||
totalWorldGenChunkCount += renderSection.ungeneratedChunkCount();
|
||||
|
||||
// 1 since we assume the position can be generated in a single go
|
||||
// TODO this is a bad assumption, can we determine what the world gen supports and determine it from that?
|
||||
totalWorldGenTaskCount += 1;
|
||||
}
|
||||
}
|
||||
this.fullDataSourceProvider.setTotalRetrievalPositionCount(totalWorldGenCount);
|
||||
|
||||
this.fullDataSourceProvider.setEstimatedRemainingRetrievalChunkCount(totalWorldGenChunkCount);
|
||||
this.fullDataSourceProvider.setTotalRetrievalPositionCount(totalWorldGenTaskCount);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -330,6 +330,23 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
|
||||
LongArrayList missingGenerationPos = this.getMissingGenerationPos();
|
||||
return missingGenerationPos != null ? missingGenerationPos.size() : 0;
|
||||
}
|
||||
public int ungeneratedChunkCount()
|
||||
{
|
||||
LongArrayList missingGenerationPos = this.getMissingGenerationPos();
|
||||
if (missingGenerationPos == null)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chunkCount = 0;
|
||||
// get the number of chunks each position contains
|
||||
for (int i = 0; i < missingGenerationPos.size(); i++)
|
||||
{
|
||||
int chunkWidth = DhSectionPos.getChunkWidth(missingGenerationPos.getLong(i));
|
||||
chunkCount += (chunkWidth * chunkWidth);
|
||||
}
|
||||
return chunkCount;
|
||||
}
|
||||
|
||||
public void tryQueuingMissingLodRetrieval()
|
||||
{
|
||||
|
||||
+2
@@ -100,6 +100,8 @@ public interface IMinecraftClientWrapper extends IBindable
|
||||
ArrayList<ILevelWrapper> getAllServerWorlds();
|
||||
|
||||
void sendChatMessage(String string);
|
||||
/** Will default to sending a chat message if not supported by the current MC version */
|
||||
void sendOverlayMessage(String string);
|
||||
|
||||
/** Sends the given message to chat with a formatted prefix and color based on the log level. */
|
||||
default void logToChat(Level logLevel, String message)
|
||||
|
||||
@@ -567,15 +567,19 @@
|
||||
"distanthorizons.config.common.worldGenerator.enableDistantGeneration":
|
||||
"Enable Distant Generation",
|
||||
"distanthorizons.config.common.worldGenerator.enableDistantGeneration.@tooltip":
|
||||
"§6True:§r in single player LODs will be generated outside the vanilla render distance.\nNote: this can use a large amount of CPU.\n\n§6False:§r LODs will only generate within the vanilla render distance.",
|
||||
"§6True:§r in single player LODs will be created outside the vanilla render distance.\nDepending on the Generator Mode pre-existing chunks will be important and/o\nmissing chunks will be generated.\nNote: this can use a large amount of CPU.\n\n§6False:§r LODs will only generate within the vanilla render distance.",
|
||||
"distanthorizons.config.common.worldGenerator.distantGeneratorMode":
|
||||
"Distance Generator Mode",
|
||||
"distanthorizons.config.common.worldGenerator.distantGeneratorMode.@tooltip":
|
||||
"How complicated the generation should be when generating LODs outside the vanilla render distance.",
|
||||
"distanthorizons.config.common.worldGenerator.worldGenerationTimeoutLengthInSeconds":
|
||||
"Timeout Length In Seconds",
|
||||
"distanthorizons.config.common.worldGenerator.worldGenerationTimeoutLengthInSeconds.@tooltip":
|
||||
"How long should a world generator thread run for before timing out? \nNote: If you are experiencing timeout errors it is better to lower your CPU usage first \nvia the thread config before changing this value.",
|
||||
"distanthorizons.config.common.worldGenerator.showGenerationProgress":
|
||||
"Show Generation Progress",
|
||||
"distanthorizons.config.common.worldGenerator.showGenerationProgress.@tooltip":
|
||||
"Determines how should distant generator progress be displayed.",
|
||||
"distanthorizons.config.common.worldGenerator.generationProgressDisplayIntervalInSeconds":
|
||||
"Progress Display Interval In Seconds",
|
||||
"distanthorizons.config.common.worldGenerator.generationProgressDisplayIntervalInSeconds.@tooltip":
|
||||
"Determines how long between progress update displays.",
|
||||
|
||||
|
||||
"distanthorizons.config.common.lodBuilding":
|
||||
@@ -877,6 +881,15 @@
|
||||
"Features",
|
||||
"distanthorizons.config.enum.EDhApiDistantGeneratorMode.INTERNAL_SERVER":
|
||||
"Internal Server",
|
||||
|
||||
"distanthorizons.config.enum.EDhApiDistantGeneratorProgressDisplayLocation.OVERLAY":
|
||||
"Overlay",
|
||||
"distanthorizons.config.enum.EDhApiDistantGeneratorProgressDisplayLocation.CHAT":
|
||||
"Chat",
|
||||
"distanthorizons.config.enum.EDhApiDistantGeneratorProgressDisplayLocation.LOG":
|
||||
"Log",
|
||||
"distanthorizons.config.enum.EDhApiDistantGeneratorProgressDisplayLocation.DISABLED":
|
||||
"Disabled",
|
||||
|
||||
"distanthorizons.config.enum.EDhApiDataCompressionMode.UNCOMPRESSED":
|
||||
"Uncompressed",
|
||||
|
||||
Reference in New Issue
Block a user