Refactor DataSources
This commit is contained in:
@@ -13,7 +13,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
/**
|
||||
* WARNING: This is not THREAD-SAFE!
|
||||
* <p>
|
||||
* Used to map a numerical ID to a Biome/BlockState pair.
|
||||
* Used to map a numerical IDs to a Biome/BlockState pair.
|
||||
*
|
||||
* @author Leetom
|
||||
* @version 2022-10-2
|
||||
@@ -25,19 +25,19 @@ public class FullDataPointIdMap
|
||||
|
||||
|
||||
|
||||
public IBiomeWrapper getBiomeWrapper(int id) { return entries.get(id).biome; }
|
||||
public IBlockStateWrapper getBlockStateWrapper(int id) { return entries.get(id).blockState; }
|
||||
public IBiomeWrapper getBiomeWrapper(int id) { return this.entries.get(id).biome; }
|
||||
public IBlockStateWrapper getBlockStateWrapper(int id) { return this.entries.get(id).blockState; }
|
||||
|
||||
/**
|
||||
* If an entry with the given values already exists nothing will
|
||||
* be added but the existing item's ID will still be returned.
|
||||
*/
|
||||
public int addIfNotPresentAndGetId(IBiomeWrapper biome, IBlockStateWrapper blockState) { return addIfNotPresentAndGetId(new Entry(biome, blockState)); }
|
||||
public int addIfNotPresentAndGetId(IBiomeWrapper biome, IBlockStateWrapper blockState) { return this.addIfNotPresentAndGetId(new Entry(biome, blockState)); }
|
||||
private int addIfNotPresentAndGetId(Entry biomeBlockStateEntry)
|
||||
{
|
||||
return idMap.computeIfAbsent(biomeBlockStateEntry, (entry) -> {
|
||||
int id = entries.size();
|
||||
entries.add(entry);
|
||||
return this.idMap.computeIfAbsent(biomeBlockStateEntry, (entry) -> {
|
||||
int id = this.entries.size();
|
||||
this.entries.add(entry);
|
||||
return id;
|
||||
});
|
||||
}
|
||||
@@ -54,7 +54,7 @@ public class FullDataPointIdMap
|
||||
int[] remappedEntryIds = new int[entriesToMerge.size()];
|
||||
for (int i = 0; i < entriesToMerge.size(); i++)
|
||||
{
|
||||
remappedEntryIds[i] = addIfNotPresentAndGetId(entriesToMerge.get(i));
|
||||
remappedEntryIds[i] = this.addIfNotPresentAndGetId(entriesToMerge.get(i));
|
||||
}
|
||||
return remappedEntryIds;
|
||||
}
|
||||
@@ -63,8 +63,8 @@ public class FullDataPointIdMap
|
||||
void serialize(OutputStream outputStream) throws IOException
|
||||
{
|
||||
DataOutputStream dataStream = new DataOutputStream(outputStream); // DO NOT CLOSE! It would close all related streams
|
||||
dataStream.writeInt(entries.size());
|
||||
for (Entry entry : entries)
|
||||
dataStream.writeInt(this.entries.size());
|
||||
for (Entry entry : this.entries)
|
||||
{
|
||||
dataStream.writeUTF(entry.serialize());
|
||||
}
|
||||
@@ -119,7 +119,7 @@ public class FullDataPointIdMap
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode() { return Objects.hash(biome, blockState); }
|
||||
public int hashCode() { return Objects.hash(this.biome, this.blockState); }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other)
|
||||
@@ -130,11 +130,11 @@ public class FullDataPointIdMap
|
||||
if (!(other instanceof Entry))
|
||||
return false;
|
||||
|
||||
return ((Entry) other).biome.equals(biome) && ((Entry) other).blockState.equals(blockState);
|
||||
return ((Entry) other).biome.equals(this.biome) && ((Entry) other).blockState.equals(this.blockState);
|
||||
}
|
||||
|
||||
|
||||
public String serialize() { return biome.serialize() + " " + blockState.serialize(); }
|
||||
public String serialize() { return this.biome.serialize() + " " + this.blockState.serialize(); }
|
||||
|
||||
public static Entry deserialize(String str) throws IOException
|
||||
{
|
||||
|
||||
@@ -38,9 +38,9 @@ public class FullDataSource extends FullArrayView implements ILodDataSource
|
||||
|
||||
|
||||
@Override
|
||||
public DhSectionPos getSectionPos() { return sectionPos; }
|
||||
public DhSectionPos getSectionPos() { return this.sectionPos; }
|
||||
@Override
|
||||
public byte getDataDetail() { return (byte) (sectionPos.sectionDetail-SECTION_SIZE_OFFSET); }
|
||||
public byte getDataDetail() { return (byte) (this.sectionPos.sectionDetail-SECTION_SIZE_OFFSET); }
|
||||
|
||||
@Override
|
||||
public byte getDataVersion() { return LATEST_VERSION; }
|
||||
@@ -48,13 +48,13 @@ public class FullDataSource extends FullArrayView implements ILodDataSource
|
||||
@Override
|
||||
public void update(ChunkSizedData data)
|
||||
{
|
||||
LodUtil.assertTrue(sectionPos.getSectionBBoxPos().overlaps(data.getBBoxLodPos()));
|
||||
if (data.dataDetail == 0 && getDataDetail() == 0)
|
||||
LodUtil.assertTrue(this.sectionPos.getSectionBBoxPos().overlaps(data.getBBoxLodPos()));
|
||||
if (data.dataDetail == 0 && this.getDataDetail() == 0)
|
||||
{
|
||||
DhBlockPos2D chunkBlockPos = new DhBlockPos2D(data.x * 16, data.z * 16);
|
||||
DhBlockPos2D blockOffset = chunkBlockPos.subtract(sectionPos.getCorner().getCorner());
|
||||
DhBlockPos2D blockOffset = chunkBlockPos.subtract(this.sectionPos.getCorner().getCorner());
|
||||
LodUtil.assertTrue(blockOffset.x >= 0 && blockOffset.x < SECTION_SIZE && blockOffset.z >= 0 && blockOffset.z < SECTION_SIZE);
|
||||
isEmpty = false;
|
||||
this.isEmpty = false;
|
||||
data.shadowCopyTo(this.subView(16, blockOffset.x, blockOffset.z));
|
||||
{ // DEBUG ASSERTION
|
||||
for (int x = 0; x < 16; x++)
|
||||
@@ -67,16 +67,16 @@ public class FullDataSource extends FullArrayView implements ILodDataSource
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (data.dataDetail == 0 && getDataDetail() < 4)
|
||||
else if (data.dataDetail == 0 && this.getDataDetail() < 4)
|
||||
{
|
||||
int dataPerFull = 1 << getDataDetail();
|
||||
int dataPerFull = 1 << this.getDataDetail();
|
||||
int fullSize = 16 / dataPerFull;
|
||||
DhLodPos dataOffset = data.getBBoxLodPos().getCorner(getDataDetail());
|
||||
DhLodPos baseOffset = sectionPos.getCorner(getDataDetail());
|
||||
DhLodPos dataOffset = data.getBBoxLodPos().getCorner(this.getDataDetail());
|
||||
DhLodPos baseOffset = this.sectionPos.getCorner(this.getDataDetail());
|
||||
int offsetX = dataOffset.x - baseOffset.x;
|
||||
int offsetZ = dataOffset.z - baseOffset.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < SECTION_SIZE && offsetZ >= 0 && offsetZ < SECTION_SIZE);
|
||||
isEmpty = false;
|
||||
this.isEmpty = false;
|
||||
for (int ox = 0; ox < fullSize; ox++)
|
||||
{
|
||||
for (int oz = 0; oz < fullSize; oz++)
|
||||
@@ -86,19 +86,19 @@ public class FullDataSource extends FullArrayView implements ILodDataSource
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (data.dataDetail == 0 && getDataDetail() >= 4)
|
||||
else if (data.dataDetail == 0 && this.getDataDetail() >= 4)
|
||||
{
|
||||
//FIXME: TEMPORARY
|
||||
int chunkPerFull = 1 << (getDataDetail() - 4);
|
||||
int chunkPerFull = 1 << (this.getDataDetail() - 4);
|
||||
if (data.x % chunkPerFull != 0 || data.z % chunkPerFull != 0)
|
||||
return;
|
||||
DhLodPos baseOffset = sectionPos.getCorner(getDataDetail());
|
||||
DhLodPos dataOffset = data.getBBoxLodPos().convertUpwardsTo(getDataDetail());
|
||||
DhLodPos baseOffset = this.sectionPos.getCorner(this.getDataDetail());
|
||||
DhLodPos dataOffset = data.getBBoxLodPos().convertUpwardsTo(this.getDataDetail());
|
||||
int offsetX = dataOffset.x - baseOffset.x;
|
||||
int offsetZ = dataOffset.z - baseOffset.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < SECTION_SIZE && offsetZ >= 0 && offsetZ < SECTION_SIZE);
|
||||
isEmpty = false;
|
||||
data.get(0, 0).deepCopyTo(get(offsetX, offsetZ));
|
||||
this.isEmpty = false;
|
||||
data.get(0, 0).deepCopyTo(this.get(offsetX, offsetZ));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -109,38 +109,38 @@ public class FullDataSource extends FullArrayView implements ILodDataSource
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() { return isEmpty; }
|
||||
public void markNotEmpty() { isEmpty = false; }
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
public void markNotEmpty() { this.isEmpty = false; }
|
||||
|
||||
@Override
|
||||
public void saveData(IDhLevel level, DataMetaFile file, OutputStream dataStream) throws IOException
|
||||
{
|
||||
DataOutputStream dos = new DataOutputStream(dataStream); // DO NOT CLOSE
|
||||
{
|
||||
dos.writeInt(getDataDetail());
|
||||
dos.writeInt(size);
|
||||
dos.writeInt(this.getDataDetail());
|
||||
dos.writeInt(this.size);
|
||||
dos.writeInt(level.getMinY());
|
||||
if (isEmpty)
|
||||
if (this.isEmpty)
|
||||
{
|
||||
dos.writeInt(0x00000001);
|
||||
return;
|
||||
}
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
// Data array length
|
||||
for (int x = 0; x < size; x++)
|
||||
for (int x = 0; x < this.size; x++)
|
||||
{
|
||||
for (int z = 0; z < size; z++)
|
||||
for (int z = 0; z < this.size; z++)
|
||||
{
|
||||
dos.writeByte(get(x, z).getSingleLength());
|
||||
dos.writeByte(this.get(x, z).getSingleLength());
|
||||
}
|
||||
}
|
||||
// Data array content (only on non-empty columns)
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
for (int x = 0; x < size; x++)
|
||||
for (int x = 0; x < this.size; x++)
|
||||
{
|
||||
for (int z = 0; z < size; z++)
|
||||
for (int z = 0; z < this.size; z++)
|
||||
{
|
||||
SingleFullArrayView column = get(x, z);
|
||||
SingleFullArrayView column = this.get(x, z);
|
||||
if (!column.doesItExist())
|
||||
continue;
|
||||
long[] raw = column.getRaw();
|
||||
@@ -152,7 +152,7 @@ public class FullDataSource extends FullArrayView implements ILodDataSource
|
||||
}
|
||||
// Id mapping
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
mapping.serialize(dos);
|
||||
this.mapping.serialize(dos);
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
@@ -220,7 +220,7 @@ public class FullDataSource extends FullArrayView implements ILodDataSource
|
||||
super(mapping, data, SECTION_SIZE);
|
||||
LodUtil.assertTrue(data.length == SECTION_SIZE * SECTION_SIZE);
|
||||
this.sectionPos = pos;
|
||||
isEmpty = false;
|
||||
this.isEmpty = false;
|
||||
}
|
||||
|
||||
public static FullDataSource createEmpty(DhSectionPos pos) { return new FullDataSource(pos); }
|
||||
@@ -239,14 +239,14 @@ public class FullDataSource extends FullArrayView implements ILodDataSource
|
||||
|
||||
public void writeFromLower(FullDataSource subData)
|
||||
{
|
||||
LodUtil.assertTrue(sectionPos.overlaps(subData.sectionPos));
|
||||
LodUtil.assertTrue(subData.sectionPos.sectionDetail < sectionPos.sectionDetail);
|
||||
if (!neededForPosition(sectionPos, subData.sectionPos))
|
||||
LodUtil.assertTrue(this.sectionPos.overlaps(subData.sectionPos));
|
||||
LodUtil.assertTrue(subData.sectionPos.sectionDetail < this.sectionPos.sectionDetail);
|
||||
if (!neededForPosition(this.sectionPos, subData.sectionPos))
|
||||
return;
|
||||
DhSectionPos lowerSectPos = subData.sectionPos;
|
||||
byte detailDiff = (byte) (sectionPos.sectionDetail - subData.sectionPos.sectionDetail);
|
||||
byte targetDataDetail = getDataDetail();
|
||||
DhLodPos minDataPos = sectionPos.getCorner(targetDataDetail);
|
||||
byte detailDiff = (byte) (this.sectionPos.sectionDetail - subData.sectionPos.sectionDetail);
|
||||
byte targetDataDetail = this.getDataDetail();
|
||||
DhLodPos minDataPos = this.sectionPos.getCorner(targetDataDetail);
|
||||
if (detailDiff <= SECTION_SIZE_OFFSET)
|
||||
{
|
||||
int count = 1 << detailDiff;
|
||||
|
||||
@@ -33,228 +33,272 @@ public class SparseDataSource implements IIncompleteDataSource
|
||||
final int dataPerChunk;
|
||||
private final DhLodPos chunkPos;
|
||||
public boolean isEmpty = true;
|
||||
|
||||
|
||||
|
||||
public static SparseDataSource createEmpty(DhSectionPos pos) { return new SparseDataSource(pos); }
|
||||
|
||||
public static SparseDataSource createEmpty(DhSectionPos pos) {
|
||||
return new SparseDataSource(pos);
|
||||
}
|
||||
|
||||
protected SparseDataSource(DhSectionPos sectionPos) {
|
||||
protected SparseDataSource(DhSectionPos sectionPos)
|
||||
{
|
||||
LodUtil.assertTrue(sectionPos.sectionDetail > SPARSE_UNIT_DETAIL);
|
||||
LodUtil.assertTrue(sectionPos.sectionDetail <= MAX_SECTION_DETAIL);
|
||||
this.sectionPos = sectionPos;
|
||||
chunks = 1 << (byte) (sectionPos.sectionDetail - SPARSE_UNIT_DETAIL);
|
||||
dataPerChunk = SECTION_SIZE / chunks;
|
||||
sparseData = new FullArrayView[chunks * chunks];
|
||||
chunkPos = sectionPos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
mapping = new FullDataPointIdMap();
|
||||
this.chunks = 1 << (byte) (sectionPos.sectionDetail - SPARSE_UNIT_DETAIL);
|
||||
this.dataPerChunk = SECTION_SIZE / this.chunks;
|
||||
this.sparseData = new FullArrayView[this.chunks * this.chunks];
|
||||
this.chunkPos = sectionPos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
this.mapping = new FullDataPointIdMap();
|
||||
}
|
||||
protected SparseDataSource(DhSectionPos sectionPos, FullDataPointIdMap mapping, FullArrayView[] data) {
|
||||
protected SparseDataSource(DhSectionPos sectionPos, FullDataPointIdMap mapping, FullArrayView[] data)
|
||||
{
|
||||
LodUtil.assertTrue(sectionPos.sectionDetail > SPARSE_UNIT_DETAIL);
|
||||
LodUtil.assertTrue(sectionPos.sectionDetail <= MAX_SECTION_DETAIL);
|
||||
this.sectionPos = sectionPos;
|
||||
chunks = 1 << (byte) (sectionPos.sectionDetail - SPARSE_UNIT_DETAIL);
|
||||
dataPerChunk = SECTION_SIZE / chunks;
|
||||
LodUtil.assertTrue(chunks*chunks == data.length);
|
||||
sparseData = data;
|
||||
chunkPos = sectionPos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
isEmpty = false;
|
||||
this.chunks = 1 << (byte) (sectionPos.sectionDetail - SPARSE_UNIT_DETAIL);
|
||||
this.dataPerChunk = SECTION_SIZE / this.chunks;
|
||||
LodUtil.assertTrue(this.chunks * this.chunks == data.length);
|
||||
this.sparseData = data;
|
||||
this.chunkPos = sectionPos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
this.isEmpty = false;
|
||||
this.mapping = mapping;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public DhSectionPos getSectionPos() { return this.sectionPos; }
|
||||
@Override
|
||||
public byte getDataDetail() { return (byte) (this.sectionPos.sectionDetail-SECTION_SIZE_OFFSET); }
|
||||
|
||||
@Override
|
||||
public DhSectionPos getSectionPos() {
|
||||
return sectionPos;
|
||||
public byte getDataVersion() { return LATEST_VERSION; }
|
||||
|
||||
private int calculateOffset(int cx, int cz)
|
||||
{
|
||||
int ox = cx - this.chunkPos.x;
|
||||
int oz = cz - this.chunkPos.z;
|
||||
LodUtil.assertTrue(ox >= 0 && oz >= 0 && ox < this.chunks && oz < this.chunks);
|
||||
return ox * this.chunks + oz;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte getDataDetail() {
|
||||
return (byte) (sectionPos.sectionDetail-SECTION_SIZE_OFFSET);
|
||||
public void update(ChunkSizedData data)
|
||||
{
|
||||
if (data.dataDetail != 0)
|
||||
{
|
||||
//TODO: Disable the throw and instead just ignore the data.
|
||||
throw new IllegalArgumentException("SparseDataSource only supports dataDetail 0!");
|
||||
}
|
||||
|
||||
int arrayOffset = this.calculateOffset(data.x, data.z);
|
||||
FullArrayView newArray = new FullArrayView(this.mapping, new long[this.dataPerChunk * this.dataPerChunk][], this.dataPerChunk);
|
||||
if (this.getDataDetail() == data.dataDetail)
|
||||
{
|
||||
data.shadowCopyTo(newArray);
|
||||
}
|
||||
else
|
||||
{
|
||||
int count = this.dataPerChunk;
|
||||
int dataPerCount = SPARSE_UNIT_SIZE / this.dataPerChunk;
|
||||
|
||||
for (int ox = 0; ox < count; ox++)
|
||||
{
|
||||
for (int oz = 0; oz < count; oz++)
|
||||
{
|
||||
SingleFullArrayView column = newArray.get(ox, oz);
|
||||
column.downsampleFrom(data.subView(dataPerCount, ox * dataPerCount, oz * dataPerCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
this.isEmpty = false;
|
||||
this.sparseData[arrayOffset] = newArray;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte getDataVersion() {
|
||||
return LATEST_VERSION;
|
||||
}
|
||||
|
||||
private int calculateOffset(int cx, int cz) {
|
||||
int ox = cx - chunkPos.x;
|
||||
int oz = cz - chunkPos.z;
|
||||
LodUtil.assertTrue(ox >= 0 && oz >= 0 && ox < chunks && oz < chunks);
|
||||
return ox * chunks + oz;
|
||||
}
|
||||
|
||||
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
|
||||
|
||||
@Override
|
||||
public void update(ChunkSizedData data) {
|
||||
if (data.dataDetail != 0) {
|
||||
//TODO: Disable the throw and instead just ignore the data.
|
||||
throw new IllegalArgumentException("SparseDataSource only supports dataDetail 0!");
|
||||
}
|
||||
int arrayOffset = calculateOffset(data.x, data.z);
|
||||
FullArrayView newArray = new FullArrayView(mapping, new long[dataPerChunk * dataPerChunk][], dataPerChunk);
|
||||
if (getDataDetail() == data.dataDetail) {
|
||||
data.shadowCopyTo(newArray);
|
||||
} else {
|
||||
int count = dataPerChunk;
|
||||
int dataPerCount = SPARSE_UNIT_SIZE / dataPerChunk;
|
||||
public void sampleFrom(ILodDataSource source)
|
||||
{
|
||||
DhSectionPos pos = source.getSectionPos();
|
||||
LodUtil.assertTrue(pos.sectionDetail < this.sectionPos.sectionDetail);
|
||||
LodUtil.assertTrue(pos.overlaps(this.sectionPos));
|
||||
if (source.isEmpty())
|
||||
return;
|
||||
|
||||
if (source instanceof SparseDataSource)
|
||||
{
|
||||
this.sampleFrom((SparseDataSource) source);
|
||||
}
|
||||
else if (source instanceof FullDataSource)
|
||||
{
|
||||
this.sampleFrom((FullDataSource) source);
|
||||
}
|
||||
else
|
||||
{
|
||||
LodUtil.assertNotReach();
|
||||
}
|
||||
}
|
||||
|
||||
for (int ox = 0; ox < count; ox++) {
|
||||
for (int oz = 0; oz < count; oz++) {
|
||||
SingleFullArrayView column = newArray.get(ox, oz);
|
||||
column.downsampleFrom(data.subView(dataPerCount, ox * dataPerCount, oz * dataPerCount));
|
||||
}
|
||||
}
|
||||
}
|
||||
isEmpty = false;
|
||||
sparseData[arrayOffset] = newArray;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return isEmpty;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void sampleFrom(ILodDataSource source) {
|
||||
DhSectionPos pos = source.getSectionPos();
|
||||
LodUtil.assertTrue(pos.sectionDetail < sectionPos.sectionDetail);
|
||||
LodUtil.assertTrue(pos.overlaps(sectionPos));
|
||||
if (source.isEmpty()) return;
|
||||
if (source instanceof SparseDataSource) {
|
||||
sampleFrom((SparseDataSource) source);
|
||||
} else if (source instanceof FullDataSource) {
|
||||
sampleFrom((FullDataSource) source);
|
||||
} else {
|
||||
LodUtil.assertNotReach();
|
||||
}
|
||||
}
|
||||
|
||||
private void sampleFrom(SparseDataSource sparseSource) {
|
||||
private void sampleFrom(SparseDataSource sparseSource)
|
||||
{
|
||||
DhSectionPos pos = sparseSource.getSectionPos();
|
||||
isEmpty = false;
|
||||
|
||||
DhLodPos basePos = sectionPos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
this.isEmpty = false;
|
||||
|
||||
DhLodPos basePos = this.sectionPos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
DhLodPos dataPos = pos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
int offsetX = dataPos.x-basePos.x;
|
||||
int offsetZ = dataPos.z-basePos.z;
|
||||
LodUtil.assertTrue(offsetX >=0 && offsetX < chunks && offsetZ >=0 && offsetZ < chunks);
|
||||
|
||||
for (int ox = 0; ox < sparseSource.chunks; ox++) {
|
||||
for (int oz = 0; oz < sparseSource.chunks; oz++) {
|
||||
FullArrayView sourceChunk = sparseSource.sparseData[ox*sparseSource.chunks + oz];
|
||||
if (sourceChunk != null) {
|
||||
FullArrayView buff = new FullArrayView(mapping, new long[dataPerChunk * dataPerChunk][], dataPerChunk);
|
||||
buff.downsampleFrom(sourceChunk);
|
||||
sparseData[(ox+offsetX)* chunks + (oz+offsetZ)] = buff;
|
||||
}
|
||||
}
|
||||
}
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < this.chunks && offsetZ >= 0 && offsetZ < this.chunks);
|
||||
|
||||
for (int ox = 0; ox < sparseSource.chunks; ox++)
|
||||
{
|
||||
for (int oz = 0; oz < sparseSource.chunks; oz++)
|
||||
{
|
||||
FullArrayView sourceChunk = sparseSource.sparseData[ox * sparseSource.chunks + oz];
|
||||
if (sourceChunk != null)
|
||||
{
|
||||
FullArrayView buff = new FullArrayView(this.mapping, new long[this.dataPerChunk * this.dataPerChunk][], this.dataPerChunk);
|
||||
buff.downsampleFrom(sourceChunk);
|
||||
this.sparseData[(ox + offsetX) * this.chunks + (oz + offsetZ)] = buff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
private void sampleFrom(FullDataSource fullSource) {
|
||||
private void sampleFrom(FullDataSource fullSource)
|
||||
{
|
||||
DhSectionPos pos = fullSource.getSectionPos();
|
||||
isEmpty = false;
|
||||
this.isEmpty = false;
|
||||
|
||||
DhLodPos basePos = sectionPos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
DhLodPos basePos = this.sectionPos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
DhLodPos dataPos = pos.getCorner(SPARSE_UNIT_DETAIL);
|
||||
int coveredChunks = pos.getWidth(SPARSE_UNIT_DETAIL).numberOfLodSectionsWide;
|
||||
int sourceDataPerChunk = SPARSE_UNIT_SIZE >>> fullSource.getDataDetail();
|
||||
LodUtil.assertTrue(coveredChunks*sourceDataPerChunk == FullDataSource.SECTION_SIZE);
|
||||
int offsetX = dataPos.x-basePos.x;
|
||||
int offsetZ = dataPos.z-basePos.z;
|
||||
LodUtil.assertTrue(offsetX >=0 && offsetX < chunks && offsetZ >=0 && offsetZ < chunks);
|
||||
|
||||
for (int ox = 0; ox < coveredChunks; ox++) {
|
||||
for (int oz = 0; oz < coveredChunks; oz++) {
|
||||
FullArrayView sourceChunk = fullSource.subView(sourceDataPerChunk, ox*sourceDataPerChunk, oz*sourceDataPerChunk);
|
||||
FullArrayView buff = new FullArrayView(mapping, new long[dataPerChunk * dataPerChunk][], dataPerChunk);
|
||||
buff.downsampleFrom(sourceChunk);
|
||||
sparseData[(ox+offsetX)* chunks + (oz+offsetZ)] = buff;
|
||||
}
|
||||
}
|
||||
LodUtil.assertTrue(offsetX >=0 && offsetX < this.chunks && offsetZ >=0 && offsetZ < this.chunks);
|
||||
|
||||
for (int ox = 0; ox < coveredChunks; ox++)
|
||||
{
|
||||
for (int oz = 0; oz < coveredChunks; oz++)
|
||||
{
|
||||
FullArrayView sourceChunk = fullSource.subView(sourceDataPerChunk, ox * sourceDataPerChunk, oz * sourceDataPerChunk);
|
||||
FullArrayView buff = new FullArrayView(this.mapping, new long[this.dataPerChunk * this.dataPerChunk][], this.dataPerChunk);
|
||||
buff.downsampleFrom(sourceChunk);
|
||||
this.sparseData[(ox + offsetX) * this.chunks + (oz + offsetZ)] = buff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveData(IDhLevel level, DataMetaFile file, OutputStream dataStream) throws IOException {
|
||||
try (DataOutputStream dos = new DataOutputStream(dataStream)) {
|
||||
dos.writeShort(getDataDetail());
|
||||
public void saveData(IDhLevel level, DataMetaFile file, OutputStream dataStream) throws IOException
|
||||
{
|
||||
try (DataOutputStream dos = new DataOutputStream(dataStream))
|
||||
{
|
||||
dos.writeShort(this.getDataDetail());
|
||||
dos.writeShort(SPARSE_UNIT_DETAIL);
|
||||
dos.writeInt(SECTION_SIZE);
|
||||
dos.writeInt(level.getMinY());
|
||||
if (isEmpty) {
|
||||
if (this.isEmpty)
|
||||
{
|
||||
dos.writeInt(0x00000001);
|
||||
return;
|
||||
}
|
||||
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
// sparse array existence bitset
|
||||
BitSet set = new BitSet(sparseData.length);
|
||||
for (int i = 0; i < sparseData.length; i++) set.set(i, sparseData[i] != null);
|
||||
BitSet set = new BitSet(this.sparseData.length);
|
||||
for (int i = 0; i < this.sparseData.length; i++) set.set(i, this.sparseData[i] != null);
|
||||
byte[] bytes = set.toByteArray();
|
||||
dos.writeInt(bytes.length);
|
||||
dos.write(bytes);
|
||||
|
||||
// Data array content (only on non-empty stuff)
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
for (int i = set.nextSetBit(0); i >= 0; i = set.nextSetBit(i + 1)) {
|
||||
FullArrayView array = sparseData[i];
|
||||
for (int i = set.nextSetBit(0); i >= 0; i = set.nextSetBit(i + 1))
|
||||
{
|
||||
FullArrayView array = this.sparseData[i];
|
||||
LodUtil.assertTrue(array != null);
|
||||
for (int x = 0; x < array.width(); x++) {
|
||||
for (int z = 0; z < array.width(); z++) {
|
||||
dos.writeByte(array.get(x, z).getSingleLength());
|
||||
}
|
||||
}
|
||||
for (int x = 0; x < array.width(); x++) {
|
||||
for (int z = 0; z < array.width(); z++) {
|
||||
SingleFullArrayView column = array.get(x, z);
|
||||
LodUtil.assertTrue(column.getMapping() == mapping); //MUST be exact equal!
|
||||
if (!column.doesItExist()) continue;
|
||||
long[] raw = column.getRaw();
|
||||
for (long l : raw) {
|
||||
dos.writeLong(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int x = 0; x < array.width(); x++)
|
||||
{
|
||||
for (int z = 0; z < array.width(); z++)
|
||||
{
|
||||
dos.writeByte(array.get(x, z).getSingleLength());
|
||||
}
|
||||
}
|
||||
|
||||
for (int x = 0; x < array.width(); x++)
|
||||
{
|
||||
for (int z = 0; z < array.width(); z++)
|
||||
{
|
||||
SingleFullArrayView column = array.get(x, z);
|
||||
LodUtil.assertTrue(column.getMapping() == this.mapping); //MUST be exact equal!
|
||||
if (!column.doesItExist())
|
||||
continue;
|
||||
long[] raw = column.getRaw();
|
||||
for (long l : raw)
|
||||
{
|
||||
dos.writeLong(l);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Id mapping
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
mapping.serialize(dos);
|
||||
this.mapping.serialize(dos);
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
public static SparseDataSource loadData(DataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException {
|
||||
public static SparseDataSource loadData(DataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException
|
||||
{
|
||||
LodUtil.assertTrue(dataFile.pos.sectionDetail > SPARSE_UNIT_DETAIL);
|
||||
LodUtil.assertTrue(dataFile.pos.sectionDetail <= MAX_SECTION_DETAIL);
|
||||
|
||||
DataInputStream dos = new DataInputStream(dataStream); // DO NOT CLOSE! It would close all related streams
|
||||
{
|
||||
int dataDetail = dos.readShort();
|
||||
if(dataDetail != dataFile.metaData.dataLevel)
|
||||
throw new IOException(LodUtil.formatLog("Data level mismatch: {} != {}", dataDetail, dataFile.metaData.dataLevel));
|
||||
|
||||
int sparseDetail = dos.readShort();
|
||||
if (sparseDetail != SPARSE_UNIT_DETAIL)
|
||||
throw new IOException((LodUtil.formatLog("Unexpected sparse detail level: {} != {}",
|
||||
sparseDetail, SPARSE_UNIT_DETAIL)));
|
||||
{
|
||||
throw new IOException((LodUtil.formatLog("Unexpected sparse detail level: {} != {}",
|
||||
sparseDetail, SPARSE_UNIT_DETAIL)));
|
||||
}
|
||||
|
||||
int size = dos.readInt();
|
||||
if (size != SECTION_SIZE)
|
||||
throw new IOException(LodUtil.formatLog(
|
||||
"Section size mismatch: {} != {} (Currently only 1 section size is supported)", size, SECTION_SIZE));
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog(
|
||||
"Section size mismatch: {} != {} (Currently only 1 section size is supported)", size, SECTION_SIZE));
|
||||
}
|
||||
|
||||
int chunks = 1 << (byte) (dataFile.pos.sectionDetail - sparseDetail);
|
||||
int dataPerChunk = size / chunks;
|
||||
|
||||
|
||||
int minY = dos.readInt();
|
||||
if (minY != level.getMinY())
|
||||
LOGGER.warn("Data minY mismatch: {} != {}. Will ignore data's y level", minY, level.getMinY());
|
||||
int end = dos.readInt();
|
||||
// Data array length
|
||||
if (end == 0x00000001) {
|
||||
if (end == 0x00000001)
|
||||
{
|
||||
// Section is empty
|
||||
return createEmpty(dataFile.pos);
|
||||
}
|
||||
|
||||
// Non-empty section
|
||||
if (end != 0xFFFFFFFF) throw new IOException("invalid header end guard");
|
||||
if (end != 0xFFFFFFFF)
|
||||
throw new IOException("invalid header end guard");
|
||||
int length = dos.readInt();
|
||||
|
||||
if (length < 0 || length > (chunks*chunks/8+64)*2)
|
||||
|
||||
if (length < 0 || length > (chunks * chunks / 8 + 64) * 2)
|
||||
throw new IOException(LodUtil.formatLog("Sparse Flag BitSet size outside reasonable range: {} (expects {} to {})",
|
||||
length, 1, chunks*chunks/8+63));
|
||||
byte[] bytes = dos.readNBytes(length);
|
||||
@@ -264,70 +308,93 @@ public class SparseDataSource implements IIncompleteDataSource
|
||||
|
||||
// Data array content (only on non-empty columns)
|
||||
end = dos.readInt();
|
||||
if (end != 0xFFFFFFFF) throw new IOException("invalid data length end guard");
|
||||
for (int i = set.nextSetBit(0); i >= 0 && i < dataChunks.length; i = set.nextSetBit(i + 1)) {
|
||||
long[][] dataColumns = new long[dataPerChunk*dataPerChunk][];
|
||||
dataChunks[i] = dataColumns;
|
||||
for (int i2 = 0; i2 < dataColumns.length; i2++) {
|
||||
dataColumns[i2] = new long[dos.readByte()];
|
||||
}
|
||||
for (int k = 0; k < dataColumns.length; k++) {
|
||||
if (dataColumns[k].length == 0) continue;
|
||||
for (int o = 0; o < dataColumns[k].length; o++) {
|
||||
dataColumns[k][o] = dos.readLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
if (end != 0xFFFFFFFF)
|
||||
throw new IOException("invalid data length end guard");
|
||||
|
||||
for (int i = set.nextSetBit(0); i >= 0 && i < dataChunks.length; i = set.nextSetBit(i + 1))
|
||||
{
|
||||
long[][] dataColumns = new long[dataPerChunk * dataPerChunk][];
|
||||
dataChunks[i] = dataColumns;
|
||||
for (int i2 = 0; i2 < dataColumns.length; i2++)
|
||||
{
|
||||
dataColumns[i2] = new long[dos.readByte()];
|
||||
}
|
||||
for (int k = 0; k < dataColumns.length; k++)
|
||||
{
|
||||
if (dataColumns[k].length == 0)
|
||||
continue;
|
||||
for (int o = 0; o < dataColumns[k].length; o++)
|
||||
{
|
||||
dataColumns[k][o] = dos.readLong();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Id mapping
|
||||
end = dos.readInt();
|
||||
if (end != 0xFFFFFFFF) throw new IOException("invalid data content end guard");
|
||||
if (end != 0xFFFFFFFF)
|
||||
throw new IOException("invalid data content end guard");
|
||||
|
||||
FullDataPointIdMap mapping = FullDataPointIdMap.deserialize(dos);
|
||||
end = dos.readInt();
|
||||
if (end != 0xFFFFFFFF) throw new IOException("invalid id mapping end guard");
|
||||
if (end != 0xFFFFFFFF)
|
||||
throw new IOException("invalid id mapping end guard");
|
||||
|
||||
FullArrayView[] objectChunks = new FullArrayView[chunks*chunks];
|
||||
for (int i=0; i<dataChunks.length; i++) {
|
||||
if (dataChunks[i] == null) continue;
|
||||
objectChunks[i] = new FullArrayView(mapping, dataChunks[i], dataPerChunk);
|
||||
}
|
||||
for (int i = 0; i < dataChunks.length; i++)
|
||||
{
|
||||
if (dataChunks[i] == null)
|
||||
continue;
|
||||
objectChunks[i] = new FullArrayView(mapping, dataChunks[i], dataPerChunk);
|
||||
}
|
||||
|
||||
return new SparseDataSource(dataFile.pos, mapping, objectChunks);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyToFullDataSource(FullDataSource dataSource) {
|
||||
LodUtil.assertTrue(dataSource.getSectionPos().equals(sectionPos));
|
||||
LodUtil.assertTrue(dataSource.getDataDetail() == getDataDetail());
|
||||
for (int x = 0; x<chunks; x++) {
|
||||
for (int z = 0; z<chunks; z++) {
|
||||
FullArrayView array = sparseData[x*chunks+z];
|
||||
if (array == null) continue;
|
||||
// Otherwise, apply data to dataSource
|
||||
dataSource.markNotEmpty();
|
||||
FullArrayView view = dataSource.subView(dataPerChunk, x*dataPerChunk, z*dataPerChunk);
|
||||
array.shadowCopyTo(view);
|
||||
}
|
||||
}
|
||||
private void applyToFullDataSource(FullDataSource dataSource)
|
||||
{
|
||||
LodUtil.assertTrue(dataSource.getSectionPos().equals(this.sectionPos));
|
||||
LodUtil.assertTrue(dataSource.getDataDetail() == this.getDataDetail());
|
||||
for (int x = 0; x < this.chunks; x++)
|
||||
{
|
||||
for (int z = 0; z < this.chunks; z++)
|
||||
{
|
||||
FullArrayView array = this.sparseData[x * this.chunks + z];
|
||||
if (array == null)
|
||||
continue;
|
||||
|
||||
// Otherwise, apply data to dataSource
|
||||
dataSource.markNotEmpty();
|
||||
FullArrayView view = dataSource.subView(this.dataPerChunk, x * this.dataPerChunk, z * this.dataPerChunk);
|
||||
array.shadowCopyTo(view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ILodDataSource trySelfPromote() {
|
||||
if (isEmpty) return this;
|
||||
for (FullArrayView array : sparseData) {
|
||||
public ILodDataSource trySelfPromote()
|
||||
{
|
||||
if (this.isEmpty)
|
||||
return this;
|
||||
for (FullArrayView array : this.sparseData)
|
||||
{
|
||||
if (array == null) return this;
|
||||
}
|
||||
FullDataSource newSource = FullDataSource.createEmpty(sectionPos);
|
||||
applyToFullDataSource(newSource);
|
||||
FullDataSource newSource = FullDataSource.createEmpty(this.sectionPos);
|
||||
this.applyToFullDataSource(newSource);
|
||||
return newSource;
|
||||
}
|
||||
|
||||
// Return null if doesn't exist
|
||||
public SingleFullArrayView tryGet(int x, int z) {
|
||||
public SingleFullArrayView tryGet(int x, int z)
|
||||
{
|
||||
LodUtil.assertTrue(x>=0 && x<SECTION_SIZE && z>=0 && z<SECTION_SIZE);
|
||||
int chunkX = x / dataPerChunk;
|
||||
int chunkZ = z / dataPerChunk;
|
||||
FullArrayView chunk = sparseData[chunkX * chunks + chunkZ];
|
||||
if (chunk == null) return null;
|
||||
return chunk.get(x % dataPerChunk, z % dataPerChunk);
|
||||
int chunkX = x / this.dataPerChunk;
|
||||
int chunkZ = z / this.dataPerChunk;
|
||||
FullArrayView chunk = this.sparseData[chunkX * this.chunks + chunkZ];
|
||||
if (chunk == null)
|
||||
return null;
|
||||
return chunk.get(x % this.dataPerChunk, z % this.dataPerChunk);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -16,7 +16,11 @@ import org.apache.logging.log4j.Logger;
|
||||
import java.io.*;
|
||||
import java.util.BitSet;
|
||||
|
||||
public class SpottyDataSource extends FullArrayView implements IIncompleteDataSource { // 1 chunk
|
||||
/**
|
||||
* 1 chunk
|
||||
*/
|
||||
public class SpottyDataSource extends FullArrayView implements IIncompleteDataSource
|
||||
{
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
public static final byte SECTION_SIZE_OFFSET = 6;
|
||||
public static final int SECTION_SIZE = 1 << SECTION_SIZE_OFFSET;
|
||||
@@ -26,43 +30,43 @@ public class SpottyDataSource extends FullArrayView implements IIncompleteDataSo
|
||||
private boolean isEmpty = true;
|
||||
private final BitSet isColumnNotEmpty;
|
||||
|
||||
protected SpottyDataSource(DhSectionPos sectionPos) {
|
||||
protected SpottyDataSource(DhSectionPos sectionPos)
|
||||
{
|
||||
super(new FullDataPointIdMap(), new long[SECTION_SIZE*SECTION_SIZE][0], SECTION_SIZE);
|
||||
LodUtil.assertTrue(sectionPos.sectionDetail > SparseDataSource.MAX_SECTION_DETAIL);
|
||||
this.sectionPos = sectionPos;
|
||||
isColumnNotEmpty = new BitSet(SECTION_SIZE*SECTION_SIZE);
|
||||
this.isColumnNotEmpty = new BitSet(SECTION_SIZE*SECTION_SIZE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhSectionPos getSectionPos() {
|
||||
return sectionPos;
|
||||
}
|
||||
public DhSectionPos getSectionPos() { return this.sectionPos; }
|
||||
@Override
|
||||
public byte getDataDetail() {
|
||||
return (byte) (sectionPos.sectionDetail-SECTION_SIZE_OFFSET);
|
||||
}
|
||||
public byte getDataDetail() { return (byte) (this.sectionPos.sectionDetail-SECTION_SIZE_OFFSET); }
|
||||
|
||||
@Override
|
||||
public byte getDataVersion() {
|
||||
return LATEST_VERSION;
|
||||
}
|
||||
public byte getDataVersion() { return LATEST_VERSION; }
|
||||
|
||||
@Override
|
||||
public void update(ChunkSizedData data) {
|
||||
LodUtil.assertTrue(sectionPos.getSectionBBoxPos().overlaps(data.getBBoxLodPos()));
|
||||
public void update(ChunkSizedData data)
|
||||
{
|
||||
LodUtil.assertTrue(this.sectionPos.getSectionBBoxPos().overlaps(data.getBBoxLodPos()));
|
||||
|
||||
if (data.dataDetail == 0 && getDataDetail() >= 4) {
|
||||
if (data.dataDetail == 0 && this.getDataDetail() >= 4)
|
||||
{
|
||||
//FIXME: TEMPORARY
|
||||
int chunkPerFull = 1 << (getDataDetail() - 4);
|
||||
if (data.x % chunkPerFull != 0 || data.z % chunkPerFull != 0) return;
|
||||
DhLodPos baseOffset = sectionPos.getCorner(getDataDetail());
|
||||
DhLodPos dataOffset = data.getBBoxLodPos().convertUpwardsTo(getDataDetail());
|
||||
int chunkPerFull = 1 << (this.getDataDetail() - 4);
|
||||
if (data.x % chunkPerFull != 0 || data.z % chunkPerFull != 0)
|
||||
return;
|
||||
DhLodPos baseOffset = this.sectionPos.getCorner(this.getDataDetail());
|
||||
DhLodPos dataOffset = data.getBBoxLodPos().convertUpwardsTo(this.getDataDetail());
|
||||
int offsetX = dataOffset.x - baseOffset.x;
|
||||
int offsetZ = dataOffset.z - baseOffset.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < SECTION_SIZE && offsetZ >= 0 && offsetZ < SECTION_SIZE);
|
||||
isEmpty = false;
|
||||
data.get(0,0).deepCopyTo(get(offsetX, offsetZ));
|
||||
} else {
|
||||
this.isEmpty = false;
|
||||
data.get(0,0).deepCopyTo(this.get(offsetX, offsetZ));
|
||||
}
|
||||
else
|
||||
{
|
||||
LodUtil.assertNotReach();
|
||||
//TODO;
|
||||
}
|
||||
@@ -70,89 +74,99 @@ public class SpottyDataSource extends FullArrayView implements IIncompleteDataSo
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return isEmpty;
|
||||
}
|
||||
public boolean isEmpty() { return this.isEmpty; }
|
||||
|
||||
public void markNotEmpty() {
|
||||
isEmpty = false;
|
||||
}
|
||||
public void markNotEmpty() { this.isEmpty = false; }
|
||||
|
||||
@Override
|
||||
public void saveData(IDhLevel level, DataMetaFile file, OutputStream dataStream) throws IOException {
|
||||
public void saveData(IDhLevel level, DataMetaFile file, OutputStream dataStream) throws IOException
|
||||
{
|
||||
DataOutputStream dos = new DataOutputStream(dataStream); // DO NOT CLOSE
|
||||
{
|
||||
dos.writeInt(getDataDetail());
|
||||
dos.writeInt(size);
|
||||
dos.writeInt(this.getDataDetail());
|
||||
dos.writeInt(this.size);
|
||||
dos.writeInt(level.getMinY());
|
||||
if (isEmpty) {
|
||||
if (this.isEmpty)
|
||||
{
|
||||
dos.writeInt(0x00000001);
|
||||
return;
|
||||
}
|
||||
|
||||
// Is column not empty
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
byte[] bytes = isColumnNotEmpty.toByteArray();
|
||||
byte[] bytes = this.isColumnNotEmpty.toByteArray();
|
||||
dos.writeInt(bytes.length);
|
||||
dos.write(bytes);
|
||||
|
||||
// Data array content
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
for (int i = isColumnNotEmpty.nextSetBit(0); i >= 0; i = isColumnNotEmpty.nextSetBit(i + 1))
|
||||
for (int i = this.isColumnNotEmpty.nextSetBit(0); i >= 0; i = this.isColumnNotEmpty.nextSetBit(i + 1))
|
||||
{
|
||||
dos.writeByte(dataArrays[i].length);
|
||||
if (dataArrays[i].length == 0) continue;
|
||||
for (long l : dataArrays[i]) {
|
||||
dos.writeByte(this.dataArrays[i].length);
|
||||
if (this.dataArrays[i].length == 0)
|
||||
continue;
|
||||
for (long l : this.dataArrays[i]) {
|
||||
dos.writeLong(l);
|
||||
}
|
||||
}
|
||||
|
||||
// Id mapping
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
mapping.serialize(dos);
|
||||
this.mapping.serialize(dos);
|
||||
dos.writeInt(0xFFFFFFFF);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static SpottyDataSource loadData(DataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException {
|
||||
public static SpottyDataSource loadData(DataMetaFile dataFile, InputStream dataStream, IDhLevel level) throws IOException
|
||||
{
|
||||
DataInputStream dos = new DataInputStream(dataStream); // DO NOT CLOSE
|
||||
{
|
||||
int dataDetail = dos.readInt();
|
||||
if(dataDetail != dataFile.metaData.dataLevel)
|
||||
throw new IOException(LodUtil.formatLog("Data level mismatch: {} != {}", dataDetail, dataFile.metaData.dataLevel));
|
||||
|
||||
int size = dos.readInt();
|
||||
if (size != SECTION_SIZE)
|
||||
throw new IOException(LodUtil.formatLog(
|
||||
"Section size mismatch: {} != {} (Currently only 1 section size is supported)", size, SECTION_SIZE));
|
||||
|
||||
int minY = dos.readInt();
|
||||
if (minY != level.getMinY())
|
||||
LOGGER.warn("Data minY mismatch: {} != {}. Will ignore data's y level", minY, level.getMinY());
|
||||
|
||||
int end = dos.readInt();
|
||||
// Data array length
|
||||
if (end == 0x00000001) {
|
||||
if (end == 0x00000001)
|
||||
{
|
||||
// Section is empty
|
||||
return new SpottyDataSource(dataFile.pos);
|
||||
}
|
||||
|
||||
// Is column not empty
|
||||
if (end != 0xFFFFFFFF) throw new IOException("invalid header end guard");
|
||||
if (end != 0xFFFFFFFF)
|
||||
throw new IOException("invalid header end guard");
|
||||
int length = dos.readInt();
|
||||
|
||||
if (length < 0 || length > (SECTION_SIZE*SECTION_SIZE/8+64)*2)
|
||||
throw new IOException(LodUtil.formatLog("Spotty Flag BitSet size outside reasonable range: {} (expects {} to {})",
|
||||
length, 1, SECTION_SIZE*SECTION_SIZE/8+63));
|
||||
{
|
||||
throw new IOException(LodUtil.formatLog("Spotty Flag BitSet size outside reasonable range: {} (expects {} to {})",
|
||||
length, 1, SECTION_SIZE * SECTION_SIZE / 8 + 63));
|
||||
}
|
||||
byte[] bytes = dos.readNBytes(length);
|
||||
BitSet isColumnNotEmpty = BitSet.valueOf(bytes);
|
||||
|
||||
// Data array content
|
||||
long[][] data = new long[SECTION_SIZE*SECTION_SIZE][];
|
||||
end = dos.readInt();
|
||||
if (end != 0xFFFFFFFF) throw new IOException("invalid spotty flag end guard");
|
||||
if (end != 0xFFFFFFFF)
|
||||
throw new IOException("invalid spotty flag end guard");
|
||||
|
||||
for (int i = isColumnNotEmpty.nextSetBit(0); i >= 0; i = isColumnNotEmpty.nextSetBit(i + 1))
|
||||
{
|
||||
long[] array = new long[dos.readByte()];
|
||||
for (int j = 0; j < array.length; j++) {
|
||||
for (int j = 0; j < array.length; j++)
|
||||
{
|
||||
array[j] = dos.readLong();
|
||||
}
|
||||
data[i] = array;
|
||||
@@ -160,128 +174,158 @@ public class SpottyDataSource extends FullArrayView implements IIncompleteDataSo
|
||||
|
||||
// Id mapping
|
||||
end = dos.readInt();
|
||||
if (end != 0xFFFFFFFF) throw new IOException("invalid data content end guard");
|
||||
if (end != 0xFFFFFFFF)
|
||||
throw new IOException("invalid data content end guard");
|
||||
|
||||
FullDataPointIdMap mapping = FullDataPointIdMap.deserialize(new UnclosableInputStream(dos));
|
||||
end = dos.readInt();
|
||||
if (end != 0xFFFFFFFF) throw new IOException("invalid id mapping end guard");
|
||||
if (end != 0xFFFFFFFF)
|
||||
throw new IOException("invalid id mapping end guard");
|
||||
|
||||
return new SpottyDataSource(dataFile.pos, mapping, isColumnNotEmpty, data);
|
||||
}
|
||||
}
|
||||
|
||||
private SpottyDataSource(DhSectionPos pos, FullDataPointIdMap mapping, BitSet isColumnNotEmpty, long[][] data) {
|
||||
private SpottyDataSource(DhSectionPos pos, FullDataPointIdMap mapping, BitSet isColumnNotEmpty, long[][] data)
|
||||
{
|
||||
super(mapping, data, SECTION_SIZE);
|
||||
LodUtil.assertTrue(data.length == SECTION_SIZE*SECTION_SIZE);
|
||||
this.sectionPos = pos;
|
||||
this.isColumnNotEmpty = isColumnNotEmpty;
|
||||
isEmpty = false;
|
||||
this.isEmpty = false;
|
||||
}
|
||||
|
||||
public static SpottyDataSource createEmpty(DhSectionPos pos) {
|
||||
return new SpottyDataSource(pos);
|
||||
}
|
||||
public static SpottyDataSource createEmpty(DhSectionPos pos) { return new SpottyDataSource(pos); }
|
||||
|
||||
public static boolean neededForPosition(DhSectionPos posToWrite, DhSectionPos posToTest) {
|
||||
if (!posToWrite.overlaps(posToTest)) return false;
|
||||
if (posToTest.sectionDetail > posToWrite.sectionDetail) return false;
|
||||
if (posToWrite.sectionDetail - posToTest.sectionDetail <= SECTION_SIZE_OFFSET) return true;
|
||||
public static boolean neededForPosition(DhSectionPos posToWrite, DhSectionPos posToTest)
|
||||
{
|
||||
if (!posToWrite.overlaps(posToTest))
|
||||
return false;
|
||||
if (posToTest.sectionDetail > posToWrite.sectionDetail)
|
||||
return false;
|
||||
if (posToWrite.sectionDetail - posToTest.sectionDetail <= SECTION_SIZE_OFFSET)
|
||||
return true;
|
||||
byte sectPerData = (byte) (1 << (posToWrite.sectionDetail - posToTest.sectionDetail - SECTION_SIZE_OFFSET));
|
||||
return posToTest.sectionX % sectPerData == 0 && posToTest.sectionZ % sectPerData == 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sampleFrom(ILodDataSource source) {
|
||||
public void sampleFrom(ILodDataSource source)
|
||||
{
|
||||
DhSectionPos pos = source.getSectionPos();
|
||||
LodUtil.assertTrue(pos.sectionDetail < sectionPos.sectionDetail);
|
||||
LodUtil.assertTrue(pos.overlaps(sectionPos));
|
||||
if (source.isEmpty()) return;
|
||||
if (source instanceof SparseDataSource) {
|
||||
sampleFrom((SparseDataSource) source);
|
||||
} else if (source instanceof FullDataSource) {
|
||||
sampleFrom((FullDataSource) source);
|
||||
} else {
|
||||
LodUtil.assertTrue(pos.sectionDetail < this.sectionPos.sectionDetail);
|
||||
LodUtil.assertTrue(pos.overlaps(this.sectionPos));
|
||||
if (source.isEmpty())
|
||||
return;
|
||||
|
||||
if (source instanceof SparseDataSource)
|
||||
{
|
||||
this.sampleFrom((SparseDataSource) source);
|
||||
}
|
||||
else if (source instanceof FullDataSource)
|
||||
{
|
||||
this.sampleFrom((FullDataSource) source);
|
||||
}
|
||||
else
|
||||
{
|
||||
LodUtil.assertNotReach();
|
||||
}
|
||||
}
|
||||
|
||||
private void sampleFrom(SparseDataSource sparseSource) {
|
||||
private void sampleFrom(SparseDataSource sparseSource)
|
||||
{
|
||||
DhSectionPos pos = sparseSource.getSectionPos();
|
||||
isEmpty = false;
|
||||
this.isEmpty = false;
|
||||
|
||||
if (getDataDetail() > sectionPos.sectionDetail) {
|
||||
DhLodPos basePos = sectionPos.getCorner(getDataDetail());
|
||||
DhLodPos dataPos = pos.getCorner(getDataDetail());
|
||||
if (this.getDataDetail() > this.sectionPos.sectionDetail)
|
||||
{
|
||||
DhLodPos basePos = this.sectionPos.getCorner(this.getDataDetail());
|
||||
DhLodPos dataPos = pos.getCorner(this.getDataDetail());
|
||||
int offsetX = dataPos.x - basePos.x;
|
||||
int offsetZ = dataPos.z - basePos.z;
|
||||
LodUtil.assertTrue(offsetX >= 0 && offsetX < SECTION_SIZE && offsetZ >= 0 && offsetZ < SECTION_SIZE);
|
||||
int chunksPerData = 1 << (getDataDetail() - SparseDataSource.SPARSE_UNIT_DETAIL);
|
||||
int dataSpan = sectionPos.getWidth(getDataDetail()).numberOfLodSectionsWide;
|
||||
int chunksPerData = 1 << (this.getDataDetail() - SparseDataSource.SPARSE_UNIT_DETAIL);
|
||||
int dataSpan = this.sectionPos.getWidth(this.getDataDetail()).numberOfLodSectionsWide;
|
||||
|
||||
for (int ox = 0; ox < dataSpan; ox++) {
|
||||
for (int oz = 0; oz < dataSpan; oz++) {
|
||||
for (int ox = 0; ox < dataSpan; ox++)
|
||||
{
|
||||
for (int oz = 0; oz < dataSpan; oz++)
|
||||
{
|
||||
SingleFullArrayView column = sparseSource.tryGet(
|
||||
ox * chunksPerData * sparseSource.dataPerChunk,
|
||||
oz * chunksPerData * sparseSource.dataPerChunk);
|
||||
if (column != null) {
|
||||
column.deepCopyTo(get(offsetX + ox, offsetZ + oz));
|
||||
isColumnNotEmpty.set((offsetX + ox) * SECTION_SIZE + offsetZ + oz, true);
|
||||
if (column != null)
|
||||
{
|
||||
column.deepCopyTo(this.get(offsetX + ox, offsetZ + oz));
|
||||
this.isColumnNotEmpty.set((offsetX + ox) * SECTION_SIZE + offsetZ + oz, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
DhLodPos dataPos = pos.getSectionBBoxPos();
|
||||
int lowerSectionsPerData = sectionPos.getWidth(dataPos.detailLevel).numberOfLodSectionsWide;
|
||||
int lowerSectionsPerData = this.sectionPos.getWidth(dataPos.detailLevel).numberOfLodSectionsWide;
|
||||
if (dataPos.x % lowerSectionsPerData != 0 || dataPos.z % lowerSectionsPerData != 0) return;
|
||||
|
||||
DhLodPos basePos = sectionPos.getCorner(getDataDetail());
|
||||
dataPos = dataPos.convertUpwardsTo(getDataDetail());
|
||||
DhLodPos basePos = this.sectionPos.getCorner(this.getDataDetail());
|
||||
dataPos = dataPos.convertUpwardsTo(this.getDataDetail());
|
||||
int offsetX = dataPos.x - basePos.x;
|
||||
int offsetZ = dataPos.z - basePos.z;
|
||||
SingleFullArrayView column = sparseSource.tryGet(0, 0);
|
||||
if (column != null) {
|
||||
column.deepCopyTo(get(offsetX, offsetZ));
|
||||
isColumnNotEmpty.set(offsetX * SECTION_SIZE + offsetZ, true);
|
||||
column.deepCopyTo(this.get(offsetX, offsetZ));
|
||||
this.isColumnNotEmpty.set(offsetX * SECTION_SIZE + offsetZ, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void sampleFrom(FullDataSource fullSource) {
|
||||
private void sampleFrom(FullDataSource fullSource)
|
||||
{
|
||||
DhSectionPos pos = fullSource.getSectionPos();
|
||||
isEmpty = false;
|
||||
downsampleFrom(fullSource);
|
||||
|
||||
if (getDataDetail() > sectionPos.sectionDetail) {
|
||||
DhLodPos basePos = sectionPos.getCorner(getDataDetail());
|
||||
DhLodPos dataPos = pos.getCorner(getDataDetail());
|
||||
int offsetX = dataPos.x - basePos.x;
|
||||
int offsetZ = dataPos.z - basePos.z;
|
||||
int dataSpan = sectionPos.getWidth(getDataDetail()).numberOfLodSectionsWide;
|
||||
for (int ox = 0; ox < dataSpan; ox++) {
|
||||
for (int oz = 0; oz < dataSpan; oz++) {
|
||||
isColumnNotEmpty.set((offsetX + ox) * SECTION_SIZE + offsetZ + oz, true);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
this.isEmpty = false;
|
||||
this.downsampleFrom(fullSource);
|
||||
|
||||
if (this.getDataDetail() > this.sectionPos.sectionDetail)
|
||||
{
|
||||
DhLodPos basePos = this.sectionPos.getCorner(this.getDataDetail());
|
||||
DhLodPos dataPos = pos.getCorner(this.getDataDetail());
|
||||
int offsetX = dataPos.x - basePos.x;
|
||||
int offsetZ = dataPos.z - basePos.z;
|
||||
int dataSpan = this.sectionPos.getWidth(this.getDataDetail()).numberOfLodSectionsWide;
|
||||
for (int ox = 0; ox < dataSpan; ox++)
|
||||
{
|
||||
for (int oz = 0; oz < dataSpan; oz++)
|
||||
{
|
||||
this.isColumnNotEmpty.set((offsetX + ox) * SECTION_SIZE + offsetZ + oz, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DhLodPos dataPos = pos.getSectionBBoxPos();
|
||||
int lowerSectionsPerData = sectionPos.getWidth(dataPos.detailLevel).numberOfLodSectionsWide;
|
||||
int lowerSectionsPerData = this.sectionPos.getWidth(dataPos.detailLevel).numberOfLodSectionsWide;
|
||||
if (dataPos.x % lowerSectionsPerData != 0 || dataPos.z % lowerSectionsPerData != 0) return;
|
||||
DhLodPos basePos = sectionPos.getCorner(getDataDetail());
|
||||
dataPos = dataPos.convertUpwardsTo(getDataDetail());
|
||||
DhLodPos basePos = this.sectionPos.getCorner(this.getDataDetail());
|
||||
dataPos = dataPos.convertUpwardsTo(this.getDataDetail());
|
||||
int offsetX = dataPos.x - basePos.x;
|
||||
int offsetZ = dataPos.z - basePos.z;
|
||||
isColumnNotEmpty.set(offsetX * SECTION_SIZE + offsetZ, true);
|
||||
this.isColumnNotEmpty.set(offsetX * SECTION_SIZE + offsetZ, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILodDataSource trySelfPromote() {
|
||||
if (isEmpty) return this;
|
||||
if (isColumnNotEmpty.cardinality() != SECTION_SIZE * SECTION_SIZE) return this;
|
||||
return new FullDataSource(sectionPos, mapping, dataArrays);
|
||||
public ILodDataSource trySelfPromote()
|
||||
{
|
||||
if (this.isEmpty)
|
||||
return this;
|
||||
if (this.isColumnNotEmpty.cardinality() != SECTION_SIZE * SECTION_SIZE)
|
||||
return this;
|
||||
return new FullDataSource(this.sectionPos, this.mapping, this.dataArrays);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SingleFullArrayView tryGet(int x, int z) {
|
||||
return isColumnNotEmpty.get(x * SECTION_SIZE + z) ? get(x, z) : null;
|
||||
}
|
||||
public SingleFullArrayView tryGet(int x, int z) { return this.isColumnNotEmpty.get(x * SECTION_SIZE + z) ? this.get(x, z) : null; }
|
||||
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public class FullArrayView implements IFullDataView
|
||||
this.size = size;
|
||||
this.dataSize = size;
|
||||
this.mapping = mapping;
|
||||
offset = 0;
|
||||
this.offset = 0;
|
||||
}
|
||||
|
||||
public FullArrayView(FullArrayView source, int size, int offsetX, int offsetZ)
|
||||
@@ -29,66 +29,51 @@ public class FullArrayView implements IFullDataView
|
||||
if (source.size < size || source.size < size + offsetX || source.size < size + offsetZ)
|
||||
throw new IllegalArgumentException(
|
||||
"tried constructing dataArrayView subview with invalid input!");
|
||||
dataArrays = source.dataArrays;
|
||||
this.dataArrays = source.dataArrays;
|
||||
this.size = size;
|
||||
this.dataSize = source.dataSize;
|
||||
mapping = source.mapping;
|
||||
offset = source.offset + offsetX * dataSize + offsetZ;
|
||||
this.mapping = source.mapping;
|
||||
this.offset = source.offset + offsetX * this.dataSize + offsetZ;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FullDataPointIdMap getMapping()
|
||||
{
|
||||
return mapping;
|
||||
}
|
||||
public FullDataPointIdMap getMapping() { return this.mapping; }
|
||||
|
||||
@Override
|
||||
public SingleFullArrayView get(int index)
|
||||
{
|
||||
return get(index / size, index % size);
|
||||
}
|
||||
public SingleFullArrayView get(int index) { return this.get(index / this.size, index % this.size); }
|
||||
|
||||
@Override
|
||||
public SingleFullArrayView get(int x, int z)
|
||||
{
|
||||
return new SingleFullArrayView(mapping, dataArrays, x * size + z + offset);
|
||||
}
|
||||
public SingleFullArrayView get(int x, int z) { return new SingleFullArrayView(this.mapping, this.dataArrays, x * this.size + z + this.offset); }
|
||||
|
||||
@Override
|
||||
public int width()
|
||||
{
|
||||
return size;
|
||||
}
|
||||
public int width() { return this.size; }
|
||||
|
||||
@Override
|
||||
public FullArrayView subView(int size, int ox, int oz)
|
||||
{
|
||||
return new FullArrayView(this, size, ox, oz);
|
||||
}
|
||||
public FullArrayView subView(int size, int ox, int oz) { return new FullArrayView(this, size, ox, oz); }
|
||||
|
||||
/** WARNING: This will potentially share the underlying array object! */
|
||||
public void shadowCopyTo(FullArrayView target)
|
||||
{
|
||||
if (target.size != size)
|
||||
if (target.size != this.size)
|
||||
{
|
||||
throw new IllegalArgumentException("Target view must have same size as this view");
|
||||
}
|
||||
if (target.mapping.equals(mapping))
|
||||
if (target.mapping.equals(this.mapping))
|
||||
{
|
||||
for (int x = 0; x < size; x++)
|
||||
for (int x = 0; x < this.size; x++)
|
||||
{
|
||||
System.arraycopy(dataArrays, offset + x * dataSize,
|
||||
target.dataArrays, target.offset + x * target.dataSize, size);
|
||||
System.arraycopy(this.dataArrays, this.offset + x * this.dataSize,
|
||||
target.dataArrays, target.offset + x * target.dataSize, this.size);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
int[] remappedIds = target.mapping.mergeAndReturnRemappedEntityIds(mapping);
|
||||
for (int x = 0; x < size; x++)
|
||||
int[] remappedIds = target.mapping.mergeAndReturnRemappedEntityIds(this.mapping);
|
||||
for (int x = 0; x < this.size; x++)
|
||||
{
|
||||
for (int o = 0; o < size; o++)
|
||||
for (int o = 0; o < this.size; o++)
|
||||
{
|
||||
long[] sourceData = dataArrays[offset + x * dataSize + o];
|
||||
long[] sourceData = this.dataArrays[this.offset + x * this.dataSize + o];
|
||||
long[] newData = new long[sourceData.length];
|
||||
for (int i = 0; i < newData.length; i++)
|
||||
{
|
||||
@@ -102,15 +87,16 @@ public class FullArrayView implements IFullDataView
|
||||
|
||||
public void downsampleFrom(FullArrayView source)
|
||||
{
|
||||
LodUtil.assertTrue(source.size > size && source.size % size == 0);
|
||||
int dataPerUnit = source.size / size;
|
||||
for (int ox = 0; ox < size; ox++)
|
||||
LodUtil.assertTrue(source.size > this.size && source.size % this.size == 0);
|
||||
int dataPerUnit = source.size / this.size;
|
||||
for (int ox = 0; ox < this.size; ox++)
|
||||
{
|
||||
for (int oz = 0; oz < size; oz++)
|
||||
for (int oz = 0; oz < this.size; oz++)
|
||||
{
|
||||
SingleFullArrayView column = get(ox, oz);
|
||||
SingleFullArrayView column = this.get(ox, oz);
|
||||
column.downsampleFrom(source.subView(dataPerUnit, ox * dataPerUnit, oz * dataPerUnit));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -213,7 +213,10 @@ public class DataFileHandler implements IDataSourceProvider {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/**
|
||||
* Returns the 64 x 64 data source for the given section position. <Br>
|
||||
* If the section hasn't been generated this will also send a generation call, which may take a while. <Br> <Br>
|
||||
*
|
||||
* This call is concurrent. I.e. it supports multiple threads calling this method at the same time.
|
||||
*/
|
||||
@Override
|
||||
|
||||
@@ -112,8 +112,8 @@ public class DhLodPos implements Comparable<DhLodPos>
|
||||
throw new IllegalArgumentException("add called with width.detailLevel < pos detail");
|
||||
|
||||
return new DhLodPos(this.detailLevel,
|
||||
x + width.createFromDetailLevel(this.detailLevel).numberOfLodSectionsWide,
|
||||
z + width.createFromDetailLevel(this.detailLevel).numberOfLodSectionsWide);
|
||||
this.x + width.createFromDetailLevel(this.detailLevel).numberOfLodSectionsWide,
|
||||
this.z + width.createFromDetailLevel(this.detailLevel).numberOfLodSectionsWide);
|
||||
}
|
||||
|
||||
/** Equivalent to adding a DhLodUnit with the same detail level as this DhLodPos */
|
||||
@@ -140,7 +140,7 @@ public class DhLodPos implements Comparable<DhLodPos>
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() { return Objects.hash(detailLevel, x, z); }
|
||||
public int hashCode() { return Objects.hash(this.detailLevel, this.x, this.z); }
|
||||
|
||||
@Override
|
||||
public int compareTo(@NotNull DhLodPos obj)
|
||||
|
||||
Reference in New Issue
Block a user