diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index eb5e699fb..6f9631bbf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -47,10 +47,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler @Override public CompletableFuture read(DhSectionPos pos) { - return super.read(pos).whenComplete((fullDataSource, ex) -> - { - //this.checkIfSectionNeedsAdditionalGeneration(pos, fullDataSource); - }); + return super.read(pos); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderMetaDataFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderMetaDataFile.java index 4d8d9dce4..645c55678 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderMetaDataFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderMetaDataFile.java @@ -139,7 +139,9 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements { return CompletableFuture.completedFuture(null); // No need to save if the file doesn't exist. } - CompletableFuture source = getCachedDataSourceAsync(true); + // FIXME: TODO: Change doTriggerUpdate to true. Currently is false cause a dead future making render handler hang, + // and that render cache aren't actually used really yet due to missing versioning atm. So disabling for now. + CompletableFuture source = getCachedDataSourceAsync(false); if (source == null) { return CompletableFuture.completedFuture(null); // If there is no cached data, there is no need to save. @@ -187,6 +189,9 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements this.fileHandler.onReadRenderSourceLoadedFromCacheAsync(this, cachedRenderDataSource) // wait for the handler to finish before returning the renderSource .handle((voidObj, ex) -> { + if (ex != null) { + LOGGER.error("Error while updating render source from cache", ex); + } newFuture.complete(cachedRenderDataSource); renderSourceLoadFutureRef.set(null); return null; @@ -218,7 +223,7 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements this.baseMetaData = this.makeMetaData(renderSource); return renderSource; }) - .thenApply((renderSource) -> this.fileHandler.onRenderFileLoaded(renderSource, this)) + .thenCompose((renderSource) -> this.fileHandler.onRenderFileLoaded(renderSource, this)) .whenComplete((renderSource, ex) -> { if (ex != null) @@ -239,13 +244,13 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements } else { - CompletableFuture.supplyAsync(() -> + CompletableFuture.supplyAsync(() -> { if (this.baseMetaData == null) { throw new IllegalStateException("Meta data not loaded!"); } - + // Load the file. ColumnRenderSource renderSource; try (FileInputStream fileInputStream = this.getFileInputStream(); @@ -257,10 +262,10 @@ public class RenderMetaDataFile extends AbstractMetaDataContainerFile implements { throw new CompletionException(ex); } - - renderSource = this.fileHandler.onRenderFileLoaded(renderSource, this); return renderSource; }, fileReaderThreads) + // TODO: Check for file version and only update if needed. + .thenCompose((renderSource) -> this.fileHandler.onRenderFileLoaded(renderSource, this)) .whenComplete((renderSource, ex) -> { if (ex != null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java index fd3fd431b..adfcf7c55 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderSourceFileHandler.java @@ -14,6 +14,7 @@ import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.util.FileScanUtil; import com.seibel.distanthorizons.core.util.FileUtil; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; import com.seibel.distanthorizons.core.config.Config; @@ -378,9 +379,9 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider DebugRenderer.BoxWithLife box = new DebugRenderer.BoxWithLife(new DebugRenderer.Box(renderSource.sectionPos, 74f, 86f, 0.1f, Color.red), 1.0, 32f, Color.green.darker()); // get the full data source loading future - CompletableFuture fullDataSourceFuture = this.fullDataSourceProvider.read(renderSource.getSectionPos()); - fullDataSourceFuture = fullDataSourceFuture.thenApply((fullDataSource) -> - { + CompletableFuture fullDataSourceFuture = + this.fullDataSourceProvider.read(renderSource.getSectionPos()) + .thenApply((fullDataSource) -> { // the fullDataSource can be null if the thread this was running on was interrupted box.box.color = Color.yellow.darker(); return fullDataSource; @@ -400,40 +401,30 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider { if (ex == null) { - this.writeRenderSourceToFile(renderSource, file, newRenderSource); + try { + this.writeRenderSourceToFile(renderSource, file, newRenderSource); + } catch (Throwable e) { + LOGGER.error("Exception when writing render data to file: ", e); + } } - else + else if (!LodUtil.isInterruptOrReject(ex)) { - if (ex instanceof InterruptedException) - { - // expected if the transformer is shut down, the exception can be ignored -// LOGGER.warn("RenderSource file transforming interrupted."); - - int ignoreEmptyWarning = 0; // explicitly handling these exceptions is important so we know where they are going and if there is an issue we can easily re-enable the logging - } - else if (ex instanceof RejectedExecutionException || ex.getCause() instanceof RejectedExecutionException) - { - // expected if the transformer was already shut down, the exception can be ignored -// LOGGER.warn("RenderSource file transforming interrupted."); - - int ignoreEmptyWarning = 0; - } - else if (!UncheckedInterruptedException.isInterrupt(ex)) - { - LOGGER.error("Exception when updating render file using data source: ", ex); - } + LOGGER.error("Exception when updating render file using data source: ", ex); } box.close(); transformationCompleteFuture.complete(null); }); return transformationCompleteFuture; } - - /** TODO at some point this method may need to be made "async" like {@link RenderSourceFileHandler#onReadRenderSourceLoadedFromCacheAsync} since the insides are async */ - public ColumnRenderSource onRenderFileLoaded(ColumnRenderSource renderSource, RenderMetaDataFile file) + + public CompletableFuture onRenderFileLoaded(ColumnRenderSource renderSource, RenderMetaDataFile file) { - this.updateCacheAsync(renderSource, file).join(); - return renderSource; + return this.updateCacheAsync(renderSource, file).handle((voidObj, ex) -> { + if (ex != null && !LodUtil.isInterruptOrReject(ex)) { + LOGGER.error("Exception when updating render file using data source: ", ex); + } + return renderSource; + }); } public CompletableFuture onReadRenderSourceLoadedFromCacheAsync(RenderMetaDataFile file, ColumnRenderSource data) { @@ -508,17 +499,18 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider // if the save futures didn't already complete, wait for them and then shut down the thread pool CompletableFuture combinedFuture = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])); - combinedFuture.thenRun(() -> + combinedFuture.handle((result, ex) -> { + if (ex != null && !LodUtil.isInterruptOrReject(ex)) { + LOGGER.error("Exception when waiting for render source files to save", ex); + } + return null; + }).thenRun(() -> { LOGGER.info("Finished closing "+this.getClass().getSimpleName()+", ["+futures.size()+"] files were saved out of ["+this.filesBySectionPos.size()+"] total files."); this.fileHandlerThreadPool.shutdown(); }); } - - // if the save futures were already completed, the above "thenRun" won't fire, - // if the executor isn't currently running anything, shut it down - if (!this.fileHandlerThreadPool.isTerminated() && this.fileHandlerThreadPool.getActiveCount() == 0) - { + else { LOGGER.info("Finished closing " + this.getClass().getSimpleName() + " when files were already saved."); this.fileHandlerThreadPool.shutdown(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/BatchGenerator.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/BatchGenerator.java index 1fd4fc79f..b63d3a012 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/BatchGenerator.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/BatchGenerator.java @@ -138,7 +138,15 @@ public class BatchGenerator implements IDhApiWorldGenerator // the consumer needs to be wrapped like this because the API can't use DH core objects (and IChunkWrapper can't be easily put into the API project) Consumer consumerWrapper = (chunkWrapper) -> resultConsumer.accept(new Object[]{ chunkWrapper }); - return this.generationEnvironment.generateChunks(chunkPosMinX, chunkPosMinZ, genChunkSize, targetStep, worldGeneratorThreadPool, consumerWrapper); + try { + return this.generationEnvironment.generateChunks(chunkPosMinX, chunkPosMinZ, genChunkSize, targetStep, worldGeneratorThreadPool, consumerWrapper); + } + catch (Exception e) { + if (!LodUtil.isInterruptOrReject(e)) LOGGER.error("Error starting future for chunk generation", e); + CompletableFuture future = new CompletableFuture<>(); + future.completeExceptionally(e); + return future; + } } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 0e003dd77..2e1a1ad1d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -426,8 +426,7 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable if (exception != null) { // don't log the shutdown exceptions - if (!UncheckedInterruptedException.isInterrupt(exception) - && !(exception instanceof CancellationException || exception.getCause() instanceof CancellationException)) + if (!LodUtil.isInterruptOrReject(exception)) { LOGGER.error("Error generating data for section "+taskPos, exception); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java index 02bad3b97..1a96cef6a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.util; import java.util.Iterator; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletionException; import java.util.concurrent.RejectedExecutionException; @@ -308,7 +309,8 @@ public class LodUtil public static boolean isInterruptOrReject(Throwable t) { Throwable unwrapped = LodUtil.ensureUnwrap(t); return UncheckedInterruptedException.isInterrupt(unwrapped) || - unwrapped instanceof RejectedExecutionException; + unwrapped instanceof RejectedExecutionException || + unwrapped instanceof CancellationException; } }