Fix reading/writing files after leaving the world
May have issues in Client-only mode
This commit is contained in:
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
+64
-9
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+236
-124
@@ -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);
|
||||
|
||||
+6
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user