diff --git a/DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java b/DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java index a1225922..b692b4b1 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java +++ b/DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java @@ -129,10 +129,10 @@ public class BiomeMap { } static { - for (int i = 0; i < 256; i++) { - BiomeMap bm = BiomeMap.byBiomeID(i); + for (int i = 0; i < biome_by_index.length; i++) { + BiomeMap bm = BiomeMap.byBiomeID(i-1); if (bm == null) { - bm = new BiomeMap(i, "BIOME_" + i); + bm = new BiomeMap(i-1, "BIOME_" + (i-1)); bm.isDef = true; } } @@ -153,7 +153,7 @@ public class BiomeMap { biome_by_index = Arrays.copyOf(biome_by_index, idx * 3 / 2); for (int i = oldlen; i < biome_by_index.length; i++) { if (biome_by_index[i] == null) { - BiomeMap bm = new BiomeMap(i, "BIOME_" + i); + BiomeMap bm = new BiomeMap(i-1, "BIOME_" + (i-1)); bm.isDef = true; } } diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericMapChunkCache.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericMapChunkCache.java index c9828e14..b864cb81 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericMapChunkCache.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericMapChunkCache.java @@ -125,7 +125,7 @@ public abstract class GenericMapChunkCache extends MapChunkCache { if ((nchunkindex >= snapcnt) || (nchunkindex < 0)) { return BiomeMap.NULL; } else { - return snaparray[chunkindex].getBiome(nx, y, nz); + return snaparray[nchunkindex].getBiome(nx, y, nz); } } diff --git a/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/ChunkSnapshot.java b/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/ChunkSnapshot.java deleted file mode 100644 index a0a1d13d..00000000 --- a/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/ChunkSnapshot.java +++ /dev/null @@ -1,352 +0,0 @@ -package org.dynmap.fabric_1_18; - -import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtList; -import net.minecraft.util.collection.PackedIntegerArray; -import net.minecraft.util.math.MathHelper; -import net.minecraft.util.math.WordPackedArray; -import org.dynmap.Log; -import org.dynmap.common.BiomeMap; -import org.dynmap.renderer.DynmapBlockState; -import net.fabricmc.fabric.api.util.NbtType; - -import java.util.Arrays; -import java.util.LinkedList; - -/** - * 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; - private final int sectionOffset; - private final int[] hmap; // Height map - 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]; - biomes = new int[BIOMES_PER_SECTION]; - Arrays.fill(biomes, BiomeMap.PLAINS.getBiomeID()); - Arrays.fill(states, DynmapBlockState.AIR); - 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) { - int off = (((y & 0xF) >> 2) << 4) | ((z >> 2) << 2) | (x >> 2); - return biomes[off]; - } - } - - /** - * 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.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[COLUMNS_PER_CHUNK]; - - this.inhabitedTicks = inhabitedTime; - } - - public static class StateListException extends Exception { - private static final long serialVersionUID = 1L; - private static boolean loggedOnce = false; - - public StateListException(int x, int z, int actualLength, int expectedLength, int expectedLegacyLength) { - if (Log.verbose || !loggedOnce) { - loggedOnce = true; - Log.info("Skipping chunk at x=" + x + ",z=" + z + ". Expected statelist of length " + expectedLength + " or " + expectedLegacyLength + " but got " + actualLength + ". This can happen if the chunk was not yet converted to the 1.16 format which can be fixed by visiting the chunk."); - if (!Log.verbose) { - Log.info("You will only see this message once. Turn on verbose logging in the configuration to see all messages."); - } - } - } - } - - public ChunkSnapshot(NbtCompound nbt, int worldheight) throws StateListException { - this.x = nbt.getInt("xPos"); - this.z = nbt.getInt("zPos"); - this.captureFulltime = 0; - this.sectionCnt = worldheight / 16; - if (nbt.contains("InhabitedTime")) { - this.inhabitedTicks = nbt.getLong("InhabitedTime"); - } else { - this.inhabitedTicks = 0; - } - this.hmap = new int[COLUMNS_PER_CHUNK]; - if (nbt.contains("Heightmaps")) { - NbtCompound hmaps = nbt.getCompound("Heightmaps"); - long[] phmap = hmaps.getLongArray("WORLD_SURFACE"); - PackedIntegerArray hmap = new PackedIntegerArray((phmap.length * 64) / COLUMNS_PER_CHUNK, COLUMNS_PER_CHUNK, - phmap); - for (int i = 0; i < this.hmap.length; i++) { - this.hmap[i] = hmap.get(i); - } - } - /* 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++; - } - /* Get sections */ - NbtList sect = nbt.getList("sections", 10); - for (int i = 0; i < sect.size(); i++) { - NbtCompound 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; - int[] biomes = cursect.biomes; - // If we've got palette and block states list, process non-empty section - if (sec.contains("block_states", NbtType.COMPOUND)) { - NbtCompound bstat = sec.getCompound("block_states"); - NbtList plist = bstat.getList("palette", 10); - long[] statelist = bstat.getLongArray("data"); - palette = new DynmapBlockState[plist.size()]; - for (int pi = 0; pi < plist.size(); pi++) { - NbtCompound tc = plist.getCompound(pi); - String pname = tc.getString("Name"); - if (tc.contains("Properties")) { - StringBuilder statestr = new StringBuilder(); - NbtCompound prop = tc.getCompound("Properties"); - for (String pid : prop.getKeys()) { - if (statestr.length() > 0) statestr.append(','); - statestr.append(pid).append('=').append(prop.get(pid).asString()); - } - palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); - } - if (palette[pi] == null) { - palette[pi] = DynmapBlockState.getBaseStateByName(pname); - } - if (palette[pi] == null) { - palette[pi] = DynmapBlockState.AIR; - } - } - - if (statelist.length > 0) { - PackedIntegerArray db = null; - WordPackedArray dbp = null; - - int bitsperblock = (statelist.length * 64) / 4096; - int expectedStatelistLength = (4096 + (64 / bitsperblock) - 1) / (64 / bitsperblock); - if (statelist.length == expectedStatelistLength) { - db = new PackedIntegerArray(bitsperblock, 4096, statelist); - } else { - int expectedLegacyStatelistLength = MathHelper.roundUpToMultiple(bitsperblock * 4096, 64) / 64; - if (statelist.length == expectedLegacyStatelistLength) { - dbp = new WordPackedArray(bitsperblock, 4096, statelist); - } else { - throw new StateListException(x, z, statelist.length, expectedStatelistLength, - expectedLegacyStatelistLength); - } - } - - if (bitsperblock > 8) { // Not palette - for (int j = 0; j < 4096; j++) { - int v = db != null ? db.get(j) : dbp.get(j); - states[j] = DynmapBlockState.getStateByGlobalIndex(v); - } - } else { - for (int j = 0; j < 4096; j++) { - int v = db != null ? db.get(j) : dbp.get(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 (sec.contains("biomes")) { - NbtCompound nbtbiomes = sec.getCompound("biomes"); - long[] bdataPacked = nbtbiomes.getLongArray("data"); - NbtList bpalette = nbtbiomes.getList("palette", NbtType.STRING); - PackedIntegerArray bdata = null; - if (bdataPacked.length > 0) - bdata = new PackedIntegerArray(bdataPacked.length, 64, bdataPacked); - for (int j = 0; j < 64; j++) { - int b = bdata != null ? bdata.get(j) : 0; - biomes[j] = b < bpalette.size() ? BiomeMap.byBiomeResourceLocation(bpalette.getString(b)).getBiomeID() : -1; - } - } - } - // 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) { - int y = getHighestBlockYAt(x, z); - final int idx = (y >> 4) + sectionOffset; - if ((idx < 0) || (idx >= section.length)) - return 0; - return section[idx].getBiome(x % 16, y % 16, z % 16); - } - - 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/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/DynmapPlugin.java b/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/DynmapPlugin.java index f78e6d74..2588f6f0 100644 --- a/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/DynmapPlugin.java +++ b/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/DynmapPlugin.java @@ -40,6 +40,7 @@ import org.dynmap.common.BiomeMap; import org.dynmap.common.DynmapCommandSender; import org.dynmap.common.DynmapListenerManager; import org.dynmap.common.DynmapPlayer; +import org.dynmap.common.chunk.GenericChunkCache; import org.dynmap.fabric_1_18.command.DmapCommand; import org.dynmap.fabric_1_18.command.DmarkerCommand; import org.dynmap.fabric_1_18.command.DynmapCommand; @@ -66,7 +67,7 @@ public class DynmapPlugin { DynmapCore core; private PermissionProvider permissions; private boolean core_enabled; - public SnapshotCache sscache; + public GenericChunkCache sscache; public PlayerList playerList; MapManager mapManager; /** @@ -505,7 +506,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/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/FabricMapChunkCache.java b/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/FabricMapChunkCache.java index 76f13727..038fb910 100644 --- a/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/FabricMapChunkCache.java +++ b/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/FabricMapChunkCache.java @@ -4,18 +4,25 @@ import net.minecraft.nbt.*; import net.minecraft.server.world.ServerChunkManager; import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ThreadedAnvilChunkStorage; +import net.minecraft.util.collection.PackedIntegerArray; import net.minecraft.util.math.ChunkPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.WordPackedArray; import net.minecraft.util.registry.Registry; import net.minecraft.world.ChunkSerializer; import net.minecraft.world.World; import net.minecraft.world.biome.Biome; import net.minecraft.world.chunk.ChunkManager; import net.minecraft.world.chunk.ChunkStatus; + import org.dynmap.DynmapChunk; import org.dynmap.DynmapCore; import org.dynmap.DynmapWorld; import org.dynmap.Log; import org.dynmap.common.BiomeMap; +import org.dynmap.common.chunk.GenericChunk; +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; @@ -27,796 +34,18 @@ import java.util.*; /** * Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread */ -public class FabricMapChunkCache extends MapChunkCache { - private static boolean init = false; - private static Field updateEntityTick = null; - /* ChunkManager fields */ - private static Field chunksToRemove = null; // Map - +public class FabricMapChunkCache extends GenericMapChunkCache { private World w; - private DynmapWorld dw; private ServerChunkManager cps; - private int nsect; - private int sectoff; - 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 BiomeMap[] biome_to_bmap; - - private static final int getIndexInChunk(int cx, int cy, int cz) { - return (cy << 8) | (cz << 4) | cx; - } /** * Construct empty cache */ public FabricMapChunkCache(DynmapPlugin plugin) { - Registry biomeRegistry = plugin.getFabricServer().getBiomeRegistry(); - Biome b[] = plugin.getFabricServer().getBiomeList(biomeRegistry); - 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; - - //FIXME: This probably not correct. In 1.16.1 it was: b[i].getTranslationKey(); - String bs = biomeRegistry.getId(b[i]).toString(); - - for (int j = 0; j < bm.length; j++) { - if (bm[j].toString().equals(bs)) { - biome_to_bmap[i] = bm[j]; - break; - } - } - } - - init(); - } - - /** - * 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 miny; - 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(); - miny = 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 >= miny) && (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) { - mult = 0xFFFFFF; - } - - 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) { - mult = 0xFFFFFF; - } - - 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) { - mult = 0xFFFFFF; - } - - return mult; - } - - @Override - public final int getSmoothWaterColorMultiplier() { - 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? */ { - return bm.getWaterColorMult(); - } - - 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; - } - } - - return ((raccum / 9) << 16) | ((gaccum / 9) << 8) | (baccum / 9); - } catch (Exception x) { - return 0xFFFFFF; - } - } - - @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) { - mult = 0xFFFFFF; - } - - 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 < miny) { - 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 < miny) || (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 > miny) { - 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 - miny)) + (y - miny)) << 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) { - Field[] f = ServerChunkManager.class.getDeclaredFields(); - - f = ServerWorld.class.getDeclaredFields(); - for (int i = 0; i < f.length; i++) { - if ((updateEntityTick == null) && f[i].getType().isAssignableFrom(int.class)) { - updateEntityTick = f[i]; - //Log.info("Found updateEntityTick - " + f[i].getName()); - updateEntityTick.setAccessible(true); - } - } - - f = ChunkManager.class.getDeclaredFields(); - for (int i = 0; i < f.length; i++) { - if ((chunksToRemove == null) && (f[i].getType().equals(Map.class))) { - chunksToRemove = f[i]; - //Log.info("Found chunksToRemove - " + f[i].getName()); - chunksToRemove.setAccessible(true); - } -// else if((pendingAnvilChunksCoordinates == null) && (f[i].getType().equals(it.unimi.dsi.fastutil.longs.LongSet.class))) { -// //Log.info("Found pendingAnvilChunksCoordinates - " + f[i].getName()); -// pendingAnvilChunksCoordinates = f[i]; -// pendingAnvilChunksCoordinates.setAccessible(true); -// } - } - if (updateEntityTick == null) { - Log.severe("ERROR: cannot find updateEntityTick - dynmap cannot drive entity cleanup when no players are active"); - } - - init = true; - } + super(plugin.sscache); } public void setChunks(FabricWorld dw, List chunks) { - this.dw = dw; this.w = dw.getWorld(); if (dw.isLoaded()) { /* Check if world's provider is ServerChunkManager */ @@ -827,53 +56,11 @@ public class FabricMapChunkCache extends MapChunkCache { } else { Log.severe("Error: world " + dw.getName() + " has unsupported chunk provider"); } - } 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); } - public NbtCompound readChunk(int x, int z) { + private NbtCompound readChunk(int x, int z) { try { ThreadedAnvilChunkStorage acl = cps.threadedAnvilChunkStorage; @@ -897,429 +84,239 @@ public class FabricMapChunkCache extends MapChunkCache { } } - private Object getNBTValue(NbtElement v) { - Object val = null; - switch (v.getType()) { - case 1: // Byte - val = ((NbtByte) v).byteValue(); - break; - case 2: // Short - val = ((NbtShort) v).shortValue(); - break; - case 3: // Int - val = ((NbtInt) v).intValue(); - break; - case 4: // Long - val = ((NbtLong) v).longValue(); - break; - case 5: // Float - val = ((NbtFloat) v).floatValue(); - break; - case 6: // Double - val = ((NbtDouble) v).doubleValue(); - break; - case 7: // Byte[] - val = ((NbtByteArray) v).getByteArray(); - break; - case 8: // String - val = ((NbtString) v).asString(); - break; - case 9: // List - NbtList tl = (NbtList) v; - ArrayList vlist = new ArrayList(); - int type = tl.getHeldType(); - 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: - NbtCompound 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 - NbtCompound tc = (NbtCompound) v; - HashMap vmap = new HashMap(); - for (Object t : tc.getKeys()) { - String st = (String) t; - NbtElement tg = tc.get(st); - vmap.put(st, getNBTValue(tg)); - } - val = vmap; - break; - case 11: // Int[] - val = ((NbtIntArray) v).getIntArray(); - break; + private GenericChunk parseChunkFromNBT(NbtCompound 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); + int x = nbt.getInt("xPos"); + int z = nbt.getInt("zPos"); + bld.coords(x, z); + if (nbt.contains("InhabitedTime")) { + bld.inhabitedTicks(nbt.getLong("InhabitedTime")); } - 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; - } + // 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]); + } + } } } - 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; - SnapshotCache.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 SnapshotCache.SnapshotRec prepChunkSnapshot(DynmapChunk chunk, NbtCompound nbt) throws ChunkSnapshot.StateListException { - ChunkSnapshot ss = new ChunkSnapshot(nbt, dw.worldheight); - DynIntHashMap tileData = new DynIntHashMap(); - - NbtList tiles = nbt.getList("TileEntities", 10); - if (tiles == null) tiles = new NbtList(); - /* Get tile entity data */ - List vals = new ArrayList(); - for (int tid = 0; tid < tiles.size(); tid++) { - NbtCompound 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) { - NbtElement v = tc.get(id); /* Get field */ - if (v != null) { - Object val = getNBTValue(v); - if (val != null) { - vals.add(id); - vals.add(val); + // Start section builder + GenericChunkSection.Builder sbld = new GenericChunkSection.Builder(); + /* Get sections */ + NbtList sect = nbt.contains("sections") ? nbt.getList("sections", 10) : nbt.getList("Sections", 10); + for (int i = 0; i < sect.size(); i++) { + NbtCompound 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)) { + NbtList plist = sec.getList("Palette", 10); + long[] statelist = sec.getLongArray("BlockStates"); + palette = new DynmapBlockState[plist.size()]; + for (int pi = 0; pi < plist.size(); pi++) { + NbtCompound tc = plist.getCompound(pi); + String pname = tc.getString("Name"); + if (tc.contains("Properties")) { + StringBuilder statestr = new StringBuilder(); + NbtCompound prop = tc.getCompound("Properties"); + for (String pid : prop.getKeys()) { + if (statestr.length() > 0) statestr.append(','); + statestr.append(pid).append('=').append(prop.get(pid).asString()); } + palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.getBaseStateByName(pname); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.AIR; } } - if (vals.size() > 0) { - Object[] vlist = vals.toArray(new Object[vals.size()]); - tileData.put(getIndexInChunk(cx, ty, cz), vlist); - } - } - } - SnapshotCache.SnapshotRec ssr = new SnapshotCache.SnapshotRec(); - ssr.ss = ss; - ssr.tileData = tileData; - DynmapPlugin.plugin.sscache.putSnapshot(dw.getName(), chunk.x, chunk.z, ssr, blockdata, biome, biomeraw, highesty); + + PackedIntegerArray db = null; + WordPackedArray dbp = null; - 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 if (cps.isChunkLoaded(chunk.x, chunk.z)) { - ChunkSnapshot ss; - DynIntHashMap tileData; - if (vis) { // If visible - NbtCompound nbt = null; - try { - nbt = ChunkSerializer.serialize((ServerWorld) w, cps.getWorldChunk(chunk.x, chunk.z, false)); - } catch (NullPointerException e) { - // TODO: find out why this is happening and why it only seems to happen since 1.16.2 - Log.severe("ChunkSerializer.serialize threw a NullPointerException", e); - continue; + int bitsperblock = (statelist.length * 64) / 4096; + int expectedStatelistLength = (4096 + (64 / bitsperblock) - 1) / (64 / bitsperblock); + if (statelist.length == expectedStatelistLength) { + db = new PackedIntegerArray(bitsperblock, 4096, statelist); + } else { + int expectedLegacyStatelistLength = MathHelper.roundUpToMultiple(bitsperblock * 4096, 64) / 64; + if (statelist.length == expectedLegacyStatelistLength) { + dbp = new WordPackedArray(bitsperblock, 4096, statelist); + } else { + bitsperblock = (statelist.length * 64) / 4096; + dbp = new WordPackedArray(bitsperblock, 4096, statelist); } - try { - SnapshotCache.SnapshotRec ssr = prepChunkSnapshot(chunk, nbt); - ss = ssr.ss; - tileData = ssr.tileData; - } catch (ChunkSnapshot.StateListException e) { - continue; + } + + if (bitsperblock > 8) { // Not palette + for (int j = 0; j < 4096; j++) { + int v = db != null ? db.get(j) : dbp.get(j); + sbld.xyzBlockState(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, DynmapBlockState.getStateByGlobalIndex(v)); } } else { - if (hidestyle == HiddenChunkStyle.FILL_STONE_PLAIN) { - ss = STONE; - } else if (hidestyle == HiddenChunkStyle.FILL_OCEAN) { - ss = OCEAN; - } else { - ss = EMPTY; + for (int j = 0; j < 4096; j++) { + int v = db != null ? db.get(j) : dbp.get(j); + DynmapBlockState bs = (v < palette.length) ? palette[v] : DynmapBlockState.AIR; + sbld.xyzBlockState(j & 0xF, (j & 0xF00) >> 8, (j & 0xF0) >> 4, bs); } - tileData = new DynIntHashMap(); } - snaparray[chunkindex] = ss; - snaptile[chunkindex] = tileData; - endChunkLoad(startTime, ChunkStats.LOADED_CHUNKS); - cnt++; } - } - return cnt; - } + else if (sec.contains("block_states")) { // 1.18 + NbtCompound 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"); + NbtList plist = block_states.getList("palette", 10); + palette = new DynmapBlockState[plist.size()]; + for (int pi = 0; pi < plist.size(); pi++) { + NbtCompound tc = plist.getCompound(pi); + String pname = tc.getString("Name"); + if (tc.contains("Properties")) { + StringBuilder statestr = new StringBuilder(); + NbtCompound prop = tc.getCompound("Properties"); + for (String pid : prop.getKeys()) { + if (statestr.length() > 0) statestr.append(','); + statestr.append(pid).append('=').append(prop.get(pid).asString()); + } + palette[pi] = DynmapBlockState.getStateByNameAndState(pname, statestr.toString()); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.getBaseStateByName(pname); + } + if (palette[pi] == null) { + palette[pi] = DynmapBlockState.AIR; + } + } + PackedIntegerArray db = null; + WordPackedArray dbp = null; - @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 { - NbtCompound 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; + int bitsperblock = (statelist.length * 64) / 4096; + int expectedStatelistLength = (4096 + (64 / bitsperblock) - 1) / (64 / bitsperblock); + if (statelist.length == expectedStatelistLength) { + db = new PackedIntegerArray(bitsperblock, 4096, statelist); + } else { + int expectedLegacyStatelistLength = MathHelper.roundUpToMultiple(bitsperblock * 4096, 64) / 64; + if (statelist.length == expectedLegacyStatelistLength) { + dbp = new WordPackedArray(bitsperblock, 4096, statelist); } else { - ss = EMPTY; - } - tileData = new DynIntHashMap(); - } else { - // Prep snapshot - try { - SnapshotCache.SnapshotRec ssr = prepChunkSnapshot(chunk, nbt); - ss = ssr.ss; - tileData = ssr.tileData; - } catch (ChunkSnapshot.StateListException e) { - continue; + bitsperblock = (statelist.length * 64) / 4096; + dbp = new WordPackedArray(bitsperblock, 4096, statelist); } } - snaparray[chunkindex] = ss; - snaptile[chunkindex] = tileData; - endChunkLoad(startTime, ChunkStats.UNLOADED_CHUNKS); - } else { - endChunkLoad(startTime, ChunkStats.UNGENERATED_CHUNKS); + + if (bitsperblock > 8) { // Not palette + for (int j = 0; j < 4096; j++) { + int v = db != null ? db.get(j) : dbp.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 = db != null ? db.get(j) : dbp.get(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")) { + NbtCompound nbtbiomes = sec.getCompound("biomes"); + long[] bdataPacked = nbtbiomes.getLongArray("data"); + NbtList bpalette = nbtbiomes.getList("palette", 8); + PackedIntegerArray bdata = null; + if (bdataPacked.length > 0) + bdata = new PackedIntegerArray(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))); } } - cnt++; + 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(); } - - 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 bld.build(); + } + + // Load generic chunk from existing and already loaded chunk + protected GenericChunk getLoadedChunk(DynmapChunk chunk) { + GenericChunk gc = null; + if (cps.isChunkLoaded(chunk.x, chunk.z)) { + DynIntHashMap tileData; + NbtCompound nbt = null; + try { + nbt = ChunkSerializer.serialize((ServerWorld) w, cps.getWorldChunk(chunk.x, chunk.z, false)); + } catch (NullPointerException e) { + // TODO: find out why this is happening and why it only seems to happen since 1.16.2 + Log.severe("ChunkSerializer.serialize threw a NullPointerException", e); } - } - 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; - } + if (nbt != null) { + gc = parseChunkFromNBT(nbt); + } + } + return gc; + } + + // Load generic chunk from unloaded chunk + protected GenericChunk loadChunk(DynmapChunk chunk) { + GenericChunk gc = null; + NbtCompound 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; + } } diff --git a/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/SnapshotCache.java b/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/SnapshotCache.java deleted file mode 100644 index f2ef9f35..00000000 --- a/fabric-1.18/src/main/java/org/dynmap/fabric_1_18/SnapshotCache.java +++ /dev/null @@ -1,201 +0,0 @@ -package org.dynmap.fabric_1_18; - -import org.dynmap.utils.DynIntHashMap; - -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.SoftReference; -import java.lang.ref.WeakReference; -import java.util.IdentityHashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -public class SnapshotCache { - public static class SnapshotRec { - public ChunkSnapshot ss; - public DynIntHashMap tileData; - } - - private CacheHashMap snapcache; - 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; - } - - @SuppressWarnings("serial") - public class CacheHashMap extends LinkedHashMap { - private int limit; - private IdentityHashMap, String> reverselookup; - - public CacheHashMap(int lim) { - super(16, (float) 0.75, true); - limit = lim; - reverselookup = new IdentityHashMap, String>(); - } - - protected boolean removeEldestEntry(Map.Entry last) { - boolean remove = (size() >= limit); - if (remove && (last != null) && (last.getValue() != null)) { - reverselookup.remove(last.getValue().ref); - } - return remove; - } - } - - /** - * Create snapshot cache - */ - public SnapshotCache(int max_size, boolean softref) { - snapcache = new CacheHashMap(max_size); - refqueue = new ReferenceQueue(); - this.softref = softref; - } - - private String getKey(String w, int cx, int cz) { - return w + ":" + cx + ":" + cz; - } - - /** - * Invalidate cached snapshot, if in cache - */ - public void invalidateSnapshot(String w, int x, int y, int z) { - String key = getKey(w, x >> 4, z >> 4); - synchronized (snapcache) { - CacheRec rec = snapcache.remove(key); - if (rec != null) { - snapcache.reverselookup.remove(rec.ref); - rec.ref.clear(); - } - } - //processRefQueue(); - } - - /** - * Invalidate cached snapshot, if in cache - */ - public void invalidateSnapshot(String w, int x0, int y0, int z0, int x1, int y1, int z1) { - for (int xx = (x0 >> 4); xx <= (x1 >> 4); xx++) { - for (int zz = (z0 >> 4); zz <= (z1 >> 4); zz++) { - String key = getKey(w, xx, zz); - synchronized (snapcache) { - CacheRec rec = snapcache.remove(key); - if (rec != null) { - snapcache.reverselookup.remove(rec.ref); - rec.ref.clear(); - } - } - } - } - //processRefQueue(); - } - - /** - * Look for chunk snapshot in cache - */ - public SnapshotRec getSnapshot(String w, int chunkx, int chunkz, - boolean blockdata, boolean biome, boolean biomeraw, boolean highesty) { - String key = getKey(w, chunkx, chunkz); - processRefQueue(); - SnapshotRec ss = null; - CacheRec rec; - synchronized (snapcache) { - rec = snapcache.get(key); - if (rec != null) { - ss = rec.ref.get(); - if (ss == null) { - snapcache.reverselookup.remove(rec.ref); - snapcache.remove(key); - } - } - } - 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++; - - return ss; - } - - /** - * Add chunk snapshot to cache - */ - public void putSnapshot(String w, int chunkx, int chunkz, SnapshotRec ss, - boolean blockdata, boolean biome, boolean biomeraw, boolean highesty) { - 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); - else - rec.ref = new WeakReference(ss, refqueue); - synchronized (snapcache) { - CacheRec prevrec = snapcache.put(key, rec); - if (prevrec != null) { - snapcache.reverselookup.remove(prevrec.ref); - } - snapcache.reverselookup.put(rec.ref, key); - } - } - - /** - * Process reference queue - */ - private void processRefQueue() { - Reference ref; - while ((ref = refqueue.poll()) != null) { - synchronized (snapcache) { - String k = snapcache.reverselookup.remove(ref); - if (k != null) { - snapcache.remove(k); - } - } - } - } - - /** - * Get hit rate (percent) - */ - public double getHitRate() { - if (cache_attempts > 0) { - return (100.0 * cache_success) / (double) cache_attempts; - } - return 0.0; - } - - /** - * Reset cache stats - */ - public void resetStats() { - cache_attempts = cache_success = 0; - } - - /** - * Cleanup - */ - public void cleanup() { - if (snapcache != null) { - snapcache.clear(); - snapcache.reverselookup.clear(); - snapcache.reverselookup = null; - snapcache = null; - } - } -}