Fix reading/writing files after leaving the world

May have issues in Client-only mode
This commit is contained in:
James Seibel
2023-03-01 07:31:40 -06:00
parent cc474caf33
commit 9f601ea6b3
10 changed files with 361 additions and 141 deletions
@@ -1,6 +1,7 @@
package com.seibel.lod.core.api.internal;
import com.seibel.lod.core.Initializer;
import com.seibel.lod.core.dataObjects.transformers.DataRenderTransformer;
import com.seibel.lod.core.world.*;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
@@ -19,7 +20,21 @@ public class SharedApi
public static EWorldEnvironment getEnvironment() { return (currentWorld == null) ? null : currentWorld.environment; }
public static void setDhWorld(AbstractDhWorld newWorld) { currentWorld = newWorld; }
public static void setDhWorld(AbstractDhWorld newWorld)
{
currentWorld = newWorld;
// starting and stopping the DataRenderTransformer is necessary to prevent attempting to
// access the MC level at inappropriate times, which can cause exceptions
if (currentWorld == null)
{
DataRenderTransformer.shutdownExecutorService();
}
else
{
DataRenderTransformer.setupExecutorService();
}
}
public static AbstractDhWorld getAbstractDhWorld() { return currentWorld; }
/** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientServerWorld} */
@@ -57,9 +57,12 @@ public class ColumnRenderLoader
}
}
public ColumnRenderSource createRenderSource(IFullDataSource dataSource, IDhClientLevel level)
/**
* @throws InterruptedException see {@link FullToColumnTransformer#transformFullDataToColumnData(IDhClientLevel, FullDataSource) FullToColumnTransformer#transformFullDataToColumnData} for documentation
*/
public ColumnRenderSource createRenderSource(IFullDataSource dataSource, IDhClientLevel level) throws InterruptedException
{
if (dataSource instanceof FullDataSource) // TODO replace with Java 7 method
if (dataSource instanceof FullDataSource)
{
return FullToColumnTransformer.transformFullDataToColumnData(level, (FullDataSource) dataSource);
}
@@ -290,7 +290,18 @@ public class ColumnRenderSource
}
}
public void fastWrite(ChunkSizedFullDataSource chunkData, IDhClientLevel level) { FullToColumnTransformer.writeFullDataChunkToColumnData(this, level, chunkData); }
public void fastWrite(ChunkSizedFullDataSource chunkData, IDhClientLevel level)
{
try
{
FullToColumnTransformer.writeFullDataChunkToColumnData(this, level, chunkData);
}
catch (InterruptedException e)
{
// expected if the transformer is shut down, the exception can be ignored
// LOGGER.warn(ColumnRenderSource.class.getSimpleName()+" fast write interrupted.");
}
}
@@ -3,8 +3,12 @@ package com.seibel.lod.core.dataObjects.transformers;
import com.seibel.lod.core.dataObjects.fullData.IFullDataSource;
import com.seibel.lod.core.dataObjects.render.ColumnRenderLoader;
import com.seibel.lod.core.dataObjects.render.ColumnRenderSource;
import com.seibel.lod.core.dependencyInjection.SingletonInjector;
import com.seibel.lod.core.level.IDhClientLevel;
import com.seibel.lod.core.logging.DhLoggerBuilder;
import com.seibel.lod.core.util.LodUtil;
import com.seibel.lod.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import org.apache.logging.log4j.Logger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
@@ -12,27 +16,78 @@ import java.util.concurrent.ExecutorService;
/** TODO: Merge this with {@link FullToColumnTransformer} */
public class DataRenderTransformer
{
public static final ExecutorService TRANSFORMER_THREADS
= LodUtil.makeThreadPool(4, "Data/Render Transformer");
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static ExecutorService transformerThreads = null;
public static CompletableFuture<ColumnRenderSource> transformDataSource(IFullDataSource data, IDhClientLevel level)
//==============//
// transformers //
//==============//
public static CompletableFuture<ColumnRenderSource> transformDataSourceAsync(IFullDataSource fullDataSource, IDhClientLevel level)
{
return CompletableFuture.supplyAsync(() -> transform(data, level), TRANSFORMER_THREADS);
return CompletableFuture.supplyAsync(() -> transform(fullDataSource, level), transformerThreads);
}
public static CompletableFuture<ColumnRenderSource> asyncTransformDataSource(CompletableFuture<IFullDataSource> fullDataSourceFuture, IDhClientLevel level)
public static CompletableFuture<ColumnRenderSource> transformDataSourceAsync(CompletableFuture<IFullDataSource> fullDataSourceFuture, IDhClientLevel level)
{
return fullDataSourceFuture.thenApplyAsync((fullDataSource) -> transform(fullDataSource, level), TRANSFORMER_THREADS);
return fullDataSourceFuture.thenApplyAsync((fullDataSource) -> transform(fullDataSource, level), transformerThreads);
}
private static ColumnRenderSource transform(IFullDataSource dataSource, IDhClientLevel level)
private static ColumnRenderSource transform(IFullDataSource fullDataSource, IDhClientLevel level)
{
if (dataSource == null)
if (fullDataSource == null)
{
return null;
}
else if (MC.getWrappedClientWorld() == null)
{
// if the client is no longer loaded in the world, render sources cannot be created
return null;
}
return ColumnRenderLoader.INSTANCE.createRenderSource(dataSource, level);
try
{
return ColumnRenderLoader.INSTANCE.createRenderSource(fullDataSource, level);
}
catch (InterruptedException e)
{
return null;
}
}
//==========================//
// executor handler methods //
//==========================//
/**
* Creates a new executor. <br>
* Does nothing if an executor already exists.
*/
public static void setupExecutorService()
{
if (transformerThreads == null || transformerThreads.isTerminated())
{
LOGGER.info("Starting "+DataRenderTransformer.class.getSimpleName());
transformerThreads = LodUtil.makeThreadPool(4, "Data/Render Transformer");
}
}
/**
* Stops any executing tasks and destroys the executor. <br>
* Does nothing if the executor isn't running.
*/
public static void shutdownExecutorService()
{
if (transformerThreads != null)
{
LOGGER.info("Stopping "+DataRenderTransformer.class.getSimpleName());
transformerThreads.shutdownNow();
}
}
}
@@ -20,34 +20,73 @@ import com.seibel.lod.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.lod.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.lod.core.wrapperInterfaces.world.IBiomeWrapper;
public class FullToColumnTransformer {
public class FullToColumnTransformer
{
private static final IBlockStateWrapper AIR = SingletonInjector.INSTANCE.get(IWrapperFactory.class).getAirBlockStateWrapper();
/**
* Called in loops that may run for an extended period of time. <br>
* This is necessary to allow canceling these transformers since running
* them after the client has left a given world will throw exceptions here.
*/
private static void throwIfThreadInterrupted() throws InterruptedException
{
if (Thread.interrupted())
{
throw new InterruptedException(FullToColumnTransformer.class.getSimpleName()+" task interrupted.");
}
}
//==============//
// transformers //
//==============//
/**
* Creates a LodNode for a chunk in the given world.
* @throws IllegalArgumentException thrown if either the chunk or world is null.
* @throws InterruptedException Can be caused by interrupting the thread upstream.
* Generally thrown if the method is running after the client leaves the current world.
*/
public static ColumnRenderSource transformFullDataToColumnData(IDhClientLevel level, FullDataSource data) {
public static ColumnRenderSource transformFullDataToColumnData(IDhClientLevel level, FullDataSource data) throws InterruptedException
{
final DhSectionPos pos = data.getSectionPos();
final byte dataDetail = data.getDataDetail();
final int vertSize = Config.Client.Graphics.Quality.verticalQuality.get().calculateMaxVerticalData(data.getDataDetail());
final ColumnRenderSource columnSource = new ColumnRenderSource(pos, vertSize, level.getMinY());
if (data.isEmpty()) return columnSource;
if (data.isEmpty())
{
return columnSource;
}
columnSource.markNotEmpty();
if (dataDetail == columnSource.getDataDetail()) {
if (dataDetail == columnSource.getDataDetail())
{
int baseX = pos.getCorner().getCornerBlockPos().x;
int baseZ = pos.getCorner().getCornerBlockPos().z;
for (int x = 0; x < pos.getWidth(dataDetail).numberOfLodSectionsWide; x++) {
for (int z = 0; z < pos.getWidth(dataDetail).numberOfLodSectionsWide; z++) {
for (int x = 0; x < pos.getWidth(dataDetail).numberOfLodSectionsWide; x++)
{
for (int z = 0; z < pos.getWidth(dataDetail).numberOfLodSectionsWide; z++)
{
throwIfThreadInterrupted();
ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
SingleFullArrayView fullArrayView = data.get(x, z);
convertColumnData(level, baseX + x, baseZ + z, columnArrayView, fullArrayView, 1);
if (fullArrayView.doesItExist()) LodUtil.assertTrue(columnSource.doesDataPointExist(x, z));
if (fullArrayView.doesItExist())
{
LodUtil.assertTrue(columnSource.doesDataPointExist(x, z));
}
}
}
columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL);
// } else if (dataDetail == 0 && columnSource.getDataDetail() > dataDetail) {
// byte deltaDetail = (byte) (columnSource.getDataDetail() - dataDetail);
// int perColumnWidth = 1 << deltaDetail;
@@ -61,136 +100,209 @@ public class FullToColumnTransformer {
// convertColumnData(level, columnArrayView, fullArrayView);
// }
// }
} else {
throw new UnsupportedOperationException("To be implemented");
//FIXME: Implement different size creation of renderData
}
return columnSource;
}
else
{
throw new UnsupportedOperationException("To be implemented");
//FIXME: Implement different size creation of renderData
}
return columnSource;
}
public static ColumnRenderSource transformIncompleteDataToColumnData(IDhClientLevel level, IIncompleteFullDataSource data)
/**
* @throws InterruptedException Can be caused by interrupting the thread upstream.
* Generally thrown if the method is running after the client leaves the current world.
*/
public static ColumnRenderSource transformIncompleteDataToColumnData(IDhClientLevel level, IIncompleteFullDataSource data) throws InterruptedException
{
final DhSectionPos pos = data.getSectionPos();
final byte dataDetail = data.getDataDetail();
final int vertSize = Config.Client.Graphics.Quality.verticalQuality.get().calculateMaxVerticalData(data.getDataDetail());
final ColumnRenderSource columnSource = new ColumnRenderSource(pos, vertSize, level.getMinY());
if (data.isEmpty()) return columnSource;
if (data.isEmpty())
{
return columnSource;
}
columnSource.markNotEmpty();
if (dataDetail == columnSource.getDataDetail()) {
int baseX = pos.getCorner().getCornerBlockPos().x;
int baseZ = pos.getCorner().getCornerBlockPos().z;
for (int x = 0; x < pos.getWidth(dataDetail).numberOfLodSectionsWide; x++) {
for (int z = 0; z < pos.getWidth(dataDetail).numberOfLodSectionsWide; z++) {
SingleFullArrayView fullArrayView = data.tryGet(x, z);
if (fullArrayView == null) continue;
ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
convertColumnData(level, baseX + x, baseZ + z, columnArrayView, fullArrayView, 1);
columnSource.fillDebugFlag(x, z, 1, 1, ColumnRenderSource.DebugSourceFlag.SPARSE);
if (fullArrayView.doesItExist()) LodUtil.assertTrue(columnSource.doesDataPointExist(x, z));
}
}
} else {
throw new UnsupportedOperationException("To be implemented");
//FIXME: Implement different size creation of renderData
}
if (dataDetail == columnSource.getDataDetail())
{
int baseX = pos.getCorner().getCornerBlockPos().x;
int baseZ = pos.getCorner().getCornerBlockPos().z;
for (int x = 0; x < pos.getWidth(dataDetail).numberOfLodSectionsWide; x++)
{
for (int z = 0; z < pos.getWidth(dataDetail).numberOfLodSectionsWide; z++)
{
throwIfThreadInterrupted();
SingleFullArrayView fullArrayView = data.tryGet(x, z);
if (fullArrayView == null)
{
continue;
}
ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z);
convertColumnData(level, baseX + x, baseZ + z, columnArrayView, fullArrayView, 1);
columnSource.fillDebugFlag(x, z, 1, 1, ColumnRenderSource.DebugSourceFlag.SPARSE);
if (fullArrayView.doesItExist())
LodUtil.assertTrue(columnSource.doesDataPointExist(x, z));
}
}
}
else
{
throw new UnsupportedOperationException("To be implemented");
//FIXME: Implement different size creation of renderData
}
return columnSource;
}
/**
* @throws InterruptedException Can be caused by interrupting the thread upstream.
* Generally thrown if the method is running after the client leaves the current world.
*/
public static void writeFullDataChunkToColumnData(ColumnRenderSource render, IDhClientLevel level, ChunkSizedFullDataSource data) throws InterruptedException
{
if (data.dataDetail != 0)
{
throw new UnsupportedOperationException("To be implemented");
}
final DhSectionPos pos = render.getSectionPos();
final int renderOffsetX = (data.x * 16) - pos.getCorner().getCornerBlockPos().x;
final int renderOffsetZ = (data.z * 16) - pos.getCorner().getCornerBlockPos().z;
final int blockX = pos.getCorner().getCornerBlockPos().x;
final int blockZ = pos.getCorner().getCornerBlockPos().z;
final int perRenderWidth = 1 << render.getDataDetail();
final int perDataWidth = 1 << data.dataDetail;
render.markNotEmpty();
if (data.dataDetail == render.getDataDetail())
{
if (renderOffsetX < 0 || renderOffsetX + 16 > render.getDataSize() || renderOffsetZ < 0 || renderOffsetZ + 16 > render.getDataSize())
{
throw new IllegalArgumentException("Data offset is out of bounds");
}
for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
{
throwIfThreadInterrupted();
ColumnArrayView columnArrayView = render.getVerticalDataPointView(renderOffsetX + x, renderOffsetZ + z);
SingleFullArrayView fullArrayView = data.get(x, z);
convertColumnData(level, blockX + perRenderWidth * (renderOffsetX + x),
blockZ + perRenderWidth * (renderOffsetZ + z),
columnArrayView, fullArrayView, 2);
if (fullArrayView.doesItExist())
{
LodUtil.assertTrue(render.doesDataPointExist(renderOffsetX + x, renderOffsetZ + z));
}
}
}
render.fillDebugFlag(renderOffsetX, renderOffsetZ, 16, 16, ColumnRenderSource.DebugSourceFlag.DIRECT);
}
else
{
final int dataPerRender = 1 << (render.getDataDetail() - data.dataDetail);
final int dataSize = 16 / dataPerRender;
final int vertSize = render.getVerticalSize();
long[] tempRender = new long[dataPerRender * dataPerRender * vertSize];
if (renderOffsetX < 0 || renderOffsetX + dataSize > render.getDataSize() || renderOffsetZ < 0 || renderOffsetZ + dataSize > render.getDataSize())
{
throw new IllegalArgumentException("Data offset is out of bounds");
}
for (int x = 0; x < dataSize; x++)
{
for (int z = 0; z < dataSize; z++)
{
ColumnQuadView tempQuadView = new ColumnQuadView(tempRender, dataPerRender, vertSize, 0, 0, dataPerRender, dataPerRender);
for (int ox = 0; ox < dataPerRender; ox++)
{
for (int oz = 0; oz < dataPerRender; oz++)
{
throwIfThreadInterrupted();
ColumnArrayView columnArrayView = tempQuadView.get(ox, oz);
SingleFullArrayView fullArrayView = data.get(x * dataPerRender + ox, z * dataPerRender + oz);
convertColumnData(level, blockX + perRenderWidth * (renderOffsetX + x) + perDataWidth * ox,
blockZ + perRenderWidth * (renderOffsetZ + z) + perDataWidth * oz,
columnArrayView, fullArrayView, 2);
}
}
ColumnArrayView downSampledArrayView = render.getVerticalDataPointView(renderOffsetX + x, renderOffsetZ + z);
downSampledArrayView.mergeMultiDataFrom(tempQuadView);
}
}
render.fillDebugFlag(renderOffsetX, renderOffsetZ, dataSize, dataSize, ColumnRenderSource.DebugSourceFlag.DIRECT);
}
}
public static void writeFullDataChunkToColumnData(ColumnRenderSource render, IDhClientLevel level, ChunkSizedFullDataSource data) {
if (data.dataDetail != 0)
throw new UnsupportedOperationException("To be implemented");
private static void convertColumnData(IDhClientLevel level, int blockX, int blockZ, ColumnArrayView columnArrayView, SingleFullArrayView fullArrayView, int genMode)
{
if (!fullArrayView.doesItExist())
{
return;
}
int dataTotalLength = fullArrayView.getSingleLength();
if (dataTotalLength == 0)
{
return;
}
final DhSectionPos pos = render.getSectionPos();
final int renderOffsetX = (data.x*16) - pos.getCorner().getCornerBlockPos().x;
final int renderOffsetZ = (data.z*16) - pos.getCorner().getCornerBlockPos().z;
final int blockX = pos.getCorner().getCornerBlockPos().x;
final int blockZ = pos.getCorner().getCornerBlockPos().z;
final int perRenderWidth = 1 << render.getDataDetail();
final int perDataWidth = 1 << data.dataDetail;
render.markNotEmpty();
if (data.dataDetail == render.getDataDetail()) {
if (renderOffsetX < 0 || renderOffsetX+16 > render.getDataSize() || renderOffsetZ < 0 || renderOffsetZ+16 > render.getDataSize())
throw new IllegalArgumentException("Data offset is out of bounds");
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
ColumnArrayView columnArrayView = render.getVerticalDataPointView(renderOffsetX + x, renderOffsetZ + z);
SingleFullArrayView fullArrayView = data.get(x, z);
convertColumnData(level, blockX + perRenderWidth * (renderOffsetX+x),
blockZ + perRenderWidth * (renderOffsetZ+z),
columnArrayView, fullArrayView, 2);
if (fullArrayView.doesItExist()) LodUtil.assertTrue(render.doesDataPointExist(renderOffsetX + x, renderOffsetZ + z));
}
}
render.fillDebugFlag(renderOffsetX, renderOffsetZ, 16, 16, ColumnRenderSource.DebugSourceFlag.DIRECT);
} else {
final int dataPerRender = 1 << (render.getDataDetail() - data.dataDetail);
final int dataSize = 16 / dataPerRender;
final int vertSize = render.getVerticalSize();
long[] tempRender = new long[dataPerRender * dataPerRender * vertSize];
if (renderOffsetX < 0 || renderOffsetX+dataSize > render.getDataSize() || renderOffsetZ < 0 || renderOffsetZ+dataSize > render.getDataSize())
throw new IllegalArgumentException("Data offset is out of bounds");
for (int x = 0; x < dataSize; x++) {
for (int z = 0; z < dataSize; z++) {
ColumnQuadView tempQuadView = new ColumnQuadView(tempRender, dataPerRender, vertSize, 0, 0, dataPerRender, dataPerRender);
for (int ox = 0; ox < dataPerRender; ox++) {
for (int oz = 0; oz < dataPerRender; oz++) {
ColumnArrayView columnArrayView = tempQuadView.get(ox, oz);
SingleFullArrayView fullArrayView = data.get(x*dataPerRender+ox, z*dataPerRender+oz);
convertColumnData(level, blockX + perRenderWidth * (renderOffsetX+x) + perDataWidth * ox,
blockZ + perRenderWidth * (renderOffsetZ+z) + perDataWidth * oz,
columnArrayView, fullArrayView, 2);
}
}
ColumnArrayView downSampledArrayView = render.getVerticalDataPointView(renderOffsetX + x, renderOffsetZ + z);
downSampledArrayView.mergeMultiDataFrom(tempQuadView);
}
}
render.fillDebugFlag(renderOffsetX, renderOffsetZ, dataSize, dataSize, ColumnRenderSource.DebugSourceFlag.DIRECT);
}
}
private static void convertColumnData(IDhClientLevel level, int blockX, int blockZ, ColumnArrayView columnArrayView, SingleFullArrayView fullArrayView, int genMode) {
if (!fullArrayView.doesItExist()) return;
int dataTotalLength = fullArrayView.getSingleLength();
if (dataTotalLength == 0) return;
if (dataTotalLength > columnArrayView.verticalSize()) {
if (dataTotalLength > columnArrayView.verticalSize())
{
ColumnArrayView totalColumnData = new ColumnArrayView(new long[dataTotalLength], dataTotalLength, 0, dataTotalLength);
iterateAndConvert(level, blockX, blockZ, genMode, totalColumnData, fullArrayView);
columnArrayView.changeVerticalSizeFrom(totalColumnData);
} else {
iterateAndConvert(level, blockX, blockZ, genMode, columnArrayView, fullArrayView); //Directly use the arrayView since it fits.
}
}
else
{
iterateAndConvert(level, blockX, blockZ, genMode, columnArrayView, fullArrayView); //Directly use the arrayView since it fits.
}
}
private static void iterateAndConvert(IDhClientLevel level, int blockX, int blockZ, int genMode, ColumnArrayView column, SingleFullArrayView data) {
FullDataPointIdMap mapping = data.getMapping();
boolean isVoid = true;
int offset = 0;
for (int i = 0; i < data.getSingleLength(); i++) {
long fullData = data.getSingle(i);
int bottomY = FullDataPointUtil.getBottomY(fullData);
int blockHeight = FullDataPointUtil.getHeight(fullData);
int id = FullDataPointUtil.getId(fullData);
int light = FullDataPointUtil.getLight(fullData);
IBiomeWrapper biome = mapping.getBiomeWrapper(id);
IBlockStateWrapper block = mapping.getBlockStateWrapper(id);
if (block.equals(AIR)) continue;
isVoid = false;
int color = level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block);
long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, light, genMode);
column.set(offset, columnData);
offset++;
}
if (isVoid) {
column.set(0, RenderDataPointUtil.createVoidDataPoint((byte) genMode));
}
}
//
//
private static void iterateAndConvert(IDhClientLevel level, int blockX, int blockZ, int genMode, ColumnArrayView column, SingleFullArrayView data)
{
FullDataPointIdMap mapping = data.getMapping();
boolean isVoid = true;
int offset = 0;
for (int i = 0; i < data.getSingleLength(); i++)
{
long fullData = data.getSingle(i);
int bottomY = FullDataPointUtil.getBottomY(fullData);
int blockHeight = FullDataPointUtil.getHeight(fullData);
int id = FullDataPointUtil.getId(fullData);
int light = FullDataPointUtil.getLight(fullData);
IBiomeWrapper biome = mapping.getBiomeWrapper(id);
IBlockStateWrapper block = mapping.getBlockStateWrapper(id);
if (block.equals(AIR))
{
continue;
}
isVoid = false;
int color = level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block);
long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, light, genMode);
column.set(offset, columnData);
offset++;
}
if (isVoid)
{
column.set(0, RenderDataPointUtil.createVoidDataPoint((byte) genMode));
}
}
// /** creates a vertical DataPoint */
// private void writeVerticalData(long[] data, int dataOffset, int maxVerticalData,
// IChunkWrapper chunk, LodBuilderConfig config, int chunkSubPosX, int chunkSubPosZ)
@@ -452,7 +452,9 @@ public class FullDataFileHandler implements IFullDataSourceProvider
public void close()
{
FullDataMetaFile.debugCheck();
//TODO
// stop any existing file tasks
fileReaderThread.shutdownNow();
}
}
@@ -2,6 +2,7 @@ package com.seibel.lod.core.file.fullDatafile;
import java.io.*;
import java.lang.ref.*;
import java.nio.channels.ClosedByInterruptException;
import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
@@ -363,6 +364,11 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile
super.writeData((outputStream) -> fullDataSource.saveData(level, this, outputStream));
doesFileExist = true;
}
catch (ClosedByInterruptException e) // thrown by buffers that are interrupted
{
// expected if the file handler is shut down, the exception can be ignored
// LOGGER.warn("FullData file writing interrupted.", e);
}
catch (IOException e)
{
LOGGER.error("Failed to save updated data file at "+file+" for sect "+pos, e);
@@ -3,6 +3,7 @@ package com.seibel.lod.core.file.metaData;
import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.Channels;
import java.nio.channels.ClosedByInterruptException;
import java.nio.channels.FileChannel;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
@@ -239,6 +240,11 @@ public abstract class AbstractMetaDataContainerFile
//LOGGER.info("replaced file: "+this.file.toPath());
}
}
catch (ClosedByInterruptException e)
{
// expected if the file handler is shut down, the exception can be ignored
// LOGGER.warn(AbstractMetaDataContainerFile.class.getSimpleName()+" file writing interrupted.");
}
finally
{
String tempDeleteErrorMessage = null;
@@ -9,7 +9,6 @@ import com.seibel.lod.core.file.fullDatafile.IFullDataSourceProvider;
import com.seibel.lod.core.level.IDhClientLevel;
import com.seibel.lod.core.pos.DhLodPos;
import com.seibel.lod.core.pos.DhSectionPos;
import com.seibel.lod.core.render.LodQuadTree;
import com.seibel.lod.core.util.FileUtil;
import com.seibel.lod.core.util.objects.UncheckedInterruptedException;
import com.seibel.lod.core.config.Config;
@@ -289,14 +288,20 @@ public class RenderSourceFileHandler implements ILodRenderSourceProvider
});
//LOGGER.info("Recreating cache for {}", data.getSectionPos());
DataRenderTransformer.asyncTransformDataSource(fullDataSourceFuture, this.level)
DataRenderTransformer.transformDataSourceAsync(fullDataSourceFuture, this.level)
.thenAccept((newRenderSource) -> this.write(renderSourceReference.get(), file, newRenderSource))
.exceptionally((ex) ->
{
if (!UncheckedInterruptedException.isThrowableInterruption(ex))
if (ex instanceof InterruptedException)
{
// expected if the transformer is shut down, the exception can be ignored
// LOGGER.warn("RenderSource file transforming interrupted.");
}
else if (!UncheckedInterruptedException.isThrowableInterruption(ex))
{
LOGGER.error("Exception when updating render file using data source: ", ex);
}
return null;
}
).thenRun(() -> this.cacheUpdateLockBySectionPos.remove(file.pos));
@@ -213,6 +213,11 @@ public abstract class AbstractDhClientLevel implements IDhClientLevel
/** Includes logic used by both {@link DhClientServerLevel} and {@link DhClientServerLevel} */
protected void baseClose()
{
// shut down to prevent reading/writing files after the client has left the world
fullDataFileHandler.close();
// shutdown the renderer
ClientRenderState ClientRenderState = this.ClientRenderStateRef.get();
if (ClientRenderState != null)
{