Fix up all file IO bugs, FullFormat bugs, FullToColumn bugs, & Tree assert issues
This commit is contained in:
@@ -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)
|
||||
|
||||
+13
-3
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user