Try out generic chunk code and 3d biomes on Forge 1.18

This commit is contained in:
Mike Primm 2021-12-04 21:33:02 -06:00
parent 13d2cc05cb
commit 8c51db5608
9 changed files with 1308 additions and 1735 deletions

View File

@ -6,19 +6,19 @@ import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.common.BiomeMap; import org.dynmap.common.BiomeMap;
// Generic chunk representation // Generic chunk representation
public class DynmapChunk { public class GenericChunk {
public final int cx, cz; // Chunk coord (world coord / 16) 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 int cy_min; // CY value of first section in sections list (index = (Y >> 4) - cy_min
public final long inhabitedTicks; 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.cx = cx;
this.cz = cz; this.cz = cz;
this.inhabitedTicks = inhabTicks; 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 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++) { for (int off = 0; off < sections.length; off++) {
if (sections[off] != null) { // If defined, set the section if (sections[off] != null) { // If defined, set the section
this.sections[off+1] = sections[off]; this.sections[off+1] = sections[off];
@ -26,36 +26,36 @@ public class DynmapChunk {
} }
} }
// Get section for given block Y coord // Get section for given block Y coord
public final DynmapChunkSection getSection(int y) { public final GenericChunkSection getSection(int y) {
try { try {
return this.sections[(y >> 4) - cy_min]; return this.sections[(y >> 4) - cy_min];
} catch (IndexOutOfBoundsException ioobx) { // Builder and padding should be avoiding this, but be safe } 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) { public final DynmapBlockState getBlockType(int x, int y, int z) {
return getSection(y).blocks.getBlock(x, y, 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); return getSection(pos.y).blocks.getBlock(pos);
} }
public final int getBlockSkyLight(int x, int y, int z) { public final int getBlockSkyLight(int x, int y, int z) {
return getSection(y).sky.getLight(x, y, 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); return getSection(pos.y).sky.getLight(pos);
} }
public final int getBlockEmittedLight(int x, int y, int z) { public final int getBlockEmittedLight(int x, int y, int z) {
return getSection(y).emitted.getLight(x, y, 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); return getSection(pos.y).emitted.getLight(pos);
} }
public final BiomeMap getBiome(int x, int y, int z) { public final BiomeMap getBiome(int x, int y, int z) {
return getSection(y).biomes.getBiome(x, y, 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); return getSection(pos.y).biomes.getBiome(pos);
} }
public final boolean isSectionEmpty(int cy) { public final boolean isSectionEmpty(int cy) {
@ -63,13 +63,17 @@ public class DynmapChunk {
} }
public final long getInhabitedTicks() { public final long getInhabitedTicks() {
return inhabitedTicks; 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 // Builder for fabricating finalized chunk
public static class Builder { public static class Builder {
int x; int x;
int z; int z;
int y_min; int y_min;
DynmapChunkSection[] sections; GenericChunkSection[] sections;
long inhabTicks; long inhabTicks;
public Builder(int world_ymin, int world_ymax) { public Builder(int world_ymin, int world_ymax) {
@ -79,7 +83,7 @@ public class DynmapChunk {
x = 0; z = 0; x = 0; z = 0;
y_min = world_ymin >> 4; y_min = world_ymin >> 4;
int y_max = (world_ymax + 15) >> 4; // Round up 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 // Set inhabited ticks
public Builder inhabitedTicks(long inh) { public Builder inhabitedTicks(long inh) {
@ -87,7 +91,7 @@ public class DynmapChunk {
return this; return this;
} }
// Set section // 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)) { if ((sy >= y_min) && ((sy - y_min) < sections.length)) {
this.sections[sy - y_min] = sect; this.sections[sy - y_min] = sect;
} }
@ -100,8 +104,8 @@ public class DynmapChunk {
return this; return this;
} }
// Build chunk // Build chunk
public DynmapChunk build() { public GenericChunk build() {
return new DynmapChunk(x, z, y_min, sections, inhabTicks); return new GenericChunk(x, z, y_min, sections, inhabTicks);
} }
} }
} }

View File

@ -1,4 +1,4 @@
package org.dynmap.forge_1_18; package org.dynmap.common.chunk;
import java.lang.ref.Reference; import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue; import java.lang.ref.ReferenceQueue;
@ -10,35 +10,32 @@ import java.util.Map;
import org.dynmap.utils.DynIntHashMap; import org.dynmap.utils.DynIntHashMap;
public class SnapshotCache { // Generic chunk cache
public static class SnapshotRec { public class GenericChunkCache {
public ChunkSnapshot ss; public static class ChunkCacheRec {
public GenericChunk ss;
public DynIntHashMap tileData; public DynIntHashMap tileData;
}; };
private CacheHashMap snapcache; private CacheHashMap snapcache;
private ReferenceQueue<SnapshotRec> refqueue; private ReferenceQueue<ChunkCacheRec> refqueue;
private long cache_attempts; private long cache_attempts;
private long cache_success; private long cache_success;
private boolean softref; private boolean softref;
private static class CacheRec { private static class CacheRec {
Reference<SnapshotRec> ref; Reference<ChunkCacheRec> ref;
boolean hasbiome;
boolean hasrawbiome;
boolean hasblockdata;
boolean hashighesty;
} }
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class CacheHashMap extends LinkedHashMap<String, CacheRec> { public class CacheHashMap extends LinkedHashMap<String, CacheRec> {
private int limit; private int limit;
private IdentityHashMap<Reference<SnapshotRec>, String> reverselookup; private IdentityHashMap<Reference<ChunkCacheRec>, String> reverselookup;
public CacheHashMap(int lim) { public CacheHashMap(int lim) {
super(16, (float)0.75, true); super(16, (float)0.75, true);
limit = lim; limit = lim;
reverselookup = new IdentityHashMap<Reference<SnapshotRec>, String>(); reverselookup = new IdentityHashMap<Reference<ChunkCacheRec>, String>();
} }
protected boolean removeEldestEntry(Map.Entry<String, CacheRec> last) { protected boolean removeEldestEntry(Map.Entry<String, CacheRec> last) {
boolean remove = (size() >= limit); boolean remove = (size() >= limit);
@ -52,9 +49,9 @@ public class SnapshotCache {
/** /**
* Create snapshot cache * Create snapshot cache
*/ */
public SnapshotCache(int max_size, boolean softref) { public GenericChunkCache(int max_size, boolean softref) {
snapcache = new CacheHashMap(max_size); snapcache = new CacheHashMap(max_size);
refqueue = new ReferenceQueue<SnapshotRec>(); refqueue = new ReferenceQueue<ChunkCacheRec>();
this.softref = softref; this.softref = softref;
} }
private String getKey(String w, int cx, int cz) { private String getKey(String w, int cx, int cz) {
@ -95,11 +92,10 @@ public class SnapshotCache {
/** /**
* Look for chunk snapshot in cache * Look for chunk snapshot in cache
*/ */
public SnapshotRec getSnapshot(String w, int chunkx, int chunkz, public ChunkCacheRec getSnapshot(String w, int chunkx, int chunkz) {
boolean blockdata, boolean biome, boolean biomeraw, boolean highesty) {
String key = getKey(w, chunkx, chunkz); String key = getKey(w, chunkx, chunkz);
processRefQueue(); processRefQueue();
SnapshotRec ss = null; ChunkCacheRec ss = null;
CacheRec rec; CacheRec rec;
synchronized(snapcache) { synchronized(snapcache) {
rec = snapcache.get(key); 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++; cache_attempts++;
if(ss != null) cache_success++; if(ss != null) cache_success++;
@ -127,19 +115,14 @@ public class SnapshotCache {
/** /**
* Add chunk snapshot to cache * Add chunk snapshot to cache
*/ */
public void putSnapshot(String w, int chunkx, int chunkz, SnapshotRec ss, public void putSnapshot(String w, int chunkx, int chunkz, ChunkCacheRec ss) {
boolean blockdata, boolean biome, boolean biomeraw, boolean highesty) {
String key = getKey(w, chunkx, chunkz); String key = getKey(w, chunkx, chunkz);
processRefQueue(); processRefQueue();
CacheRec rec = new CacheRec(); CacheRec rec = new CacheRec();
rec.hasblockdata = blockdata;
rec.hasbiome = biome;
rec.hasrawbiome = biomeraw;
rec.hashighesty = highesty;
if (softref) if (softref)
rec.ref = new SoftReference<SnapshotRec>(ss, refqueue); rec.ref = new SoftReference<ChunkCacheRec>(ss, refqueue);
else else
rec.ref = new WeakReference<SnapshotRec>(ss, refqueue); rec.ref = new WeakReference<ChunkCacheRec>(ss, refqueue);
synchronized(snapcache) { synchronized(snapcache) {
CacheRec prevrec = snapcache.put(key, rec); CacheRec prevrec = snapcache.put(key, rec);
if(prevrec != null) { if(prevrec != null) {
@ -152,7 +135,7 @@ public class SnapshotCache {
* Process reference queue * Process reference queue
*/ */
private void processRefQueue() { private void processRefQueue() {
Reference<? extends SnapshotRec> ref; Reference<? extends ChunkCacheRec> ref;
while((ref = refqueue.poll()) != null) { while((ref = refqueue.poll()) != null) {
synchronized(snapcache) { synchronized(snapcache) {
String k = snapcache.reverselookup.remove(ref); String k = snapcache.reverselookup.remove(ref);

View File

@ -1,7 +1,7 @@
package org.dynmap.common.chunk; package org.dynmap.common.chunk;
// Generic block location iterator - represents 3D position, but includes fast precomputed chunk and section offsets // 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 x, y, z; // 3D world position
public int cx, cz; // 2D chunk position (x / 16, z / 16) public int cx, cz; // 2D chunk position (x / 16, z / 16)
public int cy; // Vertical section index (Y / 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 soffset; // Section offset (256 * sy) + (16 * sz) + sx
public int sdiv4offset; // Subsection offset (16 * (sy / 4)) + (4 * (sz / 4)) + (sx / 4) (3D biomes) 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); setPos(x, y, z);
} }
// Replace X value // Replace X value

View File

@ -6,7 +6,7 @@ import org.dynmap.common.BiomeMap;
import org.dynmap.renderer.DynmapBlockState; import org.dynmap.renderer.DynmapBlockState;
// Generic section: represents 16 x 16 x 16 grid of blocks // 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 BiomeAccess biomes; // Access for biome data
public final BlockStateAccess blocks; // Access for block states public final BlockStateAccess blocks; // Access for block states
public final LightingAccess sky; // Access for sky light data public final LightingAccess sky; // Access for sky light data
@ -16,7 +16,7 @@ public class DynmapChunkSection {
// Block state access interface // Block state access interface
public interface BlockStateAccess { public interface BlockStateAccess {
public DynmapBlockState getBlock(int x, int y, int z); 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 static class BlockStateAccess3D implements BlockStateAccess {
private final DynmapBlockState blocks[]; // YZX order private final DynmapBlockState blocks[]; // YZX order
@ -25,9 +25,9 @@ public class DynmapChunkSection {
blocks = bs; blocks = bs;
} }
public final DynmapBlockState getBlock(int x, int y, int z) { 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]; return blocks[pos.soffset];
} }
} }
@ -39,14 +39,14 @@ public class DynmapChunkSection {
public final DynmapBlockState getBlock(int x, int y, int z) { public final DynmapBlockState getBlock(int x, int y, int z) {
return block; return block;
} }
public final DynmapBlockState getBlock(DynmapChunkPos pos) { public final DynmapBlockState getBlock(GenericChunkPos pos) {
return block; return block;
} }
} }
// Biome access interface // Biome access interface
public interface BiomeAccess { public interface BiomeAccess {
public BiomeMap getBiome(int x, int y, int z); public BiomeMap getBiome(int x, int y, int z);
public BiomeMap getBiome(DynmapChunkPos pos); public BiomeMap getBiome(GenericChunkPos pos);
} }
// For classic 2D biome map // For classic 2D biome map
private static class BiomeAccess2D implements BiomeAccess { private static class BiomeAccess2D implements BiomeAccess {
@ -58,7 +58,7 @@ public class DynmapChunkSection {
public final BiomeMap getBiome(int x, int y, int z) { public final BiomeMap getBiome(int x, int y, int z) {
return biomes[((z & 0xF) << 4) + (x & 0xF)]; 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 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) { public final BiomeMap getBiome(int x, int y, int z) {
return biomes[ ((y & 0xC) << 2) | (z & 0xC) | ((x & 0xC) >> 2) ]; 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]; return biomes[pos.sdiv4offset];
} }
} }
@ -85,14 +85,14 @@ public class DynmapChunkSection {
public final BiomeMap getBiome(int x, int y, int z) { public final BiomeMap getBiome(int x, int y, int z) {
return biome; return biome;
} }
public final BiomeMap getBiome(DynmapChunkPos pos) { public final BiomeMap getBiome(GenericChunkPos pos) {
return biome; return biome;
} }
} }
// Lighting access interface // Lighting access interface
public interface LightingAccess { public interface LightingAccess {
public int getLight(int x, int y, int z); 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 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))
@ -107,11 +107,10 @@ public class DynmapChunkSection {
} }
} }
public final int getLight(int x, int y, int z) { 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) { public final int getLight(GenericChunkPos pos) {
return (int)(light[pos.soffset >> 4] >> (4 * pos.sx)); return 0xF & (int)(light[pos.soffset >> 4] >> (4 * pos.sx));
} }
} }
private static class LightingAccessSingle implements LightingAccess { private static class LightingAccessSingle implements LightingAccess {
@ -122,11 +121,11 @@ public class DynmapChunkSection {
public final int getLight(int x, int y, int z) { public final int getLight(int x, int y, int z) {
return light; return light;
} }
public final int getLight(DynmapChunkPos pos) { public final int getLight(GenericChunkPos pos) {
return light; 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; blocks = blks;
biomes = bio; biomes = bio;
sky = skyac; sky = skyac;
@ -135,11 +134,10 @@ public class DynmapChunkSection {
} }
private static BiomeAccess defaultBiome = new BiomeAccessSingle(BiomeMap.NULL); private static BiomeAccess defaultBiome = new BiomeAccessSingle(BiomeMap.NULL);
private static BlockStateAccess defaultBlockState = new BlockStateAccessSingle(DynmapBlockState.AIR); private static BlockStateAccess defaultBlockState = new BlockStateAccessSingle(DynmapBlockState.AIR);
private static LightingAccess defaultSky = new LightingAccessSingle(15); private static LightingAccess defaultLight = new LightingAccessSingle(0);
private static LightingAccess defaultEmit = new LightingAccessSingle(0);
// Shared default empty section // 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 // Factory for building section
public static class Builder { public static class Builder {
@ -160,8 +158,8 @@ public class DynmapChunkSection {
bsaccum = null; bsaccum = null;
baaccumsingle = BiomeMap.NULL; baaccumsingle = BiomeMap.NULL;
baaccum = null; baaccum = null;
sk = defaultSky; sk = defaultLight;
em = defaultEmit; em = defaultLight;
empty = true; empty = true;
} }
// Set sky lighting to single value // Set sky lighting to single value
@ -201,13 +199,13 @@ public class DynmapChunkSection {
return this; return this;
} }
// Set bipme to 3D style // 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)) { if ((baaccum == null) || (baaccum.length != 64)) {
baaccum = new BiomeMap[64]; baaccum = new BiomeMap[64];
Arrays.fill(baaccum, BiomeMap.NULL); Arrays.fill(baaccum, BiomeMap.NULL);
baaccumsingle = 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; return this;
} }
// Set block state to single value // Set block state to single value
@ -229,7 +227,7 @@ public class DynmapChunkSection {
return this; return this;
} }
// Build section based on current builder state // Build section based on current builder state
public DynmapChunkSection build() { public GenericChunkSection build() {
// Process state access - see if we can reduce // Process state access - see if we can reduce
if (bsaccum != null) { if (bsaccum != null) {
DynmapBlockState v = bsaccum[0]; // Get first DynmapBlockState v = bsaccum[0]; // Get first
@ -291,7 +289,7 @@ public class DynmapChunkSection {
ba = new BiomeAccessSingle(baaccumsingle); ba = new BiomeAccessSingle(baaccumsingle);
baaccumsingle = BiomeMap.NULL; baaccumsingle = BiomeMap.NULL;
} }
return new DynmapChunkSection(bs, ba, sk, em, empty); return new GenericChunkSection(bs, ba, sk, em, empty);
} }
} }
} }

File diff suppressed because it is too large Load Diff

View File

@ -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<Section> sections = new LinkedList<Section>();
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;
}
}

View File

@ -95,6 +95,7 @@ import org.dynmap.common.DynmapCommandSender;
import org.dynmap.common.DynmapPlayer; import org.dynmap.common.DynmapPlayer;
import org.dynmap.common.DynmapServerInterface; import org.dynmap.common.DynmapServerInterface;
import org.dynmap.common.DynmapListenerManager.EventType; 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.DmapCommand;
import org.dynmap.forge_1_18.DmarkerCommand; import org.dynmap.forge_1_18.DmarkerCommand;
import org.dynmap.forge_1_18.DynmapCommand; import org.dynmap.forge_1_18.DynmapCommand;
@ -128,7 +129,7 @@ public class DynmapPlugin
private DynmapCore core; private DynmapCore core;
private PermissionProvider permissions; private PermissionProvider permissions;
private boolean core_enabled; private boolean core_enabled;
public SnapshotCache sscache; public GenericChunkCache sscache;
public PlayerList playerList; public PlayerList playerList;
private MapManager mapManager; private MapManager mapManager;
private static net.minecraft.server.MinecraftServer server; private static net.minecraft.server.MinecraftServer server;
@ -859,11 +860,6 @@ public class DynmapPlugin
c.setHiddenFillStyle(w.hiddenchunkstyle); 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? */ if (chunks.size() == 0) /* No chunks to get? */
{ {
c.loadChunks(0); c.loadChunks(0);
@ -1528,7 +1524,7 @@ public class DynmapPlugin
} }
playerList = core.playerList; playerList = core.playerList;
sscache = new SnapshotCache(core.getSnapShotCacheSize(), core.useSoftRefInSnapShotCache()); sscache = new GenericChunkCache(core.getSnapShotCacheSize(), core.useSoftRefInSnapShotCache());
/* Get map manager from core */ /* Get map manager from core */
mapManager = core.getMapManager(); mapManager = core.getMapManager();

View File

@ -214,8 +214,8 @@ public class ForgeWorld extends DynmapWorld
@Override @Override
public MapChunkCache getChunkCache(List<DynmapChunk> chunks) public MapChunkCache getChunkCache(List<DynmapChunk> chunks)
{ {
if(world != null) { if (world != null) {
ForgeMapChunkCache c = new ForgeMapChunkCache(); ForgeMapChunkCache c = new ForgeMapChunkCache(DynmapPlugin.plugin.sscache);
c.setChunks(this, chunks); c.setChunks(this, chunks);
return c; return c;
} }