Continue work on Saving the data..

This commit is contained in:
TomTheFurry
2022-05-23 20:20:27 +08:00
parent b391252c03
commit 987dfbc87f
11 changed files with 433 additions and 252 deletions
@@ -37,6 +37,7 @@ public class DataFile {
public final DhSectionPos pos;
public byte dataLevel;
public DataSourceLoader loader;
public byte loaderVersion;
public Class<?> dataType;
public LodDataSource loadedData = null;
@@ -55,6 +56,7 @@ public class DataFile {
this.dataType = loader.clazz;
this.dataLevel = loadedData.getDataDetail();
this.loadedData = loadedData;
this.loaderVersion = loader.loaderSupportedVersions[loader.loaderSupportedVersions.length - 1]; // get latest version
}
DataFile(File path, MappedByteBuffer meta) throws IOException {
@@ -82,14 +84,23 @@ public class DataFile {
throw new IOException("Invalid file: Data type loader not found: " + dataTypeId + "(v" + loaderVersion + ")");
}
this.dataType = loader.clazz;
this.loaderVersion = loaderVersion;
}
LodDataSource load(DHLevel level) throws IOException {
if (loadedData != null) return loadedData;
public FileInputStream getDataContent() throws IOException {
FileInputStream fin = new FileInputStream(path);
fin.skipNBytes(METADATA_SIZE);
loadedData = loader.loadData(level, pos, fin);
return loadedData;
return fin;
}
LodDataSource load(DHLevel level) {
if (loadedData != null) return loadedData;
try {
loadedData = loader.loadData(this, level);
return loadedData;
} catch (IOException e) {
//FIXME: Log and review this handling
return null;
}
}
public boolean verifyPath() {
@@ -102,7 +113,9 @@ public class DataFile {
DataSourceSaver saver;
if (loader instanceof DataSourceSaver) saver = (DataSourceSaver) loader;
else if (loader instanceof OldDataSourceLoader) saver = ((OldDataSourceLoader) loader).getNewSaver();
else throw new IllegalStateException("Data source does not support saving");
else saver = null;
if (saver == null) return;
byte newDataLevel = loadedData.getDataDetail();
try (FileOutputStream fout = new FileOutputStream(path, false)) {
@@ -119,7 +132,7 @@ public class DataFile {
// Write detail level, data level, loader version
out.writeByte(pos.sectionDetail);
out.writeByte(loadedData.getDataDetail());
out.writeByte(saver.loaderVersion);
out.writeByte(saver.getSaverVersion());
// Write unused
out.writeByte((byte) 0);
@@ -136,13 +136,7 @@ public class DataFileHandler implements RenderDataProvider {
public CompletableFuture<RenderDataSource> createRenderData(RenderDataSourceLoader renderSourceLoader, DhSectionPos pos) {
return CompletableFuture.supplyAsync(() -> {
List<DataFile> files = renderSourceLoader.selectFiles(pos, level, getFilesInPos(pos));
List<LodDataSource> dataSource = files.stream().map(f -> {
try {
return f.load(level);
} catch (IOException e) {
throw new RuntimeException(e);
}
}).collect(Collectors.toList());
List<LodDataSource> dataSource = files.stream().map(f -> f.load(level)).filter(Objects::nonNull).collect(Collectors.toList());
return renderSourceLoader.construct(dataSource, pos, level);
});
}
@@ -6,6 +6,7 @@ import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
@@ -15,12 +16,13 @@ public abstract class DataSourceLoader {
public static final HashMap<Long, Class<? extends LodDataSource>> datatypeIdRegistry = new HashMap<>();
public final long datatypeId;
public final byte loaderVersion;
public final byte[] loaderSupportedVersions;
public final Class<? extends LodDataSource> clazz;
public DataSourceLoader(Class<? extends LodDataSource> clazz, long datatypeId, byte loaderVersion) {
public DataSourceLoader(Class<? extends LodDataSource> clazz, long datatypeId, byte[] loaderSupportedVersions) {
this.datatypeId = datatypeId;
this.loaderVersion = loaderVersion;
this.loaderSupportedVersions = loaderSupportedVersions;
Arrays.sort(loaderSupportedVersions); // sort to allow fast access
this.clazz = clazz;
if (datatypeIdRegistry.containsKey(datatypeId) && datatypeIdRegistry.get(datatypeId) != clazz) {
@@ -28,15 +30,22 @@ public abstract class DataSourceLoader {
+ datatypeIdRegistry.get(datatypeId) + " != " + clazz);
}
Set<DataSourceLoader> loaders = loaderRegistry.get(datatypeId);
if (loaders.stream().anyMatch(l -> l.loaderVersion == loaderVersion)) {
throw new IllegalArgumentException("Loader for class " + clazz + " with version " + loaderVersion " already registered!");
if (loaders.stream().anyMatch(other -> {
// see if any loaderSupportsVersion conflicts with this one
for (byte otherVer : other.loaderSupportedVersions) {
if (Arrays.binarySearch(loaderSupportedVersions, otherVer) >= 0) return true;
}
return false;
})) {
throw new IllegalArgumentException("Loader for class " + clazz + " that supports one of the version in "
+ loaderSupportedVersions " already registered!");
}
datatypeIdRegistry.put(datatypeId, clazz);
loaderRegistry.put(datatypeId, this);
}
// Can return null as meaning the requirement is not met
public abstract LodDataSource loadData(DHLevel level, DhSectionPos sectionPos, InputStream data);
public abstract LodDataSource loadData(DataFile dataFile, DHLevel level) throws IOException;
public List<File> foldersToScan(File levelFolderPath) {
return Collections.emptyList();
@@ -44,7 +53,7 @@ public abstract class DataSourceLoader {
public static DataSourceLoader getLoader(long dataTypeId, byte loaderVersion) {
return loaderRegistry.get(dataTypeId).stream()
.filter(l -> l.loaderVersion == loaderVersion)
.filter(l -> Arrays.binarySearch(l.loaderSupportedVersions, loaderVersion) >= 0)
.findFirst().orElse(null);
}
@@ -7,7 +7,6 @@ import java.util.Objects;
public interface LodDataSource {
DataSourceLoader getLatestLoader();
DhSectionPos getSectionPos();
byte getDataDetail();
}
@@ -2,36 +2,46 @@ package com.seibel.lod.core.objects.a7.datatype.column;
import com.seibel.lod.core.enums.config.VerticalQuality;
import com.seibel.lod.core.objects.a7.DHLevel;
import com.seibel.lod.core.objects.a7.data.DataFile;
import com.seibel.lod.core.objects.a7.data.DataFileHandler;
import com.seibel.lod.core.objects.a7.data.LodDataSource;
import com.seibel.lod.core.objects.a7.data.OldFileConverter;
import com.seibel.lod.core.objects.a7.data.*;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import org.apache.commons.compress.compressors.xz.XZCompressorInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class Alpha6DataLoader extends OldDataSourceLoader implements OldFileConverter {
public Alpha6DataLoader() {
super(ColumnDatatype.class, ColumnDatatype.DATA_TYPE_ID, (byte)0);
public static final Alpha6DataLoader INSTANCE = new Alpha6DataLoader();
private Alpha6DataLoader() {
super(OldColumnDatatype.class, OldColumnDatatype.DATA_TYPE_ID, (byte)0);
DataFileHandler.CONVERTERS.add(this);
}
@Override
public LodDataSource loadData(DHLevel level, DhSectionPos sectionPos, InputStream data) {
return null;
public LodDataSource loadData(DataFile dataFile, DHLevel level) {
//TODO: Add decompressor here
try (
FileInputStream fin = dataFile.getDataContent();
XZCompressorInputStream xzIn = new XZCompressorInputStream(fin);
DataInputStream dis = new DataInputStream(xzIn);
) {
return new OldColumnDatatype(dataFile.pos, dis, dataFile.loaderVersion, level, 1);
} catch (IOException e) {
//FIXME: Log error
return null;
}
}
@Override
public DataSourceSaver getNewSaver() {
return null;
return null; // No re-saving of old datatype as any data should be converted to new format before saving
}
private static DataFile convert(File file, int detailLevel, VerticalQuality quality) {
String oldName = file.getName();
String regionStr = oldName.substring("lod.".length(), oldName.length() - ".xz".length());
@@ -46,26 +56,14 @@ public class Alpha6DataLoader extends OldDataSourceLoader implements OldFileConv
XZCompressorInputStream inputStream = new XZCompressorInputStream(fileInStream);
int fileVersion = inputStream.read();
datatype = new ColumnDatatype(fileVersion, quality, detailLevel);
DhSectionPos pos;
File newFilePath = ColumnDataLoader.INSTANCE.generateFilePathAndName(file,
detailLevel,
quality);
//TODO: Implement
} catch (Exception e) {
e.printStackTrace();
}
int version
return null;
}
@Override
@@ -3,33 +3,35 @@ package com.seibel.lod.core.objects.a7.datatype.column;
import com.seibel.lod.core.Config;
import com.seibel.lod.core.enums.config.VerticalQuality;
import com.seibel.lod.core.objects.a7.DHLevel;
import com.seibel.lod.core.objects.a7.data.DataFile;
import com.seibel.lod.core.objects.a7.data.DataFileHandler;
import com.seibel.lod.core.objects.a7.data.DataSourceLoader;
import com.seibel.lod.core.objects.a7.data.LodDataSource;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Inherited;
import java.io.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class ColumnDataLoader extends DataSourceSaver {
private static final byte COLUMN_DATA_LOADER_VERSION = 10;
private static final byte COLUMN_DATA_LOADER_VERSION = 1;
public static final ColumnDataLoader INSTANCE = new ColumnDataLoader();
private ColumnDataLoader() {
super(ColumnDatatype.class, ColumnDatatype.DATA_TYPE_ID, COLUMN_DATA_LOADER_VERSION);
super(ColumnDatatype.class, ColumnDatatype.DATA_TYPE_ID, new byte[]{COLUMN_DATA_LOADER_VERSION});
}
@Override
public LodDataSource loadData(DHLevel level, DhSectionPos sectionPos, InputStream data) {
//TODO: Add decompressor here
return ColumnDatatype.loadFile(level, sectionPos, data, COLUMN_DATA_LOADER_VERSION);
public LodDataSource loadData(DataFile dataFile, DHLevel level) {
try (
FileInputStream fin = dataFile.getDataContent();
//TODO: Add decompressor here
DataInputStream dis = new DataInputStream(fin);
) {
return new ColumnDatatype(dataFile.pos, dis, dataFile.loaderVersion, level);
} catch (IOException e) {
//FIXME: Log error
return null;
}
}
@Override
@@ -45,7 +47,7 @@ public class ColumnDataLoader extends DataSourceSaver {
public File generateFilePathAndName(File levelFolderPath, DhSectionPos sectionPos, VerticalQuality quality) {
return new File(levelFolderPath, "cache" + File.separator + quality.toString() + File.separator +
String.format("%s_v%d-%s%s", clazz.getSimpleName(), loaderVersion,
String.format("%s_v%d-%s%s", clazz.getSimpleName(), COLUMN_DATA_LOADER_VERSION,
sectionPos.serialize(), DataFileHandler.FILE_EXTENSION));
}
@@ -4,30 +4,27 @@ import com.seibel.lod.core.objects.LodDataView;
import com.seibel.lod.core.objects.a7.DHLevel;
import com.seibel.lod.core.objects.a7.LodQuadTree;
import com.seibel.lod.core.objects.a7.data.DataSourceLoader;
import com.seibel.lod.core.objects.a7.data.LodDataSource;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import com.seibel.lod.core.objects.a7.render.RenderDataSource;
import com.seibel.lod.core.objects.a7.render.RenderDataSourceLoader;
import com.seibel.lod.core.objects.opengl.RenderBuffer;
import com.seibel.lod.core.util.LodUtil;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.concurrent.atomic.AtomicReference;
public class ColumnDatatype implements LodDataSource, RenderDataSource {
public class ColumnDatatype implements RenderDataSource, IColumnDatatype {
public static final boolean DO_SAFETY_CHECKS = true;
public static final byte SECTION_SIZE_OFFSET = 6;
public static final int SECTION_SIZE = 1 << SECTION_SIZE_OFFSET;
public static final int LATEST_VERSION = 9;
public static final int LATEST_VERSION = 10;
public static final long DATA_TYPE_ID = "ColumnDatatype".hashCode();
public final int AIR_LODS_SIZE = 16;
public final int AIR_SECTION_SIZE = SECTION_SIZE/AIR_LODS_SIZE;
public static final int AIR_LODS_SIZE = 16;
public static final int AIR_SECTION_SIZE = SECTION_SIZE/AIR_LODS_SIZE;
public final int verticalSize;
public final DhSectionPos sectionPos;
public final int yOffset;
@@ -47,74 +44,43 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
this.yOffset = yOffset;
}
private long[] loadData(DataInputStream inputData, int version, int verticalSize) throws IOException {
switch (version) {
case 1:
return readDataV1(inputData, verticalSize);
default:
throw new IOException("Invalid Data: The version of the data is not supported");
}
}
private long[] readDataV1(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
short tempMinHeight = Short.reverseBytes(inputData.readShort());
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
if (tempMinHeight != yOffset) {
for (int i=0; i<result.length; i++) {
result[i] = ColumnDataPoint.shiftHeightAndDepth(result[i], (short) (tempMinHeight - yOffset));
}
}
return result;
}
// Load from data stream with maxVerticalSize loaded from the data stream
public ColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version, DHLevel level) throws IOException {
this.sectionPos = sectionPos;
this.yOffset = level.getMinY();
yOffset = level.getMinY();
byte detailLevel = inputData.readByte();
if (sectionPos.sectionDetail - SECTION_SIZE_OFFSET != detailLevel) {
throw new IOException("Invalid data: detail level does not match");
}
verticalSize = inputData.readByte() & 0b01111111;
switch (version) {
case 6:
dataContainer = readDataVersion6(inputData, verticalSize);
break;
case 7:
dataContainer = readDataVersion7(inputData, verticalSize);
break;
case 8:
dataContainer = readDataVersion8(inputData, verticalSize);
break;
case 9:
case 10:
dataContainer = readDataVersion9(inputData, verticalSize);
break;
default:
throw new IOException("Invalid Data: The version of the data is not supported");
}
dataContainer = loadData(inputData, version, verticalSize);
airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * verticalSize];
}
// Load from data stream with new maxVerticalSize
public ColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version, DHLevel level, int maxVerticalSize) throws IOException {
verticalSize = maxVerticalSize;
this.yOffset = level.getMinY();
this.sectionPos = sectionPos;
byte detailLevel = inputData.readByte();
if (sectionPos.sectionDetail - SECTION_SIZE_OFFSET != detailLevel) {
throw new IOException("Invalid data: detail level does not match");
}
int fileMaxVerticalSize = inputData.readByte() & 0b01111111;
long[] fileDataContainer = null;
switch (version) {
case 6:
fileDataContainer = readDataVersion6(inputData, fileMaxVerticalSize);
break;
case 7:
fileDataContainer = readDataVersion7(inputData, fileMaxVerticalSize);
break;
case 8:
fileDataContainer = readDataVersion8(inputData, fileMaxVerticalSize);
break;
case 9:
case 10:
fileDataContainer = readDataVersion9(inputData, fileMaxVerticalSize);
break;
default:
throw new IOException("Invalid Data: The version of the data is not supported");
}
dataContainer = new long[SECTION_SIZE * SECTION_SIZE * verticalSize];
new ColumnArrayView(dataContainer, dataContainer.length, 0, verticalSize).changeVerticalSizeFrom(
new ColumnArrayView(fileDataContainer, fileDataContainer.length, 0, fileMaxVerticalSize));
airDataContainer = new int[AIR_SECTION_SIZE * AIR_SECTION_SIZE * verticalSize];
}
/**
* This method will clear all data at relative section position
* @param posX
* @param posZ
*/
@Override
public void clear(int posX, int posZ)
{
for (int verticalIndex = 0; verticalIndex < verticalSize; verticalIndex++)
@@ -122,40 +88,15 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
ColumnDataPoint.EMPTY_DATA;
}
/**
* This method will add the data given in input at the relative position and vertical index
* @param data
* @param posX
* @param posZ
* @param verticalIndex
* @return
*/
@Override
public boolean addData(long data, int posX, int posZ, int verticalIndex)
{
dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex] = data;
return true;
}
/**
* This section will fill the data given in input at the given position
* @param data
* @param posX
* @param posZ
*/
private void forceWriteVerticalData(long[] data, int posX, int posZ)
{
int index = posX * SECTION_SIZE * verticalSize + posZ * verticalSize;
if (verticalSize >= 0) System.arraycopy(data, 0, dataContainer, index + 0, verticalSize);
}
/**
* This methods will add the data in the given position if certain condition are satisfied
* @param data
* @param posX
* @param posZ
* @param override if override is true we can override data created with same generation mode
* @return
*/
@Override
public boolean copyVerticalData(LodDataView data, int posX, int posZ, boolean override) {
if (DO_SAFETY_CHECKS) {
if (data.size() != verticalSize)
@@ -176,16 +117,13 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return true;
}
@Override
public long getData(int posX, int posZ, int verticalIndex)
{
return dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize + verticalIndex];
}
public long getSingleData(int posX, int posZ)
{
return dataContainer[posX * SECTION_SIZE * verticalSize + posZ * verticalSize];
}
@Override
public long[] getAllData(int posX, int posZ)
{
long[] result = new long[verticalSize];
@@ -194,94 +132,35 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return result;
}
@Override
public ColumnArrayView getVerticalDataView(int posX, int posZ) {
return new ColumnArrayView(dataContainer, verticalSize,
posX * SECTION_SIZE * verticalSize + posZ * verticalSize, verticalSize);
}
@Override
public ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize) {
return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, quadX, quadZ, quadXSize, quadZSize);
}
@Override
public ColumnQuadView getFullQuad() {
return new ColumnQuadView(dataContainer, SECTION_SIZE, verticalSize, 0, 0, SECTION_SIZE, SECTION_SIZE);
}
@Override
public int getVerticalSize()
{
return verticalSize;
}
@Override
public boolean doesItExist(int posX, int posZ)
{
return ColumnDataPoint.doesItExist(getSingleData(posX, posZ));
}
private long[] readDataVersion6(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
patchVersion9Reorder(result);
patchHeightAndDepth(result,-yOffset);
return result;
}
private long[] readDataVersion7(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
patchVersion9Reorder(result);
patchHeightAndDepth(result, 64 - yOffset);
return result;
}
private long[] readDataVersion8(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
short tempMinHeight = Short.reverseBytes(inputData.readShort());
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
patchVersion9Reorder(result);
if (tempMinHeight != yOffset) {
patchHeightAndDepth(result,tempMinHeight - yOffset);
}
return result;
}
private long[] readDataVersion9(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = SECTION_SIZE * SECTION_SIZE * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
short tempMinHeight = Short.reverseBytes(inputData.readShort());
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
if (tempMinHeight != yOffset) {
patchHeightAndDepth(result,tempMinHeight - yOffset);
}
return result;
}
private static void patchHeightAndDepth(long[] data, int offset) {
for (int i=0; i<data.length; i++) {
data[i] = ColumnDataPoint.shiftHeightAndDepth(data[i], (short)offset);
}
}
private static void patchVersion9Reorder(long[] data) {
for (int i=0; i<data.length; i++) {
data[i] = ColumnDataPoint.version9Reorder(data[i]);
}
}
private static final ThreadLocal<long[][]> tLocalVerticalUpdateArrays = ThreadLocal.withInitial(() ->
{
return new long[LodUtil.DETAIL_OPTIONS - 1][];
});
@Override
public void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ)
{
ColumnQuadView quadView = lowerDataContainer.getDataInQuad(posX*2, posZ*2, 2,2);
@@ -335,36 +214,26 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return stringBuilder.toString();
}
@Override
public int getMaxNumberOfLods()
{
return SECTION_SIZE * SECTION_SIZE * getVerticalSize();
}
@Override
public long getRoughRamUsage()
{
return (long) dataContainer.length * Long.BYTES;
}
// Called by ColumnDataLoader
static LodDataSource loadFile(DHLevel level, DhSectionPos pos, InputStream is, int version) {
try (DataInputStream dis = new DataInputStream(is)) {
return new ColumnDatatype(pos, dis, version, level);
} catch (IOException e) {
//FIXME: Log error
return null;
}
}
public static final ColumnRenderLoader COLUMN_LAYER_LOADER = new ColumnRenderLoader();
public static final ColumnDataLoader COLUMN_DATA_LOADER = ColumnDataLoader.INSTANCE;
static {
LodQuadTree.registerLayerLoader(COLUMN_LAYER_LOADER, (byte) 7); // 7 or above
}
@Override
public DataSourceLoader getLatestLoader() {
return COLUMN_DATA_LOADER;
return ColumnDataLoader.INSTANCE;
}
@Override
@@ -377,6 +246,11 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
return (byte) (sectionPos.sectionDetail - SECTION_SIZE_OFFSET);
}
@Override
public byte getDetailOffset() {
return SECTION_SIZE_OFFSET;
}
@Override
public void enableRender() {
@@ -396,10 +270,6 @@ public class ColumnDatatype implements LodDataSource, RenderDataSource {
public void dispose() {
}
@Override
public byte getDetailOffset() {
return SECTION_SIZE_OFFSET;
}
@Override
public boolean trySwapRenderBuffer(AtomicReference<RenderBuffer> referenceSlot) {
@@ -45,8 +45,8 @@ class ColumnRenderLoader extends RenderDataSourceLoader {
byte targetDataLevel = (byte) (sectionPos.sectionDetail - ColumnDatatype.SECTION_SIZE_OFFSET);
byte sourceDataLevel = dataSources.get(0).getDataDetail();
LodUtil.assertTrue(targetDataLevel >= sourceDataLevel);
if (dataSources.get(0) instanceof ColumnDatatype) {
ColumnDatatype dataSource = (ColumnDatatype) dataSources.get(0);
if (dataSources.get(0) instanceof IColumnDatatype) {
IColumnDatatype dataSource = (IColumnDatatype) dataSources.get(0);
DhSectionPos srcPos = dataSource.getSectionPos();
// Note that in here, the source data level will be always < target section level
@@ -77,8 +77,8 @@ class ColumnRenderLoader extends RenderDataSourceLoader {
byte sourceDataLevel = dataSource.getDataDetail();
DhSectionPos srcPos = dataSource.getSectionPos();
if (dataSource instanceof ColumnDatatype) {
ColumnDatatype clDataSource = (ColumnDatatype) dataSource;
if (dataSource instanceof IColumnDatatype) {
IColumnDatatype clDataSource = (IColumnDatatype) dataSource;
// Note that targetDataLevel can be > source section level
int srcX = srcPos.getCorner().getX().toBlock();
@@ -106,6 +106,10 @@ class ColumnRenderLoader extends RenderDataSourceLoader {
return renderDataSource;
}
private static boolean IsColumnDatatype(Class<?> clazz) {
return IColumnDatatype.class.isAssignableFrom(clazz);
}
@Override
public List<DataFile> selectFiles(DhSectionPos sectionPos, DHLevel level, List<DataFile>[] availableFiles) {
byte targetDataLevel = (byte) (sectionPos.sectionDetail - ColumnDatatype.SECTION_SIZE_OFFSET);
@@ -119,7 +123,8 @@ class ColumnRenderLoader extends RenderDataSourceLoader {
if (topValidDataLevel == Byte.MIN_VALUE) {
for (DataFile dataFile : availableFiles[detail]) {
if (dataFile.dataLevel > targetDataLevel) continue;
if (dataFile.dataType == ColumnDatatype.class || dataFile.dataType == FullDatatype.class) {
if (IsColumnDatatype(dataFile.dataType) || dataFile.dataType == FullDatatype.class
|| dataFile.dataType == OldColumnDatatype.class) {
topValidDataLevel = LodUtil.max(topValidDataLevel, dataFile.dataLevel);
break;
}
@@ -133,7 +138,7 @@ class ColumnRenderLoader extends RenderDataSourceLoader {
for (DataFile dataFile : availableFiles[detail]) {
if (dataFile.pos.getWidth().toBlock() == sectionPos.getWidth().toBlock()) {
if (dataFile.dataType == ColumnDatatype.class) {
if (IsColumnDatatype(dataFile.dataType)) {
singleCoveringColumnFile = dataFile;
break;
} else if (dataFile.dataType == FullDatatype.class) {
@@ -141,7 +146,7 @@ class ColumnRenderLoader extends RenderDataSourceLoader {
// Don't break as there may be a column file later.
}
} else if (dataFile.pos.getWidth().toBlock() > sectionPos.getWidth().toBlock()) {
if (dataFile.dataType == ColumnDatatype.class && singleCoveringColumnFile == null)
if (IsColumnDatatype(dataFile.dataType) && singleCoveringColumnFile == null)
singleCoveringColumnFile = dataFile;
else if (dataFile.dataType == FullDatatype.class && singleCoveringFullFile == null)
singleCoveringFullFile = dataFile;
@@ -11,14 +11,19 @@ import java.io.File;
import java.io.IOException;
public abstract class DataSourceSaver extends DataSourceLoader {
public DataSourceSaver(Class<? extends LodDataSource> clazz, long datatypeId, byte loaderVersion) {
super(clazz, datatypeId, loaderVersion);
public DataSourceSaver(Class<? extends LodDataSource> clazz, long datatypeId, byte[] loaderSupportedVersions) {
super(clazz, datatypeId, loaderSupportedVersions);
}
public abstract void saveData(DHLevel level, LodDataSource loadedData, DataOutputStream out) throws IOException;
// generate the default file path and file name based on various parameters.
// Ensure the file extension is '.lod'!
public File generateFilePathAndName(File levelFolderPath, DHLevel level, DhSectionPos sectionPos) {
return new File(levelFolderPath, String.format("%s_v%d-%s%s", clazz.getSimpleName(), loaderVersion,
return new File(levelFolderPath, String.format("%s_v%d-%s%s", clazz.getSimpleName(), getSaverVersion(),
sectionPos.serialize(), DataFileHandler.FILE_EXTENSION));
}
public byte getSaverVersion() {
return loaderSupportedVersions[loaderSupportedVersions.length - 1];
}
}
@@ -0,0 +1,37 @@
package com.seibel.lod.core.objects.a7.datatype.column;
import com.seibel.lod.core.objects.LodDataView;
import com.seibel.lod.core.objects.a7.data.LodDataSource;
public interface IColumnDatatype extends LodDataSource {
byte getDetailOffset();
default int getDataSize() {
return 1 << getDetailOffset();
}
int getMaxNumberOfLods();
long getRoughRamUsage();
int getVerticalSize();
boolean doesItExist(int posX, int posZ);
long getData(int posX, int posZ, int verticalIndex);
default long getSingleData(int posX, int posZ) {return getData(posX, posZ, 0);}
long[] getAllData(int posX, int posZ);
ColumnArrayView getVerticalDataView(int posX, int posZ);
ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize);
ColumnQuadView getFullQuad();
/**
* This method will clear all data at relative section position
*/
void clear(int posX, int posZ);
/**
* This method will add the data given in input at the relative position and vertical index
*/
boolean addData(long data, int posX, int posZ, int verticalIndex);
/**
* This methods will add the data in the given position if certain condition are satisfied
* @param override if override is true we can override data created with same generation mode
*/
boolean copyVerticalData(LodDataView data, int posX, int posZ, boolean override);
void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ);
}
@@ -0,0 +1,249 @@
package com.seibel.lod.core.objects.a7.datatype.column;
import com.seibel.lod.core.objects.LodDataView;
import com.seibel.lod.core.objects.a7.DHLevel;
import com.seibel.lod.core.objects.a7.data.DataSourceLoader;
import com.seibel.lod.core.objects.a7.pos.DhSectionPos;
import java.io.DataInputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class OldColumnDatatype implements IColumnDatatype {
public static final boolean DO_SAFETY_CHECKS = true;
public static final int LATEST_VERSION = 10;
public static final long DATA_TYPE_ID = "OldColumnDatatype".hashCode();
public final byte sectionSizeOffset;
public final int getSectSize() {
return 1 << sectionSizeOffset;
}
public final int verticalSize;
public final DhSectionPos sectionPos;
public final int yOffset;
public final long[] dataContainer;
/**
* Constructor of the ColumnDataType
* @param maxVerticalSize the maximum vertical size of the container
*/
public OldColumnDatatype(DhSectionPos sectionPos, int maxVerticalSize, int yOffset, int sectionSizeOffset) {
this.sectionSizeOffset = (byte) sectionSizeOffset;
verticalSize = maxVerticalSize;
dataContainer = new long[getSectSize() * getSectSize() * verticalSize];
this.sectionPos = sectionPos;
this.yOffset = yOffset;
}
private long[] loadData(DataInputStream inputData, int version, int verticalSize) throws IOException {
switch (version) {
case 6:
return readDataVersion6(inputData, verticalSize);
case 7:
return readDataVersion7(inputData, verticalSize);
case 8:
return readDataVersion8(inputData, verticalSize);
case 9:
case 10:
return readDataVersion9(inputData, verticalSize);
default:
throw new IOException("Invalid Data: The version of the data is not supported");
}
}
// Load from data stream with maxVerticalSize loaded from the data stream
public OldColumnDatatype(DhSectionPos sectionPos, DataInputStream inputData, int version, DHLevel level, int sectionSizeOffset) throws IOException {
this.sectionSizeOffset = (byte) sectionSizeOffset;
this.sectionPos = sectionPos;
yOffset = level.getMinY();
byte detailLevel = inputData.readByte();
if (sectionPos.sectionDetail - sectionSizeOffset != detailLevel) {
throw new IOException("Invalid data: detail level does not match");
}
verticalSize = inputData.readByte() & 0b01111111;
dataContainer = loadData(inputData, version, verticalSize);
}
@Override
public byte getDetailOffset() {
return sectionSizeOffset;
}
public void clear(int posX, int posZ)
{
throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." +
" Convert to ColumnDatatype first before doing any modifications.");
}
public boolean addData(long data, int posX, int posZ, int verticalIndex)
{
throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." +
" Convert to ColumnDatatype first before doing any modifications.");
}
public boolean copyVerticalData(LodDataView data, int posX, int posZ, boolean override) {
throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." +
" Convert to ColumnDatatype first before doing any modifications.");
}
public long getData(int posX, int posZ, int verticalIndex)
{
return dataContainer[posX * getSectSize() * verticalSize + posZ * verticalSize + verticalIndex];
}
public long[] getAllData(int posX, int posZ)
{
long[] result = new long[verticalSize];
int index = posX * getSectSize() * verticalSize + posZ * verticalSize;
System.arraycopy(dataContainer, index, result, 0, verticalSize);
return result;
}
public ColumnArrayView getVerticalDataView(int posX, int posZ) {
return new ColumnArrayView(dataContainer, verticalSize,
posX * getSectSize() * verticalSize + posZ * verticalSize, verticalSize);
}
public ColumnQuadView getDataInQuad(int quadX, int quadZ, int quadXSize, int quadZSize) {
return new ColumnQuadView(dataContainer, getSectSize(), verticalSize, quadX, quadZ, quadXSize, quadZSize);
}
public ColumnQuadView getFullQuad() {
return new ColumnQuadView(dataContainer, getSectSize(), verticalSize, 0, 0, getSectSize(), getSectSize());
}
public int getVerticalSize()
{
return verticalSize;
}
public boolean doesItExist(int posX, int posZ)
{
return ColumnDataPoint.doesItExist(getSingleData(posX, posZ));
}
@Override
public void generateData(ColumnDatatype lowerDataContainer, int posX, int posZ) {
throw new UnsupportedOperationException("OldColumnDatatype only supports read-only access." +
" Convert to ColumnDatatype first before doing any modifications.");
}
private long[] readDataVersion6(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = getSectSize() * getSectSize() * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
patchVersion9Reorder(result);
patchHeightAndDepth(result,-yOffset);
return result;
}
private long[] readDataVersion7(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = getSectSize() * getSectSize() * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
patchVersion9Reorder(result);
patchHeightAndDepth(result, 64 - yOffset);
return result;
}
private long[] readDataVersion8(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = getSectSize() * getSectSize() * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
short tempMinHeight = Short.reverseBytes(inputData.readShort());
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
patchVersion9Reorder(result);
if (tempMinHeight != yOffset) {
patchHeightAndDepth(result,tempMinHeight - yOffset);
}
return result;
}
private long[] readDataVersion9(DataInputStream inputData, int tempMaxVerticalData) throws IOException {
int x = getSectSize() * getSectSize() * tempMaxVerticalData;
byte[] data = new byte[x * Long.BYTES];
short tempMinHeight = Short.reverseBytes(inputData.readShort());
ByteBuffer bb = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN);
inputData.readFully(data);
long[] result = new long[x];
bb.asLongBuffer().get(result);
if (tempMinHeight != yOffset) {
patchHeightAndDepth(result,tempMinHeight - yOffset);
}
return result;
}
private static void patchHeightAndDepth(long[] data, int offset) {
for (int i=0; i<data.length; i++) {
data[i] = ColumnDataPoint.shiftHeightAndDepth(data[i], (short)offset);
}
}
private static void patchVersion9Reorder(long[] data) {
for (int i=0; i<data.length; i++) {
data[i] = ColumnDataPoint.version9Reorder(data[i]);
}
}
@Override
public String toString()
{
String LINE_DELIMITER = "\n";
String DATA_DELIMITER = " ";
String SUBDATA_DELIMITER = ",";
StringBuilder stringBuilder = new StringBuilder();
int size = sectionPos.getWidth().value;
stringBuilder.append(sectionPos);
stringBuilder.append("(LEGACY-READ-ONLY)");
stringBuilder.append(LINE_DELIMITER);
for (int z = 0; z < size; z++)
{
for (int x = 0; x < size; x++)
{
for (int y = 0; y < verticalSize; y++) {
//Converting the dataToHex
stringBuilder.append(Long.toHexString(getData(x,z,y)));
if (y != verticalSize-1) stringBuilder.append(SUBDATA_DELIMITER);
}
if (x != size-1) stringBuilder.append(DATA_DELIMITER);
}
if (z != size-1) stringBuilder.append(LINE_DELIMITER);
}
return stringBuilder.toString();
}
@Override
public int getMaxNumberOfLods()
{
return getSectSize() * getSectSize() * getVerticalSize();
}
@Override
public long getRoughRamUsage()
{
return (long) dataContainer.length * Long.BYTES;
}
@Override
public DataSourceLoader getLatestLoader() {
return Alpha6DataLoader.INSTANCE;
}
@Override
public DhSectionPos getSectionPos() {
return sectionPos;
}
@Override
public byte getDataDetail() {
return (byte) (sectionPos.sectionDetail - sectionSizeOffset);
}
}