diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunk.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java similarity index 68% rename from DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunk.java rename to DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java index 85f8e30c..1e778c34 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunk.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java @@ -6,19 +6,19 @@ import org.dynmap.renderer.DynmapBlockState; import org.dynmap.common.BiomeMap; // Generic chunk representation -public class DynmapChunk { +public class GenericChunk { public final int cx, cz; // Chunk coord (world coord / 16) - public final DynmapChunkSection[] sections; + public final GenericChunkSection[] sections; public final int cy_min; // CY value of first section in sections list (index = (Y >> 4) - cy_min public final long inhabitedTicks; - private DynmapChunk(int cx, int cz, int cy_min, DynmapChunkSection[] sections, long inhabTicks) { + private GenericChunk(int cx, int cz, int cy_min, GenericChunkSection[] sections, long inhabTicks) { this.cx = cx; this.cz = cz; this.inhabitedTicks = inhabTicks; - this.sections = new DynmapChunkSection[sections.length + 2]; // Add one empty at top and bottom + this.sections = new GenericChunkSection[sections.length + 2]; // Add one empty at top and bottom this.cy_min = cy_min - 1; // Include empty at bottom - Arrays.fill(this.sections, DynmapChunkSection.EMPTY); // Fill all spots with empty, including pad on bottom/top + Arrays.fill(this.sections, GenericChunkSection.EMPTY); // Fill all spots with empty, including pad on bottom/top for (int off = 0; off < sections.length; off++) { if (sections[off] != null) { // If defined, set the section this.sections[off+1] = sections[off]; @@ -26,36 +26,36 @@ public class DynmapChunk { } } // Get section for given block Y coord - public final DynmapChunkSection getSection(int y) { + public final GenericChunkSection getSection(int y) { try { return this.sections[(y >> 4) - cy_min]; } catch (IndexOutOfBoundsException ioobx) { // Builder and padding should be avoiding this, but be safe - return DynmapChunkSection.EMPTY; + return GenericChunkSection.EMPTY; } } public final DynmapBlockState getBlockType(int x, int y, int z) { return getSection(y).blocks.getBlock(x, y, z); } - public final DynmapBlockState getBlockType(DynmapChunkPos pos) { + public final DynmapBlockState getBlockType(GenericChunkPos 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) { + public final int getBlockSkyLight(GenericChunkPos 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) { + public final int getBlockEmittedLight(GenericChunkPos 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 BiomeMap getBiome(DynmapChunkPos pos) { + public final BiomeMap getBiome(GenericChunkPos pos) { return getSection(pos.y).biomes.getBiome(pos); } public final boolean isSectionEmpty(int cy) { @@ -63,13 +63,17 @@ public class DynmapChunk { } public final long getInhabitedTicks() { return inhabitedTicks; - } + } + + // Generic empty (coordinates are wrong, but safe otherwise + public static final GenericChunk EMPTY = new GenericChunk(0, 0, -4, new GenericChunkSection[24], 0); + // Builder for fabricating finalized chunk public static class Builder { int x; int z; int y_min; - DynmapChunkSection[] sections; + GenericChunkSection[] sections; long inhabTicks; public Builder(int world_ymin, int world_ymax) { @@ -79,7 +83,7 @@ public class DynmapChunk { x = 0; z = 0; y_min = world_ymin >> 4; int y_max = (world_ymax + 15) >> 4; // Round up - sections = new DynmapChunkSection[y_max - y_min]; // Range for all potential sections + sections = new GenericChunkSection[y_max - y_min]; // Range for all potential sections } // Set inhabited ticks public Builder inhabitedTicks(long inh) { @@ -87,7 +91,7 @@ public class DynmapChunk { return this; } // Set section - public Builder addSection(int sy, DynmapChunkSection sect) { + public Builder addSection(int sy, GenericChunkSection sect) { if ((sy >= y_min) && ((sy - y_min) < sections.length)) { this.sections[sy - y_min] = sect; } @@ -100,8 +104,8 @@ public class DynmapChunk { return this; } // Build chunk - public DynmapChunk build() { - return new DynmapChunk(x, z, y_min, sections, inhabTicks); + public GenericChunk build() { + return new GenericChunk(x, z, y_min, sections, inhabTicks); } } } diff --git a/forge-1.18/src/main/java/org/dynmap/forge_1_18/SnapshotCache.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkCache.java similarity index 73% rename from forge-1.18/src/main/java/org/dynmap/forge_1_18/SnapshotCache.java rename to DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkCache.java index 0ef76003..d64a9bba 100644 --- a/forge-1.18/src/main/java/org/dynmap/forge_1_18/SnapshotCache.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkCache.java @@ -1,4 +1,4 @@ -package org.dynmap.forge_1_18; +package org.dynmap.common.chunk; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; @@ -10,35 +10,32 @@ import java.util.Map; import org.dynmap.utils.DynIntHashMap; -public class SnapshotCache { - public static class SnapshotRec { - public ChunkSnapshot ss; +// Generic chunk cache +public class GenericChunkCache { + public static class ChunkCacheRec { + public GenericChunk ss; public DynIntHashMap tileData; }; private CacheHashMap snapcache; - private ReferenceQueue refqueue; + private ReferenceQueue refqueue; private long cache_attempts; private long cache_success; private boolean softref; private static class CacheRec { - Reference ref; - boolean hasbiome; - boolean hasrawbiome; - boolean hasblockdata; - boolean hashighesty; + Reference ref; } @SuppressWarnings("serial") public class CacheHashMap extends LinkedHashMap { private int limit; - private IdentityHashMap, String> reverselookup; + private IdentityHashMap, String> reverselookup; public CacheHashMap(int lim) { super(16, (float)0.75, true); limit = lim; - reverselookup = new IdentityHashMap, String>(); + reverselookup = new IdentityHashMap, String>(); } protected boolean removeEldestEntry(Map.Entry last) { boolean remove = (size() >= limit); @@ -52,9 +49,9 @@ public class SnapshotCache { /** * Create snapshot cache */ - public SnapshotCache(int max_size, boolean softref) { + public GenericChunkCache(int max_size, boolean softref) { snapcache = new CacheHashMap(max_size); - refqueue = new ReferenceQueue(); + refqueue = new ReferenceQueue(); this.softref = softref; } private String getKey(String w, int cx, int cz) { @@ -95,11 +92,10 @@ public class SnapshotCache { /** * Look for chunk snapshot in cache */ - public SnapshotRec getSnapshot(String w, int chunkx, int chunkz, - boolean blockdata, boolean biome, boolean biomeraw, boolean highesty) { + public ChunkCacheRec getSnapshot(String w, int chunkx, int chunkz) { String key = getKey(w, chunkx, chunkz); processRefQueue(); - SnapshotRec ss = null; + ChunkCacheRec ss = null; CacheRec rec; synchronized(snapcache) { rec = snapcache.get(key); @@ -111,14 +107,6 @@ public class SnapshotCache { } } } - if(ss != null) { - if((blockdata && (!rec.hasblockdata)) || - (biome && (!rec.hasbiome)) || - (biomeraw && (!rec.hasrawbiome)) || - (highesty && (!rec.hashighesty))) { - ss = null; - } - } cache_attempts++; if(ss != null) cache_success++; @@ -127,19 +115,14 @@ public class SnapshotCache { /** * Add chunk snapshot to cache */ - public void putSnapshot(String w, int chunkx, int chunkz, SnapshotRec ss, - boolean blockdata, boolean biome, boolean biomeraw, boolean highesty) { + public void putSnapshot(String w, int chunkx, int chunkz, ChunkCacheRec ss) { String key = getKey(w, chunkx, chunkz); processRefQueue(); CacheRec rec = new CacheRec(); - rec.hasblockdata = blockdata; - rec.hasbiome = biome; - rec.hasrawbiome = biomeraw; - rec.hashighesty = highesty; if (softref) - rec.ref = new SoftReference(ss, refqueue); + rec.ref = new SoftReference(ss, refqueue); else - rec.ref = new WeakReference(ss, refqueue); + rec.ref = new WeakReference(ss, refqueue); synchronized(snapcache) { CacheRec prevrec = snapcache.put(key, rec); if(prevrec != null) { @@ -152,7 +135,7 @@ public class SnapshotCache { * Process reference queue */ private void processRefQueue() { - Reference ref; + Reference ref; while((ref = refqueue.poll()) != null) { synchronized(snapcache) { String k = snapcache.reverselookup.remove(ref); diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkPos.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkPos.java similarity index 95% rename from DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkPos.java rename to DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkPos.java index 22b4b8c7..c1a4ea4e 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkPos.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkPos.java @@ -1,7 +1,7 @@ package org.dynmap.common.chunk; // Generic block location iterator - represents 3D position, but includes fast precomputed chunk and section offsets -public class DynmapChunkPos { +public class GenericChunkPos { 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) @@ -9,7 +9,7 @@ public class DynmapChunkPos { 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) { + public GenericChunkPos(int x, int y, int z) { setPos(x, y, z); } // Replace X value diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkSection.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java similarity index 84% rename from DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkSection.java rename to DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java index 796b6bfd..46ed5b11 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/DynmapChunkSection.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java @@ -6,7 +6,7 @@ import org.dynmap.common.BiomeMap; import org.dynmap.renderer.DynmapBlockState; // Generic section: represents 16 x 16 x 16 grid of blocks -public class DynmapChunkSection { +public class GenericChunkSection { public final BiomeAccess biomes; // Access for biome data public final BlockStateAccess blocks; // Access for block states public final LightingAccess sky; // Access for sky light data @@ -16,7 +16,7 @@ public class DynmapChunkSection { // Block state access interface public interface BlockStateAccess { public DynmapBlockState getBlock(int x, int y, int z); - public DynmapBlockState getBlock(DynmapChunkPos pos); + public DynmapBlockState getBlock(GenericChunkPos pos); } private static class BlockStateAccess3D implements BlockStateAccess { private final DynmapBlockState blocks[]; // YZX order @@ -25,9 +25,9 @@ public class DynmapChunkSection { blocks = bs; } public final DynmapBlockState getBlock(int x, int y, int z) { - return blocks[(256 * y) + (16 * z) + x]; + return blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)]; } - public final DynmapBlockState getBlock(DynmapChunkPos pos) { + public final DynmapBlockState getBlock(GenericChunkPos pos) { return blocks[pos.soffset]; } } @@ -39,14 +39,14 @@ public class DynmapChunkSection { public final DynmapBlockState getBlock(int x, int y, int z) { return block; } - public final DynmapBlockState getBlock(DynmapChunkPos pos) { + public final DynmapBlockState getBlock(GenericChunkPos pos) { return block; } } // Biome access interface public interface BiomeAccess { public BiomeMap getBiome(int x, int y, int z); - public BiomeMap getBiome(DynmapChunkPos pos); + public BiomeMap getBiome(GenericChunkPos pos); } // For classic 2D biome map private static class BiomeAccess2D implements BiomeAccess { @@ -58,7 +58,7 @@ public class DynmapChunkSection { public final BiomeMap getBiome(int x, int y, int z) { return biomes[((z & 0xF) << 4) + (x & 0xF)]; } - public final BiomeMap getBiome(DynmapChunkPos pos) { + public final BiomeMap getBiome(GenericChunkPos pos) { return biomes[pos.soffset & 0xFF]; // Just ZX portion } } @@ -72,7 +72,7 @@ public class DynmapChunkSection { 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) { + public final BiomeMap getBiome(GenericChunkPos pos) { return biomes[pos.sdiv4offset]; } } @@ -85,14 +85,14 @@ public class DynmapChunkSection { public final BiomeMap getBiome(int x, int y, int z) { return biome; } - public final BiomeMap getBiome(DynmapChunkPos pos) { + public final BiomeMap getBiome(GenericChunkPos pos) { return biome; } } // Lighting access interface public interface LightingAccess { public int getLight(int x, int y, int z); - public int getLight(DynmapChunkPos pos); + public int getLight(GenericChunkPos pos); } private static class LightingAccess3D implements LightingAccess { private final long[] light; // Nibble array (16 * y) * z (nibble at << (4*x)) @@ -107,11 +107,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); + return 0xF & (int)(light[(16 * (y & 0xF)) + (z & 0xF)] >> (4 * (x & 0xF))); } - public final int getLight(DynmapChunkPos pos) { - return (int)(light[pos.soffset >> 4] >> (4 * pos.sx)); - + public final int getLight(GenericChunkPos pos) { + return 0xF & (int)(light[pos.soffset >> 4] >> (4 * pos.sx)); } } private static class LightingAccessSingle implements LightingAccess { @@ -122,11 +121,11 @@ public class DynmapChunkSection { public final int getLight(int x, int y, int z) { return light; } - public final int getLight(DynmapChunkPos pos) { + public final int getLight(GenericChunkPos pos) { return light; } } - private DynmapChunkSection(BlockStateAccess blks, BiomeAccess bio, LightingAccess skyac, LightingAccess emitac, boolean empty) { + private GenericChunkSection(BlockStateAccess blks, BiomeAccess bio, LightingAccess skyac, LightingAccess emitac, boolean empty) { blocks = blks; biomes = bio; sky = skyac; @@ -135,11 +134,10 @@ public class DynmapChunkSection { } private static BiomeAccess defaultBiome = new BiomeAccessSingle(BiomeMap.NULL); private static BlockStateAccess defaultBlockState = new BlockStateAccessSingle(DynmapBlockState.AIR); - private static LightingAccess defaultSky = new LightingAccessSingle(15); - private static LightingAccess defaultEmit = new LightingAccessSingle(0); + private static LightingAccess defaultLight = new LightingAccessSingle(0); // Shared default empty section - public static final DynmapChunkSection EMPTY = new DynmapChunkSection(defaultBlockState, defaultBiome, defaultSky, defaultEmit, true); + public static final GenericChunkSection EMPTY = new GenericChunkSection(defaultBlockState, defaultBiome, new LightingAccessSingle(15), defaultLight, true); // Factory for building section public static class Builder { @@ -160,8 +158,8 @@ public class DynmapChunkSection { bsaccum = null; baaccumsingle = BiomeMap.NULL; baaccum = null; - sk = defaultSky; - em = defaultEmit; + sk = defaultLight; + em = defaultLight; empty = true; } // Set sky lighting to single value @@ -201,13 +199,13 @@ public class DynmapChunkSection { return this; } // Set bipme to 3D style - public Builder xyzBiome(int x, int y, int z, BiomeMap bio) { + public Builder xyzBiome(int xdiv4, int ydiv4, int zdiv4, 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; + baaccum[((ydiv4 & 0x3) << 4) + ((zdiv4 & 0x3) << 2) + (xdiv4 & 0x3)] = bio; return this; } // Set block state to single value @@ -229,7 +227,7 @@ public class DynmapChunkSection { return this; } // Build section based on current builder state - public DynmapChunkSection build() { + public GenericChunkSection build() { // Process state access - see if we can reduce if (bsaccum != null) { DynmapBlockState v = bsaccum[0]; // Get first @@ -291,7 +289,7 @@ public class DynmapChunkSection { ba = new BiomeAccessSingle(baaccumsingle); baaccumsingle = BiomeMap.NULL; } - return new DynmapChunkSection(bs, ba, sk, em, empty); + return new GenericChunkSection(bs, ba, sk, em, empty); } } } diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericMapChunkCache.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericMapChunkCache.java new file mode 100644 index 00000000..6cfe0b2c --- /dev/null +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericMapChunkCache.java @@ -0,0 +1,1009 @@ +package org.dynmap.common.chunk; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import org.dynmap.DynmapChunk; +import org.dynmap.DynmapCore; +import org.dynmap.DynmapWorld; +import org.dynmap.common.BiomeMap; +import org.dynmap.common.chunk.GenericChunkCache.ChunkCacheRec; +import org.dynmap.hdmap.HDBlockModels; +import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.renderer.RenderPatchFactory; +import org.dynmap.utils.DynIntHashMap; +import org.dynmap.utils.MapChunkCache; +import org.dynmap.utils.MapIterator; +import org.dynmap.utils.BlockStep; +import org.dynmap.utils.VisibilityLimit; + +/** + * Abstract container for handling map cache and map iterator, using DynmapChunks + */ +public abstract class GenericMapChunkCache extends MapChunkCache { + private static boolean init = false; + protected DynmapWorld dw; + private int nsect; + private int sectoff; // Offset for sake of negative section indexes + private List chunks; + private ListIterator iterator; + private int x_min, x_max, z_min, z_max; + private int x_dim; + private HiddenChunkStyle hidestyle = HiddenChunkStyle.FILL_AIR; + private List visible_limits = null; + private List hidden_limits = null; + private boolean isempty = true; + private int snapcnt; + private GenericChunk[] snaparray; /* Index = (x-x_min) + ((z-z_min)*x_dim) */ + private byte[][] sameneighborbiomecnt; + private BiomeMap[][] biomemap; + private boolean[][] isSectionNotEmpty; /* Indexed by snapshot index, then by section index */ + + private static final BlockStep unstep[] = { BlockStep.X_MINUS, BlockStep.Y_MINUS, BlockStep.Z_MINUS, + BlockStep.X_PLUS, BlockStep.Y_PLUS, BlockStep.Z_PLUS }; + + private static final int getIndexInChunk(int cx, int cy, int cz) { + return (cy << 8) | (cz << 4) | cx; + } + + /** + * Iterator for traversing map chunk cache (base is for non-snapshot) + */ + public class OurMapIterator implements MapIterator { + private int x, y, z, chunkindex, bx, bz; + private GenericChunk snap; + private BlockStep laststep; + private DynmapBlockState blk; + private final int worldheight; + private final int ymin; + private final int x_base; + private final int z_base; + + OurMapIterator(int x0, int y0, int z0) { + x_base = x_min << 4; + z_base = z_min << 4; + + biomePrep(); + + initialize(x0, y0, z0); + worldheight = dw.worldheight; + ymin = dw.minY; + } + + @Override + public final void initialize(int x0, int y0, int z0) { + this.x = x0; + this.y = y0; + this.z = z0; + this.chunkindex = ((x >> 4) - x_min) + (((z >> 4) - z_min) * x_dim); + this.bx = x & 0xF; + this.bz = z & 0xF; + + if ((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = GenericChunk.EMPTY; + } else { + snap = snaparray[chunkindex]; + } + + laststep = BlockStep.Y_MINUS; + + if ((y >= ymin) && (y < worldheight)) { + blk = null; + } else { + blk = DynmapBlockState.AIR; + } + } + + @Override + public int getBlockSkyLight() { + try { + return snap.getBlockSkyLight(bx, y, bz); + } catch (ArrayIndexOutOfBoundsException aioobx) { + return 15; + } + } + + @Override + public final int getBlockEmittedLight() { + try { + return snap.getBlockEmittedLight(bx, y, bz); + } catch (ArrayIndexOutOfBoundsException aioobx) { + return 0; + } + } + + private void biomePrep() { + if (sameneighborbiomecnt != null) { + return; + } + + int x_size = x_dim << 4; + int z_size = (z_max - z_min + 1) << 4; + sameneighborbiomecnt = new byte[x_size][]; + biomemap = new BiomeMap[x_size][]; + + for (int i = 0; i < x_size; i++) { + sameneighborbiomecnt[i] = new byte[z_size]; + biomemap[i] = new BiomeMap[z_size]; + } + + for (int i = 0; i < x_size; i++) { + for (int j = 0; j < z_size; j++) { + if (j == 0) + initialize(i + x_base, 64, z_base); + else + stepPosition(BlockStep.Z_PLUS); + + BiomeMap bm = snap.getBiome(bx, y, bz); + + biomemap[i][j] = bm; + int cnt = 0; + + if (i > 0) { + if (bm == biomemap[i - 1][j]) /* Same as one to left */ + { + cnt++; + sameneighborbiomecnt[i - 1][j]++; + } + + if ((j > 0) && (bm == biomemap[i - 1][j - 1])) { + cnt++; + sameneighborbiomecnt[i - 1][j - 1]++; + } + + if ((j < (z_size - 1)) && (bm == biomemap[i - 1][j + 1])) { + cnt++; + sameneighborbiomecnt[i - 1][j + 1]++; + } + } + + if ((j > 0) && (biomemap[i][j] == biomemap[i][j - 1])) /* Same as one to above */ + { + cnt++; + sameneighborbiomecnt[i][j - 1]++; + } + + sameneighborbiomecnt[i][j] = (byte) cnt; + } + } + } + + @Override + public final BiomeMap getBiome() { + try { + return biomemap[x - x_base][z - z_base]; + } catch (Exception ex) { + return BiomeMap.NULL; + } + } + + @Override + public final int getSmoothGrassColorMultiplier(int[] colormap) { + int mult = 0xFFFFFF; + + try { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ + { + mult = bm.getModifiedGrassMultiplier(colormap[bm.biomeLookup()]); + } else { + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) { + for (int zoff = -1; zoff < 2; zoff++) { + bm = biomemap[rx + xoff][rz + zoff]; + int rmult = bm.getModifiedGrassMultiplier(colormap[bm.biomeLookup()]); + raccum += (rmult >> 16) & 0xFF; + gaccum += (rmult >> 8) & 0xFF; + baccum += rmult & 0xFF; + } + } + + mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + } catch (Exception x) { + //Log.info("getSmoothGrassColorMultiplier() error: " + x); + mult = 0xFFFFFF; + } + + //Log.info(String.format("getSmoothGrassColorMultiplier() at %d, %d = %X", x, z, mult)); + return mult; + } + + @Override + public final int getSmoothFoliageColorMultiplier(int[] colormap) { + int mult = 0xFFFFFF; + + try { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ + { + mult = bm.getModifiedFoliageMultiplier(colormap[bm.biomeLookup()]); + } else { + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) { + for (int zoff = -1; zoff < 2; zoff++) { + bm = biomemap[rx + xoff][rz + zoff]; + int rmult = bm.getModifiedFoliageMultiplier(colormap[bm.biomeLookup()]); + raccum += (rmult >> 16) & 0xFF; + gaccum += (rmult >> 8) & 0xFF; + baccum += rmult & 0xFF; + } + } + + mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + } catch (Exception x) { + //Log.info("getSmoothFoliageColorMultiplier() error: " + x); + mult = 0xFFFFFF; + } + //Log.info(String.format("getSmoothFoliageColorMultiplier() at %d, %d = %X", x, z, mult)); + + return mult; + } + + @Override + public final int getSmoothColorMultiplier(int[] colormap, int[] swampmap) { + int mult = 0xFFFFFF; + + try { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ + { + if (bm == BiomeMap.SWAMPLAND) { + mult = swampmap[bm.biomeLookup()]; + } else { + mult = colormap[bm.biomeLookup()]; + } + } else { + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) { + for (int zoff = -1; zoff < 2; zoff++) { + bm = biomemap[rx + xoff][rz + zoff]; + int rmult; + + if (bm == BiomeMap.SWAMPLAND) { + rmult = swampmap[bm.biomeLookup()]; + } else { + rmult = colormap[bm.biomeLookup()]; + } + + raccum += (rmult >> 16) & 0xFF; + gaccum += (rmult >> 8) & 0xFF; + baccum += rmult & 0xFF; + } + } + + mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + } catch (Exception x) { + //Log.info("getSmoothColorMultiplier() error: " + x); + mult = 0xFFFFFF; + } + //Log.info(String.format("getSmoothColorMultiplier() at %d, %d = %X", x, z, mult)); + + return mult; + } + + @Override + public final int getSmoothWaterColorMultiplier() { + int multv; + try { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ + { + multv = bm.getWaterColorMult(); + } else { + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) { + for (int zoff = -1; zoff < 2; zoff++) { + bm = biomemap[rx + xoff][rz + zoff]; + int mult = bm.getWaterColorMult(); + raccum += (mult >> 16) & 0xFF; + gaccum += (mult >> 8) & 0xFF; + baccum += mult & 0xFF; + } + } + + multv = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + } catch (Exception x) { + //Log.info("getSmoothWaterColorMultiplier(nomap) error: " + x); + + multv = 0xFFFFFF; + } + //Log.info(String.format("getSmoothWaterColorMultiplier(nomap) at %d, %d = %X", x, z, multv)); + + return multv; + } + + @Override + public final int getSmoothWaterColorMultiplier(int[] colormap) { + int mult = 0xFFFFFF; + + try { + int rx = x - x_base; + int rz = z - z_base; + BiomeMap bm = biomemap[rx][rz]; + + if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ + { + mult = colormap[bm.biomeLookup()]; + } else { + int raccum = 0; + int gaccum = 0; + int baccum = 0; + + for (int xoff = -1; xoff < 2; xoff++) { + for (int zoff = -1; zoff < 2; zoff++) { + bm = biomemap[rx + xoff][rz + zoff]; + int rmult = colormap[bm.biomeLookup()]; + raccum += (rmult >> 16) & 0xFF; + gaccum += (rmult >> 8) & 0xFF; + baccum += rmult & 0xFF; + } + } + + mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); + } + } catch (Exception x) { + //Log.info("getSmoothWaterColorMultiplier() error: " + x); + mult = 0xFFFFFF; + } + //Log.info(String.format("getSmoothWaterColorMultiplier() at %d, %d = %X", x, z, mult)); + + return mult; + } + + /** + * Step current position in given direction + */ + @Override + public final void stepPosition(BlockStep step) { + blk = null; + + switch (step.ordinal()) { + case 0: + x++; + bx++; + + if (bx == 16) /* Next chunk? */ + { + bx = 0; + chunkindex++; + if ((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = GenericChunk.EMPTY; + } else { + snap = snaparray[chunkindex]; + } + } + + break; + + case 1: + y++; + + if (y >= worldheight) { + blk = DynmapBlockState.AIR; + } + + break; + + case 2: + z++; + bz++; + + if (bz == 16) /* Next chunk? */ + { + bz = 0; + chunkindex += x_dim; + if ((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = GenericChunk.EMPTY; + } else { + snap = snaparray[chunkindex]; + } + } + break; + + case 3: + x--; + bx--; + + if (bx == -1) /* Next chunk? */ + { + bx = 15; + chunkindex--; + if ((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = GenericChunk.EMPTY; + } else { + snap = snaparray[chunkindex]; + } + } + + break; + + case 4: + y--; + + if (y < ymin) { + blk = DynmapBlockState.AIR; + } + + break; + + case 5: + z--; + bz--; + + if (bz == -1) /* Next chunk? */ + { + bz = 15; + chunkindex -= x_dim; + if ((chunkindex >= snapcnt) || (chunkindex < 0)) { + snap = GenericChunk.EMPTY; + } else { + snap = snaparray[chunkindex]; + } + } + break; + } + + laststep = step; + } + + /** + * Unstep current position to previous position + */ + @Override + public BlockStep unstepPosition() { + BlockStep ls = laststep; + stepPosition(unstep[ls.ordinal()]); + return ls; + } + + /** + * Unstep current position in oppisite director of given step + */ + @Override + public void unstepPosition(BlockStep s) { + stepPosition(unstep[s.ordinal()]); + } + + @Override + public final void setY(int y) { + if (y > this.y) { + laststep = BlockStep.Y_PLUS; + } else { + laststep = BlockStep.Y_MINUS; + } + + this.y = y; + + if ((y < ymin) || (y >= worldheight)) { + blk = DynmapBlockState.AIR; + } else { + blk = null; + } + } + + @Override + public final int getX() { + return x; + } + + @Override + public final int getY() { + return y; + } + + @Override + public final int getZ() { + return z; + } + + @Override + public final DynmapBlockState getBlockTypeAt(BlockStep s) { + if (s == BlockStep.Y_MINUS) { + if (y > ymin) { + return snap.getBlockType(bx, y - 1, bz); + } + } else if (s == BlockStep.Y_PLUS) { + if (y < (worldheight - 1)) { + return snap.getBlockType(bx, y + 1, bz); + } + } else { + BlockStep ls = laststep; + stepPosition(s); + DynmapBlockState tid = snap.getBlockType(bx, y, bz); + unstepPosition(); + laststep = ls; + return tid; + } + + return DynmapBlockState.AIR; + } + + @Override + public BlockStep getLastStep() { + return laststep; + } + + @Override + public int getWorldHeight() { + return worldheight; + } + + @Override + public long getBlockKey() { + return (((chunkindex * (worldheight - ymin)) + (y - ymin)) << 8) | (bx << 4) | bz; + } + + @Override + public final boolean isEmptySection() { + boolean[] flags = isSectionNotEmpty[chunkindex]; + if(flags == null) { + initSectionData(chunkindex); + flags = isSectionNotEmpty[chunkindex]; + } + return !flags[(y >> 4) + sectoff]; + } + + @Override + public RenderPatchFactory getPatchFactory() { + return HDBlockModels.getPatchDefinitionFactory(); + } + + @Override + public Object getBlockTileEntityField(String fieldId) { + // TODO: handle tile entities here + return null; + } + + @Override + public DynmapBlockState getBlockTypeAt(int xoff, int yoff, int zoff) { + int xx = this.x + xoff; + int yy = this.y + yoff; + int zz = this.z + zoff; + int idx = ((xx >> 4) - x_min) + (((zz >> 4) - z_min) * x_dim); + try { + return snaparray[idx].getBlockType(xx & 0xF, yy, zz & 0xF); + } catch (Exception x) { + return DynmapBlockState.AIR; + } + } + + @Override + public Object getBlockTileEntityFieldAt(String fieldId, int xoff, int yoff, int zoff) { + return null; + } + + @Override + public long getInhabitedTicks() { + try { + return snap.getInhabitedTicks(); + } catch (Exception x) { + return 0; + } + } + + @Override + public DynmapBlockState getBlockType() { + if (blk == null) { + blk = snap.getBlockType(bx, y, bz); + } + return blk; + } + } + + private class OurEndMapIterator extends OurMapIterator { + OurEndMapIterator(int x0, int y0, int z0) { + super(x0, y0, z0); + } + + @Override + public final int getBlockSkyLight() { + return 15; + } + } + + private static final GenericChunkSection STONESECTION = (new GenericChunkSection.Builder()).singleBlockState(DynmapBlockState.getBaseStateByName(DynmapBlockState.STONE_BLOCK)).build(); + private static final GenericChunkSection WATERSECTION = (new GenericChunkSection.Builder()).singleBlockState(DynmapBlockState.getBaseStateByName(DynmapBlockState.WATER_BLOCK)).build(); + + // Generate generic chunks for STONE and OCEAN hidden areas + private static final GenericChunk STONE = (new GenericChunk.Builder(-64, 319)) + .addSection(0, STONESECTION).addSection(1, STONESECTION).addSection(2, STONESECTION).addSection(0, STONESECTION).build(); + private static final GenericChunk OCEAN = (new GenericChunk.Builder(-64, 319)) + .addSection(0, WATERSECTION).addSection(1, WATERSECTION).addSection(2, WATERSECTION).addSection(0, WATERSECTION).build(); + + public static void init() { + if (!init) { + init = true; + } + } + + private GenericChunkCache cache; + /** + * Construct empty cache + */ + public GenericMapChunkCache(GenericChunkCache c) { + cache = c; // Save reference to cache + + init(); + } + + public void setChunks(DynmapWorld dw, List chunks) { + this.dw = dw; + nsect = (dw.worldheight - dw.minY) >> 4; + sectoff = (-dw.minY) >> 4; + this.chunks = chunks; + + /* Compute range */ + if (chunks.size() == 0) { + this.x_min = 0; + this.x_max = 0; + this.z_min = 0; + this.z_max = 0; + x_dim = 1; + } + else { + x_min = x_max = chunks.get(0).x; + z_min = z_max = chunks.get(0).z; + + for (DynmapChunk c : chunks) { + if (c.x > x_max) { + x_max = c.x; + } + + if (c.x < x_min) { + x_min = c.x; + } + + if (c.z > z_max) { + z_max = c.z; + } + + if (c.z < z_min) { + z_min = c.z; + } + } + + x_dim = x_max - x_min + 1; + } + + snapcnt = x_dim * (z_max - z_min + 1); + snaparray = new GenericChunk[snapcnt]; + isSectionNotEmpty = new boolean[snapcnt][]; + + } + + private static boolean didError = false; + + private boolean isChunkVisible(DynmapChunk chunk) { + boolean vis = true; + if (visible_limits != null) { + vis = false; + for (VisibilityLimit limit : visible_limits) { + if (limit.doIntersectChunk(chunk.x, chunk.z)) { + vis = true; + break; + } + } + } + if (vis && (hidden_limits != null)) { + for (VisibilityLimit limit : hidden_limits) { + if (limit.doIntersectChunk(chunk.x, chunk.z)) { + vis = false; + break; + } + } + } + return vis; + } + + private boolean tryChunkCache(DynmapChunk chunk, boolean vis) { + /* Check if cached chunk snapshot found */ + GenericChunk ss = null; + ChunkCacheRec ssr = cache.getSnapshot(dw.getName(), chunk.x, chunk.z); + if (ssr != null) { + ss = ssr.ss; + if (!vis) { + if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) { + ss = STONE; + } else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) { + ss = OCEAN; + } else { + ss = GenericChunk.EMPTY; + } + } + int idx = (chunk.x - x_min) + (chunk.z - z_min) * x_dim; + snaparray[idx] = ss; + } + return (ssr != null); + } + + // Prep snapshot and add to cache + private void prepChunkSnapshot(DynmapChunk chunk, GenericChunk ss) { + DynIntHashMap tileData = new DynIntHashMap(); + + ChunkCacheRec ssr = new ChunkCacheRec(); + ssr.ss = ss; + ssr.tileData = tileData; + + cache.putSnapshot(dw.getName(), chunk.x, chunk.z, ssr); + } + + // Load generic chunk from existing and already loaded chunk + protected abstract GenericChunk getLoadedChunk(DynmapChunk ch); + // Load generic chunk from unloaded chunk + protected abstract GenericChunk loadChunk(DynmapChunk ch); + + /** + * Read NBT data from loaded chunks - needs to be called from server/world + * thread to be safe + * + * @returns number loaded + */ + public int getLoadedChunks() { + int cnt = 0; + if (!dw.isLoaded()) { + isempty = true; + unloadChunks(); + return 0; + } + ListIterator iter = chunks.listIterator(); + while (iter.hasNext()) { + long startTime = System.nanoTime(); + DynmapChunk chunk = iter.next(); + int chunkindex = (chunk.x - x_min) + (chunk.z - z_min) * x_dim; + if (snaparray[chunkindex] != null) + continue; // Skip if already processed + + boolean vis = isChunkVisible(chunk); + + /* Check if cached chunk snapshot found */ + if (tryChunkCache(chunk, vis)) { + endChunkLoad(startTime, ChunkStats.CACHED_SNAPSHOT_HIT); + cnt++; + } + // If chunk is loaded and not being unloaded, we're grabbing its NBT data + else { + // Get generic chunk from already loaded chunk, if we can + GenericChunk ss = getLoadedChunk(chunk); + if (ss != null) { + if (vis) { // If visible + prepChunkSnapshot(chunk, ss); + } + else { + if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) { + ss = STONE; + } + else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) { + ss = OCEAN; + } + else { + ss = GenericChunk.EMPTY; + } + } + snaparray[chunkindex] = ss; + endChunkLoad(startTime, ChunkStats.LOADED_CHUNKS); + cnt++; + } + } + } + return cnt; + } + + @Override + public int loadChunks(int max_to_load) { + return getLoadedChunks() + readChunks(max_to_load); + + } + + public int readChunks(int max_to_load) { + if (!dw.isLoaded()) { + isempty = true; + unloadChunks(); + return 0; + } + + int cnt = 0; + + if (iterator == null) { + iterator = chunks.listIterator(); + } + + DynmapCore.setIgnoreChunkLoads(true); + + // Load the required chunks. + while ((cnt < max_to_load) && iterator.hasNext()) { + long startTime = System.nanoTime(); + + DynmapChunk chunk = iterator.next(); + + int chunkindex = (chunk.x - x_min) + (chunk.z - z_min) * x_dim; + + if (snaparray[chunkindex] != null) + continue; // Skip if already processed + + boolean vis = isChunkVisible(chunk); + + /* Check if cached chunk snapshot found */ + if (tryChunkCache(chunk, vis)) { + endChunkLoad(startTime, ChunkStats.CACHED_SNAPSHOT_HIT); + } + else { + GenericChunk ss = loadChunk(chunk); + // If read was good + if (ss != null) { + // If hidden + if (!vis) { + if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) { + ss = STONE; + } + else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) { + ss = OCEAN; + } + else { + ss = GenericChunk.EMPTY; + } + } + else { + // Prep snapshot + prepChunkSnapshot(chunk, ss); + } + snaparray[chunkindex] = ss; + endChunkLoad(startTime, ChunkStats.UNLOADED_CHUNKS); + } + else { + endChunkLoad(startTime, ChunkStats.UNGENERATED_CHUNKS); + } + } + cnt++; + } + + DynmapCore.setIgnoreChunkLoads(false); + + if (iterator.hasNext() == false) { /* If we're done */ + isempty = true; + + /* Fill missing chunks with empty dummy chunk */ + for (int i = 0; i < snaparray.length; i++) { + if (snaparray[i] == null) { + snaparray[i] = GenericChunk.EMPTY; + } + else if (snaparray[i] != GenericChunk.EMPTY) { + isempty = false; + } + } + } + return cnt; + } + + /** + * Test if done loading + */ + public boolean isDoneLoading() { + if (!dw.isLoaded()) { + return true; + } + if (iterator != null) { + return !iterator.hasNext(); + } + + return false; + } + + /** + * Test if all empty blocks + */ + public boolean isEmpty() { + return isempty; + } + + /** + * Unload chunks + */ + public void unloadChunks() { + if (snaparray != null) { + for (int i = 0; i < snaparray.length; i++) { + snaparray[i] = null; + } + + snaparray = null; + } + } + + private void initSectionData(int idx) { + isSectionNotEmpty[idx] = new boolean[nsect + 1]; + + if (snaparray[idx] != GenericChunk.EMPTY) { + for (int i = 0; i < nsect; i++) { + if (snaparray[idx].isSectionEmpty(i - sectoff) == false) { + isSectionNotEmpty[idx][i] = true; + } + } + } + } + + public boolean isEmptySection(int sx, int sy, int sz) { + int idx = (sx - x_min) + (sz - z_min) * x_dim; + boolean[] flags = isSectionNotEmpty[idx]; + if(flags == null) { + initSectionData(idx); + flags = isSectionNotEmpty[idx]; + } + return !flags[sy + sectoff]; + } + + /** + * Get cache iterator + */ + public MapIterator getIterator(int x, int y, int z) { + if (dw.getEnvironment().equals("the_end")) { + return new OurEndMapIterator(x, y, z); + } + return new OurMapIterator(x, y, z); + } + + /** + * Set hidden chunk style (default is FILL_AIR) + */ + public void setHiddenFillStyle(HiddenChunkStyle style) { + this.hidestyle = style; + } + + /** + * Add visible area limit - can be called more than once Needs to be set before + * chunks are loaded Coordinates are block coordinates + */ + public void setVisibleRange(VisibilityLimit lim) { + if (visible_limits == null) + visible_limits = new ArrayList(); + visible_limits.add(lim); + } + + /** + * Add hidden area limit - can be called more than once Needs to be set before + * chunks are loaded Coordinates are block coordinates + */ + public void setHiddenRange(VisibilityLimit lim) { + if (hidden_limits == null) + hidden_limits = new ArrayList(); + hidden_limits.add(lim); + } + + @Override + public DynmapWorld getWorld() { + return dw; + } + + @Override + public boolean setChunkDataTypes(boolean blockdata, boolean biome, boolean highestblocky, boolean rawbiome) { + return true; + } + +} diff --git a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ChunkSnapshot.java b/forge-1.18/src/main/java/org/dynmap/forge_1_18/ChunkSnapshot.java deleted file mode 100644 index e1692c2f..00000000 --- a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ChunkSnapshot.java +++ /dev/null @@ -1,405 +0,0 @@ -package org.dynmap.forge_1_18; - -import java.util.Arrays; -import java.util.LinkedList; - -import org.dynmap.common.BiomeMap; -import org.dynmap.renderer.DynmapBlockState; -import org.dynmap.utils.DataBitsPacked; - -import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.ListTag; -import net.minecraft.util.BitStorage; -import net.minecraft.util.SimpleBitStorage; - - -/** - * Represents a static, thread-safe snapshot of chunk of blocks - * Purpose is to allow clean, efficient copy of a chunk data to be made, and then handed off for processing in another thread (e.g. map rendering) - */ -public class ChunkSnapshot -{ - private static interface Section { - public DynmapBlockState getBlockType(int x, int y, int z); - public int getBlockSkyLight(int x, int y, int z); - public int getBlockEmittedLight(int x, int y, int z); - public boolean isEmpty(); - public int getBiome(int x, int y, int z); - } - - private final int x, z; - private final Section[] section; // Section, indexed by (Y/16) + sectionOffset (to handle negatives) - private final int sectionOffset; // Offset - section[N] = section for Y = N-sectionOffset - private final int[] hmap; // Height map - private final int[] biome; - private final long captureFulltime; - private final int sectionCnt; - private final long inhabitedTicks; - - private static final int BLOCKS_PER_SECTION = 16 * 16 * 16; - private static final int BIOMES_PER_SECTION = 4 * 4 * 4; - private static final int COLUMNS_PER_CHUNK = 16 * 16; - private static final byte[] emptyData = new byte[BLOCKS_PER_SECTION / 2]; - private static final byte[] fullData = new byte[BLOCKS_PER_SECTION / 2]; - - static - { - Arrays.fill(fullData, (byte)0xFF); - } - - private static class EmptySection implements Section { - @Override - public DynmapBlockState getBlockType(int x, int y, int z) { - return DynmapBlockState.AIR; - } - @Override - public int getBlockSkyLight(int x, int y, int z) { - return 15; - } - @Override - public int getBlockEmittedLight(int x, int y, int z) { - return 0; - } - @Override - public boolean isEmpty() { - return true; - } - @Override - public int getBiome(int x, int y, int z) { - return BiomeMap.PLAINS.getBiomeID(); - } - } - - private static final EmptySection empty_section = new EmptySection(); - - private static class StdSection implements Section { - DynmapBlockState[] states; - int[] biomes; - byte[] skylight; - byte[] emitlight; - - public StdSection() { - states = new DynmapBlockState[BLOCKS_PER_SECTION]; - Arrays.fill(states, DynmapBlockState.AIR); - biomes = new int[BIOMES_PER_SECTION]; - skylight = emptyData; - emitlight = emptyData; - } - @Override - public DynmapBlockState getBlockType(int x, int y, int z) { - return states[((y & 0xF) << 8) | (z << 4) | x]; - } - @Override - public int getBlockSkyLight(int x, int y, int z) { - int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); - return (skylight[off] >> (4 * (x & 1))) & 0xF; - } - @Override - public int getBlockEmittedLight(int x, int y, int z) - { - int off = ((y & 0xF) << 7) | (z << 3) | (x >> 1); - return (emitlight[off] >> (4 * (x & 1))) & 0xF; - } - @Override - public boolean isEmpty() { - return false; - } - @Override - public int getBiome(int x, int y, int z) { - return BiomeMap.PLAINS.getBiomeID(); - } - } - /** - * Construct empty chunk snapshot - * - * @param x - * @param z - */ - public ChunkSnapshot(int worldheight, int x, int z, long captime, long inhabitedTime) - { - this.x = x; - this.z = z; - this.captureFulltime = captime; - this.biome = new int[COLUMNS_PER_CHUNK]; - this.sectionCnt = worldheight / 16; - /* Allocate arrays indexed by section */ - this.section = new Section[this.sectionCnt+1]; - this.sectionOffset = 0; - - /* Fill with empty data */ - for (int i = 0; i <= this.sectionCnt; i++) { - this.section[i] = empty_section; - } - - /* Create empty height map */ - this.hmap = new int[16 * 16]; - - this.inhabitedTicks = inhabitedTime; - } - - public ChunkSnapshot(CompoundTag nbt, int worldheight) { - this.x = nbt.getInt("xPos"); - this.z = nbt.getInt("zPos"); - this.captureFulltime = 0; - this.hmap = nbt.getIntArray("HeightMap"); - this.sectionCnt = worldheight / 16; - if (nbt.contains("InhabitedTime")) { - this.inhabitedTicks = nbt.getLong("InhabitedTime"); - } - else { - this.inhabitedTicks = 0; - } - /* Allocate arrays indexed by section */ - LinkedList
sections = new LinkedList
(); - int sectoff = 0; // Default to zero - int sectcnt = 0; - /* Fill with empty data */ - for (int i = 0; i <= this.sectionCnt; i++) { - sections.add(empty_section); - sectcnt++; - } - //System.out.println("nbt.keys()=" + nbt.d().toString()); - StdSection lastsectwithbiome = null; - /* Get sections */ - ListTag sect = nbt.contains("sections") ? nbt.getList("sections", 10) : nbt.getList("Sections", 10); - for (int i = 0; i < sect.size(); i++) { - CompoundTag sec = sect.getCompound(i); - int secnum = sec.getByte("Y"); - // Beyond end - extend up - while (secnum >= (sectcnt - sectoff)) { - sections.addLast(empty_section); // Pad with empty - sectcnt++; - } - // Negative - see if we need to extend sectionOffset - while ((secnum + sectoff) < 0) { - sections.addFirst(empty_section); // Pad with empty - sectoff++; - sectcnt++; - } - //System.out.println("section(" + secnum + ")=" + sec.asString()); - // Create normal section to initialize - StdSection cursect = new StdSection(); - sections.set(secnum + sectoff, cursect); - DynmapBlockState[] states = cursect.states; - DynmapBlockState[] palette = null; - // If we've got palette and block states list, process non-empty section - if (sec.contains("Palette", 9) && sec.contains("BlockStates", 12)) { - ListTag plist = sec.getList("Palette", 10); - long[] statelist = sec.getLongArray("BlockStates"); - palette = new DynmapBlockState[plist.size()]; - for (int pi = 0; pi < plist.size(); pi++) { - CompoundTag tc = plist.getCompound(pi); - String pname = tc.getString("Name"); - if (tc.contains("Properties")) { - StringBuilder statestr = new StringBuilder(); - CompoundTag prop = tc.getCompound("Properties"); - for (String pid : prop.getAllKeys()) { - if (statestr.length() > 0) statestr.append(','); - statestr.append(pid).append('=').append(prop.get(pid).getAsString()); - } - palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); - } - if (palette[pi] == null) { - palette[pi] = DynmapBlockState.getBaseStateByName(pname); - } - if (palette[pi] == null) { - palette[pi] = DynmapBlockState.AIR; - } - } - int recsperblock = (4096 + statelist.length - 1) / statelist.length; - int bitsperblock = 64 / recsperblock; - BitStorage db = null; - DataBitsPacked dbp = null; - try { - db = new SimpleBitStorage(bitsperblock, 4096, statelist); - } catch (Exception x) { // Handle legacy encoded - bitsperblock = (statelist.length * 64) / 4096; - dbp = new DataBitsPacked(bitsperblock, 4096, statelist); - } - if (bitsperblock > 8) { // Not palette - for (int j = 0; j < 4096; j++) { - int v = (dbp != null) ? dbp.getAt(j) : db.get(j); - states[j] = DynmapBlockState.getStateByGlobalIndex(v); - } - } - else { - for (int j = 0; j < 4096; j++) { - int v = (dbp != null) ? dbp.getAt(j) : db.get(j); - states[j] = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; - } - } - } - else if (sec.contains("block_states")) { // 1.18 - CompoundTag block_states = sec.getCompound("block_states"); - // If we've block state data, process non-empty section - if (block_states.contains("data", 12)) { - long[] statelist = block_states.getLongArray("data"); - ListTag plist = block_states.getList("palette", 10); - palette = new DynmapBlockState[plist.size()]; - for (int pi = 0; pi < plist.size(); pi++) { - CompoundTag tc = plist.getCompound(pi); - String pname = tc.getString("Name"); - if (tc.contains("Properties")) { - StringBuilder statestr = new StringBuilder(); - CompoundTag prop = tc.getCompound("Properties"); - for (String pid : prop.getAllKeys()) { - if (statestr.length() > 0) statestr.append(','); - statestr.append(pid).append('=').append(prop.get(pid).getAsString()); - } - palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); - } - if (palette[pi] == null) { - palette[pi] = DynmapBlockState.getBaseStateByName(pname); - } - if (palette[pi] == null) { - palette[pi] = DynmapBlockState.AIR; - } - } - SimpleBitStorage db = null; - DataBitsPacked dbp = null; - - int bitsperblock = (statelist.length * 64) / 4096; - int expectedStatelistLength = (4096 + (64 / bitsperblock) - 1) / (64 / bitsperblock); - if (statelist.length == expectedStatelistLength) { - db = new SimpleBitStorage(bitsperblock, 4096, statelist); - } - else { - bitsperblock = (statelist.length * 64) / 4096; - dbp = new DataBitsPacked(bitsperblock, 4096, statelist); - } - if (bitsperblock > 8) { // Not palette - for (int j = 0; j < 4096; j++) { - int v = db != null ? db.get(j) : dbp.getAt(j); - states[j] = DynmapBlockState.getStateByGlobalIndex(v); - } - } - else { - for (int j = 0; j < 4096; j++) { - int v = db != null ? db.get(j) : dbp.getAt(j); - states[j] = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; - } - } - } - } - - if (sec.contains("BlockLight")) { - cursect.emitlight = sec.getByteArray("BlockLight"); - } - if (sec.contains("SkyLight")) { - cursect.skylight = sec.getByteArray("SkyLight"); - } - // If section biome palette - if (sec.contains("biomes")) { - CompoundTag nbtbiomes = sec.getCompound("biomes"); - long[] bdataPacked = nbtbiomes.getLongArray("data"); - ListTag bpalette = nbtbiomes.getList("palette", 8); - SimpleBitStorage bdata = null; - if (bdataPacked.length > 0) - bdata = new SimpleBitStorage(bdataPacked.length, 64, bdataPacked); - for (int j = 0; j < 64; j++) { - int b = bdata != null ? bdata.get(j) : 0; - cursect.biomes[j] = b < bpalette.size() ? BiomeMap.byBiomeResourceLocation(bpalette.getString(b)).getBiomeID() : -1; - } - // Favor the Y=64 version - if ((secnum == 4) || (lastsectwithbiome == null)) { - lastsectwithbiome = cursect; - } - } - } - /* Get biome data */ - this.biome = new int[COLUMNS_PER_CHUNK]; - if (nbt.contains("Biomes")) { - int[] bb = nbt.getIntArray("Biomes"); - if (bb != null) { - // If v1.15+ format - if (bb.length > COLUMNS_PER_CHUNK) { - // For now, just pad the grid with the first 16 - for (int i = 0; i < COLUMNS_PER_CHUNK; i++) { - int off = ((i >> 4) & 0xC) + ((i >> 2) & 0x3); - int bv = bb[off + 64]; // Offset to y=64 - if (bv < 0) bv = 0; - this.biome[i] = bv; - } - } - else { // Else, older chunks - for (int i = 0; i < bb.length; i++) { - int bv = bb[i]; - if (bv < 0) bv = 0; - this.biome[i] = bv; - } - } - } - } - else { // Make up 2D version for now - if (lastsectwithbiome != null) { - // For now, just pad the grid with the first 16 - for (int i = 0; i < COLUMNS_PER_CHUNK; i++) { - int off = ((i >> 4) & 0xC) + ((i >> 2) & 0x3); - int bv = lastsectwithbiome.biomes[off]; // Offset to y=64 - if (bv < 0) bv = 0; - this.biome[i] = bv; - } - } - } - // Finalize sections array - this.section = sections.toArray(new Section[sections.size()]); - this.sectionOffset = sectoff; - } - - public int getX() - { - return x; - } - - public int getZ() - { - return z; - } - - public DynmapBlockState getBlockType(int x, int y, int z) - { - int idx = (y >> 4) + sectionOffset; - if ((idx < 0) || (idx >= section.length)) return DynmapBlockState.AIR; - return section[idx].getBlockType(x, y, z); - } - - public int getBlockSkyLight(int x, int y, int z) - { - int idx = (y >> 4) + sectionOffset; - if ((idx < 0) || (idx >= section.length)) return 15; - return section[idx].getBlockSkyLight(x, y, z); - } - - public int getBlockEmittedLight(int x, int y, int z) - { - int idx = (y >> 4) + sectionOffset; - if ((idx < 0) || (idx >= section.length)) return 0; - return section[idx].getBlockEmittedLight(x, y, z); - } - - public int getHighestBlockYAt(int x, int z) - { - return hmap[z << 4 | x]; - } - - public int getBiome(int x, int z) - { - return biome[z << 4 | x]; - } - - public final long getCaptureFullTime() - { - return captureFulltime; - } - - public boolean isSectionEmpty(int sy) - { - int idx = sy + sectionOffset; - if ((idx < 0) || (idx >= section.length)) return true; - return section[idx].isEmpty(); - } - - public long getInhabitedTicks() { - return inhabitedTicks; - } -} diff --git a/forge-1.18/src/main/java/org/dynmap/forge_1_18/DynmapPlugin.java b/forge-1.18/src/main/java/org/dynmap/forge_1_18/DynmapPlugin.java index dbaf6b8f..620b5472 100644 --- a/forge-1.18/src/main/java/org/dynmap/forge_1_18/DynmapPlugin.java +++ b/forge-1.18/src/main/java/org/dynmap/forge_1_18/DynmapPlugin.java @@ -95,6 +95,7 @@ import org.dynmap.common.DynmapCommandSender; import org.dynmap.common.DynmapPlayer; import org.dynmap.common.DynmapServerInterface; import org.dynmap.common.DynmapListenerManager.EventType; +import org.dynmap.common.chunk.GenericChunkCache; import org.dynmap.forge_1_18.DmapCommand; import org.dynmap.forge_1_18.DmarkerCommand; import org.dynmap.forge_1_18.DynmapCommand; @@ -128,7 +129,7 @@ public class DynmapPlugin private DynmapCore core; private PermissionProvider permissions; private boolean core_enabled; - public SnapshotCache sscache; + public GenericChunkCache sscache; public PlayerList playerList; private MapManager mapManager; private static net.minecraft.server.MinecraftServer server; @@ -859,11 +860,6 @@ public class DynmapPlugin c.setHiddenFillStyle(w.hiddenchunkstyle); } - if (c.setChunkDataTypes(blockdata, biome, highesty, rawbiome) == false) - { - Log.severe("CraftBukkit build does not support biome APIs"); - } - if (chunks.size() == 0) /* No chunks to get? */ { c.loadChunks(0); @@ -1528,7 +1524,7 @@ public class DynmapPlugin } playerList = core.playerList; - sscache = new SnapshotCache(core.getSnapShotCacheSize(), core.useSoftRefInSnapShotCache()); + sscache = new GenericChunkCache(core.getSnapShotCacheSize(), core.useSoftRefInSnapShotCache()); /* Get map manager from core */ mapManager = core.getMapManager(); diff --git a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeMapChunkCache.java b/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeMapChunkCache.java index bd2840ce..64a9a877 100644 --- a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeMapChunkCache.java +++ b/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeMapChunkCache.java @@ -11,7 +11,10 @@ import org.dynmap.DynmapCore; import org.dynmap.DynmapWorld; import org.dynmap.Log; import org.dynmap.common.BiomeMap; -import org.dynmap.forge_1_18.SnapshotCache.SnapshotRec; +import org.dynmap.common.chunk.GenericChunk; +import org.dynmap.common.chunk.GenericChunkCache; +import org.dynmap.common.chunk.GenericChunkSection; +import org.dynmap.common.chunk.GenericMapChunkCache; import org.dynmap.hdmap.HDBlockModels; import org.dynmap.renderer.DynmapBlockState; import org.dynmap.renderer.RenderPatchFactory; @@ -19,8 +22,11 @@ import org.dynmap.utils.DynIntHashMap; import org.dynmap.utils.MapChunkCache; import org.dynmap.utils.MapIterator; import org.dynmap.utils.BlockStep; +import org.dynmap.utils.DataBitsPacked; import org.dynmap.utils.VisibilityLimit; +import net.minecraft.util.BitStorage; +import net.minecraft.util.SimpleBitStorage; import net.minecraft.nbt.ByteArrayTag; import net.minecraft.nbt.ByteTag; import net.minecraft.nbt.CompoundTag; @@ -45,818 +51,246 @@ import net.minecraft.world.level.chunk.storage.ChunkSerializer; * Container for managing chunks - dependent upon using chunk snapshots, since * rendering is off server thread */ -public class ForgeMapChunkCache extends MapChunkCache { - private static boolean init = false; - private static Field updateEntityTick = null; - +public class ForgeMapChunkCache extends GenericMapChunkCache { private ServerLevel w; - private DynmapWorld dw; private ServerChunkCache cps; - private int nsect; - private int sectoff; // Offset for sake of negative section indexes - private List chunks; - private ListIterator iterator; - private int x_min, x_max, z_min, z_max; - private int x_dim; - private boolean biome, biomeraw, highesty, blockdata; - private HiddenChunkStyle hidestyle = HiddenChunkStyle.FILL_AIR; - private List visible_limits = null; - private List hidden_limits = null; - private boolean isempty = true; - private int snapcnt; - private ChunkSnapshot[] snaparray; /* Index = (x-x_min) + ((z-z_min)*x_dim) */ - private DynIntHashMap[] snaptile; - private byte[][] sameneighborbiomecnt; - private BiomeMap[][] biomemap; - private boolean[][] isSectionNotEmpty; /* Indexed by snapshot index, then by section index */ - - private static final BlockStep unstep[] = { BlockStep.X_MINUS, BlockStep.Y_MINUS, BlockStep.Z_MINUS, - BlockStep.X_PLUS, BlockStep.Y_PLUS, BlockStep.Z_PLUS }; - - private static BiomeMap[] biome_to_bmap; - - private static final int getIndexInChunk(int cx, int cy, int cz) { - return (cy << 8) | (cz << 4) | cx; - } - - /** - * Iterator for traversing map chunk cache (base is for non-snapshot) - */ - public class OurMapIterator implements MapIterator { - private int x, y, z, chunkindex, bx, bz; - private ChunkSnapshot snap; - private BlockStep laststep; - private DynmapBlockState blk; - private final int worldheight; - private final int ymin; - private final int x_base; - private final int z_base; - - OurMapIterator(int x0, int y0, int z0) { - x_base = x_min << 4; - z_base = z_min << 4; - - if (biome) { - biomePrep(); - } - - initialize(x0, y0, z0); - worldheight = w.getHeight(); - ymin = dw.minY; - } - - @Override - public final void initialize(int x0, int y0, int z0) { - this.x = x0; - this.y = y0; - this.z = z0; - this.chunkindex = ((x >> 4) - x_min) + (((z >> 4) - z_min) * x_dim); - this.bx = x & 0xF; - this.bz = z & 0xF; - - if ((chunkindex >= snapcnt) || (chunkindex < 0)) { - snap = EMPTY; - } else { - snap = snaparray[chunkindex]; - } - - laststep = BlockStep.Y_MINUS; - - if ((y >= ymin) && (y < worldheight)) { - blk = null; - } else { - blk = DynmapBlockState.AIR; - } - } - - @Override - public int getBlockSkyLight() { - try { - return snap.getBlockSkyLight(bx, y, bz); - } catch (ArrayIndexOutOfBoundsException aioobx) { - return 15; - } - } - - @Override - public final int getBlockEmittedLight() { - try { - return snap.getBlockEmittedLight(bx, y, bz); - } catch (ArrayIndexOutOfBoundsException aioobx) { - return 0; - } - } - - private void biomePrep() { - if (sameneighborbiomecnt != null) { - return; - } - - int x_size = x_dim << 4; - int z_size = (z_max - z_min + 1) << 4; - sameneighborbiomecnt = new byte[x_size][]; - biomemap = new BiomeMap[x_size][]; - - for (int i = 0; i < x_size; i++) { - sameneighborbiomecnt[i] = new byte[z_size]; - biomemap[i] = new BiomeMap[z_size]; - } - - for (int i = 0; i < x_size; i++) { - for (int j = 0; j < z_size; j++) { - if (j == 0) - initialize(i + x_base, 64, z_base); - else - stepPosition(BlockStep.Z_PLUS); - - int bb = snap.getBiome(bx, bz); - BiomeMap bm = BiomeMap.byBiomeID(bb); - - biomemap[i][j] = bm; - int cnt = 0; - - if (i > 0) { - if (bm == biomemap[i - 1][j]) /* Same as one to left */ - { - cnt++; - sameneighborbiomecnt[i - 1][j]++; - } - - if ((j > 0) && (bm == biomemap[i - 1][j - 1])) { - cnt++; - sameneighborbiomecnt[i - 1][j - 1]++; - } - - if ((j < (z_size - 1)) && (bm == biomemap[i - 1][j + 1])) { - cnt++; - sameneighborbiomecnt[i - 1][j + 1]++; - } - } - - if ((j > 0) && (biomemap[i][j] == biomemap[i][j - 1])) /* Same as one to above */ - { - cnt++; - sameneighborbiomecnt[i][j - 1]++; - } - - sameneighborbiomecnt[i][j] = (byte) cnt; - } - } - } - - @Override - public final BiomeMap getBiome() { - try { - return biomemap[x - x_base][z - z_base]; - } catch (Exception ex) { - return BiomeMap.NULL; - } - } - - @Override - public final int getSmoothGrassColorMultiplier(int[] colormap) { - int mult = 0xFFFFFF; - - try { - int rx = x - x_base; - int rz = z - z_base; - BiomeMap bm = biomemap[rx][rz]; - - if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ - { - mult = bm.getModifiedGrassMultiplier(colormap[bm.biomeLookup()]); - } else { - int raccum = 0; - int gaccum = 0; - int baccum = 0; - - for (int xoff = -1; xoff < 2; xoff++) { - for (int zoff = -1; zoff < 2; zoff++) { - bm = biomemap[rx + xoff][rz + zoff]; - int rmult = bm.getModifiedGrassMultiplier(colormap[bm.biomeLookup()]); - raccum += (rmult >> 16) & 0xFF; - gaccum += (rmult >> 8) & 0xFF; - baccum += rmult & 0xFF; - } - } - - mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); - } - } catch (Exception x) { - //Log.info("getSmoothGrassColorMultiplier() error: " + x); - mult = 0xFFFFFF; - } - - //Log.info(String.format("getSmoothGrassColorMultiplier() at %d, %d = %X", x, z, mult)); - return mult; - } - - @Override - public final int getSmoothFoliageColorMultiplier(int[] colormap) { - int mult = 0xFFFFFF; - - try { - int rx = x - x_base; - int rz = z - z_base; - BiomeMap bm = biomemap[rx][rz]; - - if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ - { - mult = bm.getModifiedFoliageMultiplier(colormap[bm.biomeLookup()]); - } else { - int raccum = 0; - int gaccum = 0; - int baccum = 0; - - for (int xoff = -1; xoff < 2; xoff++) { - for (int zoff = -1; zoff < 2; zoff++) { - bm = biomemap[rx + xoff][rz + zoff]; - int rmult = bm.getModifiedFoliageMultiplier(colormap[bm.biomeLookup()]); - raccum += (rmult >> 16) & 0xFF; - gaccum += (rmult >> 8) & 0xFF; - baccum += rmult & 0xFF; - } - } - - mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); - } - } catch (Exception x) { - //Log.info("getSmoothFoliageColorMultiplier() error: " + x); - mult = 0xFFFFFF; - } - //Log.info(String.format("getSmoothFoliageColorMultiplier() at %d, %d = %X", x, z, mult)); - - return mult; - } - - @Override - public final int getSmoothColorMultiplier(int[] colormap, int[] swampmap) { - int mult = 0xFFFFFF; - - try { - int rx = x - x_base; - int rz = z - z_base; - BiomeMap bm = biomemap[rx][rz]; - - if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ - { - if (bm == BiomeMap.SWAMPLAND) { - mult = swampmap[bm.biomeLookup()]; - } else { - mult = colormap[bm.biomeLookup()]; - } - } else { - int raccum = 0; - int gaccum = 0; - int baccum = 0; - - for (int xoff = -1; xoff < 2; xoff++) { - for (int zoff = -1; zoff < 2; zoff++) { - bm = biomemap[rx + xoff][rz + zoff]; - int rmult; - - if (bm == BiomeMap.SWAMPLAND) { - rmult = swampmap[bm.biomeLookup()]; - } else { - rmult = colormap[bm.biomeLookup()]; - } - - raccum += (rmult >> 16) & 0xFF; - gaccum += (rmult >> 8) & 0xFF; - baccum += rmult & 0xFF; - } - } - - mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); - } - } catch (Exception x) { - //Log.info("getSmoothColorMultiplier() error: " + x); - mult = 0xFFFFFF; - } - //Log.info(String.format("getSmoothColorMultiplier() at %d, %d = %X", x, z, mult)); - - return mult; - } - - @Override - public final int getSmoothWaterColorMultiplier() { - int multv; - try { - int rx = x - x_base; - int rz = z - z_base; - BiomeMap bm = biomemap[rx][rz]; - - if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ - { - multv = bm.getWaterColorMult(); - } else { - int raccum = 0; - int gaccum = 0; - int baccum = 0; - - for (int xoff = -1; xoff < 2; xoff++) { - for (int zoff = -1; zoff < 2; zoff++) { - bm = biomemap[rx + xoff][rz + zoff]; - int mult = bm.getWaterColorMult(); - raccum += (mult >> 16) & 0xFF; - gaccum += (mult >> 8) & 0xFF; - baccum += mult & 0xFF; - } - } - - multv = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); - } - } catch (Exception x) { - //Log.info("getSmoothWaterColorMultiplier(nomap) error: " + x); - - multv = 0xFFFFFF; - } - //Log.info(String.format("getSmoothWaterColorMultiplier(nomap) at %d, %d = %X", x, z, multv)); - - return multv; - } - - @Override - public final int getSmoothWaterColorMultiplier(int[] colormap) { - int mult = 0xFFFFFF; - - try { - int rx = x - x_base; - int rz = z - z_base; - BiomeMap bm = biomemap[rx][rz]; - - if (sameneighborbiomecnt[rx][rz] >= (byte) 8) /* All neighbors same? */ - { - mult = colormap[bm.biomeLookup()]; - } else { - int raccum = 0; - int gaccum = 0; - int baccum = 0; - - for (int xoff = -1; xoff < 2; xoff++) { - for (int zoff = -1; zoff < 2; zoff++) { - bm = biomemap[rx + xoff][rz + zoff]; - int rmult = colormap[bm.biomeLookup()]; - raccum += (rmult >> 16) & 0xFF; - gaccum += (rmult >> 8) & 0xFF; - baccum += rmult & 0xFF; - } - } - - mult = ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); - } - } catch (Exception x) { - //Log.info("getSmoothWaterColorMultiplier() error: " + x); - mult = 0xFFFFFF; - } - //Log.info(String.format("getSmoothWaterColorMultiplier() at %d, %d = %X", x, z, mult)); - - return mult; - } - - /** - * Step current position in given direction - */ - @Override - public final void stepPosition(BlockStep step) { - blk = null; - - switch (step.ordinal()) { - case 0: - x++; - bx++; - - if (bx == 16) /* Next chunk? */ - { - bx = 0; - chunkindex++; - if ((chunkindex >= snapcnt) || (chunkindex < 0)) { - snap = EMPTY; - } else { - snap = snaparray[chunkindex]; - } - } - - break; - - case 1: - y++; - - if (y >= worldheight) { - blk = DynmapBlockState.AIR; - } - - break; - - case 2: - z++; - bz++; - - if (bz == 16) /* Next chunk? */ - { - bz = 0; - chunkindex += x_dim; - if ((chunkindex >= snapcnt) || (chunkindex < 0)) { - snap = EMPTY; - } else { - snap = snaparray[chunkindex]; - } - } - break; - - case 3: - x--; - bx--; - - if (bx == -1) /* Next chunk? */ - { - bx = 15; - chunkindex--; - if ((chunkindex >= snapcnt) || (chunkindex < 0)) { - snap = EMPTY; - } else { - snap = snaparray[chunkindex]; - } - } - - break; - - case 4: - y--; - - if (y < ymin) { - blk = DynmapBlockState.AIR; - } - - break; - - case 5: - z--; - bz--; - - if (bz == -1) /* Next chunk? */ - { - bz = 15; - chunkindex -= x_dim; - if ((chunkindex >= snapcnt) || (chunkindex < 0)) { - snap = EMPTY; - } else { - snap = snaparray[chunkindex]; - } - } - break; - } - - laststep = step; - } - - /** - * Unstep current position to previous position - */ - @Override - public BlockStep unstepPosition() { - BlockStep ls = laststep; - stepPosition(unstep[ls.ordinal()]); - return ls; - } - - /** - * Unstep current position in oppisite director of given step - */ - @Override - public void unstepPosition(BlockStep s) { - stepPosition(unstep[s.ordinal()]); - } - - @Override - public final void setY(int y) { - if (y > this.y) { - laststep = BlockStep.Y_PLUS; - } else { - laststep = BlockStep.Y_MINUS; - } - - this.y = y; - - if ((y < ymin) || (y >= worldheight)) { - blk = DynmapBlockState.AIR; - } else { - blk = null; - } - } - - @Override - public final int getX() { - return x; - } - - @Override - public final int getY() { - return y; - } - - @Override - public final int getZ() { - return z; - } - - @Override - public final DynmapBlockState getBlockTypeAt(BlockStep s) { - if (s == BlockStep.Y_MINUS) { - if (y > ymin) { - return snap.getBlockType(bx, y - 1, bz); - } - } else if (s == BlockStep.Y_PLUS) { - if (y < (worldheight - 1)) { - return snap.getBlockType(bx, y + 1, bz); - } - } else { - BlockStep ls = laststep; - stepPosition(s); - DynmapBlockState tid = snap.getBlockType(bx, y, bz); - unstepPosition(); - laststep = ls; - return tid; - } - - return DynmapBlockState.AIR; - } - - @Override - public BlockStep getLastStep() { - return laststep; - } - - @Override - public int getWorldHeight() { - return worldheight; - } - - @Override - public long getBlockKey() { - return (((chunkindex * (worldheight - ymin)) + (y - ymin)) << 8) | (bx << 4) | bz; - } - - @Override - public final boolean isEmptySection() { - boolean[] flags = isSectionNotEmpty[chunkindex]; - if(flags == null) { - initSectionData(chunkindex); - flags = isSectionNotEmpty[chunkindex]; - } - return !flags[(y >> 4) + sectoff]; - } - - @Override - public RenderPatchFactory getPatchFactory() { - return HDBlockModels.getPatchDefinitionFactory(); - } - - @Override - public Object getBlockTileEntityField(String fieldId) { - try { - int idx = getIndexInChunk(bx, y, bz); - Object[] vals = (Object[]) snaptile[chunkindex].get(idx); - for (int i = 0; i < vals.length; i += 2) { - if (vals[i].equals(fieldId)) { - return vals[i + 1]; - } - } - } catch (Exception x) { - } - return null; - } - - @Override - public DynmapBlockState getBlockTypeAt(int xoff, int yoff, int zoff) { - int xx = this.x + xoff; - int yy = this.y + yoff; - int zz = this.z + zoff; - int idx = ((xx >> 4) - x_min) + (((zz >> 4) - z_min) * x_dim); - try { - return snaparray[idx].getBlockType(xx & 0xF, yy, zz & 0xF); - } catch (Exception x) { - return DynmapBlockState.AIR; - } - } - - @Override - public Object getBlockTileEntityFieldAt(String fieldId, int xoff, int yoff, int zoff) { - return null; - } - - @Override - public long getInhabitedTicks() { - try { - return snap.getInhabitedTicks(); - } catch (Exception x) { - return 0; - } - } - - @Override - public DynmapBlockState getBlockType() { - if (blk == null) { - blk = snap.getBlockType(bx, y, bz); - } - return blk; - } - } - - private class OurEndMapIterator extends OurMapIterator { - OurEndMapIterator(int x0, int y0, int z0) { - super(x0, y0, z0); - } - - @Override - public final int getBlockSkyLight() { - return 15; - } - } - - /** - * Chunk cache for representing unloaded chunk (or air) - */ - private static class EmptyChunk extends ChunkSnapshot { - public EmptyChunk() { - super(256, 0, 0, 0, 0); - } - - /* Need these for interface, but not used */ - @Override - public int getX() { - return 0; - } - - @Override - public int getZ() { - return 0; - } - - @Override - public final DynmapBlockState getBlockType(int x, int y, int z) { - return DynmapBlockState.AIR; - } - - @Override - public final int getBlockSkyLight(int x, int y, int z) { - return 15; - } - - @Override - public final int getBlockEmittedLight(int x, int y, int z) { - return 0; - } - - @Override - public final int getHighestBlockYAt(int x, int z) { - return 0; - } - - @Override - public int getBiome(int x, int z) { - return -1; - } - - @Override - public boolean isSectionEmpty(int sy) { - return true; - } - } - - /** - * Chunk cache for representing generic stone chunk - */ - private static class PlainChunk extends ChunkSnapshot { - private DynmapBlockState fill; - - PlainChunk(String fill) { - super(256, 0, 0, 0, 0); - this.fill = DynmapBlockState.getBaseStateByName(fill); - } - - /* Need these for interface, but not used */ - @Override - public int getX() { - return 0; - } - - @Override - public int getZ() { - return 0; - } - - @Override - public int getBiome(int x, int z) { - return -1; - } - - @Override - public final DynmapBlockState getBlockType(int x, int y, int z) { - if (y < 64) { - return fill; - } - - return DynmapBlockState.AIR; - } - - @Override - public final int getBlockSkyLight(int x, int y, int z) { - if (y < 64) { - return 0; - } - - return 15; - } - - @Override - public final int getBlockEmittedLight(int x, int y, int z) { - return 0; - } - - @Override - public final int getHighestBlockYAt(int x, int z) { - return 64; - } - - @Override - public boolean isSectionEmpty(int sy) { - return (sy > 3); - } - } - - private static final EmptyChunk EMPTY = new EmptyChunk(); - private static final PlainChunk STONE = new PlainChunk(DynmapBlockState.STONE_BLOCK); - private static final PlainChunk OCEAN = new PlainChunk(DynmapBlockState.WATER_BLOCK); - - public static void init() { - if (!init) { - init = true; - } - } - /** * Construct empty cache */ - public ForgeMapChunkCache() { + public ForgeMapChunkCache(GenericChunkCache cc) { + super(cc); init(); } + private GenericChunk parseChunkFromNBT(CompoundTag nbt) { + if ((nbt != null) && nbt.contains("Level")) { + nbt = nbt.getCompound("Level"); + } + if (nbt == null) return null; + // Start generic chunk builder + GenericChunk.Builder bld = new GenericChunk.Builder(dw.minY, dw.worldheight); + bld.coords(nbt.getInt("xPos"), nbt.getInt("zPos")); + if (nbt.contains("InhabitedTime")) { + bld.inhabitedTicks(nbt.getLong("InhabitedTime")); + } + // Check for 2D or old 3D biome data from chunk level: need these when we build old sections + List old3d = null; // By section, then YZX list + BiomeMap[] old2d = null; + if (nbt.contains("Biomes")) { + int[] bb = nbt.getIntArray("Biomes"); + if (bb != null) { + // If v1.15+ format + if (bb.length > 256) { + old3d = new ArrayList(); + // Get 4 x 4 x 4 list for each section + for (int sect = 0; sect < (bb.length / 64); sect++) { + BiomeMap smap[] = new BiomeMap[64]; + for (int i = 0; i < 64; i++) { + smap[i] = BiomeMap.byBiomeID(bb[sect*64 + i]); + } + old3d.add(smap); + } + } + else { // Else, older chunks + old2d = new BiomeMap[256]; + for (int i = 0; i < bb.length; i++) { + old2d[i] = BiomeMap.byBiomeID(bb[i]); + } + } + } + } + // Start section builder + GenericChunkSection.Builder sbld = new GenericChunkSection.Builder(); + /* Get sections */ + ListTag sect = nbt.contains("sections") ? nbt.getList("sections", 10) : nbt.getList("Sections", 10); + for (int i = 0; i < sect.size(); i++) { + CompoundTag sec = sect.getCompound(i); + int secnum = sec.getByte("Y"); + + DynmapBlockState[] palette = null; + // If we've got palette and block states list, process non-empty section + if (sec.contains("Palette", 9) && sec.contains("BlockStates", 12)) { + ListTag plist = sec.getList("Palette", 10); + long[] statelist = sec.getLongArray("BlockStates"); + palette = new DynmapBlockState[plist.size()]; + for (int pi = 0; pi < plist.size(); pi++) { + CompoundTag tc = plist.getCompound(pi); + String pname = tc.getString("Name"); + if (tc.contains("Properties")) { + StringBuilder statestr = new StringBuilder(); + CompoundTag prop = tc.getCompound("Properties"); + for (String pid : prop.getAllKeys()) { + if (statestr.length() > 0) statestr.append(','); + statestr.append(pid).append('=').append(prop.get(pid).getAsString()); + } + palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.getBaseStateByName(pname); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.AIR; + } + } + int recsperblock = (4096 + statelist.length - 1) / statelist.length; + int bitsperblock = 64 / recsperblock; + BitStorage db = null; + DataBitsPacked dbp = null; + try { + db = new SimpleBitStorage(bitsperblock, 4096, statelist); + } catch (Exception x) { // Handle legacy encoded + bitsperblock = (statelist.length * 64) / 4096; + dbp = new DataBitsPacked(bitsperblock, 4096, statelist); + } + if (bitsperblock > 8) { // Not palette + for (int j = 0; j < 4096; j++) { + int v = (dbp != null) ? dbp.getAt(j) : db.get(j); + sbld.xyzBlockState(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, DynmapBlockState.getStateByGlobalIndex(v)); + } + } + else { + for (int j = 0; j < 4096; j++) { + int v = (dbp != null) ? dbp.getAt(j) : db.get(j); + DynmapBlockState bs = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; + sbld.xyzBlockState(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, bs); + } + } + } + else if (sec.contains("block_states")) { // 1.18 + CompoundTag block_states = sec.getCompound("block_states"); + // If we've block state data, process non-empty section + if (block_states.contains("data", 12)) { + long[] statelist = block_states.getLongArray("data"); + ListTag plist = block_states.getList("palette", 10); + palette = new DynmapBlockState[plist.size()]; + for (int pi = 0; pi < plist.size(); pi++) { + CompoundTag tc = plist.getCompound(pi); + String pname = tc.getString("Name"); + if (tc.contains("Properties")) { + StringBuilder statestr = new StringBuilder(); + CompoundTag prop = tc.getCompound("Properties"); + for (String pid : prop.getAllKeys()) { + if (statestr.length() > 0) statestr.append(','); + statestr.append(pid).append('=').append(prop.get(pid).getAsString()); + } + palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.getBaseStateByName(pname); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.AIR; + } + } + SimpleBitStorage db = null; + DataBitsPacked dbp = null; + + int bitsperblock = (statelist.length * 64) / 4096; + int expectedStatelistLength = (4096 + (64 / bitsperblock) - 1) / (64 / bitsperblock); + if (statelist.length == expectedStatelistLength) { + db = new SimpleBitStorage(bitsperblock, 4096, statelist); + } + else { + bitsperblock = (statelist.length * 64) / 4096; + dbp = new DataBitsPacked(bitsperblock, 4096, statelist); + } + if (bitsperblock > 8) { // Not palette + for (int j = 0; j < 4096; j++) { + int v = db != null ? db.get(j) : dbp.getAt(j); + sbld.xyzBlockState(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, DynmapBlockState.getStateByGlobalIndex(v)); + } + } + else { + for (int j = 0; j < 4096; j++) { + int v = db != null ? db.get(j) : dbp.getAt(j); + DynmapBlockState bs = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; + sbld.xyzBlockState(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, bs); + } + } + } + } + if (sec.contains("BlockLight")) { + sbld.emittedLight(sec.getByteArray("BlockLight")); + } + if (sec.contains("SkyLight")) { + sbld.skyLight(sec.getByteArray("SkyLight")); + } + // If section biome palette + if (sec.contains("biomes")) { + CompoundTag nbtbiomes = sec.getCompound("biomes"); + long[] bdataPacked = nbtbiomes.getLongArray("data"); + ListTag bpalette = nbtbiomes.getList("palette", 8); + SimpleBitStorage bdata = null; + if (bdataPacked.length > 0) + bdata = new SimpleBitStorage(bdataPacked.length, 64, bdataPacked); + for (int j = 0; j < 64; j++) { + int b = bdata != null ? bdata.get(j) : 0; + sbld.xyzBiome(j & 0x3, (j & 0x30) >> 4, (j & 0xC) >> 2, BiomeMap.byBiomeResourceLocation(bpalette.getString(b))); + } + } + else { // Else, apply legacy biomes + if (old3d != null) { + BiomeMap m[] = old3d.get(secnum); + if (m != null) { + for (int j = 0; j < 64; j++) { + sbld.xyzBiome(j & 0x3, (j & 0x30) >> 4, (j & 0xC) >> 2, m[j]); + } + } + } + else if (old2d != null) { + for (int j = 0; j < 256; j++) { + sbld.xzBiome(j & 0xF, (j & 0xF0) >> 4, old2d[j]); + } + } + } + // Finish and add section + bld.addSection(secnum, sbld.build()); + sbld.reset(); + } + return bld.build(); + } + + // Load generic chunk from existing and already loaded chunk + protected GenericChunk getLoadedChunk(DynmapChunk chunk) { + GenericChunk gc = null; + ChunkAccess ch = cps.getChunk(chunk.x, chunk.z, ChunkStatus.FULL, false); + if (ch != null) { + CompoundTag nbt = ChunkSerializer.write(w, ch); + if ((nbt != null) && nbt.contains("Level")) { + nbt = nbt.getCompound("Level"); + } + if (nbt != null) { + gc = parseChunkFromNBT(nbt); + } + } + return gc; + } + // Load generic chunk from unloaded chunk + protected GenericChunk loadChunk(DynmapChunk chunk) { + GenericChunk gc = null; + CompoundTag nbt = readChunk(chunk.x, chunk.z); + // If read was good + if (nbt != null) { + if ((nbt != null) && nbt.contains("Level")) { + nbt = nbt.getCompound("Level"); + } + if (nbt != null) { + gc = parseChunkFromNBT(nbt); + } + } + return gc; + } + public void setChunks(ForgeWorld dw, List chunks) { - this.dw = dw; this.w = dw.getWorld(); if (dw.isLoaded()) { /* Check if world's provider is ServerChunkProvider */ cps = this.w.getChunkSource(); - } else { - chunks = new ArrayList(); } - nsect = (dw.worldheight - dw.minY) >> 4; - sectoff = (-dw.minY) >> 4; - this.chunks = chunks; - - /* Compute range */ - if (chunks.size() == 0) { - this.x_min = 0; - this.x_max = 0; - this.z_min = 0; - this.z_max = 0; - x_dim = 1; - } else { - x_min = x_max = chunks.get(0).x; - z_min = z_max = chunks.get(0).z; - - for (DynmapChunk c : chunks) { - if (c.x > x_max) { - x_max = c.x; - } - - if (c.x < x_min) { - x_min = c.x; - } - - if (c.z > z_max) { - z_max = c.z; - } - - if (c.z < z_min) { - z_min = c.z; - } - } - - x_dim = x_max - x_min + 1; - } - - snapcnt = x_dim * (z_max - z_min + 1); - snaparray = new ChunkSnapshot[snapcnt]; - snaptile = new DynIntHashMap[snapcnt]; - isSectionNotEmpty = new boolean[snapcnt][]; - + super.setChunks(dw, chunks); } - private static boolean didError = false; - - public CompoundTag readChunk(int x, int z) { + private CompoundTag readChunk(int x, int z) { try { CompoundTag rslt = cps.chunkMap.readChunk(new ChunkPos(x, z)); if (rslt != null) { @@ -880,450 +314,4 @@ public class ForgeMapChunkCache extends MapChunkCache { return null; } } - - private Object getNBTValue(Tag v) { - Object val = null; - switch (v.getId()) { - case 1: // Byte - val = Byte.valueOf(((ByteTag) v).getAsByte()); - break; - case 2: // Short - val = Short.valueOf(((ShortTag) v).getAsShort()); - break; - case 3: // Int - val = Integer.valueOf(((IntTag) v).getAsInt()); - break; - case 4: // Long - val = Long.valueOf(((LongTag) v).getAsLong()); - break; - case 5: // Float - val = Float.valueOf(((FloatTag) v).getAsFloat()); - break; - case 6: // Double - val = Double.valueOf(((DoubleTag) v).getAsDouble()); - break; - case 7: // Byte[] - val = ((ByteArrayTag) v).getAsByteArray(); - break; - case 8: // String - val = ((StringTag) v).getAsString(); - break; - case 9: // List - ListTag tl = (ListTag) v; - ArrayList vlist = new ArrayList(); - byte type = tl.getElementType(); - for (int i = 0; i < tl.size(); i++) { - switch (type) { - case 5: - float fv = tl.getFloat(i); - vlist.add(fv); - break; - case 6: - double dv = tl.getDouble(i); - vlist.add(dv); - break; - case 8: - String sv = tl.getString(i); - vlist.add(sv); - break; - case 10: - CompoundTag tc = tl.getCompound(i); - vlist.add(getNBTValue(tc)); - break; - case 11: - int[] ia = tl.getIntArray(i); - vlist.add(ia); - break; - } - } - val = vlist; - break; - case 10: // Map - CompoundTag tc = (CompoundTag) v; - HashMap vmap = new HashMap(); - for (Object t : tc.getAllKeys()) { - String st = (String) t; - Tag tg = tc.get(st); - vmap.put(st, getNBTValue(tg)); - } - val = vmap; - break; - case 11: // Int[] - val = ((IntArrayTag) v).getAsIntArray(); - break; - } - return val; - } - - private boolean isChunkVisible(DynmapChunk chunk) { - boolean vis = true; - if (visible_limits != null) { - vis = false; - for (VisibilityLimit limit : visible_limits) { - if (limit.doIntersectChunk(chunk.x, chunk.z)) { - vis = true; - break; - } - } - } - if (vis && (hidden_limits != null)) { - for (VisibilityLimit limit : hidden_limits) { - if (limit.doIntersectChunk(chunk.x, chunk.z)) { - vis = false; - break; - } - } - } - return vis; - } - - private boolean tryChunkCache(DynmapChunk chunk, boolean vis) { - /* Check if cached chunk snapshot found */ - ChunkSnapshot ss = null; - SnapshotRec ssr = DynmapPlugin.plugin.sscache.getSnapshot(dw.getName(), chunk.x, chunk.z, blockdata, biome, - biomeraw, highesty); - if (ssr != null) { - ss = ssr.ss; - if (!vis) { - if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) { - ss = STONE; - } else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) { - ss = OCEAN; - } else { - ss = EMPTY; - } - } - int idx = (chunk.x - x_min) + (chunk.z - z_min) * x_dim; - snaparray[idx] = ss; - snaptile[idx] = ssr.tileData; - } - return (ssr != null); - } - - // Prep snapshot and add to cache - private SnapshotRec prepChunkSnapshot(DynmapChunk chunk, CompoundTag nbt) { - ChunkSnapshot ss = new ChunkSnapshot(nbt, dw.worldheight); - DynIntHashMap tileData = new DynIntHashMap(); - - ListTag tiles = nbt.getList("TileEntities", 10); - if (tiles == null) - tiles = new ListTag(); - /* Get tile entity data */ - List vals = new ArrayList(); - for (int tid = 0; tid < tiles.size(); tid++) { - CompoundTag tc = tiles.getCompound(tid); - int tx = tc.getInt("x"); - int ty = tc.getInt("y"); - int tz = tc.getInt("z"); - int cx = tx & 0xF; - int cz = tz & 0xF; - DynmapBlockState blk = ss.getBlockType(cx, ty, cz); - String[] te_fields = HDBlockModels.getTileEntityFieldsNeeded(blk); - if (te_fields != null) { - vals.clear(); - for (String id : te_fields) { - Tag v = tc.get(id); /* Get field */ - if (v != null) { - Object val = getNBTValue(v); - if (val != null) { - vals.add(id); - vals.add(val); - } - } - } - if (vals.size() > 0) { - Object[] vlist = vals.toArray(new Object[vals.size()]); - tileData.put(getIndexInChunk(cx, ty, cz), vlist); - } - } - } - SnapshotRec ssr = new SnapshotRec(); - ssr.ss = ss; - ssr.tileData = tileData; - DynmapPlugin.plugin.sscache.putSnapshot(dw.getName(), chunk.x, chunk.z, ssr, blockdata, biome, biomeraw, - highesty); - - return ssr; - } - - /** - * Read NBT data from loaded chunks - needs to be called from server/world - * thread to be safe - * - * @returns number loaded - */ - public int getLoadedChunks() { - int cnt = 0; - if (!dw.isLoaded()) { - isempty = true; - unloadChunks(); - return 0; - } - ListIterator iter = chunks.listIterator(); - while (iter.hasNext()) { - long startTime = System.nanoTime(); - DynmapChunk chunk = iter.next(); - int chunkindex = (chunk.x - x_min) + (chunk.z - z_min) * x_dim; - if (snaparray[chunkindex] != null) - continue; // Skip if already processed - - boolean vis = isChunkVisible(chunk); - - /* Check if cached chunk snapshot found */ - if (tryChunkCache(chunk, vis)) { - endChunkLoad(startTime, ChunkStats.CACHED_SNAPSHOT_HIT); - cnt++; - } - // If chunk is loaded and not being unloaded, we're grabbing its NBT data - else { - ChunkAccess ch = cps.getChunk(chunk.x, chunk.z, ChunkStatus.FULL, false); - if (ch != null) { - ChunkSnapshot ss; - DynIntHashMap tileData; - if (vis) { // If visible - CompoundTag nbt = ChunkSerializer.write(w, ch); - if ((nbt != null) && nbt.contains("Level")) { - nbt = nbt.getCompound("Level"); - } - SnapshotRec ssr = prepChunkSnapshot(chunk, nbt); - ss = ssr.ss; - tileData = ssr.tileData; - } else { - if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) { - ss = STONE; - } else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) { - ss = OCEAN; - } else { - ss = EMPTY; - } - tileData = new DynIntHashMap(); - } - snaparray[chunkindex] = ss; - snaptile[chunkindex] = tileData; - endChunkLoad(startTime, ChunkStats.LOADED_CHUNKS); - cnt++; - } - } - } - return cnt; - } - - @Override - public int loadChunks(int max_to_load) { - return getLoadedChunks() + readChunks(max_to_load); - - } - - public int readChunks(int max_to_load) { - if (!dw.isLoaded()) { - isempty = true; - unloadChunks(); - return 0; - } - - int cnt = 0; - - if (iterator == null) { - iterator = chunks.listIterator(); - } - - DynmapCore.setIgnoreChunkLoads(true); - - // Load the required chunks. - while ((cnt < max_to_load) && iterator.hasNext()) { - long startTime = System.nanoTime(); - - DynmapChunk chunk = iterator.next(); - - int chunkindex = (chunk.x - x_min) + (chunk.z - z_min) * x_dim; - - if (snaparray[chunkindex] != null) - continue; // Skip if already processed - - boolean vis = isChunkVisible(chunk); - - /* Check if cached chunk snapshot found */ - if (tryChunkCache(chunk, vis)) { - endChunkLoad(startTime, ChunkStats.CACHED_SNAPSHOT_HIT); - } else { - CompoundTag nbt = readChunk(chunk.x, chunk.z); - // If read was good - if (nbt != null) { - ChunkSnapshot ss; - DynIntHashMap tileData; - // If hidden - if (!vis) { - if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) { - ss = STONE; - } else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) { - ss = OCEAN; - } else { - ss = EMPTY; - } - tileData = new DynIntHashMap(); - } else { - // Prep snapshot - SnapshotRec ssr = prepChunkSnapshot(chunk, nbt); - ss = ssr.ss; - tileData = ssr.tileData; - } - snaparray[chunkindex] = ss; - snaptile[chunkindex] = tileData; - endChunkLoad(startTime, ChunkStats.UNLOADED_CHUNKS); - } else { - endChunkLoad(startTime, ChunkStats.UNGENERATED_CHUNKS); - } - } - cnt++; - } - - DynmapCore.setIgnoreChunkLoads(false); - - if (iterator.hasNext() == false) /* If we're done */ - { - isempty = true; - - /* Fill missing chunks with empty dummy chunk */ - for (int i = 0; i < snaparray.length; i++) { - if (snaparray[i] == null) { - snaparray[i] = EMPTY; - } else if (snaparray[i] != EMPTY) { - isempty = false; - } - } - } - return cnt; - } - - /** - * Test if done loading - */ - public boolean isDoneLoading() { - if (!dw.isLoaded()) { - return true; - } - if (iterator != null) { - return !iterator.hasNext(); - } - - return false; - } - - /** - * Test if all empty blocks - */ - public boolean isEmpty() { - return isempty; - } - - /** - * Unload chunks - */ - public void unloadChunks() { - if (snaparray != null) { - for (int i = 0; i < snaparray.length; i++) { - snaparray[i] = null; - } - - snaparray = null; - } - } - - private void initSectionData(int idx) { - isSectionNotEmpty[idx] = new boolean[nsect + 1]; - - if (snaparray[idx] != EMPTY) { - for (int i = 0; i < nsect; i++) { - if (snaparray[idx].isSectionEmpty(i - sectoff) == false) { - isSectionNotEmpty[idx][i] = true; - } - } - } - } - - public boolean isEmptySection(int sx, int sy, int sz) { - int idx = (sx - x_min) + (sz - z_min) * x_dim; - boolean[] flags = isSectionNotEmpty[idx]; - if(flags == null) { - initSectionData(idx); - flags = isSectionNotEmpty[idx]; - } - return !flags[sy + sectoff]; - } - - /** - * Get cache iterator - */ - public MapIterator getIterator(int x, int y, int z) { - if (dw.getEnvironment().equals("the_end")) { - return new OurEndMapIterator(x, y, z); - } - - return new OurMapIterator(x, y, z); - } - - /** - * Set hidden chunk style (default is FILL_AIR) - */ - public void setHiddenFillStyle(HiddenChunkStyle style) { - this.hidestyle = style; - } - - /** - * Add visible area limit - can be called more than once Needs to be set before - * chunks are loaded Coordinates are block coordinates - */ - public void setVisibleRange(VisibilityLimit lim) { - if (visible_limits == null) - visible_limits = new ArrayList(); - visible_limits.add(lim); - } - - /** - * Add hidden area limit - can be called more than once Needs to be set before - * chunks are loaded Coordinates are block coordinates - */ - public void setHiddenRange(VisibilityLimit lim) { - if (hidden_limits == null) - hidden_limits = new ArrayList(); - hidden_limits.add(lim); - } - - @Override - public boolean setChunkDataTypes(boolean blockdata, boolean biome, boolean highestblocky, boolean rawbiome) { - this.biome = biome; - this.biomeraw = rawbiome; - this.highesty = highestblocky; - this.blockdata = blockdata; - return true; - } - - @Override - public DynmapWorld getWorld() { - return dw; - } - - static { - Biome b[] = DynmapPlugin.getBiomeList(); - BiomeMap[] bm = BiomeMap.values(); - biome_to_bmap = new BiomeMap[256]; - - for (int i = 0; i < biome_to_bmap.length; i++) { - biome_to_bmap[i] = BiomeMap.NULL; - } - - for (int i = 0; i < b.length; i++) { - if (b[i] == null) - continue; - - String bs = b[i].toString(); - - for (int j = 0; j < bm.length; j++) { - if (bm[j].toString().equals(bs)) { - biome_to_bmap[i] = bm[j]; - break; - } - } - } - } } diff --git a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeWorld.java b/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeWorld.java index 3170f1b3..186a0959 100644 --- a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeWorld.java +++ b/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeWorld.java @@ -214,8 +214,8 @@ public class ForgeWorld extends DynmapWorld @Override public MapChunkCache getChunkCache(List chunks) { - if(world != null) { - ForgeMapChunkCache c = new ForgeMapChunkCache(); + if (world != null) { + ForgeMapChunkCache c = new ForgeMapChunkCache(DynmapPlugin.plugin.sscache); c.setChunks(this, chunks); return c; }