From 67819b30ebb869d23a974258ace542837fb32b5b Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 15 Jun 2024 19:20:20 -0500 Subject: [PATCH] Improve F3 menu logic and visuals --- .../core/api/internal/SharedApi.java | 26 ++- .../FullDataSourceProviderV2.java | 142 ++++++----- .../core/level/ClientLevelModule.java | 52 ----- .../core/level/DhClientLevel.java | 30 +++ .../core/level/DhClientServerLevel.java | 48 ++++ .../core/level/DhServerLevel.java | 15 ++ .../core/level/IDhClientLevel.java | 1 - .../distanthorizons/core/level/IDhLevel.java | 4 + .../core/level/WorldGenModule.java | 46 ++-- .../core/logging/f3/F3Screen.java | 220 ++++++++---------- .../core/render/RenderBufferHandler.java | 61 ++--- .../core/world/AbstractDhWorld.java | 14 +- .../core/world/DhClientServerWorld.java | 16 +- .../core/world/DhClientWorld.java | 2 + .../core/world/DhServerWorld.java | 8 + .../distanthorizons/core/world/IDhWorld.java | 1 + 16 files changed, 375 insertions(+), 311 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index 0d5f4f08e..51d0b529f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -65,23 +65,13 @@ public class SharedApi private static int lastWorldGenTickDelta = 0; private static long lastOverloadedLogMessageMsTime = 0; - public F3Screen.DynamicMessage f3Message; - //=============// // constructor // //=============// - private SharedApi() - { - this.f3Message = new F3Screen.DynamicMessage(() -> - { - int maxUpdateCount = MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get(); - return LodUtil.formatLog("Queued chunk updates: " + UPDATING_CHUNK_POS_SET.size() + " / " + maxUpdateCount); - }); - } - + private SharedApi() { } public static void init() { Initializer.init(); } @@ -381,4 +371,18 @@ public class SharedApi } + + //=========// + // F3 Menu // + //=========// + + public String getDebugMenuString() + { + int maxUpdateCount = MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get(); + String updatingCountStr = F3Screen.NUMBER_FORMAT.format(UPDATING_CHUNK_POS_SET.size()); + String maxUpdateCountStr = F3Screen.NUMBER_FORMAT.format(maxUpdateCount); + return "Queued chunk updates: "+updatingCountStr+" / "+maxUpdateCountStr; + } + + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index d2e61e34b..e19751fc9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -92,6 +92,7 @@ public class FullDataSourceProviderV2 protected long legacyDeletionCount = -1; protected long migrationCount = -1; + protected boolean migrationStoppedWithError = false; /** * Tracks which positions are currently being updated @@ -386,82 +387,94 @@ public class FullDataSourceProviderV2 { this.showMigrationStartMessage(); - - // keep going until every data source has been migrated - int progressCount = 0; - while (!legacyDataSourceList.isEmpty() && this.migrationThreadRunning.get()) + try { - LOGGER.info("Migrating [" + dimensionName + "] - [" + progressCount + "/" + totalMigrationCount + "]..."); - - ArrayList> updateFutureList = new ArrayList<>(); - for (int i = 0; i < legacyDataSourceList.size() && this.migrationThreadRunning.get(); i++) + // keep going until every data source has been migrated + int progressCount = 0; + while (!legacyDataSourceList.isEmpty() && this.migrationThreadRunning.get()) { - FullDataSourceV1 legacyDataSource = legacyDataSourceList.get(i); + LOGGER.info("Migrating [" + dimensionName + "] - [" + progressCount + "/" + totalMigrationCount + "]..."); + + ArrayList> updateFutureList = new ArrayList<>(); + for (int i = 0; i < legacyDataSourceList.size() && this.migrationThreadRunning.get(); i++) + { + FullDataSourceV1 legacyDataSource = legacyDataSourceList.get(i); + + try + { + // convert the legacy data source to the new format, + // this is a relatively cheap operation + FullDataSourceV2 newDataSource = FullDataSourceV2.createFromLegacyDataSourceV1(legacyDataSource); + newDataSource.applyToParent = true; + + // the actual update process can be moderately expensive due to having to update + // the render data along with the full data, so running it async on the update threads gains us a good bit of speed + CompletableFuture future = this.updateDataSourceAsync(newDataSource); + updateFutureList.add(future); + future.thenRun(() -> + { + // after the update finishes the legacy data source can be safely deleted + this.legacyFileHandler.repo.deleteWithKey(legacyDataSource.getPos()); + + try + { + newDataSource.close(); + } + catch (Exception ignore) + { + } + }); + } + catch (Exception e) + { + Long migrationPos = legacyDataSource.getPos(); + LOGGER.warn("Unexpected issue migrating data source at pos " + migrationPos + ". Error: " + e.getMessage(), e); + this.legacyFileHandler.markMigrationFailed(migrationPos); + } + } + try { - // convert the legacy data source to the new format, - // this is a relatively cheap operation - FullDataSourceV2 newDataSource = FullDataSourceV2.createFromLegacyDataSourceV1(legacyDataSource); - newDataSource.applyToParent = true; - - // the actual update process can be moderately expensive due to having to update - // the render data along with the full data, so running it async on the update threads gains us a good bit of speed - CompletableFuture future = this.updateDataSourceAsync(newDataSource); - updateFutureList.add(future); - future.thenRun(() -> - { - // after the update finishes the legacy data source can be safely deleted - this.legacyFileHandler.repo.deleteWithKey(legacyDataSource.getPos()); - - try - { - newDataSource.close(); - } - catch (Exception ignore){ } - }); + // wait for each thread to finish updating + CompletableFuture combinedFutures = CompletableFuture.allOf(updateFutureList.toArray(new CompletableFuture[0])); + combinedFutures.get(MIGRATION_MAX_UPDATE_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); } - catch (Exception e) + catch (InterruptedException | TimeoutException e) { - Long migrationPos = legacyDataSource.getPos(); - LOGGER.warn("Unexpected issue migrating data source at pos " + migrationPos + ". Error: " + e.getMessage(), e); - this.legacyFileHandler.markMigrationFailed(migrationPos); + LOGGER.warn("Migration update timed out after [" + MIGRATION_MAX_UPDATE_TIMEOUT_IN_MS + "] milliseconds. Migration will re-try the same positions again in a moment..", e); } + catch (ExecutionException e) + { + LOGGER.warn("Migration update failed. Migration will re-try the same positions again. Error:" + e.getMessage(), e); + } + + legacyDataSourceList = this.legacyFileHandler.getDataSourcesToMigrate(MIGRATION_BATCH_COUNT); + + progressCount += legacyDataSourceList.size(); + this.migrationCount -= legacyDataSourceList.size(); } - - - try - { - // wait for each thread to finish updating - CompletableFuture combinedFutures = CompletableFuture.allOf(updateFutureList.toArray(new CompletableFuture[0])); - combinedFutures.get(MIGRATION_MAX_UPDATE_TIMEOUT_IN_MS, TimeUnit.MILLISECONDS); - } - catch (InterruptedException | TimeoutException e) - { - LOGGER.warn("Migration update timed out after ["+MIGRATION_MAX_UPDATE_TIMEOUT_IN_MS+"] milliseconds. Migration will re-try the same positions again in a moment..", e); - } - catch (ExecutionException e) - { - LOGGER.warn("Migration update failed. Migration will re-try the same positions again. Error:"+e.getMessage(), e); - } - - legacyDataSourceList = this.legacyFileHandler.getDataSourcesToMigrate(MIGRATION_BATCH_COUNT); - - progressCount += legacyDataSourceList.size(); - this.migrationCount -= legacyDataSourceList.size(); } - - - if (this.migrationThreadRunning.get()) + catch (Exception e) { - LOGGER.info("migration complete for: ["+dimensionName+"]-["+this.saveDir+"]."); - this.showMigrationEndMessage(true); - this.migrationCount = 0; - } - else - { - LOGGER.info("migration stopped for: ["+dimensionName+"]-["+this.saveDir+"]."); + LOGGER.info("migration stopped due to error for: ["+dimensionName+"]-["+this.saveDir+"], error: ["+e.getMessage()+"].", e); this.showMigrationEndMessage(false); + this.migrationStoppedWithError = true; + } + finally + { + if (this.migrationThreadRunning.get()) + { + LOGGER.info("migration complete for: ["+dimensionName+"]-["+this.saveDir+"]."); + this.showMigrationEndMessage(true); + this.migrationCount = 0; + } + else + { + LOGGER.info("migration stopped for: ["+dimensionName+"]-["+this.saveDir+"]."); + this.showMigrationEndMessage(false); + this.migrationStoppedWithError = true; + } } } else @@ -474,6 +487,7 @@ public class FullDataSourceProviderV2 public long getLegacyDeletionCount() { return this.legacyDeletionCount; } public long getTotalMigrationCount() { return this.migrationCount; } + public boolean getMigrationStoppedWithError() { return this.migrationStoppedWithError; } private void showMigrationStartMessage() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java index f474c15d3..7ed8e1e43 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java @@ -57,8 +57,6 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I public final FullDataSourceProviderV2 fullDataSourceProvider; public final AtomicReference ClientRenderStateRef = new AtomicReference<>(); - public final F3Screen.NestedMessage f3Message; - //=============// @@ -68,7 +66,6 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I public ClientLevelModule(IDhClientLevel clientLevel) { this.clientLevel = clientLevel; - this.f3Message = new F3Screen.NestedMessage(this::f3Log); this.fullDataSourceProvider = this.clientLevel.getFullDataProvider(); this.fullDataSourceProvider.dateSourceUpdateListeners.add(this); @@ -246,8 +243,6 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I } this.fullDataSourceProvider.dateSourceUpdateListeners.remove(this); - - this.f3Message.close(); } @@ -256,53 +251,6 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I // misc helper functions // //=======================// - private String[] f3Log() - { - String dimName = this.clientLevel.getLevelWrapper().getDimensionType().getDimensionName(); - boolean rendererActive = this.ClientRenderStateRef.get() != null; - - ThreadPoolExecutor fileExecutor = ThreadPoolUtil.getFileHandlerExecutor(); - String fileQueueSize = (fileExecutor != null) ? fileExecutor.getQueue().size()+"" : "-"; - String fileCompletedTaskSize = (fileExecutor != null) ? fileExecutor.getCompletedTaskCount()+"" : "-"; - - ThreadPoolExecutor updateExecutor = ThreadPoolUtil.getUpdatePropagatorExecutor(); - String updateQueueSize = (updateExecutor != null) ? updateExecutor.getQueue().size()+"" : "-"; - String updateCompletedTaskSize = (updateExecutor != null) ? updateExecutor.getCompletedTaskCount()+"" : "-"; - - int unsavedDataSourceCount = this.fullDataSourceProvider.getUnsavedDataSourceCount(); - long legacyDeletionCount = this.fullDataSourceProvider.getLegacyDeletionCount(); - long migrationCount = this.fullDataSourceProvider.getTotalMigrationCount(); - - - - ArrayList lines = new ArrayList<>(); - lines.add(""); - lines.add("level [" + dimName + "] rendering: " + (rendererActive ? "Active" : "Inactive")); - // TODO a lot of these items only need to be rendered once, but we don't currently have a way of doing that, so only add them for the rendered level - if (rendererActive) - { - lines.add("File Handler [" + dimName + "]"); - lines.add(" File thread pool tasks: " + fileQueueSize + " (completed: " + fileCompletedTaskSize + ")"); - if (legacyDeletionCount > 0) - { - lines.add(" Legacy Deletion #: " + legacyDeletionCount); - } - if (migrationCount > 0) - { - lines.add(" Legacy Migration #: " + migrationCount); - } - lines.add(" Update thread pool tasks: " + updateQueueSize + " (completed: " + updateCompletedTaskSize + ")"); - lines.add(" Level Unsaved #: " + this.clientLevel.getUnsavedDataSourceCount()); - if (unsavedDataSourceCount != -1) - { - lines.add(" File Handler Unsaved #: " + unsavedDataSourceCount); - } - lines.add(" Parent Update #: " + this.fullDataSourceProvider.parentUpdatingPosSet.size()); - } - - return lines.toArray(new String[0]); - } - public void clearRenderCache() { ClientRenderState ClientRenderState = this.ClientRenderStateRef.get(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index f39d12ec8..42a9d5746 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -35,6 +35,8 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; import java.io.File; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; /** The level used when connected to a server */ @@ -122,6 +124,34 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel @Override public int getMinY() { return this.levelWrapper.getMinHeight(); } + @Override + public void addDebugMenuStringsToList(List messageList) + { + String dimName = this.levelWrapper.getDimensionType().getDimensionName(); + boolean rendering = this.clientside.isRendering(); + messageList.add("["+dimName+"] rendering: "+(rendering ? "yes" : "no")); + + + boolean migrationErrored = this.dataFileHandler.getMigrationStoppedWithError(); + if (!migrationErrored) + { + long legacyDeletionCount = this.dataFileHandler.getLegacyDeletionCount(); + if (legacyDeletionCount > 0) + { + messageList.add(" Migrating - Deleting #: " + legacyDeletionCount); + } + long migrationCount = this.dataFileHandler.getTotalMigrationCount(); + if (migrationCount > 0) + { + messageList.add(" Migrating - Conversion #: " + migrationCount); + } + } + else + { + messageList.add(" Migration Failed"); + } + } + @Override public void close() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 066acea0d..42b204e21 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -23,6 +23,7 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; @@ -38,6 +39,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp import org.apache.logging.log4j.Logger; import java.awt.*; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; /** The level used on a singleplayer world */ @@ -191,6 +194,51 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev + //===========// + // debugging // + //===========// + + @Override + public void addDebugMenuStringsToList(List messageList) + { + // header + String dimName = this.serverLevelWrapper.getDimensionType().getDimensionName(); + boolean rendering = this.clientside.isRendering(); + messageList.add("["+dimName+"] rendering: "+(rendering ? "yes" : "no")); + + + // migration + boolean migrationErrored = this.serverside.fullDataFileHandler.getMigrationStoppedWithError(); + if (!migrationErrored) + { + long legacyDeletionCount = this.serverside.fullDataFileHandler.getLegacyDeletionCount(); + if (legacyDeletionCount > 0) + { + messageList.add(" Migrating - Deleting #: " + F3Screen.NUMBER_FORMAT.format(legacyDeletionCount)); + } + long migrationCount = this.serverside.fullDataFileHandler.getTotalMigrationCount(); + if (migrationCount > 0) + { + messageList.add(" Migrating - Conversion #: " + F3Screen.NUMBER_FORMAT.format(migrationCount)); + } + } + else + { + messageList.add(" Migration Failed"); + } + + + // world gen + WorldGenModule worldGenState = this.serverside.worldGenModule; + String worldGenDisplayString = worldGenState.getDebugMenuString(); + if (worldGenDisplayString != null) + { + messageList.add(worldGenDisplayString); + } + } + + + //===============// // data handling // //===============// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 7f9f6e682..142c1ade1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -28,6 +28,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import org.apache.logging.log4j.Logger; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.CompletableFuture; public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel @@ -115,4 +117,17 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel //TODO: Send packet to client } + + + //===========// + // debugging // + //===========// + + @Override + public void addDebugMenuStringsToList(List messageList) + { + String dimName = this.serverLevelWrapper.getDimensionType().getDimensionName(); + messageList.add("["+dimName+"]"); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhClientLevel.java index be7040607..d06cb0ca3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhClientLevel.java @@ -22,7 +22,6 @@ package com.seibel.distanthorizons.core.level; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index 41f560e1a..66e32c3da 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import java.util.List; import java.util.concurrent.CompletableFuture; public interface IDhLevel extends AutoCloseable @@ -57,4 +58,7 @@ public interface IDhLevel extends AutoCloseable */ int getUnsavedDataSourceCount(); + void addDebugMenuStringsToList(List messageList); + + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java index 07c9dd3b7..b12683f24 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java @@ -37,30 +37,16 @@ public class WorldGenModule implements Closeable private final GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener; private final AtomicReference worldGenStateRef = new AtomicReference<>(); - private final F3Screen.DynamicMessage worldGenF3Message; + //=============// + // constructor // + //=============// + public WorldGenModule(GeneratedFullDataSourceProvider.IOnWorldGenCompleteListener onWorldGenCompleteListener) { this.onWorldGenCompleteListener = onWorldGenCompleteListener; - this.worldGenF3Message = new F3Screen.DynamicMessage(() -> - { - AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) - { - int waitingCount = worldGenState.worldGenerationQueue.getWaitingTaskCount(); - int inProgressCount = worldGenState.worldGenerationQueue.getInProgressTaskCount(); - int totalCountEstimate = worldGenState.worldGenerationQueue.getEstimatedTotalTaskCount(); - - return "World Gen Tasks: "+waitingCount+"/"+totalCountEstimate+", (in progress: "+inProgressCount+")"; - } - else - { - return "World Gen Disabled"; - } - }); - } @@ -114,6 +100,12 @@ public class WorldGenModule implements Closeable } } + + + //=======================// + // base method overrides // + //=======================// + @Override public void close() { @@ -135,8 +127,6 @@ public class WorldGenModule implements Closeable worldGenState.closeAsync(true).join(); //TODO: Make it async. } } - - this.worldGenF3Message.close(); } @@ -147,6 +137,22 @@ public class WorldGenModule implements Closeable public boolean isWorldGenRunning() { return this.worldGenStateRef.get() != null; } + public String getDebugMenuString() + { + AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + return null; + } + + + 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()); + + return "World Gen Tasks: "+waitingCountStr+"/"+totalCountEstimateStr+" (in progress: "+inProgressCountStr+")"; + } + //================// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index 595628a15..a3d4666be 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -19,159 +19,127 @@ package com.seibel.distanthorizons.core.logging.f3; +import com.seibel.distanthorizons.core.api.internal.SharedApi; +import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.render.RenderBufferHandler; +import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; +import com.seibel.distanthorizons.core.world.AbstractDhWorld; import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; -import java.io.Closeable; +import java.lang.ref.WeakReference; +import java.text.NumberFormat; import java.util.*; -import java.util.function.Supplier; +import java.util.concurrent.ThreadPoolExecutor; public class F3Screen { private static final Logger LOGGER = LogManager.getLogger(); - private static final String[] DEFAULT_STRING = { - "", // blank line for spacing - ModInfo.READABLE_NAME + " version: " + ModInfo.VERSION - }; - private static final List SELF_UPDATE_MESSAGE_LIST = Collections.synchronizedList(new LinkedList<>()); + public static final NumberFormat NUMBER_FORMAT = NumberFormat.getIntegerInstance(); - public static void addStringToDisplay(List list) + + + //============// + // properties // + //============// + + private static WeakReference renderBufferHandlerRef = new WeakReference<>(null); + public static void setRenderBufferHandler(@Nullable RenderBufferHandler renderBufferHandler) { - list.addAll(Arrays.asList(DEFAULT_STRING)); - synchronized (SELF_UPDATE_MESSAGE_LIST) + if (renderBufferHandler != null && renderBufferHandlerRef.get() != null) { - Iterator iterator = SELF_UPDATE_MESSAGE_LIST.iterator(); - while (iterator.hasNext()) + LOGGER.warn("multiple RenderBufferHandlers are active at once, the F3 menu may not be accurate."); + } + + renderBufferHandlerRef = new WeakReference<>(renderBufferHandler); + } + + + + //=================// + // injection point // + //=================// + + /** + * F3 menu example:
+ + Distant Horizons v: 2.1.1-a-dev

+ + Queued chunk updates: 0 / 1000
+ World Gen Tasks: 40/5304, (in progress: 7)

+ + File thread pool tasks: 0 (complete: 759)
+ Update thread pool tasks: 10 (complete: 24)
+ Level Unsaved #: 0
+ File Handler Unsaved #: 0
+ Parent Update #: 12

+ + Client_Server World with 3 levels
+ [overworld] rendering: Active
+ [the_end] rendering: Inactive
+ [the_nether] rendering: Inactive

+ + VBO Render Count: 199/374
+
+ */ + public static void addStringToDisplay(List messageList) + { + ThreadPoolExecutor worldGenPool = ThreadPoolUtil.getWorldGenExecutor(); + ThreadPoolExecutor fileHandlerPool = ThreadPoolUtil.getFileHandlerExecutor(); + ThreadPoolExecutor updatePool = ThreadPoolUtil.getUpdatePropagatorExecutor(); + + AbstractDhWorld world = SharedApi.getAbstractDhWorld(); + Iterable levelIterator = world.getAllLoadedLevels(); + + + messageList.add(""); + messageList.add(ModInfo.READABLE_NAME+": "+ModInfo.VERSION); + messageList.add(""); + // thread pools + messageList.add(getThreadPoolStatString("World Gen", worldGenPool));//"World Gen Tasks: 40/5304, (in progress: 7)"); + messageList.add(getThreadPoolStatString("File Handler", fileHandlerPool)); + messageList.add(getThreadPoolStatString("Update Propagator", updatePool)); + messageList.add(""); + // chunk updates + messageList.add(SharedApi.INSTANCE.getDebugMenuString()); + messageList.add(""); + // rendering + RenderBufferHandler renderBufferHandler = renderBufferHandlerRef.get(); + if (renderBufferHandler != null) + { + messageList.add(renderBufferHandler.getVboRenderDebugMenuString()); + String showPassString = renderBufferHandler.getShadowPassRenderDebugMenuString(); + if (showPassString != null) { - Message message = iterator.next(); - if (message == null) - { - iterator.remove(); - } - else - { - message.printTo(list); - } + messageList.add(showPassString); } + messageList.add(""); + } + // world / levels + messageList.add(world.GetDebugMenuString()); + for (IDhLevel level : levelIterator) + { + level.addDebugMenuStringsToList(messageList); } } //================// - // helper classes // + // helper methods // //================// - // we are using Closeable instead of AutoCloseable because the close method should never throw exceptions - // and because this class shouldn't be used in a try {} block. - public static abstract class Message implements Closeable + private static String getThreadPoolStatString(String name, ThreadPoolExecutor pool) { - protected Message() - { - SELF_UPDATE_MESSAGE_LIST.add(this); - } - - public abstract void printTo(List output); - - @Override - public void close() - { - boolean removed = SELF_UPDATE_MESSAGE_LIST.remove(this); - } + String queueSize = (pool != null) ? NUMBER_FORMAT.format(pool.getQueue().size()) : "-"; + String completedCount = (pool != null) ? NUMBER_FORMAT.format(pool.getCompletedTaskCount()) : "-"; + return name+", tasks: "+queueSize+", complete: "+completedCount; } - public static class StaticMessage extends Message - { - private final String[] lines; - - public StaticMessage(String... lines) { this.lines = lines; } - - @Override - public void printTo(List output) { output.addAll(Arrays.asList(this.lines)); } - - } - public static class DynamicMessage extends Message - { - private final Supplier supplier; - - public DynamicMessage(Supplier message) { this.supplier = message; } - - public void printTo(List list) - { - - try - { - String message = this.supplier.get(); - if (message != null) - { - list.add(message); - } - } - catch (Exception e) - { - LOGGER.error("Unexpected Exception in F3 ["+DynamicMessage.class.getSimpleName()+"], error: "+e.getMessage(), e); - } - } - - } - - public static class MultiDynamicMessage extends Message - { - private final Supplier[] supplierList; - - @SafeVarargs - public MultiDynamicMessage(Supplier... suppliers) { this.supplierList = suppliers; } - - public void printTo(List list) - { - for (Supplier supplier : this.supplierList) - { - try - { - String message = supplier.get(); - if (message != null) - { - list.add(message); - } - } - catch (Exception e) - { - LOGGER.error("Unexpected Exception in F3 ["+DynamicMessage.class.getSimpleName()+"], error: "+e.getMessage(), e); - } - } - } - - } - - public static class NestedMessage extends Message - { - private final Supplier supplier; - - public NestedMessage(Supplier message) - { - this.supplier = message; - } - - public void printTo(List list) - { - try - { - String[] message = this.supplier.get(); - if (message != null) - { - list.addAll(Arrays.asList(message)); - } - } - catch (Exception e) - { - LOGGER.error("Unexpected Exception in F3 ["+DynamicMessage.class.getSimpleName()+"], error: "+e.getMessage(), e); - } - } - - } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java index 3c151d7d9..f36280ae8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java @@ -73,8 +73,6 @@ public class RenderBufferHandler implements AutoCloseable private final AtomicBoolean rebuildAllBuffers = new AtomicBoolean(false); - public F3Screen.MultiDynamicMessage f3Message; - private int visibleBufferCount; private int culledBufferCount; private int shadowVisibleBufferCount; @@ -104,31 +102,7 @@ public class RenderBufferHandler implements AutoCloseable } - this.f3Message = new F3Screen.MultiDynamicMessage( - () -> - { - String countText = this.visibleBufferCount + ""; - if (!Config.Client.Advanced.Graphics.AdvancedGraphics.disableFrustumCulling.get()) - { - countText += "/" + (this.visibleBufferCount + this.culledBufferCount); - } - return LodUtil.formatLog("Rendered Buffer Count: " + countText); - }, - () -> - { - boolean hasIrisShaders = (IRIS_ACCESSOR != null && IRIS_ACCESSOR.isShaderPackInUse()); - if (!hasIrisShaders) - { - return null; - } - - String countText = this.shadowVisibleBufferCount + ""; - if (!Config.Client.Advanced.Graphics.AdvancedGraphics.disableFrustumCulling.get()) - { - countText += "/" + (this.shadowVisibleBufferCount + this.shadowCulledBufferCount); - } - return LodUtil.formatLog("Shadow Buffer Count: " + countText); - }); + F3Screen.setRenderBufferHandler(this); } @@ -396,6 +370,37 @@ public class RenderBufferHandler implements AutoCloseable + //=========// + // F3 menu // + //=========// + + public String getVboRenderDebugMenuString() + { + String countText = F3Screen.NUMBER_FORMAT.format(this.visibleBufferCount); + if (!Config.Client.Advanced.Graphics.AdvancedGraphics.disableFrustumCulling.get()) + { + countText += "/" + F3Screen.NUMBER_FORMAT.format(this.visibleBufferCount + this.culledBufferCount); + } + return LodUtil.formatLog("VBO Render Count: " + countText); + } + public String getShadowPassRenderDebugMenuString() + { + boolean hasIrisShaders = (IRIS_ACCESSOR != null && IRIS_ACCESSOR.isShaderPackInUse()); + if (!hasIrisShaders) + { + return null; + } + + String countText = F3Screen.NUMBER_FORMAT.format(this.shadowVisibleBufferCount); + if (!Config.Client.Advanced.Graphics.AdvancedGraphics.disableFrustumCulling.get()) + { + countText += "/" + F3Screen.NUMBER_FORMAT.format(this.shadowVisibleBufferCount + this.shadowCulledBufferCount); + } + return LodUtil.formatLog("Shadow VBO Render Count: " + countText); + } + + + //=========// // cleanup // //=========// @@ -413,7 +418,7 @@ public class RenderBufferHandler implements AutoCloseable } } - this.f3Message.close(); + F3Screen.setRenderBufferHandler(null); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java index 42bf5b06d..5b08c99a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.world; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import org.apache.logging.log4j.Logger; import java.io.Closeable; @@ -36,11 +37,22 @@ public abstract class AbstractDhWorld implements IDhWorld, Closeable + // constructor // + protected AbstractDhWorld(EWorldEnvironment environment) { this.environment = environment; } - // remove the "throws IOException" + + // abstract methods // + + // removes the "throws IOException" @Override public abstract void close(); + + + // helper methods // + + public String GetDebugMenuString() { return this.environment + " World with " + F3Screen.NUMBER_FORMAT.format(this.getLoadedLevelCount()) + " levels"; } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java index 2f31238ac..7b39f48e7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java @@ -46,8 +46,6 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("Client Server World Ticker Thread", 2); public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick); //TODO: Rate-limit the loop - public F3Screen.DynamicMessage f3Message; - //=============// @@ -57,10 +55,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor public DhClientServerWorld() { super(EWorldEnvironment.Client_Server); - LOGGER.info("Started DhWorld of type " + this.environment); - - this.f3Message = new F3Screen.DynamicMessage(() -> LodUtil.formatLog(this.environment + " World with " + this.dhLevels.size() + " levels")); } @@ -113,6 +108,8 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor @Override public Iterable getAllLoadedLevels() { return this.dhLevels; } + @Override + public int getLoadedLevelCount() { return this.dhLevels.size(); } @Override public void unloadLevel(@NotNull ILevelWrapper wrapper) @@ -154,13 +151,16 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor public void doWorldGen() { this.dhLevels.forEach(DhClientServerLevel::doWorldGen); } + + + //================// + // base overrides // + //================// + /** synchronized to prevent a rare issue where the server tries closing the same world multiple times in rapid succession. */ @Override public synchronized void close() { - this.f3Message.close(); - - // clear dhLevels to prevent concurrent modification errors HashSet levelsToClose = new HashSet<>(this.dhLevels); this.dhLevels.clear(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index b68cc9936..50af77234 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -145,6 +145,8 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld @Override public Iterable getAllLoadedLevels() { return this.levels.values(); } + @Override + public int getLoadedLevelCount() { return this.levels.size(); } @Override public void unloadLevel(@NotNull ILevelWrapper wrapper) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index c306e2b04..e036733e0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -167,6 +167,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld @Override public Iterable getAllLoadedLevels() { return this.levels.values(); } + @Override + public int getLoadedLevelCount() { return this.levels.size(); } @Override public void unloadLevel(@NotNull ILevelWrapper wrapper) @@ -188,6 +190,12 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld public void doWorldGen() { this.levels.values().forEach(DhServerLevel::doWorldGen); } + + + //================// + // base overrides // + //================// + @Override public void close() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhWorld.java index 9334ccbbd..c3bf59165 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhWorld.java @@ -31,6 +31,7 @@ public interface IDhWorld IDhLevel getOrLoadLevel(@NotNull ILevelWrapper levelWrapper); IDhLevel getLevel(@NotNull ILevelWrapper wrapper); Iterable getAllLoadedLevels(); + int getLoadedLevelCount(); void unloadLevel(@NotNull ILevelWrapper levelWrapper);