More generic chunk and iterator support classes

This commit is contained in:
Mike Primm 2021-12-04 12:12:37 -06:00
parent d3639bd4a6
commit 13d2cc05cb
3 changed files with 200 additions and 52 deletions

View File

@ -37,23 +37,30 @@ public class DynmapChunk {
public final DynmapBlockState getBlockType(int x, int y, int z) {
return getSection(y).blocks.getBlock(x, y, z);
}
public final DynmapBlockState getBlockType(DynmapChunkPos pos) {
return getSection(pos.y).blocks.getBlock(pos);
}
public final int getBlockSkyLight(int x, int y, int z) {
return getSection(y).sky.getLight(x, y, z);
}
public final int getBlockSkyLight(DynmapChunkPos pos) {
return getSection(pos.y).sky.getLight(pos);
}
public final int getBlockEmittedLight(int x, int y, int z) {
return getSection(y).emitted.getLight(x, y, z);
}
public final int getBlockEmittedLight(DynmapChunkPos pos) {
return getSection(pos.y).emitted.getLight(pos);
}
public final BiomeMap getBiome(int x, int y, int z) {
return getSection(y).biomes.getBiome(x, y, z);
}
public final boolean isSectionEmpty(int sy) {
return getSection(sy << 4).isEmpty;
public final BiomeMap getBiome(DynmapChunkPos pos) {
return getSection(pos.y).biomes.getBiome(pos);
}
public final boolean isSectionEmpty(int cy) {
return getSection(cy << 4).isEmpty;
}
public final long getInhabitedTicks() {
return inhabitedTicks;
}

View File

@ -0,0 +1,53 @@
package org.dynmap.common.chunk;
// Generic block location iterator - represents 3D position, but includes fast precomputed chunk and section offsets
public class DynmapChunkPos {
public int x, y, z; // 3D world position
public int cx, cz; // 2D chunk position (x / 16, z / 16)
public int cy; // Vertical section index (Y / 16)
public int sx, sy, sz; // 3D section position (x % 16, y % 16, z % 16)
public int soffset; // Section offset (256 * sy) + (16 * sz) + sx
public int sdiv4offset; // Subsection offset (16 * (sy / 4)) + (4 * (sz / 4)) + (sx / 4) (3D biomes)
public DynmapChunkPos(int x, int y, int z) {
setPos(x, y, z);
}
// Replace X value
public final void setX(int x) {
this.x = x;
this.cx = x >> 4;
this.sx = x & 0xF;
this.soffset = (sy << 8) | (sz << 4) | sx;
this.sdiv4offset = ((sy & 0xC) << 4) | (sz & 0xC) | ((sx & 0xC) >> 2);
}
// Replace Y value
public final void setY(int y) {
this.y = y;
this.cy = y >> 4;
this.sy = y & 0xF;
this.soffset = (sy << 8) | (sz << 4) | sx;
this.sdiv4offset = ((sy & 0xC) << 4) | (sz & 0xC) | ((sx & 0xC) >> 2);
}
// Replace Z value
public final void setZ(int z) {
this.z = z;
this.cz = z >> 4;
this.sz = z & 0xF;
this.soffset = (sy << 8) | (sz << 4) | sx;
this.sdiv4offset = ((sy & 0xC) << 4) | (sz & 0xC) | ((sx & 0xC) >> 2);
}
// Replace X, Y, and Z values
public final void setPos(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
this.cx = x >> 4;
this.cy = y >> 4;
this.cz = z >> 4;
this.sx = x & 0xF;
this.sy = y & 0xF;
this.sz = z & 0xF;
this.soffset = (sy << 8) | (sz << 4) | sx;
this.sdiv4offset = ((sy & 0xC) << 4) | (sz & 0xC) | ((sx & 0xC) >> 2);
}
}

View File

@ -16,23 +16,20 @@ public class DynmapChunkSection {
// Block state access interface
public interface BlockStateAccess {
public DynmapBlockState getBlock(int x, int y, int z);
public DynmapBlockState getBlock(DynmapChunkPos pos);
}
private static class BlockStateAccess3D implements BlockStateAccess {
private final DynmapBlockState blocks[];
BlockStateAccess3D(DynmapBlockState[][][] bs) { // (16 x 16 x 16) X, Y, Z
blocks = new DynmapBlockState[4096];
Arrays.fill(blocks, DynmapBlockState.AIR); // Initialize to AIR
for (int x = 0; (x < 16) && (x < bs.length); x++) {
for (int y = 0; (y < 16) && (y < bs[x].length); y++) {
for (int z = 0; (z < 16) && (z < bs[x][y].length); z++) {
blocks[(256 * y) + (16 * z) + x] = bs[x][y][z];
}
}
}
private final DynmapBlockState blocks[]; // YZX order
// Array given to us by builder
BlockStateAccess3D(DynmapBlockState bs[]) {
blocks = bs;
}
public final DynmapBlockState getBlock(int x, int y, int z) {
return blocks[(256 * y) + (16 * z) + x];
}
public final DynmapBlockState getBlock(DynmapChunkPos pos) {
return blocks[pos.soffset];
}
}
private static class BlockStateAccessSingle implements BlockStateAccess {
private final DynmapBlockState block;
@ -42,44 +39,42 @@ public class DynmapChunkSection {
public final DynmapBlockState getBlock(int x, int y, int z) {
return block;
}
public final DynmapBlockState getBlock(DynmapChunkPos pos) {
return block;
}
}
// Biome access interface
public interface BiomeAccess {
public BiomeMap getBiome(int x, int y, int z);
public BiomeMap getBiome(DynmapChunkPos pos);
}
// For classic 2D biome map
private static class BiomeAccess2D implements BiomeAccess {
private final BiomeMap biomes[]; // (16 * Z) + X
BiomeAccess2D(BiomeMap[][] b) { // Grid is 16 x 16 (X, Z)
biomes = new BiomeMap[256];
Arrays.fill(biomes, BiomeMap.NULL); // Initialize to null
for (int x = 0; (x < 16) && (x < b.length); x++) {
for (int z = 0; (z < 16) && (z < b[x].length); z++) {
biomes[(z << 4) + x] = b[x][z];
}
}
// Array given to us by builder in right format
BiomeAccess2D(BiomeMap b[]) {
biomes = b;
}
public final BiomeMap getBiome(int x, int y, int z) {
return biomes[((z & 0xF) << 4) + (x & 0xF)];
}
public final BiomeMap getBiome(DynmapChunkPos pos) {
return biomes[pos.soffset & 0xFF]; // Just ZX portion
}
}
// For 3D biome map
private static class BiomeAccess3D implements BiomeAccess {
private final BiomeMap biomes[]; // (16 * (Y >> 2)) + (4 * (Z >> 2)) + (X >> 2)
BiomeAccess3D(BiomeMap[][][] b) { // Grid is 4 x 4 x 4 (X, Y, Z)
biomes = new BiomeMap[64];
Arrays.fill(biomes, BiomeMap.NULL); // Initialize to null
for (int x = 0; (x < 4) && (x < b.length); x++) {
for (int y = 0; (y < 4) && (y < b[x].length); y++) {
for (int z = 0; (z < 4) && (z < b[x][y].length); z++) {
biomes[((y & 0xC) << 2) | (z & 0xC) | ((x & 0xC) >> 2)] = b[x][y][z];
}
}
}
// Array given to us by builder in right format (64 - YZX divided by 4)
BiomeAccess3D(BiomeMap[] b) {
biomes = b;
}
public final BiomeMap getBiome(int x, int y, int z) {
return biomes[ ((y & 0xC) << 2) | (z & 0xC) | ((x & 0xC) >> 2) ];
}
public final BiomeMap getBiome(DynmapChunkPos pos) {
return biomes[pos.sdiv4offset];
}
}
// For single biome map
private static class BiomeAccessSingle implements BiomeAccess {
@ -90,13 +85,17 @@ public class DynmapChunkSection {
public final BiomeMap getBiome(int x, int y, int z) {
return biome;
}
public final BiomeMap getBiome(DynmapChunkPos pos) {
return biome;
}
}
// Lighting access interface
public interface LightingAccess {
public int getLight(int x, int y, int z);
public int getLight(DynmapChunkPos pos);
}
private static class LightingAccess3D implements LightingAccess {
private final long[] light; // Nibble array (16 * y) * z (nibble at 4 << x)
private final long[] light; // Nibble array (16 * y) * z (nibble at << (4*x))
// Construct using nibble array (same as lighting format in NBT fields) (128*Y + 8*Z + X/2) (oddX high, evenX low)
LightingAccess3D(byte[] lig) {
@ -110,6 +109,10 @@ public class DynmapChunkSection {
public final int getLight(int x, int y, int z) {
return (int)(light[(16 * (y & 0xF)) + (z & 0xF)] >> (4 * (x & 0xF)) & 0xFL);
}
public final int getLight(DynmapChunkPos pos) {
return (int)(light[pos.soffset >> 4] >> (4 * pos.sx));
}
}
private static class LightingAccessSingle implements LightingAccess {
private final int light;
@ -119,6 +122,9 @@ public class DynmapChunkSection {
public final int getLight(int x, int y, int z) {
return light;
}
public final int getLight(DynmapChunkPos pos) {
return light;
}
}
private DynmapChunkSection(BlockStateAccess blks, BiomeAccess bio, LightingAccess skyac, LightingAccess emitac, boolean empty) {
blocks = blks;
@ -137,10 +143,12 @@ public class DynmapChunkSection {
// Factory for building section
public static class Builder {
private BiomeAccess ba;
private BlockStateAccess bs;
private LightingAccess sk;
private LightingAccess em;
private DynmapBlockState bsaccumsing; // Used for single
private DynmapBlockState bsaccum[]; // Use for incremental setting of 3D - YZX order
private BiomeMap baaccumsingle; // Use for single
private BiomeMap baaccum[]; // Use for incremental setting of 3D biome - YZX order or 2D biome (ZX order) length used to control which
private boolean empty;
// Initialize builder with empty state
public Builder() {
@ -148,8 +156,10 @@ public class DynmapChunkSection {
}
// Reset builder to default state
public void reset() {
ba = defaultBiome;
bs = defaultBlockState;
bsaccumsing = DynmapBlockState.AIR;
bsaccum = null;
baaccumsingle = BiomeMap.NULL;
baaccum = null;
sk = defaultSky;
em = defaultEmit;
empty = true;
@ -176,33 +186,111 @@ public class DynmapChunkSection {
}
// Set bipme to single value
public Builder singleBiome(BiomeMap bio) {
ba = new BiomeAccessSingle(bio);
baaccumsingle = bio;
baaccum = null;
return this;
}
// Set bipme to 2D array of values (bio[x][z] = 16 x 16)
public Builder xzBiome(BiomeMap bio[][]) {
ba = new BiomeAccess2D(bio);
// Set bipme for 2D style
public Builder xzBiome(int x, int z, BiomeMap bio) {
if ((baaccum == null) || (baaccum.length != 256)) {
baaccum = new BiomeMap[256];
Arrays.fill(baaccum, BiomeMap.NULL);
baaccumsingle = BiomeMap.NULL;
}
baaccum[((z & 0xF) << 4) + (x & 0xF)] = bio;
return this;
}
// Set bipme to 3D array of values (bio[x][y][z] = 4 x 4 x 4)
public Builder xyzBiome(BiomeMap bio[][][]) {
ba = new BiomeAccess3D(bio);
// Set bipme to 3D style
public Builder xyzBiome(int x, int y, int z, BiomeMap bio) {
if ((baaccum == null) || (baaccum.length != 64)) {
baaccum = new BiomeMap[64];
Arrays.fill(baaccum, BiomeMap.NULL);
baaccumsingle = BiomeMap.NULL;
}
baaccum[((y & 0xC) << 4) + (z & 0xC) + ((x & 0xC) >> 2)] = bio;
return this;
}
// Set block state to single value
public Builder singleBlockState(DynmapBlockState block) {
bs = new BlockStateAccessSingle(block);
bsaccumsing = block;
bsaccum = null;
empty = block.isAir();
return this;
}
// Set block state to 3D array (blocks[x][y][z])
public Builder xyzBlockState(DynmapBlockState blocks[][][]) {
bs = new BlockStateAccess3D(blocks);
// Set block state
public Builder xyzBlockState(int x, int y, int z, DynmapBlockState block) {
if (bsaccum == null) {
bsaccum = new DynmapBlockState[4096];
Arrays.fill(bsaccum, DynmapBlockState.AIR);
bsaccumsing = DynmapBlockState.AIR;
}
bsaccum[((y & 0xF) << 8) + ((z & 0xF) << 4) + (x & 0xF)] = block;
empty = false;
return this;
}
// Build section based on current builder state
public DynmapChunkSection build() {
// Process state access - see if we can reduce
if (bsaccum != null) {
DynmapBlockState v = bsaccum[0]; // Get first
boolean mismatch = false;
for (int i = 0; i < bsaccum.length; i++) {
if (bsaccum[i] != v) {
mismatch = true;
break;
}
}
if (!mismatch) { // All the same?
bsaccumsing = v;
bsaccum = null;
}
}
BlockStateAccess bs;
if (bsaccum != null) {
bs = new BlockStateAccess3D(bsaccum);
bsaccum = null;
empty = false;
}
else if (bsaccumsing == DynmapBlockState.AIR) { // Just air?
bs = defaultBlockState;
empty = true;
}
else {
bs = new BlockStateAccessSingle(bsaccumsing);
bsaccumsing = DynmapBlockState.AIR;
}
// See if biome access can be reduced to single
if (baaccum != null) {
BiomeMap v = baaccum[0]; // Get first
boolean mismatch = false;
for (int i = 0; i < baaccum.length; i++) {
if (baaccum[i] != v) {
mismatch = true;
break;
}
}
if (!mismatch) { // All the same?
baaccumsingle = v;
baaccum = null;
}
}
BiomeAccess ba;
if (baaccum != null) {
if (baaccum.length == 64) { // 3D?
ba = new BiomeAccess3D(baaccum);
}
else {
ba = new BiomeAccess2D(baaccum);
}
baaccum = null;
}
else if (baaccumsingle == BiomeMap.NULL) { // Just null?
ba = defaultBiome;
}
else {
ba = new BiomeAccessSingle(baaccumsingle);
baaccumsingle = BiomeMap.NULL;
}
return new DynmapChunkSection(bs, ba, sk, em, empty);
}
}