basic reformatting

This commit is contained in:
James Seibel
2022-09-30 20:20:22 -05:00
parent 8b1cb258b4
commit a082f2ae86
8 changed files with 639 additions and 530 deletions
@@ -20,6 +20,6 @@ public interface ILodDataSource
boolean isEmpty();
// Saving related
void saveData(IDhLevel level, DataMetaFile file, OutputStream dataStream) throws IOException;
}
@@ -3,34 +3,38 @@ package com.seibel.lod.core.datatype.full;
import com.seibel.lod.core.datatype.full.accessor.FullArrayView;
import com.seibel.lod.core.pos.DhLodPos;
public class ChunkSizedData extends FullArrayView {
public final byte dataDetail;
public final int x;
public final int z;
public ChunkSizedData(byte dataDetail, int x, int z) {
super(new IdBiomeBlockStateMap(), new long[16*16][0], 16);
this.dataDetail = dataDetail;
this.x = x;
this.z = z;
}
public void setSingleColumn(long[] data, int x, int z) {
dataArrays[x*16+z] = data;
}
public long nonEmptyCount() {
long count = 0;
for (long[] data : dataArrays) {
if (data.length != 0)
count += 1;
}
return count;
}
public long emptyCount() {
return 16*16 - nonEmptyCount();
}
public DhLodPos getBBoxLodPos() {
return new DhLodPos((byte) (dataDetail+4), x, z);
}
public class ChunkSizedData extends FullArrayView
{
public final byte dataDetail;
public final int x;
public final int z;
public ChunkSizedData(byte dataDetail, int x, int z)
{
super(new IdBiomeBlockStateMap(), new long[16 * 16][0], 16);
this.dataDetail = dataDetail;
this.x = x;
this.z = z;
}
public void setSingleColumn(long[] data, int x, int z)
{
dataArrays[x * 16 + z] = data;
}
public long nonEmptyCount()
{
long count = 0;
for (long[] data : dataArrays)
{
if (data.length != 0)
count += 1;
}
return count;
}
public long emptyCount() { return 16 * 16 - nonEmptyCount(); }
public DhLodPos getBBoxLodPos() { return new DhLodPos((byte) (dataDetail + 4), x, z); }
}
@@ -10,13 +10,16 @@ import java.io.InputStream;
public class FullDataLoader extends AbstractDataSourceLoader
{
public FullDataLoader() {
public FullDataLoader()
{
super(FullDataSource.class, FullDataSource.TYPE_ID, new byte[]{FullDataSource.LATEST_VERSION});
}
@Override
public ILodDataSource loadData(DataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException {
public ILodDataSource loadData(DataMetaFile dataFile, InputStream data, IDhLevel level) throws IOException
{
//TODO: Add decompressor here
return FullDataSource.loadData(dataFile, data, level);
}
}
@@ -17,223 +17,263 @@ import java.io.*;
public class FullDataSource extends FullArrayView implements ILodDataSource
{ // 1 chunk
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;
public static final byte LATEST_VERSION = 0;
public static final long TYPE_ID = "FullDataSource".hashCode();
private final DhSectionPos sectionPos;
private boolean isEmpty = true;
protected FullDataSource(DhSectionPos sectionPos) {
protected FullDataSource(DhSectionPos sectionPos)
{
super(new IdBiomeBlockStateMap(), new long[SECTION_SIZE*SECTION_SIZE][0], SECTION_SIZE);
this.sectionPos = sectionPos;
}
@Override
public DhSectionPos getSectionPos() { return sectionPos; }
@Override
public byte getDataDetail() { return (byte) (sectionPos.sectionDetail-SECTION_SIZE_OFFSET); }
@Override
public DhSectionPos getSectionPos() {
return sectionPos;
}
@Override
public byte getDataDetail() {
return (byte) (sectionPos.sectionDetail-SECTION_SIZE_OFFSET);
}
public byte getDataVersion() { return LATEST_VERSION; }
@Override
public void update(ChunkSizedData data)
{
LodUtil.assertTrue(sectionPos.getSectionBBoxPos().overlaps(data.getBBoxLodPos()));
if (data.dataDetail == 0 && getDataDetail() == 0)
{
DhBlockPos2D chunkBlockPos = new DhBlockPos2D(data.x * 16, data.z * 16);
DhBlockPos2D blockOffset = chunkBlockPos.subtract(sectionPos.getCorner().getCorner());
LodUtil.assertTrue(blockOffset.x >= 0 && blockOffset.x < SECTION_SIZE && blockOffset.z >= 0 && blockOffset.z < SECTION_SIZE);
isEmpty = false;
data.shadowCopyTo(this.subView(16, blockOffset.x, blockOffset.z));
{ // DEBUG ASSERTION
for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
{
SingleFullArrayView column = this.get(x + blockOffset.x, z + blockOffset.z);
LodUtil.assertTrue(column.doesItExist());
}
}
}
}
else if (data.dataDetail == 0 && getDataDetail() < 4)
{
int dataPerFull = 1 << getDataDetail();
int fullSize = 16 / dataPerFull;
DhLodPos dataOffset = data.getBBoxLodPos().getCorner(getDataDetail());
DhLodPos baseOffset = sectionPos.getCorner(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;
for (int ox = 0; ox < fullSize; ox++)
{
for (int oz = 0; oz < fullSize; oz++)
{
SingleFullArrayView column = this.get(ox + offsetX, oz + offsetZ);
column.downsampleFrom(data.subView(dataPerFull, ox * dataPerFull, oz * dataPerFull));
}
}
}
else if (data.dataDetail == 0 && 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 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
{
LodUtil.assertNotReach();
//TODO;
}
}
@Override
public byte getDataVersion() {
return LATEST_VERSION;
}
public boolean isEmpty() { return isEmpty; }
public void markNotEmpty() { 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(level.getMinY());
if (isEmpty)
{
dos.writeInt(0x00000001);
return;
}
dos.writeInt(0xFFFFFFFF);
// Data array length
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
dos.writeByte(get(x, z).getSingleLength());
}
}
// Data array content (only on non-empty columns)
dos.writeInt(0xFFFFFFFF);
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
SingleFullArrayView column = get(x, z);
if (!column.doesItExist())
continue;
long[] raw = column.getRaw();
for (long l : raw)
{
dos.writeLong(l);
}
}
}
// Id mapping
dos.writeInt(0xFFFFFFFF);
mapping.serialize(dos);
dos.writeInt(0xFFFFFFFF);
}
}
public static FullDataSource 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)
{
// Section is empty
return new FullDataSource(dataFile.pos);
}
// Non-empty section
if (end != 0xFFFFFFFF)
throw new IOException("invalid header end guard");
long[][] data = new long[size * size][];
for (int x = 0; x < size; x++)
{
for (int z = 0; z < size; z++)
{
data[x * size + z] = new long[dos.readByte()];
}
}
// 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 = 0; i < data.length; i++)
{
if (data[i].length == 0)
continue;
for (int j = 0; j < data[i].length; j++)
{
data[i][j] = dos.readLong();
}
}
// Id mapping
end = dos.readInt();
if (end != 0xFFFFFFFF)
throw new IOException("invalid data content end guard");
IdBiomeBlockStateMap mapping = IdBiomeBlockStateMap.deserialize(new UnclosableInputStream(dos));
end = dos.readInt();
if (end != 0xFFFFFFFF)
throw new IOException("invalid id mapping end guard");
return new FullDataSource(dataFile.pos, mapping, data);
}
}
private FullDataSource(DhSectionPos pos, IdBiomeBlockStateMap mapping, long[][] data)
{
super(mapping, data, SECTION_SIZE);
LodUtil.assertTrue(data.length == SECTION_SIZE * SECTION_SIZE);
this.sectionPos = pos;
isEmpty = false;
}
@Override
public void update(ChunkSizedData data) {
LodUtil.assertTrue(sectionPos.getSectionBBoxPos().overlaps(data.getBBoxLodPos()));
if (data.dataDetail == 0 && getDataDetail() == 0) {
DhBlockPos2D chunkBlockPos = new DhBlockPos2D(data.x * 16, data.z * 16);
DhBlockPos2D blockOffset = chunkBlockPos.subtract(sectionPos.getCorner().getCorner());
LodUtil.assertTrue(blockOffset.x >= 0 && blockOffset.x < SECTION_SIZE && blockOffset.z >= 0 && blockOffset.z < SECTION_SIZE);
isEmpty = false;
data.shadowCopyTo(this.subView(16, blockOffset.x, blockOffset.z));
{ // DEBUG ASSERTION
for (int x = 0; x < 16; x++) {
for (int z = 0; z < 16; z++) {
SingleFullArrayView column = this.get(x + blockOffset.x, z + blockOffset.z);
LodUtil.assertTrue(column.doesItExist());
}
}
}
} else if (data.dataDetail == 0 && getDataDetail() < 4) {
int dataPerFull = 1 << getDataDetail();
int fullSize = 16 / dataPerFull;
DhLodPos dataOffset = data.getBBoxLodPos().getCorner(getDataDetail());
DhLodPos baseOffset = sectionPos.getCorner(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;
for (int ox = 0; ox < fullSize; ox++) {
for (int oz = 0; oz < fullSize; oz++) {
SingleFullArrayView column = this.get(ox + offsetX, oz + offsetZ);
column.downsampleFrom(data.subView(dataPerFull, ox * dataPerFull, oz * dataPerFull));
}
}
} else if (data.dataDetail == 0 && 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 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 {
LodUtil.assertNotReach();
//TODO;
}
}
@Override
public boolean isEmpty() {
return isEmpty;
}
public void markNotEmpty() {
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(level.getMinY());
if (isEmpty) {
dos.writeInt(0x00000001);
return;
}
dos.writeInt(0xFFFFFFFF);
// Data array length
for (int x = 0; x < size; x++) {
for (int z = 0; z < size; z++) {
dos.writeByte(get(x, z).getSingleLength());
}
}
// Data array content (only on non-empty columns)
dos.writeInt(0xFFFFFFFF);
for (int x = 0; x < size; x++) {
for (int z = 0; z < size; z++) {
SingleFullArrayView column = get(x, z);
if (!column.doesItExist()) continue;
long[] raw = column.getRaw();
for (long l : raw) {
dos.writeLong(l);
}
}
}
// Id mapping
dos.writeInt(0xFFFFFFFF);
mapping.serialize(dos);
dos.writeInt(0xFFFFFFFF);
}
}
public static FullDataSource 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) {
// Section is empty
return new FullDataSource(dataFile.pos);
}
// Non-empty section
if (end != 0xFFFFFFFF) throw new IOException("invalid header end guard");
long[][] data = new long[size*size][];
for (int x = 0; x < size; x++) {
for (int z = 0; z < size; z++) {
data[x*size+z] = new long[dos.readByte()];
}
}
// 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 = 0; i < data.length; i++) {
if (data[i].length == 0) continue;
for (int j = 0; j < data[i].length; j++) {
data[i][j] = dos.readLong();
}
}
// Id mapping
end = dos.readInt();
if (end != 0xFFFFFFFF) throw new IOException("invalid data content end guard");
IdBiomeBlockStateMap mapping = IdBiomeBlockStateMap.deserialize(new UnclosableInputStream(dos));
end = dos.readInt();
if (end != 0xFFFFFFFF) throw new IOException("invalid id mapping end guard");
return new FullDataSource(dataFile.pos, mapping, data);
}
}
private FullDataSource(DhSectionPos pos, IdBiomeBlockStateMap mapping, long[][] data) {
super(mapping, data, SECTION_SIZE);
LodUtil.assertTrue(data.length == SECTION_SIZE*SECTION_SIZE);
this.sectionPos = pos;
isEmpty = false;
}
public static FullDataSource createEmpty(DhSectionPos pos) {
return new FullDataSource(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;
byte sectPerData = (byte) (1 << (posToWrite.sectionDetail - posToTest.sectionDetail - SECTION_SIZE_OFFSET));
return posToTest.sectionX % sectPerData == 0 && posToTest.sectionZ % sectPerData == 0;
}
public void writeFromLower(FullDataSource subData) {
LodUtil.assertTrue(sectionPos.overlaps(subData.sectionPos));
LodUtil.assertTrue(subData.sectionPos.sectionDetail < sectionPos.sectionDetail);
if (!neededForPosition(sectionPos, subData.sectionPos)) return;
DhSectionPos lowerSectPos = subData.sectionPos;
byte detailDiff = (byte) (sectionPos.sectionDetail - subData.sectionPos.sectionDetail);
byte targetDataDetail = getDataDetail();
DhLodPos minDataPos = sectionPos.getCorner(targetDataDetail);
if (detailDiff <= SECTION_SIZE_OFFSET) {
int count = 1 << detailDiff;
int dataPerCount = SECTION_SIZE / count;
DhLodPos subDataPos = lowerSectPos.getSectionBBoxPos().getCorner(targetDataDetail);
int dataOffsetX = subDataPos.x - minDataPos.x;
int dataOffsetZ = subDataPos.z - minDataPos.z;
LodUtil.assertTrue(dataOffsetX >= 0 && dataOffsetX < SECTION_SIZE && dataOffsetZ >= 0 && dataOffsetZ < SECTION_SIZE);
for (int ox = 0; ox < count; ox++) {
for (int oz = 0; oz < count; oz++) {
SingleFullArrayView column = this.get(ox + dataOffsetX, oz + dataOffsetZ);
column.downsampleFrom(subData.subView(dataPerCount, ox * dataPerCount, oz * dataPerCount));
}
}
} else {
// Count == 1
DhLodPos subDataPos = lowerSectPos.getSectionBBoxPos().convertUpwardsTo(targetDataDetail);
int dataOffsetX = subDataPos.x - minDataPos.x;
int dataOffsetZ = subDataPos.z - minDataPos.z;
LodUtil.assertTrue(dataOffsetX >= 0 && dataOffsetX < SECTION_SIZE && dataOffsetZ >= 0 && dataOffsetZ < SECTION_SIZE);
subData.get(0,0).deepCopyTo(get(dataOffsetX, dataOffsetZ));
}
}
public static FullDataSource createEmpty(DhSectionPos pos) { return new FullDataSource(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;
byte sectPerData = (byte) (1 << (posToWrite.sectionDetail - posToTest.sectionDetail - SECTION_SIZE_OFFSET));
return posToTest.sectionX % sectPerData == 0 && posToTest.sectionZ % sectPerData == 0;
}
public void writeFromLower(FullDataSource subData)
{
LodUtil.assertTrue(sectionPos.overlaps(subData.sectionPos));
LodUtil.assertTrue(subData.sectionPos.sectionDetail < sectionPos.sectionDetail);
if (!neededForPosition(sectionPos, subData.sectionPos))
return;
DhSectionPos lowerSectPos = subData.sectionPos;
byte detailDiff = (byte) (sectionPos.sectionDetail - subData.sectionPos.sectionDetail);
byte targetDataDetail = getDataDetail();
DhLodPos minDataPos = sectionPos.getCorner(targetDataDetail);
if (detailDiff <= SECTION_SIZE_OFFSET)
{
int count = 1 << detailDiff;
int dataPerCount = SECTION_SIZE / count;
DhLodPos subDataPos = lowerSectPos.getSectionBBoxPos().getCorner(targetDataDetail);
int dataOffsetX = subDataPos.x - minDataPos.x;
int dataOffsetZ = subDataPos.z - minDataPos.z;
LodUtil.assertTrue(dataOffsetX >= 0 && dataOffsetX < SECTION_SIZE && dataOffsetZ >= 0 && dataOffsetZ < SECTION_SIZE);
for (int ox = 0; ox < count; ox++)
{
for (int oz = 0; oz < count; oz++)
{
SingleFullArrayView column = this.get(ox + dataOffsetX, oz + dataOffsetZ);
column.downsampleFrom(subData.subView(dataPerCount, ox * dataPerCount, oz * dataPerCount));
}
}
}
else
{
// Count == 1
DhLodPos subDataPos = lowerSectPos.getSectionBBoxPos().convertUpwardsTo(targetDataDetail);
int dataOffsetX = subDataPos.x - minDataPos.x;
int dataOffsetZ = subDataPos.z - minDataPos.z;
LodUtil.assertTrue(dataOffsetX >= 0 && dataOffsetX < SECTION_SIZE && dataOffsetZ >= 0 && dataOffsetZ < SECTION_SIZE);
subData.get(0, 0).deepCopyTo(get(dataOffsetX, dataOffsetZ));
}
}
}
@@ -11,99 +11,121 @@ import java.util.HashMap;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
// WARNING: This is not THREAD-SAFE!
public class IdBiomeBlockStateMap {
public static final IWrapperFactory FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
public static final class Entry {
public final IBiomeWrapper biome;
public final IBlockStateWrapper blockState;
public Entry(IBiomeWrapper biome, IBlockStateWrapper blockState) {
this.biome = biome;
this.blockState = blockState;
}
@Override
public int hashCode() {
return Objects.hash(biome, blockState);
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
if (!(other instanceof Entry)) return false;
return ((Entry) other).biome.equals(biome) && ((Entry) other).blockState.equals(blockState);
}
public String serialize() {
return biome.serialize() + " " + blockState.serialize();
}
public static Entry deserialize(String str) throws IOException {
String[] strs = str.split(" ");
if (strs.length != 2) throw new IOException("Failed to deserialize BiomeBlockStateEntry");
IBiomeWrapper biome = FACTORY.deserializeBiomeWrapper(strs[0]);
IBlockStateWrapper blockState = FACTORY.deserializeBlockStateWrapper(strs[1]);
return new Entry(biome, blockState);
}
}
final ArrayList<Entry> entries = new ArrayList<>();
final ConcurrentHashMap<Entry, Integer> idMap = new ConcurrentHashMap<>(); // FIXME: Improve performance
public Entry get(int id) {
return entries.get(id);
}
public int setAndGetId(IBiomeWrapper biome, IBlockStateWrapper blockState) {
return idMap.computeIfAbsent(new Entry(biome, blockState), (e) -> {
int id = entries.size();
entries.add(e);
return id;
});
}
public int setAndGetId(Entry biomeBlockStateEntry) {
return idMap.computeIfAbsent(biomeBlockStateEntry, (e) -> {
int id = entries.size();
entries.add(e);
return id;
});
}
public int[] computeAndMergeMapFrom(IdBiomeBlockStateMap target) {
ArrayList<Entry> mergeEntry = target.entries;
int[] mapper = new int[mergeEntry.size()];
for (int i=0; i<mergeEntry.size(); i++) {
mapper[i] = setAndGetId(mergeEntry.get(i));
}
return mapper;
}
void serialize(OutputStream os) throws IOException {
DataOutputStream dos = new DataOutputStream(os); // DO NOT CLOSE!
dos.writeInt(entries.size());
for (Entry e : entries) {
dos.writeUTF(e.serialize());
}
}
static IdBiomeBlockStateMap deserialize(InputStream is) throws IOException {
DataInputStream dis = new DataInputStream(is); // DO NOT CLOSE!
int size = dis.readInt();
IdBiomeBlockStateMap map = new IdBiomeBlockStateMap();
for (int i = 0; i < size; i++) {
map.entries.add(Entry.deserialize(dis.readUTF()));
}
return map;
}
@Override
public boolean equals(Object other) {
if (other == this) return true;
/** WARNING: This is not THREAD-SAFE! */
public class IdBiomeBlockStateMap
{
public static final IWrapperFactory FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class);
public static final class Entry
{
public final IBiomeWrapper biome;
public final IBlockStateWrapper blockState;
public Entry(IBiomeWrapper biome, IBlockStateWrapper blockState)
{
this.biome = biome;
this.blockState = blockState;
}
@Override
public int hashCode()
{
return Objects.hash(biome, blockState);
}
@Override
public boolean equals(Object other)
{
if (other == this)
return true;
if (!(other instanceof Entry))
return false;
return ((Entry) other).biome.equals(biome) && ((Entry) other).blockState.equals(blockState);
}
public String serialize() { return biome.serialize() + " " + blockState.serialize(); }
public static Entry deserialize(String str) throws IOException
{
String[] strs = str.split(" ");
if (strs.length != 2)
throw new IOException("Failed to deserialize BiomeBlockStateEntry");
IBiomeWrapper biome = FACTORY.deserializeBiomeWrapper(strs[0]);
IBlockStateWrapper blockState = FACTORY.deserializeBlockStateWrapper(strs[1]);
return new Entry(biome, blockState);
}
}
final ArrayList<Entry> entries = new ArrayList<>();
final ConcurrentHashMap<Entry, Integer> idMap = new ConcurrentHashMap<>(); // FIXME: Improve performance
public Entry get(int id) { return entries.get(id); }
public int setAndGetId(IBiomeWrapper biome, IBlockStateWrapper blockState)
{
return idMap.computeIfAbsent(new Entry(biome, blockState), (e) -> {
int id = entries.size();
entries.add(e);
return id;
});
}
public int setAndGetId(Entry biomeBlockStateEntry)
{
return idMap.computeIfAbsent(biomeBlockStateEntry, (e) -> {
int id = entries.size();
entries.add(e);
return id;
});
}
public int[] computeAndMergeMapFrom(IdBiomeBlockStateMap target)
{
ArrayList<Entry> mergeEntry = target.entries;
int[] mapper = new int[mergeEntry.size()];
for (int i = 0; i < mergeEntry.size(); i++)
{
mapper[i] = setAndGetId(mergeEntry.get(i));
}
return mapper;
}
void serialize(OutputStream os) throws IOException
{
DataOutputStream dos = new DataOutputStream(os); // DO NOT CLOSE!
dos.writeInt(entries.size());
for (Entry e : entries)
{
dos.writeUTF(e.serialize());
}
}
static IdBiomeBlockStateMap deserialize(InputStream is) throws IOException
{
DataInputStream dis = new DataInputStream(is); // DO NOT CLOSE!
int size = dis.readInt();
IdBiomeBlockStateMap map = new IdBiomeBlockStateMap();
for (int i = 0; i < size; i++)
{
map.entries.add(Entry.deserialize(dis.readUTF()));
}
return map;
}
@Override
public boolean equals(Object other)
{
if (other == this)
return true;
// if (!(other instanceof IdBiomeBlockStateMap)) return false;
// IdBiomeBlockStateMap otherMap = (IdBiomeBlockStateMap) other;
// if (entries.size() != otherMap.entries.size()) return false;
// for (int i=0; i<entries.size(); i++) {
// if (!entries.get(i).equals(otherMap.entries.get(i))) return false;
// }
return false;
}
return false;
}
}
@@ -4,92 +4,111 @@ import com.seibel.lod.core.datatype.full.FullFormat;
import com.seibel.lod.core.datatype.full.IdBiomeBlockStateMap;
import com.seibel.lod.core.util.LodUtil;
public class FullArrayView implements IFullDataView {
protected final long[][] dataArrays;
protected final int offset;
protected final int size;
protected final int dataSize;
protected final IdBiomeBlockStateMap mapping;
public FullArrayView(IdBiomeBlockStateMap mapping, long[][] dataArrays, int size) {
if (dataArrays.length != size*size)
throw new IllegalArgumentException(
"tried constructing dataArrayView with invalid input!");
this.dataArrays = dataArrays;
this.size = size;
this.dataSize = size;
this.mapping = mapping;
offset = 0;
}
public FullArrayView(FullArrayView source, int size, int offsetX, int offsetZ) {
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.size = size;
this.dataSize = source.dataSize;
mapping = source.mapping;
offset = source.offset + offsetX * dataSize + offsetZ;
}
@Override
public IdBiomeBlockStateMap getMapping() {
return mapping;
}
@Override
public SingleFullArrayView get(int index) {
return get(index/size, index%size);
}
@Override
public SingleFullArrayView get(int x, int z) {
return new SingleFullArrayView(mapping, dataArrays, x*size + z + offset);
}
@Override
public int width() {
return size;
}
@Override
public FullArrayView subView(int size, int ox, int oz) {
return new FullArrayView(this, size, ox, oz);
}
//WARNING: It will potentially share the underlying array object!
public void shadowCopyTo(FullArrayView target) {
if (target.size != size)
throw new IllegalArgumentException("Target view must have same size as this view");
if (target.mapping.equals(mapping)) {
for (int x = 0; x < size; x++) {
System.arraycopy(dataArrays, offset + x * dataSize,
target.dataArrays, target.offset + x * target.dataSize, size);
}
}
else {
int[] map = target.mapping.computeAndMergeMapFrom(mapping);
for (int x = 0; x < size; x++) {
for (int o=0; o<size; o++) {
long[] sourceData = dataArrays[offset + x * dataSize + o];
long[] newData = new long[sourceData.length];
for (int i = 0; i < newData.length; i++) {
newData[i] = FullFormat.remap(map, sourceData[i]);
}
target.dataArrays[target.offset + x * target.dataSize + o] = newData;
}
}
}
}
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++) {
for (int oz = 0; oz < size; oz++) {
SingleFullArrayView column = get(ox, oz);
column.downsampleFrom(source.subView(dataPerUnit, ox * dataPerUnit, oz * dataPerUnit));
}
}
}
public class FullArrayView implements IFullDataView
{
protected final long[][] dataArrays;
protected final int offset;
protected final int size;
protected final int dataSize;
protected final IdBiomeBlockStateMap mapping;
public FullArrayView(IdBiomeBlockStateMap mapping, long[][] dataArrays, int size)
{
if (dataArrays.length != size * size)
throw new IllegalArgumentException(
"tried constructing dataArrayView with invalid input!");
this.dataArrays = dataArrays;
this.size = size;
this.dataSize = size;
this.mapping = mapping;
offset = 0;
}
public FullArrayView(FullArrayView source, int size, int offsetX, int offsetZ)
{
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.size = size;
this.dataSize = source.dataSize;
mapping = source.mapping;
offset = source.offset + offsetX * dataSize + offsetZ;
}
@Override
public IdBiomeBlockStateMap getMapping()
{
return mapping;
}
@Override
public SingleFullArrayView get(int index)
{
return get(index / size, index % size);
}
@Override
public SingleFullArrayView get(int x, int z)
{
return new SingleFullArrayView(mapping, dataArrays, x * size + z + offset);
}
@Override
public int width()
{
return size;
}
@Override
public FullArrayView subView(int size, int ox, int oz)
{
return new FullArrayView(this, size, ox, oz);
}
/** WARNING: It will potentially share the underlying array object! */
public void shadowCopyTo(FullArrayView target)
{
if (target.size != size)
throw new IllegalArgumentException("Target view must have same size as this view");
if (target.mapping.equals(mapping))
{
for (int x = 0; x < size; x++)
{
System.arraycopy(dataArrays, offset + x * dataSize,
target.dataArrays, target.offset + x * target.dataSize, size);
}
}
else
{
int[] map = target.mapping.computeAndMergeMapFrom(mapping);
for (int x = 0; x < size; x++)
{
for (int o = 0; o < size; o++)
{
long[] sourceData = dataArrays[offset + x * dataSize + o];
long[] newData = new long[sourceData.length];
for (int i = 0; i < newData.length; i++)
{
newData[i] = FullFormat.remap(map, sourceData[i]);
}
target.dataArrays[target.offset + x * target.dataSize + o] = newData;
}
}
}
}
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++)
{
for (int oz = 0; oz < size; oz++)
{
SingleFullArrayView column = get(ox, oz);
column.downsampleFrom(source.subView(dataPerUnit, ox * dataPerUnit, oz * dataPerUnit));
}
}
}
}
@@ -5,27 +5,39 @@ import com.seibel.lod.core.util.LodUtil;
import java.util.Iterator;
public interface IFullDataView {
IdBiomeBlockStateMap getMapping();
SingleFullArrayView get(int index);
SingleFullArrayView get(int x, int z);
int width();
default Iterator<SingleFullArrayView> iterator() {
return new Iterator<SingleFullArrayView>() {
private int index = 0;
private final int size = width()*width();
@Override
public boolean hasNext() {
return index < size;
}
@Override
public SingleFullArrayView next() {
LodUtil.assertTrue(hasNext(), "No more data to iterate!");
return get(index++);
}
};
}
IFullDataView subView(int size, int ox, int oz);
public interface IFullDataView
{
IdBiomeBlockStateMap getMapping();
SingleFullArrayView get(int index);
SingleFullArrayView get(int x, int z);
int width();
/** Returns an iterator that */
default Iterator<SingleFullArrayView> iterator()
{
return new Iterator<SingleFullArrayView>()
{
private int index = 0;
private final int size = width() * width();
@Override
public boolean hasNext()
{
return index < size;
}
@Override
public SingleFullArrayView next()
{
LodUtil.assertTrue(hasNext(), "No more data to iterate!");
return get(index++);
}
};
}
IFullDataView subView(int size, int ox, int oz);
}
@@ -3,97 +3,106 @@ package com.seibel.lod.core.datatype.full.accessor;
import com.seibel.lod.core.datatype.full.FullFormat;
import com.seibel.lod.core.datatype.full.IdBiomeBlockStateMap;
public class SingleFullArrayView implements IFullDataView {
private final long[][] dataArrays;
private final int offset;
private final IdBiomeBlockStateMap mapping;
public SingleFullArrayView(IdBiomeBlockStateMap mapping, long[][] dataArrays, int offset) {
this.dataArrays = dataArrays;
this.offset = offset;
this.mapping = mapping;
}
public boolean doesItExist() {
return dataArrays[offset].length!=0;
}
@Override
public IdBiomeBlockStateMap getMapping() {
return mapping;
}
@Override
public SingleFullArrayView get(int index) {
if (index != 0) throw new IllegalArgumentException("Only contains 1 column of full data!");
return this;
}
@Override
public SingleFullArrayView get(int x, int z) {
if (x != 0 || z != 0) throw new IllegalArgumentException("Only contains 1 column of full data!");
return this;
}
public long[] getRaw() {
return dataArrays[offset];
}
public long getSingle(int yIndex) {
return dataArrays[offset][yIndex];
}
public void setSingle(int yIndex, long value) {
dataArrays[offset][yIndex] = value;
}
public void setNew(long[] newArray) {
dataArrays[offset] = newArray;
}
public int getSingleLength() { return dataArrays[offset].length; }
@Override
public int width() {
return 1;
}
@Override
public IFullDataView subView(int size, int ox, int oz) {
if (size != 1 || ox != 1 || oz != 1)
throw new IllegalArgumentException("Getting invalid range of subView from SingleFullArrayView!");
return this;
}
//WARNING: It may potentially share the underlying array object!
public void shadowCopyTo(SingleFullArrayView target) {
if (target.mapping.equals(mapping)) {
target.dataArrays[target.offset] = dataArrays[offset];
}
else {
int[] map = target.mapping.computeAndMergeMapFrom(mapping);
long[] sourceData = dataArrays[offset];
long[] newData = new long[sourceData.length];
for (int i = 0; i < newData.length; i++) {
newData[i] = FullFormat.remap(map, sourceData[i]);
}
target.dataArrays[target.offset] = newData;
}
}
public void deepCopyTo(SingleFullArrayView target) {
if (target.mapping.equals(mapping)) {
target.dataArrays[target.offset] = dataArrays[offset].clone();
}
else {
int[] map = target.mapping.computeAndMergeMapFrom(mapping);
long[] sourceData = dataArrays[offset];
long[] newData = new long[sourceData.length];
for (int i = 0; i < newData.length; i++) {
newData[i] = FullFormat.remap(map, sourceData[i]);
}
target.dataArrays[target.offset] = newData;
}
}
public void downsampleFrom(IFullDataView source) {
//TODO: Temp downsample method
SingleFullArrayView firstColumn = source.get(0);
firstColumn.deepCopyTo(this);
}
public class SingleFullArrayView implements IFullDataView
{
private final long[][] dataArrays;
private final int offset;
private final IdBiomeBlockStateMap mapping;
public SingleFullArrayView(IdBiomeBlockStateMap mapping, long[][] dataArrays, int offset)
{
this.dataArrays = dataArrays;
this.offset = offset;
this.mapping = mapping;
}
public boolean doesItExist() { return dataArrays[offset].length != 0; }
@Override
public IdBiomeBlockStateMap getMapping() { return mapping; }
@Override
public SingleFullArrayView get(int index)
{
if (index != 0)
throw new IllegalArgumentException("Only contains 1 column of full data!");
return this;
}
@Override
public SingleFullArrayView get(int x, int z)
{
if (x != 0 || z != 0)
throw new IllegalArgumentException("Only contains 1 column of full data!");
return this;
}
public long[] getRaw() { return dataArrays[offset]; }
public long getSingle(int yIndex) { return dataArrays[offset][yIndex]; }
public void setSingle(int yIndex, long value) { dataArrays[offset][yIndex] = value; }
public void setNew(long[] newArray) { dataArrays[offset] = newArray; }
public int getSingleLength() { return dataArrays[offset].length; }
@Override
public int width() { return 1; }
@Override
public IFullDataView subView(int size, int ox, int oz)
{
if (size != 1 || ox != 1 || oz != 1)
throw new IllegalArgumentException("Getting invalid range of subView from SingleFullArrayView!");
return this;
}
/** WARNING: It may potentially share the underlying array object! */
public void shadowCopyTo(SingleFullArrayView target)
{
if (target.mapping.equals(mapping))
{
target.dataArrays[target.offset] = dataArrays[offset];
}
else
{
int[] map = target.mapping.computeAndMergeMapFrom(mapping);
long[] sourceData = dataArrays[offset];
long[] newData = new long[sourceData.length];
for (int i = 0; i < newData.length; i++)
{
newData[i] = FullFormat.remap(map, sourceData[i]);
}
target.dataArrays[target.offset] = newData;
}
}
public void deepCopyTo(SingleFullArrayView target)
{
if (target.mapping.equals(mapping))
{
target.dataArrays[target.offset] = dataArrays[offset].clone();
}
else
{
int[] map = target.mapping.computeAndMergeMapFrom(mapping);
long[] sourceData = dataArrays[offset];
long[] newData = new long[sourceData.length];
for (int i = 0; i < newData.length; i++)
{
newData[i] = FullFormat.remap(map, sourceData[i]);
}
target.dataArrays[target.offset] = newData;
}
}
public void downsampleFrom(IFullDataView source)
{
//TODO: Temp downsample method
SingleFullArrayView firstColumn = source.get(0);
firstColumn.deepCopyTo(this);
}
}