diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStore.java index 9e48f4ed7..5514df13c 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStore.java @@ -1,9 +1,11 @@ package com.gmail.nossr50.util.blockmeta; -import java.io.Externalizable; import java.io.Serializable; -public interface ChunkletStore extends Serializable, Externalizable { +/** + * A ChunkletStore should be responsible for a 16x16x64 area of data + */ +public interface ChunkletStore extends Serializable { /** * Checks the value at the given coordinates * @@ -36,4 +38,11 @@ public interface ChunkletStore extends Serializable, Externalizable { * @return true if all values in the chunklet are false, false if otherwise */ public boolean isEmpty(); + + /** + * Set all values in this ChunkletStore to the values from another provided ChunkletStore + * + * @param otherStore Another ChunkletStore that this one should copy all data from + */ + public void copyFrom(ChunkletStore otherStore); } diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStoreFactory.java b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStoreFactory.java index 4d2f70945..1fb4a315a 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStoreFactory.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/ChunkletStoreFactory.java @@ -3,6 +3,6 @@ package com.gmail.nossr50.util.blockmeta; public class ChunkletStoreFactory { protected static ChunkletStore getChunkletStore() { // TODO: Add in loading from config what type of store we want. - return new PrimitiveChunkletStore(); + return new PrimitiveExChunkletStore(); } } diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkletManager.java b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkletManager.java index 97293264a..1aee93fcd 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkletManager.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/HashChunkletManager.java @@ -338,6 +338,15 @@ public class HashChunkletManager implements ChunkletManager { } } + // TODO: Make this less messy, as it is, it's kinda... depressing to do it like this. + // Might also make a mess when we move to stacks, but at that point I think I will write a new Manager... + // IMPORTANT! If ChunkletStoreFactory is going to be returning something other than PrimitiveEx we need to remove this, as it will be breaking time for old maps + if(!(storeIn instanceof PrimitiveExChunkletStore)) { + ChunkletStore tempStore = ChunkletStoreFactory.getChunkletStore(); + tempStore.copyFrom(storeIn); + storeIn = tempStore; + } + return storeIn; } } diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveChunkletStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveChunkletStore.java index 478a2c94e..635296db6 100644 --- a/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveChunkletStore.java +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveChunkletStore.java @@ -1,14 +1,10 @@ package com.gmail.nossr50.util.blockmeta; -import java.io.IOException; -import java.io.ObjectInput; -import java.io.ObjectOutput; - public class PrimitiveChunkletStore implements ChunkletStore { private static final long serialVersionUID = -3453078050608607478L; /** X, Z, Y */ - private boolean[][][] store = new boolean[16][16][64]; + protected boolean[][][] store = new boolean[16][16][64]; @Override public boolean isTrue(int x, int y, int z) { @@ -38,129 +34,13 @@ public class PrimitiveChunkletStore implements ChunkletStore { } @Override - public void writeExternal(ObjectOutput out) throws IOException { - byte[] buffer = new byte[2304]; // 2304 is 16*16*9 - int bufferIndex = 0; - + public void copyFrom(ChunkletStore otherStore) { for(int x = 0; x < 16; x++) { for(int z = 0; z < 16; z++) { for(int y = 0; y < 64; y++) { - if(store[x][z][y]) { - byte[] temp = constructColumn(x, z); - - for(int i = 0; i < 9; i++) { - buffer[bufferIndex] = temp[i]; - bufferIndex++; - } - - break; - } + store[x][z][y] = otherStore.isTrue(x, y, z); } } } - - out.write(buffer, 0, bufferIndex); - out.flush(); - } - - // For this we assume that store has been initialized to be all false by now - @Override - public void readExternal(ObjectInput in) throws IOException { - byte[] temp = new byte[9]; - - // Could probably reorganize this loop to print nasty things if it does not equal 9 or -1 - while(in.read(temp, 0, 9) == 9) { - int x = addressByteX(temp[0]); - int z = addressByteZ(temp[0]); - boolean[] yColumn = new boolean[64]; - - for(int i = 0; i < 8; i++) { - for(int j = 0; j < 8; j++) { - yColumn[j + (i * 8)] = (temp[i + 1] & (1 << j)) != 0; - } - } - - store[x][z] = yColumn; - } - } - - /* - * The column: An array of 9 bytes which represent all y values for a given (x,z) Chunklet-coordinate - * - * The first byte is an address byte, this provides the x and z values. - * The next 8 bytes are all y values from 0 to 63, with each byte containing 8 bits of true/false data - * - * Each of these 8 bytes address to a y value from right to left - * - * Examples: - * 00000001 represents that the lowest y value in this byte is true, all others are off - * 10000000 represents that the highest y value in this byte is true, all others are off - * 10000001 represents that the lowest and highest y values in this byte are true, all others are off - * - * Full columns: - * See comment on Address byte for information on how to use that byte - * - * Example: - * ADDRESS_BYTE 10000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 - * - x, z from ADDRESS_BYTE - * - The next byte contains data from 0 to 7 - * - 1 is set in the highest bit position, this is 7 in y coordinate - * - The next byte contains data from 8 to 15 - * - 1 is set in the lowest bit position, this is 8 in the y coordinate - * Therefore, for this column: There are true values at (x, 7, z) and (x, 8, z) - */ - private byte[] constructColumn(int x, int z) { - byte[] column = new byte[9]; - int index = 1; - - column[0] = makeAddressByte(x, z); - - for (int i = 0; i < 8; i++){ - byte yCompressed = 0x0; - int subColumnIndex = 8 * i; - int subColumnEnd = subColumnIndex + 8; - - for(int y = subColumnIndex; y < subColumnEnd; y++) { - if(store[x][z][y]) { - yCompressed |= 1 << (y % 8); - } - } - - column[index] = yCompressed; - index++; - } - - return column; - } - - /* - * The address byte: A single byte which contains x and z values which correspond to the x and z Chunklet-coordinates - * - * In Chunklet-coordinates, the only valid values are 0-15, so we can fit both into a single byte. - * - * The top 4 bits of the address byte are for the x value - * The bottom 4 bits of the address byte are for the z value - * - * Examples: - * An address byte with a value 00000001 would be split like so: - * - x = 0000 = 0 - * - z = 0001 = 1 - * => Chunklet coordinates (0, 1) - * - * 01011111 - * - x = 0101 = 5 - * - z = 1111 = 15 - * => Chunklet coordinates (5, 15) - */ - private static byte makeAddressByte(int x, int z) { - return (byte) ((x << 4) + z); - } - - private static int addressByteX(byte address) { - return (address & 0xF0) >>> 4; - } - - private static int addressByteZ(byte address) { - return address & 0x0F; } } diff --git a/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveExChunkletStore.java b/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveExChunkletStore.java new file mode 100644 index 000000000..be649f728 --- /dev/null +++ b/src/main/java/com/gmail/nossr50/util/blockmeta/PrimitiveExChunkletStore.java @@ -0,0 +1,178 @@ +package com.gmail.nossr50.util.blockmeta; + +import java.io.Externalizable; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; + +public class PrimitiveExChunkletStore implements ChunkletStore, Externalizable { + private static final long serialVersionUID = 8603603827094383873L; + + /** X, Z, Y */ + private boolean[][][] store = new boolean[16][16][64]; + + @Override + public boolean isTrue(int x, int y, int z) { + return store[x][z][y]; + } + + @Override + public void setTrue(int x, int y, int z) { + store[x][z][y] = true; + } + + @Override + public void setFalse(int x, int y, int z) { + store[x][z][y] = false; + } + + @Override + public boolean isEmpty() { + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + for(int y = 0; y < 64; y++) { + if(store[x][z][y]) return false; + } + } + } + return true; + } + + @Override + public void copyFrom(ChunkletStore otherStore) { + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + for(int y = 0; y < 64; y++) { + store[x][z][y] = otherStore.isTrue(x, y, z); + } + } + } + } + + @Override + public void writeExternal(ObjectOutput out) throws IOException { + byte[] buffer = new byte[2304]; // 2304 is 16*16*9 + int bufferIndex = 0; + + for(int x = 0; x < 16; x++) { + for(int z = 0; z < 16; z++) { + for(int y = 0; y < 64; y++) { + if(store[x][z][y]) { + byte[] temp = constructColumn(x, z); + + for(int i = 0; i < 9; i++) { + buffer[bufferIndex] = temp[i]; + bufferIndex++; + } + + break; + } + } + } + } + + out.write(buffer, 0, bufferIndex); + out.flush(); + } + + // For this we assume that store has been initialized to be all false by now + @Override + public void readExternal(ObjectInput in) throws IOException { + byte[] temp = new byte[9]; + + // Could probably reorganize this loop to print nasty things if it does not equal 9 or -1 + while(in.read(temp, 0, 9) == 9) { + int x = addressByteX(temp[0]); + int z = addressByteZ(temp[0]); + boolean[] yColumn = new boolean[64]; + + for(int i = 0; i < 8; i++) { + for(int j = 0; j < 8; j++) { + yColumn[j + (i * 8)] = (temp[i + 1] & (1 << j)) != 0; + } + } + + store[x][z] = yColumn; + } + } + + /* + * The column: An array of 9 bytes which represent all y values for a given (x,z) Chunklet-coordinate + * + * The first byte is an address byte, this provides the x and z values. + * The next 8 bytes are all y values from 0 to 63, with each byte containing 8 bits of true/false data + * + * Each of these 8 bytes address to a y value from right to left + * + * Examples: + * 00000001 represents that the lowest y value in this byte is true, all others are off + * 10000000 represents that the highest y value in this byte is true, all others are off + * 10000001 represents that the lowest and highest y values in this byte are true, all others are off + * + * Full columns: + * See comment on Address byte for information on how to use that byte + * + * Example: + * ADDRESS_BYTE 10000000 00000001 00000000 00000000 00000000 00000000 00000000 00000000 + * - x, z from ADDRESS_BYTE + * - The next byte contains data from 0 to 7 + * - 1 is set in the highest bit position, this is 7 in y coordinate + * - The next byte contains data from 8 to 15 + * - 1 is set in the lowest bit position, this is 8 in the y coordinate + * Therefore, for this column: There are true values at (x, 7, z) and (x, 8, z) + */ + private byte[] constructColumn(int x, int z) { + byte[] column = new byte[9]; + int index = 1; + + column[0] = makeAddressByte(x, z); + + for (int i = 0; i < 8; i++){ + byte yCompressed = 0x0; + int subColumnIndex = 8 * i; + int subColumnEnd = subColumnIndex + 8; + + for(int y = subColumnIndex; y < subColumnEnd; y++) { + if(store[x][z][y]) { + yCompressed |= 1 << (y % 8); + } + } + + column[index] = yCompressed; + index++; + } + + return column; + } + + /* + * The address byte: A single byte which contains x and z values which correspond to the x and z Chunklet-coordinates + * + * In Chunklet-coordinates, the only valid values are 0-15, so we can fit both into a single byte. + * + * The top 4 bits of the address byte are for the x value + * The bottom 4 bits of the address byte are for the z value + * + * Examples: + * An address byte with a value 00000001 would be split like so: + * - x = 0000 = 0 + * - z = 0001 = 1 + * => Chunklet coordinates (0, 1) + * + * 01011111 + * - x = 0101 = 5 + * - z = 1111 = 15 + * => Chunklet coordinates (5, 15) + */ + private static byte makeAddressByte(int x, int z) { + return (byte) ((x << 4) + z); + } + + private static int addressByteX(byte address) { + return (address & 0xF0) >>> 4; + } + + private static int addressByteZ(byte address) { + return address & 0x0F; + } +}