Add chunkstatus and chunkversion shaders (for problem determination)

This commit is contained in:
Mike Primm 2021-12-19 21:02:35 -06:00
parent 322a3dd03b
commit 24d78e5f78
14 changed files with 537 additions and 117 deletions

View File

@ -11,11 +11,15 @@ public class GenericChunk {
public final GenericChunkSection[] sections;
public final int cy_min; // CY value of first section in sections list (index = (Y >> 4) - cy_min
public final long inhabitedTicks;
public final int dataVersion; // Version of chunk data loaded
public final String chunkStatus; // Chunk status of loaded chunk
private GenericChunk(int cx, int cz, int cy_min, GenericChunkSection[] sections, long inhabTicks) {
private GenericChunk(int cx, int cz, int cy_min, GenericChunkSection[] sections, long inhabTicks, int dataversion, String chunkstatus) {
this.cx = cx;
this.cz = cz;
this.inhabitedTicks = inhabTicks;
this.dataVersion = dataversion;
this.chunkStatus = chunkstatus;
this.sections = new GenericChunkSection[sections.length + 2]; // Add one empty at top and bottom
this.cy_min = cy_min - 1; // Include empty at bottom
Arrays.fill(this.sections, GenericChunkSection.EMPTY); // Fill all spots with empty, including pad on bottom/top
@ -69,7 +73,7 @@ public class GenericChunk {
}
// Generic empty (coordinates are wrong, but safe otherwise
public static final GenericChunk EMPTY = new GenericChunk(0, 0, -4, new GenericChunkSection[24], 0);
public static final GenericChunk EMPTY = new GenericChunk(0, 0, -4, new GenericChunkSection[24], 0, 0, null);
// Builder for fabricating finalized chunk
public static class Builder {
@ -78,6 +82,8 @@ public class GenericChunk {
int y_min;
GenericChunkSection[] sections;
long inhabTicks;
String chunkstatus;
int dataversion;
public Builder(int world_ymin, int world_ymax) {
reset(world_ymin, world_ymax);
@ -85,6 +91,8 @@ public class GenericChunk {
public void reset(int world_ymin, int world_ymax) {
x = 0; z = 0;
y_min = world_ymin >> 4;
dataversion = 0;
chunkstatus = null;
int y_max = (world_ymax + 15) >> 4; // Round up
sections = new GenericChunkSection[y_max - y_min]; // Range for all potential sections
}
@ -135,9 +143,19 @@ public class GenericChunk {
}
return this;
}
// Set chunk status
public Builder chunkStatus(String chunkstat) {
this.chunkstatus = chunkstat;
return this;
}
// Set data version
public Builder dataVersion(int dataver) {
this.dataversion = dataver;
return this;
}
// Build chunk
public GenericChunk build() {
return new GenericChunk(x, z, y_min, sections, inhabTicks);
return new GenericChunk(x, z, y_min, sections, inhabTicks, dataversion, chunkstatus);
}
}
}

View File

@ -443,16 +443,6 @@ public abstract class GenericMapChunkCache extends MapChunkCache {
return (((chunkindex * (worldheight - ymin)) + (y - ymin)) << 8) | (bx << 4) | bz;
}
@Override
public final boolean isEmptySection() {
boolean[] flags = isSectionNotEmpty[chunkindex];
if(flags == null) {
initSectionData(chunkindex);
flags = isSectionNotEmpty[chunkindex];
}
return !flags[(y >> 4) + sectoff];
}
@Override
public final RenderPatchFactory getPatchFactory() {
return HDBlockModels.getPatchDefinitionFactory();
@ -498,6 +488,16 @@ public abstract class GenericMapChunkCache extends MapChunkCache {
}
return blk;
}
@Override
public int getDataVersion() {
return (snap != null) ? snap.dataVersion : 0;
}
@Override
public String getChunkStatus() {
return (snap != null) ? snap.chunkStatus : null;
}
}
private class OurEndMapIterator extends OurMapIterator {
@ -887,13 +887,14 @@ public abstract class GenericMapChunkCache extends MapChunkCache {
private static final String litStates[] = { "light", "spawn", "heightmaps", "full" };
public GenericChunk parseChunkFromNBT(GenericNBTCompound nbt) {
if ((nbt != null) && nbt.contains("Level")) {
public GenericChunk parseChunkFromNBT(GenericNBTCompound orignbt) {
GenericNBTCompound nbt = orignbt;
if ((nbt != null) && nbt.contains("Level", GenericNBTCompound.TAG_COMPOUND)) {
nbt = nbt.getCompound("Level");
}
if (nbt == null) return null;
String status = nbt.getString("Status");
int version = nbt.getInt("DataVersion");
int version = orignbt.getInt("DataVersion");
boolean hasLitState = false;
if (status != null) {
@ -907,8 +908,9 @@ public abstract class GenericMapChunkCache extends MapChunkCache {
GenericChunk.Builder bld = new GenericChunk.Builder(dw.minY, dw.worldheight);
int x = nbt.getInt("xPos");
int z = nbt.getInt("zPos");
// Set chunk info
bld.coords(x, z).chunkStatus(status).dataVersion(version);
bld.coords(x, z);
if (nbt.contains("InhabitedTime")) {
bld.inhabitedTicks(nbt.getLong("InhabitedTime"));
}

View File

@ -0,0 +1,230 @@
package org.dynmap.hdmap;
import static org.dynmap.JSONUtils.s;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import org.dynmap.Color;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.Log;
import org.dynmap.MapManager;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.exporter.OBJExport;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.DynLongHashMap;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.json.simple.JSONObject;
// Shader for color coding by chunk data version
public class ChunkStatusHDShader implements HDShader {
private final String name;
private static class ChunkStatusMap {
Color defcolor;
ChunkStatusMap(String s, int c) {
defcolor = new Color((c>>16)&0xFF, (c>>8)&0xFF, c&0xFF);
statusmap.put(s, this);
}
};
private static HashMap<String, ChunkStatusMap> statusmap = new HashMap<String, ChunkStatusMap>();
private static ChunkStatusMap[] statusvals = {
new ChunkStatusMap("empty", 0xFF0000),
new ChunkStatusMap("structure_starts", 0xFF1493),
new ChunkStatusMap("structure_references", 0xFF7F50),
new ChunkStatusMap("biomes", 0xFFA500),
new ChunkStatusMap("noise", 0xFFD700),
new ChunkStatusMap("surface", 0xFFFF00),
new ChunkStatusMap("carvers", 0xFFEFD5),
new ChunkStatusMap("liquid_carvers", 0xF0E68C),
new ChunkStatusMap("features", 0xBDB76B),
new ChunkStatusMap("light", 0xDDA0DD),
new ChunkStatusMap("spawn", 0xFF00FF),
new ChunkStatusMap("heightmaps", 0x9370DB),
new ChunkStatusMap("full", 0x32CD32),
};
final static Color unknown_color = new Color(255, 255, 255);
private ArrayList<String> unknown_state = new ArrayList<String>();
public ChunkStatusHDShader(DynmapCore core, ConfigurationNode configuration) {
name = (String) configuration.get("name");
}
@Override
public boolean isBiomeDataNeeded() {
return false;
}
@Override
public boolean isRawBiomeDataNeeded() {
return false;
}
@Override
public boolean isHightestBlockYDataNeeded() {
return false;
}
@Override
public boolean isBlockTypeDataNeeded() {
return true;
}
@Override
public boolean isSkyLightLevelNeeded() {
return false;
}
@Override
public boolean isEmittedLightLevelNeeded() {
return false;
}
@Override
public String getName() {
return name;
}
private class OurShaderState implements HDShaderState {
private Color color[];
private Color c;
protected HDMap map;
private HDLighting lighting;
final int[] lightingTable;
private OurShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache, int scale) {
this.map = map;
this.lighting = map.getLighting();
if(lighting.isNightAndDayEnabled()) {
color = new Color[] { new Color(), new Color() };
}
else {
color = new Color[] { new Color() };
}
c = new Color();
if (MapManager.mapman.useBrightnessTable()) {
lightingTable = cache.getWorld().getBrightnessTable();
}
else {
lightingTable = null;
}
}
/**
* Get our shader
*/
public HDShader getShader() {
return ChunkStatusHDShader.this;
}
/**
* Get our map
*/
public HDMap getMap() {
return map;
}
/**
* Get our lighting
*/
public HDLighting getLighting() {
return lighting;
}
/**
* Reset renderer state for new ray
*/
public void reset(HDPerspectiveState ps) {
for(int i = 0; i < color.length; i++)
color[i].setTransparent();
}
/**
* Process next ray step - called for each block on route
* @return true if ray is done, false if ray needs to continue
*/
public boolean processBlock(HDPerspectiveState ps) {
if (ps.getBlockState().isAir()) {
return false;
}
String cs = ps.getMapIterator().getChunkStatus(); // Get data version
ChunkStatusMap csm = statusmap.get(cs);
if (csm != null) {
c.setColor(csm.defcolor);
}
else {
c.setColor(unknown_color);
if (!unknown_state.contains(cs)) {
Log.warning("Unknown chunk status: " + cs);
unknown_state.add(cs);
}
}
/* Handle light level, if needed */
lighting.applyLighting(ps, this, c, color);
return true;
}
/**
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
*/
public void rayFinished(HDPerspectiveState ps) {
}
/**
* Get result color - get resulting color for ray
* @param c - object to store color value in
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
*/
public void getRayColor(Color c, int index) {
c.setColor(color[index]);
}
/**
* Clean up state object - called after last ray completed
*/
public void cleanup() {
}
@Override
public DynLongHashMap getCTMTextureCache() {
return null;
}
@Override
public int[] getLightingTable() {
return lightingTable;
}
@Override
public void setLastBlockState(DynmapBlockState new_lastbs) {
}
}
/**
* Get renderer state object for use rendering a tile
* @param map - map being rendered
* @param cache - chunk cache containing data for tile to be rendered
* @param mapiter - iterator used when traversing rays in tile
* @param scale - scale of perspecitve
* @return state object to use for all rays in tile
*/
@Override
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
return new OurShaderState(mapiter, map, cache, scale);
}
/* Add shader's contributions to JSON for map object */
public void addClientConfiguration(JSONObject mapObject) {
s(mapObject, "shader", name);
}
@Override
public void exportAsMaterialLibrary(DynmapCommandSender sender, OBJExport out) throws IOException {
throw new IOException("Export unsupported");
}
private static final String[] nulllist = new String[0];
@Override
public String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx, BlockStep[] steps) {
return nulllist;
}
}

View File

@ -0,0 +1,245 @@
package org.dynmap.hdmap;
import static org.dynmap.JSONUtils.s;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeSet;
import org.dynmap.Color;
import org.dynmap.ConfigurationNode;
import org.dynmap.DynmapCore;
import org.dynmap.Log;
import org.dynmap.MapManager;
import org.dynmap.common.DynmapCommandSender;
import org.dynmap.exporter.OBJExport;
import org.dynmap.renderer.DynmapBlockState;
import org.dynmap.utils.BlockStep;
import org.dynmap.utils.DynLongHashMap;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
import org.json.simple.JSONObject;
// Shader for color coding by chunk data version
public class VersionHDShader implements HDShader {
private final String name;
private static class DataVersionMap {
int dataVersion;
String version;
Color defcolor;
DataVersionMap(int dv, String v, int c) {
dataVersion = dv;
version = v;
defcolor = new Color((c>>16)&0xFF, (c>>8)&0xFF, c&0xFF);
}
};
final static DataVersionMap[] versionmap = {
new DataVersionMap(0, "unknown", 0x202020),
new DataVersionMap(1519, "1.13.0", 0xF9E79F),
new DataVersionMap(1628, "1.13.1", 0xF4D03F),
new DataVersionMap(1631, "1.13.2", 0xD4AC0D),
new DataVersionMap(1952, "1.14.0", 0xABEBC6),
new DataVersionMap(1957, "1.14.1", 0x58D68D),
new DataVersionMap(1963, "1.14.2", 0x28B463),
new DataVersionMap(1968, "1.14.3", 0x239B56),
new DataVersionMap(1976, "1.14.4", 0x1D8348),
new DataVersionMap(2225, "1.15.0", 0xAED6F1),
new DataVersionMap(2227, "1.15.1", 0x5DADE2),
new DataVersionMap(2230, "1.15.2", 0x2E86C1),
new DataVersionMap(2566, "1.16.0", 0xD7BDE2),
new DataVersionMap(2567, "1.16.1", 0xC39BD3),
new DataVersionMap(2578, "1.16.2", 0xAF7AC5),
new DataVersionMap(2580, "1.16.3", 0x9B59B6),
new DataVersionMap(2584, "1.16.4", 0x884EA0),
new DataVersionMap(2586, "1.16.5", 0x76448A),
new DataVersionMap(2724, "1.17.0", 0xF5CBA7),
new DataVersionMap(2730, "1.17.1", 0xEB984E),
new DataVersionMap(2860, "1.18.0", 0xA3E4D7),
new DataVersionMap(2865, "1.18.1", 0x48C9B0),
};
final static Color unknown_color = new Color(255, 255, 255);
private ArrayList<Integer> unknown_vers = new ArrayList<Integer>();
public VersionHDShader(DynmapCore core, ConfigurationNode configuration) {
name = (String) configuration.get("name");
}
@Override
public boolean isBiomeDataNeeded() {
return false;
}
@Override
public boolean isRawBiomeDataNeeded() {
return false;
}
@Override
public boolean isHightestBlockYDataNeeded() {
return false;
}
@Override
public boolean isBlockTypeDataNeeded() {
return true;
}
@Override
public boolean isSkyLightLevelNeeded() {
return false;
}
@Override
public boolean isEmittedLightLevelNeeded() {
return false;
}
@Override
public String getName() {
return name;
}
private class OurShaderState implements HDShaderState {
private Color color[];
private Color c;
protected HDMap map;
private HDLighting lighting;
final int[] lightingTable;
private OurShaderState(MapIterator mapiter, HDMap map, MapChunkCache cache, int scale) {
this.map = map;
this.lighting = map.getLighting();
if(lighting.isNightAndDayEnabled()) {
color = new Color[] { new Color(), new Color() };
}
else {
color = new Color[] { new Color() };
}
c = new Color();
if (MapManager.mapman.useBrightnessTable()) {
lightingTable = cache.getWorld().getBrightnessTable();
}
else {
lightingTable = null;
}
}
/**
* Get our shader
*/
public HDShader getShader() {
return VersionHDShader.this;
}
/**
* Get our map
*/
public HDMap getMap() {
return map;
}
/**
* Get our lighting
*/
public HDLighting getLighting() {
return lighting;
}
/**
* Reset renderer state for new ray
*/
public void reset(HDPerspectiveState ps) {
for(int i = 0; i < color.length; i++)
color[i].setTransparent();
}
/**
* Process next ray step - called for each block on route
* @return true if ray is done, false if ray needs to continue
*/
public boolean processBlock(HDPerspectiveState ps) {
if (ps.getBlockState().isAir()) {
return false;
}
int ver = ps.getMapIterator().getDataVersion(); // Get data version
boolean match = false;
// Find last record <= version
for (int i = 0; i < versionmap.length; i++) {
if (ver <= versionmap[i].dataVersion) {
c.setColor(versionmap[i].defcolor);
match = true;
break;
}
}
if (!match) {
c.setColor(unknown_color);
if (!unknown_vers.contains(ver)) {
Log.warning("Unknown chunk dataVersion: " + ver);
unknown_vers.add(ver);
}
}
/* Handle light level, if needed */
lighting.applyLighting(ps, this, c, color);
return true;
}
/**
* Ray ended - used to report that ray has exited map (called if renderer has not reported complete)
*/
public void rayFinished(HDPerspectiveState ps) {
}
/**
* Get result color - get resulting color for ray
* @param c - object to store color value in
* @param index - index of color to request (renderer specific - 0=default, 1=day for night/day renderer
*/
public void getRayColor(Color c, int index) {
c.setColor(color[index]);
}
/**
* Clean up state object - called after last ray completed
*/
public void cleanup() {
}
@Override
public DynLongHashMap getCTMTextureCache() {
return null;
}
@Override
public int[] getLightingTable() {
return lightingTable;
}
@Override
public void setLastBlockState(DynmapBlockState new_lastbs) {
}
}
/**
* Get renderer state object for use rendering a tile
* @param map - map being rendered
* @param cache - chunk cache containing data for tile to be rendered
* @param mapiter - iterator used when traversing rays in tile
* @param scale - scale of perspecitve
* @return state object to use for all rays in tile
*/
@Override
public HDShaderState getStateInstance(HDMap map, MapChunkCache cache, MapIterator mapiter, int scale) {
return new OurShaderState(mapiter, map, cache, scale);
}
/* Add shader's contributions to JSON for map object */
public void addClientConfiguration(JSONObject mapObject) {
s(mapObject, "shader", name);
}
@Override
public void exportAsMaterialLibrary(DynmapCommandSender sender, OBJExport out) throws IOException {
throw new IOException("Export unsupported");
}
private static final String[] nulllist = new String[0];
@Override
public String[] getCurrentBlockMaterials(DynmapBlockState blk, MapIterator mapiter, int[] txtidx, BlockStep[] steps) {
return nulllist;
}
}

View File

@ -102,14 +102,17 @@ public interface MapIterator extends MapDataContext {
* @return block key
*/
long getBlockKey();
/**
* Test if current section (16 x 16 x 16) is empty (all air)
* @return true if empty
*/
boolean isEmptySection();
/**
* Get inhabited ticks for current position
* @return ticks inhabited
*/
long getInhabitedTicks();
/**
* Get chunk dataVersion
*/
default int getDataVersion() { return 0; }
/**
* Get chunk status
*/
default String getChunkStatus() { return null; }
}

View File

@ -193,3 +193,11 @@ shaders:
color300: "#FFFF00"
color1000: "#FF8000"
color3000: "#FF0000"
# Chunk version shader (shades by version of data format)
- class: org.dynmap.hdmap.VersionHDShader
name: chunkversion
# Chunk status shader (shades by chunk state)
- class: org.dynmap.hdmap.ChunkStatusHDShader
name: chunkstatus

View File

@ -553,15 +553,6 @@ public abstract class AbstractMapChunkCache extends MapChunkCache {
return (((chunkindex * (worldheight - ymin)) + (y - ymin)) << 8) | (bx << 4) | bz;
}
@Override
public final boolean isEmptySection() {
boolean[] flags = isSectionNotEmpty[chunkindex];
if (flags == null) {
initSectionData(chunkindex);
flags = isSectionNotEmpty[chunkindex];
}
return !flags[(y >> 4) + sectoff];
}
@Override
public RenderPatchFactory getPatchFactory() {
return HDBlockModels.getPatchDefinitionFactory();
}
@ -604,6 +595,14 @@ public abstract class AbstractMapChunkCache extends MapChunkCache {
return 0;
}
}
@Override
public int getDataVersion() {
return 0;
}
@Override
public String getChunkStatus() {
return null;
}
}
// Special iterator for END : forces skylight to 15

View File

@ -558,16 +558,6 @@ public class FabricMapChunkCache extends MapChunkCache {
return (((chunkindex * worldheight) + y) << 8) | (bx << 4) | bz;
}
@Override
public final boolean isEmptySection() {
try {
return !isSectionNotEmpty[chunkindex][y >> 4];
} catch (Exception x) {
initSectionData(chunkindex);
return !isSectionNotEmpty[chunkindex][y >> 4];
}
}
@Override
public RenderPatchFactory getPatchFactory() {
return HDBlockModels.getPatchDefinitionFactory();

View File

@ -558,16 +558,6 @@ public class FabricMapChunkCache extends MapChunkCache {
return (((chunkindex * worldheight) + y) << 8) | (bx << 4) | bz;
}
@Override
public final boolean isEmptySection() {
try {
return !isSectionNotEmpty[chunkindex][y >> 4];
} catch (Exception x) {
initSectionData(chunkindex);
return !isSectionNotEmpty[chunkindex][y >> 4];
}
}
@Override
public RenderPatchFactory getPatchFactory() {
return HDBlockModels.getPatchDefinitionFactory();

View File

@ -683,19 +683,6 @@ public class ForgeMapChunkCache extends MapChunkCache
return (((chunkindex * worldheight) + y) << 8) | (bx << 4) | bz;
}
@Override
public final boolean isEmptySection()
{
try
{
return !isSectionNotEmpty[chunkindex][y >> 4];
}
catch (Exception x)
{
initSectionData(chunkindex);
return !isSectionNotEmpty[chunkindex][y >> 4];
}
}
@Override
public RenderPatchFactory getPatchFactory() {
return HDBlockModels.getPatchDefinitionFactory();
}

View File

@ -683,19 +683,6 @@ public class ForgeMapChunkCache extends MapChunkCache
return (((chunkindex * worldheight) + y) << 8) | (bx << 4) | bz;
}
@Override
public final boolean isEmptySection()
{
try
{
return !isSectionNotEmpty[chunkindex][y >> 4];
}
catch (Exception x)
{
initSectionData(chunkindex);
return !isSectionNotEmpty[chunkindex][y >> 4];
}
}
@Override
public RenderPatchFactory getPatchFactory() {
return HDBlockModels.getPatchDefinitionFactory();
}

View File

@ -684,19 +684,6 @@ public class ForgeMapChunkCache extends MapChunkCache
return (((chunkindex * worldheight) + y) << 8) | (bx << 4) | bz;
}
@Override
public final boolean isEmptySection()
{
try
{
return !isSectionNotEmpty[chunkindex][y >> 4];
}
catch (Exception x)
{
initSectionData(chunkindex);
return !isSectionNotEmpty[chunkindex][y >> 4];
}
}
@Override
public RenderPatchFactory getPatchFactory() {
return HDBlockModels.getPatchDefinitionFactory();
}

View File

@ -680,19 +680,6 @@ public class ForgeMapChunkCache extends MapChunkCache
return (((chunkindex * worldheight) + y) << 8) | (bx << 4) | bz;
}
@Override
public final boolean isEmptySection()
{
try
{
return !isSectionNotEmpty[chunkindex][y >> 4];
}
catch (Exception x)
{
initSectionData(chunkindex);
return !isSectionNotEmpty[chunkindex][y >> 4];
}
}
@Override
public RenderPatchFactory getPatchFactory() {
return HDBlockModels.getPatchDefinitionFactory();
}

View File

@ -680,19 +680,6 @@ public class ForgeMapChunkCache extends MapChunkCache
return (((chunkindex * worldheight) + y) << 8) | (bx << 4) | bz;
}
@Override
public final boolean isEmptySection()
{
try
{
return !isSectionNotEmpty[chunkindex][y >> 4];
}
catch (Exception x)
{
initSectionData(chunkindex);
return !isSectionNotEmpty[chunkindex][y >> 4];
}
}
@Override
public RenderPatchFactory getPatchFactory() {
return HDBlockModels.getPatchDefinitionFactory();
}