From eff72aeef020313aed1c8bfc6405d50978da081d Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 15 Dec 2021 23:34:53 -0600 Subject: [PATCH] Add simple lighting generation for migrated chunks with missing light --- .../org/dynmap/common/chunk/GenericChunk.java | 29 +++++++++++++++++ .../common/chunk/GenericChunkSection.java | 6 ++++ .../common/chunk/GenericMapChunkCache.java | 31 ++++++++++++++++--- .../bukkit/helper/v118/MapChunkCache118.java | 20 ------------ .../fabric_1_18/FabricMapChunkCache.java | 22 +++---------- .../dynmap/forge_1_18/ForgeMapChunkCache.java | 18 ++--------- 6 files changed, 67 insertions(+), 59 deletions(-) diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java index 9cc198bc..b9ffac49 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java @@ -106,6 +106,35 @@ public class GenericChunk { this.z = sz; return this; } + // Generate simple sky lighting (must be after all sections have been added) + public Builder generateSky() { + int sky[] = new int[256]; // ZX array + Arrays.fill(sky, 15); // Start fully lit at top + GenericChunkSection.Builder bld = new GenericChunkSection.Builder(); + // Make light array for each section, start from top + for (int i = (sections.length - 1); i >= 0; i--) { + if (sections[i] == null) continue; + byte[] ssky = new byte[2048]; + for (int x = 0; x < 16; x++) { + for (int z = 0; z < 16; z++) { + int idx = (z << 4) + x; + for (int y = 15; y >= 0; y--) { + DynmapBlockState bs = sections[i].blocks.getBlock(x, y, z); // Get block + if (bs.isWater() || bs.isWaterlogged()) { // Drop light by 1 level for water + sky[idx] = sky[idx] < 1 ? 0 : sky[idx] - 1; + } + else if (bs.isLeaves()) { // Drop light by 2 levels for leaves + sky[idx] = sky[idx] < 2 ? 0 : sky[idx] - 2; + } + ssky[(y << 7) | (z << 3) | (x >> 1)] |= (sky[idx] << (4 * (x & 1))); + } + } + } + // Replace section with new one with new lighting + sections[i] = bld.buildFrom(sections[i], ssky); + } + return this; + } // Build chunk public GenericChunk build() { return new GenericChunk(x, z, y_min, sections, inhabTicks); diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java index 6022d8f5..ab92ee6d 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java @@ -238,6 +238,12 @@ public class GenericChunkSection { empty = false; return this; } + // Build copy from existing section with new skylight (YZX nibble array) + public GenericChunkSection buildFrom(GenericChunkSection s, byte[] sky) { + LightingAccess skyA = new LightingAccess3D(sky); + return new GenericChunkSection(s.blocks, s.biomes, skyA, s.emitted, s.isEmpty); + } + // Build section based on current builder state public GenericChunkSection build() { // Process state access - see if we can reduce 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 c3710abf..989c02ea 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericMapChunkCache.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericMapChunkCache.java @@ -7,6 +7,7 @@ import java.util.ListIterator; 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.GenericChunkCache.ChunkCacheRec; import org.dynmap.hdmap.HDBlockModels; @@ -901,17 +902,25 @@ public abstract class GenericMapChunkCache extends MapChunkCache { return true; } + private static final String litStates[] = { "light", "spawn", "heightmaps", "full" }; + public GenericChunk parseChunkFromNBT(GenericNBTCompound nbt) { if ((nbt != null) && nbt.contains("Level")) { nbt = nbt.getCompound("Level"); } if (nbt == null) return null; + String status = nbt.getString("Status"); boolean hasLight = false; - boolean isEmpty = nbt.getString("Status").equals("empty"); // Incomplete migration + if (status != null) { + for (int i = 0; i < litStates.length; i++) { + if (status.equals(litStates[i])) hasLight = true; + } + } // 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")); @@ -946,6 +955,14 @@ public abstract class GenericMapChunkCache extends MapChunkCache { GenericChunkSection.Builder sbld = new GenericChunkSection.Builder(); /* Get sections */ GenericNBTList sect = nbt.contains("sections") ? nbt.getList("sections", 10) : nbt.getList("Sections", 10); + // Prescan sections to see if lit + for (int i = 0; i < sect.size(); i++) { + GenericNBTCompound sec = sect.getCompound(i); + if (sec.contains("BlockLight") || sec.contains("SkyLight")) { + hasLight = true; + } + } + // And process sections for (int i = 0; i < sect.size(); i++) { GenericNBTCompound sec = sect.getCompound(i); int secnum = sec.getByte("Y"); @@ -1054,11 +1071,12 @@ public abstract class GenericMapChunkCache extends MapChunkCache { } if (sec.contains("BlockLight")) { sbld.emittedLight(sec.getByteArray("BlockLight")); - hasLight = true; } if (sec.contains("SkyLight")) { sbld.skyLight(sec.getByteArray("SkyLight")); - hasLight = true; + } + else if (!hasLight) { + sbld.singleSkyLight(15); } // If section biome palette if (sec.contains("biomes")) { @@ -1092,8 +1110,11 @@ public abstract class GenericMapChunkCache extends MapChunkCache { bld.addSection(secnum, sbld.build()); sbld.reset(); } - // If isEmpty and has no light, drop it - return (isEmpty && (!hasLight)) ? null : bld.build(); + // If no light, do simple generate + if (!hasLight) { + bld.generateSky(); + } + return bld.build(); } } 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 62d62b36..2108c254 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 @@ -50,20 +50,6 @@ public class MapChunkCache118 extends GenericMapChunkCache { init(); } - private boolean isLitChunk(NBTTagCompound nbt) { - if ((nbt != null) && nbt.e("Level")) { - nbt = nbt.p("Level"); - } - if (nbt != null) { - String stat = nbt.l("Status"); - ChunkStatus cs = ChunkStatus.a(stat); - if ((stat != null) && (cs.b(ChunkStatus.l) || (cs == ChunkStatus.c))) { // ChunkStatus.LIGHT OR ChunkStatus.EMPTY (migrated use this for some reason) - return true; - } - } - return false; - } - // Load generic chunk from existing and already loaded chunk protected GenericChunk getLoadedChunk(DynmapChunk chunk) { CraftWorld cw = (CraftWorld) w; @@ -74,9 +60,6 @@ public class MapChunkCache118 extends GenericMapChunkCache { if ((c != null) && c.o) { // c.loaded nbt = ChunkRegionLoader.a(cw.getHandle(), c); } - if (!isLitChunk(nbt)) { - nbt = null; - } if (nbt != null) { gc = parseChunkFromNBT(new NBT.NBTCompound(nbt)); } @@ -93,9 +76,6 @@ public class MapChunkCache118 extends GenericMapChunkCache { nbt = cw.getHandle().k().a.f(cc); // playerChunkMap } catch (IOException iox) { } - if (!isLitChunk(nbt)) { - nbt = null; - } if (nbt != null) { gc = parseChunkFromNBT(new NBT.NBTCompound(nbt)); } 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 45f71c11..cc575c4e 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 @@ -60,20 +60,6 @@ public class FabricMapChunkCache extends GenericMapChunkCache { super.setChunks(dw, chunks); } - private boolean isLitChunk(NbtCompound nbt) { - if ((nbt != null) && nbt.contains("Level")) { - nbt = nbt.getCompound("Level"); - } - if (nbt != null) { - String stat = nbt.getString("Status"); - ChunkStatus cs = ChunkStatus.byId(stat); - if ((stat != null) && (cs.isAtLeast(ChunkStatus.LIGHT) || (cs == ChunkStatus.EMPTY))) { // ChunkStatus.LIGHT OR migrated EMPTY - return true; - } - } - return false; - } - // Load generic chunk from existing and already loaded chunk protected GenericChunk getLoadedChunk(DynmapChunk chunk) { GenericChunk gc = null; @@ -85,9 +71,9 @@ public class FabricMapChunkCache extends GenericMapChunkCache { // 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); } - if (isLitChunk(nbt)) { - gc = parseChunkFromNBT(new NBT.NBTCompound(nbt)); - } + if (nbt != null) { + gc = parseChunkFromNBT(new NBT.NBTCompound(nbt)); + } } return gc; } @@ -109,7 +95,7 @@ public class FabricMapChunkCache extends GenericMapChunkCache { GenericChunk gc = null; NbtCompound nbt = readChunk(chunk.x, chunk.z); // If read was good - if (isLitChunk(nbt)) { + if (nbt != null) { gc = parseChunkFromNBT(new NBT.NBTCompound(nbt)); } return gc; 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 2ddd8e24..c964db7f 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 @@ -31,27 +31,13 @@ public class ForgeMapChunkCache extends GenericMapChunkCache { init(); } - private boolean isLitChunk(CompoundTag nbt) { - if ((nbt != null) && nbt.contains("Level")) { - nbt = nbt.getCompound("Level"); - } - if (nbt != null) { - String stat = nbt.getString("Status"); - ChunkStatus cs = ChunkStatus.byName(stat); - if ((stat != null) && (cs.isOrAfter(ChunkStatus.LIGHT) || (cs == ChunkStatus.EMPTY))) { // ChunkStatus.LIGHT or migrated EMPTY - return true; - } - } - return false; - } - // Load generic chunk from existing and already loaded chunk protected GenericChunk getLoadedChunk(DynmapChunk chunk) { GenericChunk gc = null; ChunkAccess ch = cps.getChunk(chunk.x, chunk.z, ChunkStatus.FULL, false); if (ch != null) { CompoundTag nbt = ChunkSerializer.write(w, ch); - if (isLitChunk(nbt)) { + if (nbt != null) { gc = parseChunkFromNBT(new NBT.NBTCompound(nbt)); } } @@ -62,7 +48,7 @@ public class ForgeMapChunkCache extends GenericMapChunkCache { GenericChunk gc = null; CompoundTag nbt = readChunk(chunk.x, chunk.z); // If read was good - if (isLitChunk(nbt)) { + if (nbt != null) { gc = parseChunkFromNBT(new NBT.NBTCompound(nbt)); } return gc;