Refactor DataSources

This commit is contained in:
James Seibel
2022-11-12 11:05:59 -06:00
parent 244a1c273c
commit 904288e6ec
7 changed files with 487 additions and 387 deletions
@@ -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)