From 13d2cc05cbc522c2832466164583489939b198b6 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sat, 4 Dec 2021 12:12:37 -0600 Subject: [PATCH] More generic chunk and iterator support classes --- .../org/dynmap/common/chunk/DynmapChunk.java | 21 ++- .../dynmap/common/chunk/DynmapChunkPos.java | 53 ++++++ .../common/chunk/DynmapChunkSection.java | 178 +++++++++++++----- 3 files changed, 200 insertions(+), 52 deletions(-) create mode 100644 DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkPos.java diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunk.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunk.java index af25034a..85f8e30c 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunk.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunk.java @@ -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; } diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkPos.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkPos.java new file mode 100644 index 00000000..22b4b8c7 --- /dev/null +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkPos.java @@ -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); + } +} diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkSection.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkSection.java index abe0b492..796b6bfd 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkSection.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkSection.java @@ -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); } }