diff --git a/bukkit-helper-118/src/main/java/org/dynmap/bukkit/helper/v118/MapChunkCache118.java b/bukkit-helper-118/src/main/java/org/dynmap/bukkit/helper/v118/MapChunkCache118.java index dc18cf62..970a1f0a 100644 --- a/bukkit-helper-118/src/main/java/org/dynmap/bukkit/helper/v118/MapChunkCache118.java +++ b/bukkit-helper-118/src/main/java/org/dynmap/bukkit/helper/v118/MapChunkCache118.java @@ -327,7 +327,10 @@ public class MapChunkCache118 extends AbstractMapChunkCache { int b = bdata != null ? bdata.a(j) : 0; cursect.biomes[j] = b < bpalette.size() ? BiomeMap.byBiomeName(bpalette.j(b)).getBiomeID() : -1; } - lastsectwithbiome = cursect; + // Favor the Y=64 version + if ((secnum == 4) || (lastsectwithbiome == null)) { + lastsectwithbiome = cursect; + } } } /* Get biome data */ diff --git a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ChunkSnapshot.java b/forge-1.18/src/main/java/org/dynmap/forge_1_18/ChunkSnapshot.java index 262ffd7e..4cf2aa01 100644 --- a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ChunkSnapshot.java +++ b/forge-1.18/src/main/java/org/dynmap/forge_1_18/ChunkSnapshot.java @@ -3,6 +3,7 @@ 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; @@ -23,6 +24,7 @@ public class ChunkSnapshot 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; @@ -35,6 +37,7 @@ public class ChunkSnapshot 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]; @@ -61,18 +64,24 @@ public class ChunkSnapshot 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; } @@ -95,6 +104,10 @@ public class ChunkSnapshot public boolean isEmpty() { return false; } + @Override + public int getBiome(int x, int y, int z) { + return BiomeMap.PLAINS.getBiomeID(); + } } /** * Construct empty chunk snapshot @@ -145,8 +158,10 @@ public class ChunkSnapshot sections.add(empty_section); sectcnt++; } + //System.out.println("nbt.keys()=" + nbt.d().toString()); + StdSection lastsectwithbiome = null; /* Get sections */ - ListTag sect = nbt.getList("Sections", 10); + 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"); @@ -214,11 +229,81 @@ public class ChunkSnapshot } } } + 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.byBiomeName(bpalette.getString(b)).getBiomeID() : -1; + } + // Favor the Y=64 version + if ((secnum == 4) || (lastsectwithbiome == null)) { + lastsectwithbiome = cursect; + } } } /* Get biome data */ @@ -245,6 +330,17 @@ public class ChunkSnapshot } } } + 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; diff --git a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeMapChunkCache.java b/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeMapChunkCache.java index 745d8673..bd2840ce 100644 --- a/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeMapChunkCache.java +++ b/forge-1.18/src/main/java/org/dynmap/forge_1_18/ForgeMapChunkCache.java @@ -860,7 +860,9 @@ public class ForgeMapChunkCache extends MapChunkCache { try { CompoundTag rslt = cps.chunkMap.readChunk(new ChunkPos(x, z)); if (rslt != null) { - rslt = rslt.getCompound("Level"); + if (rslt.contains("Level")) { + rslt = rslt.getCompound("Level"); + } // Don't load uncooked chunks String stat = rslt.getString("Status"); ChunkStatus cs = ChunkStatus.byName(stat); @@ -1080,8 +1082,9 @@ public class ForgeMapChunkCache extends MapChunkCache { DynIntHashMap tileData; if (vis) { // If visible CompoundTag nbt = ChunkSerializer.write(w, ch); - if (nbt != null) + if ((nbt != null) && nbt.contains("Level")) { nbt = nbt.getCompound("Level"); + } SnapshotRec ssr = prepChunkSnapshot(chunk, nbt); ss = ssr.ss; tileData = ssr.tileData;