Fix up all file IO bugs, FullFormat bugs, FullToColumn bugs, & Tree assert issues

This commit is contained in:
TomTheFurry
2022-07-28 19:33:47 +08:00
parent 64ee49de03
commit f08e7974cd
15 changed files with 157 additions and 84 deletions
@@ -23,6 +23,7 @@ import com.seibel.lod.core.a7.datatype.column.accessor.ColumnArrayView;
import com.seibel.lod.core.a7.datatype.column.accessor.IColumnDataView;
import com.seibel.lod.core.logging.SpamReducedLogger;
import com.seibel.lod.core.util.ColorUtil;
import com.seibel.lod.core.util.LodUtil;
import java.util.Arrays;
@@ -107,8 +108,9 @@ public class ColumnFormat
height, depth, lightSky, lightBlock, generationMode);
}
public static long createDataPoint(int height, int depth, int color, byte light, int generationMode)
public static long createDataPoint(int height, int depth, int color, int light, int generationMode)
{
LodUtil.assertTrue(light >= 0 && light <= 255, "Raw Light value must be between 0 and 255!");
return createDataPoint(
ColorUtil.getAlpha(color),
ColorUtil.getRed(color),
@@ -119,28 +121,17 @@ public class ColumnFormat
public static long createDataPoint(int alpha, int red, int green, int blue, int height, int depth, int lightSky, int lightBlock, int generationMode)
{
if (generationMode == 0)
throw new IllegalArgumentException("Trying to create datapoint with genMode 0, which is NOT allowed in DataPoint version 10!");
if (height < 0 || height > 4096)
throw new IllegalArgumentException("Height must be between 0 and 4096!");
if (depth < 0 || depth > 4096)
throw new IllegalArgumentException("Depth must be between 0 and 4096!");
if (lightSky < 0 || lightSky > 15)
throw new IllegalArgumentException("Sky light must be between 0 and 15!");
if (lightBlock < 0 || lightBlock > 15)
throw new IllegalArgumentException("Block light must be between 0 and 15!");
if (alpha < 0 || alpha > 255)
throw new IllegalArgumentException("Alpha must be between 0 and 255!");
if (red < 0 || red > 255)
throw new IllegalArgumentException("Red must be between 0 and 255!");
if (green < 0 || green > 255)
throw new IllegalArgumentException("Green must be between 0 and 255!");
if (blue < 0 || blue > 255)
throw new IllegalArgumentException("Blue must be between 0 and 255!");
if (generationMode < 0 || generationMode > 7)
throw new IllegalArgumentException("Generation mode must be between 0 and 7!");
if (depth > height)
throw new IllegalArgumentException("Depth must be less than or equal to height!");
LodUtil.assertTrue(generationMode != 0, "Trying to create datapoint with genMode 0, which is NOT allowed in DataPoint version 10!");
LodUtil.assertTrue(height >= 0 && height < MAX_WORLD_Y_SIZE, "Trying to create datapoint with height[{}] out of range!", height);
LodUtil.assertTrue(depth >= 0 && depth < MAX_WORLD_Y_SIZE, "Trying to create datapoint with depth[{}] out of range!", depth);
LodUtil.assertTrue(lightSky >= 0 && lightSky < 16, "Trying to create datapoint with lightSky[{}] out of range!", lightSky);
LodUtil.assertTrue(lightBlock >= 0 && lightBlock < 16, "Trying to create datapoint with lightBlock[{}] out of range!", lightBlock);
LodUtil.assertTrue(alpha >= 0 && alpha < 255, "Trying to create datapoint with alpha[{}] out of range!", alpha);
LodUtil.assertTrue(red >= 0 && red < 255, "Trying to create datapoint with red[{}] out of range!", red);
LodUtil.assertTrue(green >= 0 && green < 255, "Trying to create datapoint with green[{}] out of range!", green);
LodUtil.assertTrue(blue >= 0 && blue < 255, "Trying to create datapoint with blue[{}] out of range!", blue);
LodUtil.assertTrue(generationMode >= 0 && generationMode < 8, "Trying to create datapoint with genMode[{}] out of range!", generationMode);
LodUtil.assertTrue(depth <= height, "Trying to create datapoint with depth[{}] greater than height[{}]!", depth, height);
return (long) (alpha >>> ALPHA_DOWNSIZE_SHIFT) << ALPHA_SHIFT
| (red & RED_MASK) << RED_SHIFT
@@ -256,7 +256,7 @@ public class ColumnRenderSource implements LodRenderSource, IColumnDatatype {
ColumnRenderSource[] data = new ColumnRenderSource[ELodDirection.ADJ_DIRECTIONS.length];
for (ELodDirection direction : ELodDirection.ADJ_DIRECTIONS) {
LodRenderSection section = quadTree.getSection(sectionPos.getAdjacent(direction)); //FIXME: Handle traveling through different detail levels
if (section.getRenderContainer() != null && section.getRenderContainer() instanceof ColumnRenderBuffer) {
if (section != null && section.getRenderContainer() != null && section.getRenderContainer() instanceof ColumnRenderBuffer) {
data[direction.ordinal()-2] = ((ColumnRenderSource) section.getRenderContainer());
}
}
@@ -1,7 +1,7 @@
package com.seibel.lod.core.a7.datatype.full;
// Static class for the data format:
// ID: blockState id Y: Height(signed) DP: Depth(signed?)
// ID: blockState id Y: Height(signed) DP: Depth(signed?) (Depth means the length of the block!)
// BL: Block light SL: Sky light
// =======Bit layout=======
// BL BL BL BL SL SL SL SL <-- Top bits
@@ -13,8 +13,11 @@ package com.seibel.lod.core.a7.datatype.full;
// ID ID ID ID ID ID IO ID
// ID ID ID ID ID ID IO ID <-- Bottom bits
import com.seibel.lod.core.util.LodUtil;
import org.jetbrains.annotations.Contract;
import static com.seibel.lod.core.a7.datatype.column.ColumnFormat.MAX_WORLD_Y_SIZE;
public class FullFormat {
public static final int ID_WIDTH = 32;
@@ -31,13 +34,20 @@ public class FullFormat {
public static final long INVERSE_ID_MASK = ~ID_MASK;
public static final int DP_MASK = (int)Math.pow(2, DP_WIDTH) - 1;
public static final int Y_MASK = (int)Math.pow(2, Y_WIDTH) - 1;
public static final int LIGHT_MASK = (int)Math.pow(2, LIGHT_WIDTH) - 1;
public static long encode(int id, int depth, int y, byte lightPair) {
LodUtil.assertTrue(y >= 0 && y < MAX_WORLD_Y_SIZE, "Trying to create datapoint with y[{}] out of range!", y);
LodUtil.assertTrue(depth > 0 && depth < MAX_WORLD_Y_SIZE, "Trying to create datapoint with depth[{}] out of range!", depth);
LodUtil.assertTrue(y+depth <= MAX_WORLD_Y_SIZE, "Trying to create datapoint with y+depth[{}] out of range!", y+depth);
long data = 0;
data |= id & ID_MASK;
data |= (long) (depth & DP_MASK) << DP_OFFSET;
data |= (long) (y & Y_MASK) << Y_OFFSET;
data |= (long) lightPair << LIGHT_OFFSET;
LodUtil.assertTrue(getId(data) == id && getDepth(data) == depth && getY(data) == y && getLight(data) == Byte.toUnsignedInt(lightPair),
"Trying to create datapoint with id[{}], depth[{}], y[{}], lightPair[{}] but got id[{}], depth[{}], y[{}], lightPair[{}]!",
id, depth, y, Byte.toUnsignedInt(lightPair), getId(data), getDepth(data), getY(data), getLight(data));
return data;
}
@@ -46,15 +56,19 @@ public class FullFormat {
}
public static int getDepth(long data) {
return (int) (data << (64 - DP_OFFSET - DP_WIDTH) >> DP_OFFSET);
return (int) ((data >> DP_OFFSET) & DP_MASK);
}
public static int getY(long data) {
return (int) (data << (64 - Y_OFFSET - Y_WIDTH) >> Y_OFFSET);
return (int) ((data >> Y_OFFSET) & Y_MASK);
}
public static byte getLight(long data) {
return (byte) (data << (64 - LIGHT_OFFSET - LIGHT_WIDTH) >> LIGHT_OFFSET);
public static int getLight(long data) {
return (int) ((data >> LIGHT_OFFSET) & LIGHT_MASK);
}
public static String toString(long data) {
return "[ID:" + getId(data) + ",Y:" + getY(data) + ",Depth:" + getY(data) + ",Light:" + getLight(data) + "]";
}
@Contract(pure = true)
@@ -10,11 +10,15 @@ import com.seibel.lod.core.a7.datatype.full.accessor.SingleFullArrayView;
import com.seibel.lod.core.a7.level.IClientLevel;
import com.seibel.lod.core.a7.pos.DhSectionPos;
import com.seibel.lod.core.config.Config;
import com.seibel.lod.core.handlers.dependencyInjection.SingletonInjector;
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 {
private static final IBlockStateWrapper AIR = SingletonInjector.INSTANCE.get(IWrapperFactory.class).getAirBlockStateWrapper();
/**
* Creates a LodNode for a chunk in the given world.
* @throws IllegalArgumentException thrown if either the chunk or world is null.
@@ -74,19 +78,25 @@ public class FullToColumnTransformer {
private static void iterateAndConvert(IClientLevel level, int genMode, ColumnArrayView column, SingleFullArrayView data) {
IdBiomeBlockStateMap mapping = data.getMapping();
boolean isVoid = true;
for (int i = 0; i < data.getSingleLength(); i++) {
long fullData = data.getSingle(i);
int y = FullFormat.getY(fullData);
int depth = FullFormat.getDepth(fullData);
int blockLength = FullFormat.getDepth(fullData);
int id = FullFormat.getId(fullData);
byte light = FullFormat.getLight(fullData);
int light = FullFormat.getLight(fullData);
IdBiomeBlockStateMap.Entry entry = mapping.get(id);
IBiomeWrapper biome = entry.biome;
IBlockStateWrapper block = entry.blockState;
if (block.equals(AIR)) continue;
isVoid = false;
int color = level.computeBaseColor(biome, block);
long columnData = ColumnFormat.createDataPoint(y, depth, color, light, genMode);
long columnData = ColumnFormat.createDataPoint(y + blockLength, y, color, light, genMode);
column.set(i, columnData);
}
if (isVoid) {
column.set(0, ColumnFormat.createVoidDataPoint((byte) genMode));
}
}
//
@@ -32,19 +32,19 @@ public class LodDataBuilder {
byte newLight = (byte) (chunk.getBlockLight(x,y,z) << 4 + chunk.getSkyLight(x,y,z));
if (!newBiome.equals(biome) || !newBlockState.equals(blockState)) {
longs.add(FullFormat.encode(mappedId, lastY-y+1, y+1, light));
longs.add(FullFormat.encode(mappedId, lastY-y, y+1 - chunk.getMinBuildHeight(), light));
biome = newBiome;
blockState = newBlockState;
mappedId = chunkData.getMapping().setAndGetId(biome, blockState);
light = newLight;
lastY = y;
} else if (newLight != light) {
longs.add(FullFormat.encode(mappedId, lastY-y+1, y+1, light));
longs.add(FullFormat.encode(mappedId, lastY-y, y+1 - chunk.getMinBuildHeight(), light));
light = newLight;
lastY = y;
}
}
longs.add(FullFormat.encode(mappedId, lastY-y+1, y+1, light));
longs.add(FullFormat.encode(mappedId, lastY-y, y+1 - chunk.getMinBuildHeight(), light));
chunkData.setSingleColumn(longs.toArray(new long[0]), x, z);
}
@@ -180,6 +180,5 @@ public class GenerationQueue implements PlaceHolderQueue {
if (source == null) return; // Same as above.
source.markInvalid(); // Mark the placeholder as invalid, so it will be refreshed on next lodTree update.
});
}
}
@@ -37,7 +37,7 @@ public class DhClientLevel implements IClientLevel {
save.getDataFolder(level).mkdirs();
save.getRenderCacheFolder(level).mkdirs();
dataFileHandler = new RemoteDataFileHandler();
renderFileHandler = new RenderFileHandler(dataFileHandler, this, save.getRenderCacheFolder(level), null);
renderFileHandler = new RenderFileHandler(dataFileHandler, this, save.getRenderCacheFolder(level));
tree = new LodQuadTree(this, Config.Client.Graphics.Quality.lodChunkRenderDistance.get()*16,
MC_CLIENT.getPlayerBlockPos().x, MC_CLIENT.getPlayerBlockPos().z, renderFileHandler);
renderBufferHandler = new RenderBufferHandler(tree);
@@ -64,8 +64,12 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel {
return;
}
generationQueue = new GenerationQueue((a,b) -> this.renderFileHandler.write(a,b));
renderFileHandler = new RenderFileHandler(dataFileHandler, this, save.getRenderCacheFolder(level), generationQueue);
// FIXME: This A need B and B need A messes needs to be reworked!
renderFileHandler = new RenderFileHandler(dataFileHandler, this, save.getRenderCacheFolder(level));
final RenderFileHandler f_renderFileHandler = renderFileHandler;
generationQueue = new GenerationQueue(f_renderFileHandler::write);
renderFileHandler.setPlaceHolderQueue(generationQueue);
tree = new LodQuadTree(this, Config.Client.Graphics.Quality.lodChunkRenderDistance.get()*16,
MC_CLIENT.getPlayerBlockPos().x, MC_CLIENT.getPlayerBlockPos().z, renderFileHandler);
renderBufferHandler = new RenderBufferHandler(tree);
@@ -93,10 +97,10 @@ public class DhClientServerLevel implements IClientLevel, IServerLevel {
renderBufferHandler.close();
renderBufferHandler = null;
tree = null; //TODO Close the tree
generationQueue = null;
renderFileHandler.flushAndSave(); //Ignore the completion feature so that this action is async
renderFileHandler.close();
renderFileHandler = null;
generationQueue = null;
}
@Override
@@ -227,9 +227,6 @@ public class LodQuadTree {
LOGGER.info("TreeTick: Moving ring list {} from {} to {}", sectLevel,
ringLists[sectLevel - LAYER_BEGINNING_OFFSET].getCenter(),
new Pos2D(playerPos.x >> sectLevel, playerPos.z >> sectLevel));
LOGGER.info("Tree State:\n{}", getDebugString());
ringLists[sectLevel - LAYER_BEGINNING_OFFSET]
.move(playerPos.x >> sectLevel, playerPos.z >> sectLevel,
LodRenderSection::dispose);
@@ -404,6 +401,8 @@ public class LodQuadTree {
// Call load on new sections, and tick on existing ones, and dispose old sections
if (section.childCount == -1) {
if (section.pos.sectionDetail < numbersOfSectionLevels-1)
LodUtil.assertTrue(getParentSection(section.pos).childCount == 0);
ringList.set(pos.x, pos.y, null);
section.dispose();
return;
@@ -421,24 +420,27 @@ public class LodQuadTree {
// Assertion steps
LodUtil.assertTrue(section.childCount == 4 || section.childCount == 0);
if (section.pos.sectionDetail == LAYER_BEGINNING_OFFSET) LodUtil.assertTrue(section.childCount == 0);
if (section.childCount == 4) LodUtil.assertTrue(
getChildSection(section.pos, 0) != null &&
getChildSection(section.pos, 1) != null &&
getChildSection(section.pos, 2) != null &&
getChildSection(section.pos, 3) != null,
"Sect {} child count 4 but childs have null: {} {} {} {}",
section.pos, getChildSection(section.pos, 0), getChildSection(section.pos, 1),
getChildSection(section.pos, 2), getChildSection(section.pos, 3));
if (section.childCount == 0 && section.pos.sectionDetail > LAYER_BEGINNING_OFFSET) LodUtil.assertTrue(
getChildSection(section.pos, 0) == null &&
getChildSection(section.pos, 1) == null &&
getChildSection(section.pos, 2) == null &&
getChildSection(section.pos, 3) == null,
"Sect {} child count 0 but childs are not null: {} {} {} {}",
section.pos, getChildSection(section.pos, 0), getChildSection(section.pos, 1),
getChildSection(section.pos, 2), getChildSection(section.pos, 3));
if (section.childCount == -1 && section.pos.sectionDetail < numbersOfSectionLevels-1) LodUtil.assertTrue(
getParentSection(section.pos).childCount == 0);
if (section.pos.sectionDetail != LAYER_BEGINNING_OFFSET) {
LodRenderSection child0 = getChildSection(section.pos, 0);
LodRenderSection child1 = getChildSection(section.pos, 1);
LodRenderSection child2 = getChildSection(section.pos, 2);
LodRenderSection child3 = getChildSection(section.pos, 3);
if (section.childCount == 4) LodUtil.assertTrue(
child0 != null && child0.childCount != -1 &&
child1 != null && child1.childCount != -1 &&
child2 != null && child2.childCount != -1 &&
child3 != null && child3.childCount != -1,
"Sect {} child count 4 but child has null or is being disposed: {} {} {} {}",
section.pos, child0, child1, child2, child3);
if (section.childCount == 0) LodUtil.assertTrue(
(child0 == null || child0.childCount == -1) &&
(child1 == null || child1.childCount == -1) &&
(child2 == null || child2.childCount == -1) &&
(child3 == null || child3.childCount == -1),
"Sect {} child count 0 but child is neither null or being disposed: {} {} {} {}",
section.pos, child0, child1, child2, child3);
}
});
}
}
@@ -8,6 +8,7 @@ import java.nio.channels.FileChannel;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.function.Consumer;
import java.util.zip.Adler32;
import java.util.zip.CheckedOutputStream;
@@ -45,6 +46,9 @@ public class MetaFile {
public static final int METADATA_RESERVED_SIZE = 24;
public static final int METADATA_MAGIC_BYTES = 0x44_48_76_30;
// Currently set to false because for some reason Window is throwing PermissionDeniedException when trying to atomic replace a file...
public static final boolean USE_ATOMIC_MOVE_REPLACE = false;
public final DhSectionPos pos;
public File path;
@@ -56,9 +60,13 @@ public class MetaFile {
public long dataTypeId;
public byte loaderVersion;
private static final ReentrantReadWriteLock assertLock = new ReentrantReadWriteLock();
// Load a metaFile in this path. It also automatically read the metadata.
protected MetaFile(File path) throws IOException {
this.path = path;
validateFile();
LodUtil.assertTrue(assertLock.readLock().tryLock());
try (FileChannel channel = FileChannel.open(path.toPath(), StandardOpenOption.READ)) {
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, METADATA_SIZE);
this.path = path;
@@ -79,6 +87,8 @@ public class MetaFile {
timestamp = buffer.getLong();
LodUtil.assertTrue(buffer.remaining() == METADATA_RESERVED_SIZE);
pos = new DhSectionPos(detailLevel, x, z);
} finally {
assertLock.readLock().unlock();
}
}
@@ -97,6 +107,7 @@ public class MetaFile {
protected void updateMetaData() throws IOException {
validateFile();
LodUtil.assertTrue(assertLock.readLock().tryLock());
try (FileChannel channel = FileChannel.open(path.toPath(), StandardOpenOption.READ)) {
MappedByteBuffer buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, METADATA_SIZE);
int magic = buffer.getInt();
@@ -106,13 +117,13 @@ public class MetaFile {
int x = buffer.getInt();
int y = buffer.getInt(); // Unused
int z = buffer.getInt();
int checksum = buffer.getInt();
checksum = buffer.getInt();
byte detailLevel = buffer.get();
dataLevel = buffer.get();
byte loaderVersion = buffer.get();
byte unused = buffer.get();
long dataTypeId = buffer.getLong();
long timestamp = buffer.getLong();
dataTypeId = buffer.getLong();
timestamp = buffer.getLong();
LodUtil.assertTrue(buffer.remaining() == METADATA_RESERVED_SIZE);
DhSectionPos newPos = new DhSectionPos(detailLevel, x, z);
@@ -120,14 +131,22 @@ public class MetaFile {
throw new IOException("Invalid file: Section position changed.");
}
this.loaderVersion = loaderVersion;
} finally {
assertLock.readLock().unlock();
}
}
protected void writeData(Consumer<OutputStream> dataWriter) throws IOException {
if (path.exists()) validateFile();
File tempFile = File.createTempFile("lodDataFile", "tmp", path.getParentFile());
tempFile.deleteOnExit();
try (FileChannel file = FileChannel.open(tempFile.toPath(),
File writerFile;
if (USE_ATOMIC_MOVE_REPLACE) {
writerFile = new File(path.getPath() + ".tmp");
writerFile.deleteOnExit();
} else {
writerFile = path;
}
LodUtil.assertTrue(assertLock.writeLock().tryLock());
try (FileChannel file = FileChannel.open(writerFile.toPath(),
StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
{
file.position(METADATA_SIZE);
@@ -157,12 +176,16 @@ public class MetaFile {
file.write(buff);
}
file.close();
// Atomic move / replace the actual file
Files.move(tempFile.toPath(), path.toPath(), StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.ATOMIC_MOVE);
if (USE_ATOMIC_MOVE_REPLACE) {
// Atomic move / replace the actual file
Files.move(writerFile.toPath(), path.toPath(), StandardCopyOption.ATOMIC_MOVE);
}
} finally {
assertLock.writeLock().unlock();
try {
boolean i = tempFile.delete(); // Delete temp file. Ignore errors if fails.
if (USE_ATOMIC_MOVE_REPLACE && writerFile.exists()) {
boolean i = writerFile.delete(); // Delete temp file. Ignore errors if fails.
}
} catch (Exception ignored) {}
}
}
@@ -215,6 +215,10 @@ public class DataMetaFile extends MetaFile {
LOGGER.warn("Metadata for file {} changed unexpectedly and in an invalid state. Dropping file.", path, e);
return null;
}
if (loader == null) {
//LOGGER.warn("No loader for file {}. Dropping file.", path); // Disable as data lod has no loader yet.
return null;
}
// Load the file.
try (FileInputStream fio = getDataContent()){
@@ -44,13 +44,18 @@ public class LocalDataFileHandler implements IDataSourceProvider {
@Override
public void addScannedFile(Collection<File> detectedFiles) {
HashMultimap<DhSectionPos, DataMetaFile> filesByPos = HashMultimap.create();
LOGGER.info("Detected {} valid files in {}", detectedFiles.size(), saveDir);
{ // Sort files by pos.
for (File file : detectedFiles) {
try {
DataMetaFile metaFile = new DataMetaFile(level, file);
filesByPos.put(metaFile.pos, metaFile);
} catch (IOException e) {
throw new RuntimeException(e);
LOGGER.error("Failed to read file {}. File will be deleted.", file, e);
if (!file.delete()) {
LOGGER.error("Failed to delete file {}.", file);
}
}
}
}
@@ -120,6 +125,8 @@ public class LocalDataFileHandler implements IDataSourceProvider {
}
// Slow path: if there is no file for this section, create one.
File file = computeDefaultFilePath(sectionPos);
//FIXME: Handle file already exists issue. Possibly by renaming the file.
LodUtil.assertTrue(!file.exists(), "File {} already exist for path {}", file, sectionPos);
DataMetaFile newMetaFile = new DataMetaFile(level, file, sectionPos);
LOGGER.info("Created new Data file at {} for sect {}", newMetaFile.path, sectionPos);
@@ -28,13 +28,16 @@ public class RenderFileHandler implements IRenderSourceProvider {
final File saveDir;
final IDataSourceProvider dataSourceProvider;
@Nullable
final PlaceHolderQueue placeHolderQueue;
PlaceHolderQueue placeHolderQueue = null;
public RenderFileHandler(IDataSourceProvider sourceProvider, IClientLevel level, File saveRootDir, @Nullable PlaceHolderQueue placeHolderQueue) {
public RenderFileHandler(IDataSourceProvider sourceProvider, IClientLevel level, File saveRootDir) {
this.dataSourceProvider = sourceProvider;
this.level = level;
this.saveDir = saveRootDir;
this.placeHolderQueue = placeHolderQueue;
}
public void setPlaceHolderQueue(@Nullable PlaceHolderQueue queue) {
this.placeHolderQueue = queue;
}
/*
@@ -23,8 +23,8 @@ public class FileScanner {
@Nullable IRenderSourceProvider renderSource) {
if (dataSource != null) {
try (Stream<Path> pathStream = Files.walk(save.getDataFolder(level).toPath(), MAX_SCAN_DEPTH)) {
dataSource.addScannedFile(pathStream.filter((
path -> path.endsWith(LOD_FILE_POSTFIX) && path.toFile().isFile())
dataSource.addScannedFile(pathStream.filter(
path -> path.toFile().getName().endsWith(LOD_FILE_POSTFIX) && path.toFile().isFile()
).map(Path::toFile).collect(Collectors.toList())
);
} catch (Exception e) {
@@ -34,7 +34,7 @@ public class FileScanner {
if (renderSource != null) {
try (Stream<Path> pathStream = Files.walk(save.getRenderCacheFolder(level).toPath(), MAX_SCAN_DEPTH)) {
renderSource.addScannedFile(pathStream.filter((
path -> path.endsWith(LOD_FILE_POSTFIX) && path.toFile().isFile())
path -> path.toFile().getName().endsWith(LOD_FILE_POSTFIX) && path.toFile().isFile())
).map(Path::toFile).collect(Collectors.toList())
);
} catch (Exception e) {
@@ -455,23 +455,39 @@ public class LodUtil
}
}
static class AssertFailureException extends RuntimeException {
public AssertFailureException(String message) {
super(message);
debugBreak();
}
}
private static void debugBreak() {
int a = 0; // Set breakpoint here for auto pause on assert failure
}
public static void assertTrue(boolean condition) {
if (!condition) throw new RuntimeException("Assertion failed");
if (!condition) {
throw new AssertFailureException("Assertion failed");
}
}
public static void assertTrue(boolean condition, String message) {
if (!condition) throw new RuntimeException("Assertion failed:\n " + message);
if (!condition) {
throw new AssertFailureException("Assertion failed:\n " + message);
}
}
public static void assertTrue(boolean condition, String message, Object... args) {
if (!condition) throw new RuntimeException("Assertion failed:\n " + formatLog(message, args));
if (!condition) {
throw new AssertFailureException("Assertion failed:\n " + formatLog(message, args));
}
}
public static void assertNotReach() {
throw new RuntimeException("Assert Not Reach failed");
throw new AssertFailureException("Assert Not Reach failed");
}
public static void assertNotReach(String message) {
throw new RuntimeException("Assert Not Reach failed:\n " + message);
throw new AssertFailureException("Assert Not Reach failed:\n " + message);
}
public static void assertNotReach(String message, Object... args) {
throw new RuntimeException("Assert Not Reach failed:\n " + formatLog(message, args));
throw new AssertFailureException("Assert Not Reach failed:\n " + formatLog(message, args));
}
public static ExecutorService makeSingleThreadPool(String name, int relativePriority) {
return Executors.newSingleThreadExecutor(new LodThreadFactory(name, Thread.NORM_PRIORITY+relativePriority));