From c6fb0be7e7c0f14b4b20a70f60337d8a78ea7a18 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Thu, 5 Mar 2026 18:45:42 -0500 Subject: [PATCH] Tinker with performance on some base data classes --- .../src/main/java/org/dynmap/Color.java | 52 ++++++++---- .../main/java/org/dynmap/common/BiomeMap.java | 51 +++++------ .../org/dynmap/common/chunk/GenericChunk.java | 2 +- .../common/chunk/GenericChunkCache.java | 48 +++++++---- .../common/chunk/GenericChunkSection.java | 8 +- .../org/dynmap/hdmap/ShadowHDLighting.java | 11 +-- .../org/dynmap/utils/PatchDefinition.java | 85 ++++++++++--------- .../org/dynmap/renderer/DynmapBlockState.java | 34 +++++--- 8 files changed, 172 insertions(+), 119 deletions(-) diff --git a/DynmapCore/src/main/java/org/dynmap/Color.java b/DynmapCore/src/main/java/org/dynmap/Color.java index e6792707..2339c82d 100644 --- a/DynmapCore/src/main/java/org/dynmap/Color.java +++ b/DynmapCore/src/main/java/org/dynmap/Color.java @@ -38,12 +38,13 @@ public class Color { val = TRANSPARENT; } public final void setGrayscale() { - int alpha = (val >> 24) & 0xFF; - int red = (val >> 16) & 0xFF; - int green = (val >> 8) & 0xFF; - int blue = val & 0xFF; - int gray = ((red * 76) + (green * 151) + (blue * 28)) / 255; - setRGBA(gray, gray, gray, alpha); + int alpha = val & 0xFF000000; + int num = (((val >> 16) & 0xFF) * 76) + + (((val >> 8) & 0xFF) * 151) + + (( val & 0xFF) * 28); + // weights sum to 255, so num ∈ [0, 65025]; fast /255 via shift + int gray = (num + (num >> 8) + 1) >> 8; + val = alpha | (gray << 16) | (gray << 8) | gray; } public final void setColor(Color c) { val = c.val; @@ -85,11 +86,10 @@ public class Color { * @param argb - ARGB to blend */ public final void blendColor(int argb) { - int nval = (((((val >> 24) & 0xFF) * ((argb >> 24) & 0xFF)) / 255) << 24); - nval = nval | (((((val >> 16) & 0xFF) * ((argb >> 16) & 0xFF)) / 255) << 16); - nval = nval | (((((val >> 8) & 0xFF) * ((argb >> 8) & 0xFF)) / 255) << 8); - nval = nval | (((val & 0xFF) * (argb & 0xFF)) / 255); - val = nval; + val = (mulDiv255(val >>> 24, argb >>> 24 ) << 24) + | (mulDiv255((val >> 16) & 0xFF, (argb >> 16) & 0xFF) << 16) + | (mulDiv255((val >> 8) & 0xFF, (argb >> 8) & 0xFF) << 8) + | mulDiv255( val & 0xFF, argb & 0xFF); } /** * Scale each color component, based on the corresponding component @@ -98,10 +98,30 @@ public class Color { * @return blended color */ public static final int blendColor(int argb0, int argb1) { - int nval = (((((argb0 >> 24) & 0xFF) * ((argb1 >> 24) & 0xFF)) / 255) << 24); - nval = nval | (((((argb0 >> 16) & 0xFF) * ((argb1 >> 16) & 0xFF)) / 255) << 16); - nval = nval | (((((argb0 >> 8) & 0xFF) * ((argb1 >> 8) & 0xFF)) / 255) << 8); - nval = nval | (((argb0 & 0xFF) * (argb1 & 0xFF)) / 255); - return nval; + return (mulDiv255(argb0 >>> 24, argb1 >>> 24 ) << 24) + | (mulDiv255((argb0 >> 16) & 0xFF, (argb1 >> 16) & 0xFF) << 16) + | (mulDiv255((argb0 >> 8) & 0xFF, (argb1 >> 8) & 0xFF) << 8) + | mulDiv255( argb0 & 0xFF, argb1 & 0xFF); + } + /** + * Scale the RGB channels by scale/256, leaving alpha unchanged. + * Equivalent to setRGBA(getRed()*scale>>8, getGreen()*scale>>8, getBlue()*scale>>8, getAlpha()) + * but avoids redundant unpack/repack of the alpha channel. + * @param scale - scale factor 0..256 (256 = full brightness) + */ + public final void scaleRGB(int scale) { + val = (val & 0xFF000000) + | ((((val >> 16) & 0xFF) * scale >> 8) << 16) + | ((((val >> 8) & 0xFF) * scale >> 8) << 8) + | ((val & 0xFF) * scale >> 8); + } + /** + * Fast multiply-then-divide-by-255 for two values a, b each in [0, 255]. + * Returns floor(a*b/255), equivalent to the standard integer division but + * computed with shifts only: (x + (x >> 8) + 1) >> 8 where x = a * b. + */ + private static int mulDiv255(int a, int b) { + int x = a * b; + return (x + (x >> 8) + 1) >> 8; } } diff --git a/DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java b/DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java index 1f5950b7..291627a2 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java +++ b/DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java @@ -2,6 +2,7 @@ package org.dynmap.common; import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; import java.util.Optional; @@ -11,7 +12,9 @@ import org.dynmap.hdmap.HDBlockModels; public class BiomeMap { public static final int NO_INDEX = -2; private static BiomeMap[] biome_by_index = new BiomeMap[256]; - private static Map biome_by_rl = new HashMap(); + private static Map biome_by_rl = new HashMap(256); + // Tracks registered IDs for O(1) uniqueness checks during initialization + private static final HashSet biome_ids = new HashSet(256); public static final BiomeMap NULL = new BiomeMap(-1, "NULL", 0.5, 0.5, 0xFFFFFF, 0, 0, null); public static final BiomeMap OCEAN = new BiomeMap(0, "OCEAN", "minecraft:ocean"); @@ -145,12 +148,17 @@ public class BiomeMap { } private static boolean isUniqueID(String id) { - for(int i = 0; i < biome_by_index.length; i++) { - if(biome_by_index[i] == null) continue; - if(biome_by_index[i].id.equals(id)) - return false; - } - return true; + return !biome_ids.contains(id); + } + + /** + * Encodes a grass/foliage color multiplier for efficient hot-path dispatch: + * == 0 → 0 (passthrough: return raw value unchanged) + * 0 < val ≤ 0xFFFFFF → positive (blend: average with raw) + * val > 0xFFFFFF → -(val & 0xFFFFFF) (negative sentinel: fixed override color) + */ + private static int encodeColorMult(int val) { + return (val > 0xFFFFFF) ? -(val & 0xFFFFFF) : val; } private static void resizeIfNeeded(int idx) { @@ -171,8 +179,8 @@ public class BiomeMap { setTemperature(tmp); setRainfall(rain); this.watercolormult = waterColorMultiplier; - this.grassmult = grassmult; - this.foliagemult = foliagemult; + this.grassmult = encodeColorMult(grassmult); + this.foliagemult = encodeColorMult(foliagemult); // Handle null biome if (id == null) { id = "biome_" + idx; } id = id.toUpperCase().replace(' ', '_'); @@ -180,6 +188,7 @@ public class BiomeMap { id = id + "_" + idx; } this.id = id; + biome_ids.add(this.id); // If index is NO_INDEX, find one after the well known ones if (idx == NO_INDEX) { idx = LAST_WELL_KNOWN; @@ -235,21 +244,15 @@ public class BiomeMap { } public final int getModifiedGrassMultiplier(int rawgrassmult) { - if(grassmult == 0) - return rawgrassmult; - else if(grassmult > 0xFFFFFF) - return grassmult & 0xFFFFFF; - else - return ((rawgrassmult & 0xfefefe) + grassmult) / 2; + if (grassmult == 0) return rawgrassmult; // common case: no override + if (grassmult < 0) return -grassmult; // fixed color (pre-masked at set-time) + return ((rawgrassmult & 0xfefefe) + grassmult) >> 1; // blend } - + public final int getModifiedFoliageMultiplier(int rawfoliagemult) { - if(foliagemult == 0) - return rawfoliagemult; - else if(foliagemult > 0xFFFFFF) - return foliagemult & 0xFFFFFF; - else - return ((rawfoliagemult & 0xfefefe) + foliagemult) / 2; + if (foliagemult == 0) return rawfoliagemult; // common case: no override + if (foliagemult < 0) return -foliagemult; // fixed color (pre-masked at set-time) + return ((rawfoliagemult & 0xfefefe) + foliagemult) >> 1; // blend } public final int getWaterColorMult() { return watercolormult; @@ -278,10 +281,10 @@ public class BiomeMap { this.watercolormult = watercolormult; } public void setGrassColorMultiplier(int grassmult) { - this.grassmult = grassmult; + this.grassmult = encodeColorMult(grassmult); } public void setFoliageColorMultiplier(int foliagemult) { - this.foliagemult = foliagemult; + this.foliagemult = encodeColorMult(foliagemult); } public void setTemperature(double tmp) { if(tmp < 0.0) tmp = 0.0; 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 95a16745..e5e01213 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java @@ -38,7 +38,7 @@ public class GenericChunk { // Get section for given block Y coord public final GenericChunkSection getSection(int y) { int idx = (y >> 4) - this.cy_min; - if ((idx < 0) || (idx >= sectionCnt)) { + if ((idx < 0) || (idx >= sections.length)) { return GenericChunkSection.EMPTY; } return this.sections[idx]; diff --git a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkCache.java b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkCache.java index c59c90e9..a836c6ea 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkCache.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkCache.java @@ -4,13 +4,14 @@ import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.lang.ref.SoftReference; +import java.util.HashMap; import java.util.IdentityHashMap; import java.util.LinkedHashMap; import java.util.Map; import org.dynmap.utils.DynIntHashMap; -// Generic chunk cache +// Generic chunk cache public class GenericChunkCache { public static class ChunkCacheRec { public GenericChunk ss; @@ -23,22 +24,26 @@ public class GenericChunkCache { private long cache_attempts; private long cache_success; private boolean softref; + // World name -> small integer ID, used to build long cache keys without String concatenation. + // Accessed only while holding snapcachelock. + private final HashMap worldIds = new HashMap(); + private int nextWorldId = 0; private static class CacheRec { Reference ref; } - + @SuppressWarnings("serial") - public class CacheHashMap extends LinkedHashMap { + public class CacheHashMap extends LinkedHashMap { private int limit; - private IdentityHashMap, String> reverselookup; + private IdentityHashMap, Long> reverselookup; public CacheHashMap(int lim) { super(16, (float)0.75, true); limit = lim; - reverselookup = new IdentityHashMap, String>(); + reverselookup = new IdentityHashMap, Long>(); } - protected boolean removeEldestEntry(Map.Entry last) { + protected boolean removeEldestEntry(Map.Entry last) { boolean remove = (size() >= limit); if(remove && (last != null) && (last.getValue() != null)) { reverselookup.remove(last.getValue().ref); @@ -56,15 +61,26 @@ public class GenericChunkCache { refqueue = new ReferenceQueue(); this.softref = softref; } - private String getKey(String w, int cx, int cz) { - return w + ":" + cx + ":" + cz; + /** + * Encode world name + chunk coords as a single long key. + * Worlds are assigned small integer IDs (10 bits) on first use. + * cx and cz are each encoded in 27 bits (signed, supports ±8M chunks / ±128M blocks). + * Must be called while holding snapcachelock. + */ + private long getKey(String w, int cx, int cz) { + Integer wid = worldIds.get(w); + if (wid == null) { + wid = nextWorldId++; + worldIds.put(w, wid); + } + return ((long)(wid & 0x3FF) << 54) | ((long)(cx & 0x7FFFFFF) << 27) | (long)(cz & 0x7FFFFFF); } /** * 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(snapcachelock) { + long key = getKey(w, x>>4, z>>4); CacheRec rec = (snapcache != null) ? snapcache.remove(key) : null; if(rec != null) { snapcache.reverselookup.remove(rec.ref); @@ -77,10 +93,10 @@ public class GenericChunkCache { * 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(snapcachelock) { + synchronized(snapcachelock) { + for(int xx = (x0>>4); xx <= (x1>>4); xx++) { + for(int zz = (z0>>4); zz <= (z1>>4); zz++) { + long key = getKey(w, xx, zz); CacheRec rec = (snapcache != null) ? snapcache.remove(key) : null; if(rec != null) { snapcache.reverselookup.remove(rec.ref); @@ -95,11 +111,11 @@ public class GenericChunkCache { * Look for chunk snapshot in cache */ public ChunkCacheRec getSnapshot(String w, int chunkx, int chunkz) { - String key = getKey(w, chunkx, chunkz); processRefQueue(); ChunkCacheRec ss = null; CacheRec rec; synchronized(snapcachelock) { + long key = getKey(w, chunkx, chunkz); rec = (snapcache != null) ? snapcache.get(key) : null; if(rec != null) { ss = rec.ref.get(); @@ -118,7 +134,6 @@ public class GenericChunkCache { * Add chunk snapshot to cache */ public void putSnapshot(String w, int chunkx, int chunkz, ChunkCacheRec ss) { - String key = getKey(w, chunkx, chunkz); processRefQueue(); CacheRec rec = new CacheRec(); if (softref) @@ -126,6 +141,7 @@ public class GenericChunkCache { else rec.ref = new WeakReference(ss, refqueue); synchronized(snapcachelock) { + long key = getKey(w, chunkx, chunkz); CacheRec prevrec = (snapcache != null) ? snapcache.put(key, rec) : null; if(prevrec != null) { snapcache.reverselookup.remove(prevrec.ref); @@ -140,7 +156,7 @@ public class GenericChunkCache { Reference ref; while((ref = refqueue.poll()) != null) { synchronized(snapcachelock) { - String k = (snapcache != null) ? snapcache.reverselookup.remove(ref) : null; + Long k = (snapcache != null) ? snapcache.reverselookup.remove(ref) : null; if(k != null) { snapcache.remove(k); } 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 0d7fd630..5eb85196 100644 --- a/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java +++ b/DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java @@ -25,7 +25,7 @@ public class GenericChunkSection { blocks = bs; } public final DynmapBlockState getBlock(int x, int y, int z) { - return blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)]; + return blocks[((y & 0xF) << 8) | ((z & 0xF) << 4) | (x & 0xF)]; } public final DynmapBlockState getBlock(GenericChunkPos pos) { return blocks[pos.soffset]; @@ -40,7 +40,7 @@ public class GenericChunkSection { palette = pal; } public final DynmapBlockState getBlock(int x, int y, int z) { - return palette[blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)]]; + return palette[blocks[((y & 0xF) << 8) | ((z & 0xF) << 4) | (x & 0xF)]]; } public final DynmapBlockState getBlock(GenericChunkPos pos) { return palette[blocks[pos.soffset]]; @@ -126,12 +126,12 @@ public class GenericChunkSection { light = new long[256]; if (lig != null) { for (int off = 0; (off < lig.length) && (off < 2048); off++) { - light[off >> 3] |= (0xFFL & (long)lig[off]) << (8 * (off & 0x7)); + light[off >> 3] |= (0xFFL & (long)lig[off]) << ((off & 0x7) << 3); } } } public final int getLight(int x, int y, int z) { - return 0xF & (int)(light[(16 * (y & 0xF)) + (z & 0xF)] >> (4 * (x & 0xF))); + return 0xF & (int)(light[((y & 0xF) << 4) | (z & 0xF)] >> ((x & 0xF) << 2)); } public final int getLight(GenericChunkPos pos) { return 0xF & (int)(light[pos.soffset >> 4] >> (4 * pos.sx)); diff --git a/DynmapCore/src/main/java/org/dynmap/hdmap/ShadowHDLighting.java b/DynmapCore/src/main/java/org/dynmap/hdmap/ShadowHDLighting.java index 8d41546a..4302918b 100644 --- a/DynmapCore/src/main/java/org/dynmap/hdmap/ShadowHDLighting.java +++ b/DynmapCore/src/main/java/org/dynmap/hdmap/ShadowHDLighting.java @@ -154,9 +154,7 @@ public class ShadowHDLighting extends DefaultHDLighting { } } if(cscale < 256) { - Color c = outcolor[0]; - c.setRGBA((c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8, - (c.getBlue() * cscale) >> 8, c.getAlpha()); + outcolor[0].scaleRGB(cscale); } if(outcolor.length > 1) { ll0 = getLightLevel(skyemit0, false); @@ -194,9 +192,7 @@ public class ShadowHDLighting extends DefaultHDLighting { } } if(cscale < 256) { - Color c = outcolor[1]; - c.setRGBA((c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8, - (c.getBlue() * cscale) >> 8, c.getAlpha()); + outcolor[1].scaleRGB(cscale); } } } @@ -269,8 +265,7 @@ public class ShadowHDLighting extends DefaultHDLighting { private final void shadowColor(Color c, int lightlevel, int[] shadowscale) { int scale = shadowscale[lightlevel]; if(scale < 256) - c.setRGBA((c.getRed() * scale) >> 8, (c.getGreen() * scale) >> 8, - (c.getBlue() * scale) >> 8, c.getAlpha()); + c.scaleRGB(scale); } diff --git a/DynmapCore/src/main/java/org/dynmap/utils/PatchDefinition.java b/DynmapCore/src/main/java/org/dynmap/utils/PatchDefinition.java index 67a47939..f2658715 100644 --- a/DynmapCore/src/main/java/org/dynmap/utils/PatchDefinition.java +++ b/DynmapCore/src/main/java/org/dynmap/utils/PatchDefinition.java @@ -97,16 +97,21 @@ public class PatchDefinition implements RenderPatch { */ PatchDefinition(PatchDefinition orig, double rotatex, double rotatey, double rotatez, Vector3D rotorigin, int textureindex) { if (rotorigin == null) rotorigin = offsetCenter; + /* Precompute trig once - reused for all three vector rotations */ + double sinX = 0, cosX = 1, sinY = 0, cosY = 1, sinZ = 0, cosZ = 1; + if (rotatex != 0) { double r = Math.toRadians(rotatex); sinX = Math.sin(r); cosX = Math.cos(r); } + if (rotatey != 0) { double r = Math.toRadians(rotatey); sinY = Math.sin(r); cosY = Math.cos(r); } + if (rotatez != 0) { double r = Math.toRadians(rotatez); sinZ = Math.sin(r); cosZ = Math.cos(r); } Vector3D vec = new Vector3D(orig.x0, orig.y0, orig.z0); - rotate(vec, rotatex, rotatey, rotatez, rotorigin); /* Rotate origin */ + rotatePrecomputed(vec, sinX, cosX, sinY, cosY, sinZ, cosZ, rotorigin); /* Rotate origin */ x0 = vec.x; y0 = vec.y; z0 = vec.z; /* Rotate U */ vec.x = orig.xu; vec.y = orig.yu; vec.z = orig.zu; - rotate(vec, rotatex, rotatey, rotatez, rotorigin); /* Rotate origin */ + rotatePrecomputed(vec, sinX, cosX, sinY, cosY, sinZ, cosZ, rotorigin); xu = vec.x; yu = vec.y; zu = vec.z; /* Rotate V */ vec.x = orig.xv; vec.y = orig.yv; vec.z = orig.zv; - rotate(vec, rotatex, rotatey, rotatez, rotorigin); /* Rotate origin */ + rotatePrecomputed(vec, sinX, cosX, sinY, cosY, sinZ, cosZ, rotorigin); xv = vec.x; yv = vec.y; zv = vec.z; umin = orig.umin; vmin = orig.vmin; umax = orig.umax; vmax = orig.vmax; @@ -119,27 +124,31 @@ public class PatchDefinition implements RenderPatch { v = new Vector3D(); update(); } - - private void rotate(Vector3D vec, double xcnt, double ycnt, double zcnt, Vector3D origin) { - // If no rotation, skip - if ((xcnt == 0) && (ycnt == 0) && (zcnt == 0)) return; - vec.subtract(origin); /* Shoft to center of block */ + + private static void rotatePrecomputed(Vector3D vec, + double sinX, double cosX, double sinY, double cosY, double sinZ, double cosZ, + Vector3D origin) { + if (sinX == 0 && sinY == 0 && sinZ == 0) return; + vec.subtract(origin); /* Do X rotation */ - double rot = Math.toRadians(xcnt); - double nval = vec.z * Math.sin(rot) + vec.y * Math.cos(rot); - vec.z = vec.z * Math.cos(rot) - vec.y * Math.sin(rot); - vec.y = nval; + if (sinX != 0) { + double nval = vec.z * sinX + vec.y * cosX; + vec.z = vec.z * cosX - vec.y * sinX; + vec.y = nval; + } /* Do Y rotation */ - rot = Math.toRadians(ycnt); - nval = vec.x * Math.cos(rot) - vec.z * Math.sin(rot); - vec.z = vec.x * Math.sin(rot) + vec.z * Math.cos(rot); - vec.x = nval; + if (sinY != 0) { + double nval = vec.x * cosY - vec.z * sinY; + vec.z = vec.x * sinY + vec.z * cosY; + vec.x = nval; + } /* Do Z rotation */ - rot = Math.toRadians(zcnt); - nval = vec.y * Math.sin(rot) + vec.x * Math.cos(rot); - vec.y = vec.y * Math.cos(rot) - vec.x * Math.sin(rot); - vec.x = nval; - vec.add(origin); /* Shoft back to corner */ + if (sinZ != 0) { + double nval = vec.y * sinZ + vec.x * cosZ; + vec.y = vec.y * cosZ - vec.x * sinZ; + vec.x = nval; + } + vec.add(origin); } public void update(double x0, double y0, double z0, double xu, double yu, double zu, double xv, double yv, double zv, double umin, @@ -171,7 +180,7 @@ public class PatchDefinition implements RenderPatch { /* Compute hash code */ hc = (int)((Double.doubleToLongBits(x0 + xu + xv) >> 32) ^ (Double.doubleToLongBits(y0 + yu + yv) >> 34) ^ - (Double.doubleToLongBits(z0 + yu + yv) >> 36) ^ + (Double.doubleToLongBits(z0 + zu + zv) >> 36) ^ (Double.doubleToLongBits(umin + umax + vmin + vmax + vmaxatumax) >> 38)) ^ (sidevis.ordinal() << 8) ^ textureindex; /* Now compute normal of surface - U cross V */ @@ -221,30 +230,30 @@ public class PatchDefinition implements RenderPatch { } public boolean validate() { boolean good = true; - // Compute visible corners to see if we're inside cube - double xx0 = x0 + (xu - x0) * umin + (xv - x0) * vmin; - double xx1 = x0 + (xu - x0) * vmin + (xv - x0) * vmax; - double xx2 = x0 + (xu - x0) * umax + (xv - x0) * vmin; - double xx3 = x0 + (xu - x0) * vmax + (xv - x0) * vmax;; + // Compute visible corners to see if we're inside cube (u.x = xu-x0, v.x = xv-x0) + double xx0 = x0 + u.x * umin + v.x * vmin; + double xx1 = x0 + u.x * vmin + v.x * vmax; + double xx2 = x0 + u.x * umax + v.x * vmin; + double xx3 = x0 + u.x * vmax + v.x * vmax; if (outOfRange(xx0) || outOfRange(xx1) || outOfRange(xx2) || outOfRange(xx3)) { Log.verboseinfo(String.format("Invalid visible range xu=[%f:%f], xv=[%f:%f]", xx0, xx2, xx1, xx3)); - good = false; + good = false; } - double yy0 = y0 + (yu - y0) * umin + (yv - y0) * vmin; - double yy1 = y0 + (yu - y0) * vmin + (yv - y0) * vmax; - double yy2 = y0 + (yu - y0) * umax + (yv - y0) * vmin; - double yy3 = y0 + (yu - y0) * vmax + (yv - y0) * vmax;; + double yy0 = y0 + u.y * umin + v.y * vmin; + double yy1 = y0 + u.y * vmin + v.y * vmax; + double yy2 = y0 + u.y * umax + v.y * vmin; + double yy3 = y0 + u.y * vmax + v.y * vmax; if (outOfRange(yy0) || outOfRange(yy1) || outOfRange(yy2) || outOfRange(yy3)) { Log.verboseinfo(String.format("Invalid visible range yu=[%f:%f], yv=[%f:%f]", yy0, yy2, yy1, yy3)); - good = false; + good = false; } - double zz0 = z0 + (zu - z0) * umin + (zv - z0) * vmin; - double zz1 = z0 + (zu - z0) * vmin + (zv - z0) * vmax; - double zz2 = z0 + (zu - z0) * umax + (zv - z0) * vmin; - double zz3 = z0 + (zu - z0) * vmax + (zv - z0) * vmax; + double zz0 = z0 + u.z * umin + v.z * vmin; + double zz1 = z0 + u.z * vmin + v.z * vmax; + double zz2 = z0 + u.z * umax + v.z * vmin; + double zz3 = z0 + u.z * vmax + v.z * vmax; if (outOfRange(zz0) || outOfRange(zz1) || outOfRange(zz2) || outOfRange(zz3)) { Log.verboseinfo(String.format("Invalid visible range zu=[%f:%f], zv=[%f:%f]", zz0, zz2, zz1, zz3)); - good = false; + good = false; } if (!good) { Log.verboseinfo("Bad patch: " + this); diff --git a/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java b/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java index 06578ce5..6af4671c 100644 --- a/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java +++ b/DynmapCoreAPI/src/main/java/org/dynmap/renderer/DynmapBlockState.java @@ -306,11 +306,10 @@ public class DynmapBlockState { */ public static final DynmapBlockState getStateByGlobalIndex(int gidx) { if (blockArrayByIndex != null) { - try { + if (gidx >= 0 && gidx < blockArrayByIndex.length) { return blockArrayByIndex[gidx]; - } catch (ArrayIndexOutOfBoundsException aioob) { - return AIR; } + return AIR; } DynmapBlockState bs = blocksByIndex.get(gidx); return (bs != null) ? bs : AIR; @@ -322,12 +321,11 @@ public class DynmapBlockState { */ public static final DynmapBlockState getStateByLegacyBlockID(int legacyid) { if (blockArrayByLegacyID != null) { - try { + if (legacyid >= 0 && legacyid < blockArrayByLegacyID.length) { return blockArrayByLegacyID[legacyid]; - } catch (ArrayIndexOutOfBoundsException aioob) { - return null; } - } + return null; + } return blocksByLegacyID.get(legacyid); } /** @@ -348,7 +346,9 @@ public class DynmapBlockState { rslt = AIR; // Assume miss String[] statelist = statename.toLowerCase().split(","); - for (DynmapBlockState bb : blk.states) { + for (int sidx = 0; sidx <= blk.stateLastIdx; sidx++) { + DynmapBlockState bb = blk.states[sidx]; + if (bb == AIR) continue; // Skip AIR fill slots boolean match = true; for (int i = 0; i < statelist.length; i++) { boolean valmatch = false; @@ -367,7 +367,7 @@ public class DynmapBlockState { rslt = bb; break; } - } + } blk.lookup.put(statename, rslt); // Cache the lookup } } @@ -546,10 +546,20 @@ public class DynmapBlockState { * Test if matches attrib=value pair */ public final boolean isStateMatch(String attrib, String value) { - String v = attrib + "=" + value; - v = v.toLowerCase(); + int alen = attrib.length(); + int vlen = value.length(); + int total = alen + 1 + vlen; + outer: for (String state : stateList) { - if (state.equals(v)) return true; + if (state.length() != total) continue; + for (int i = 0; i < alen; i++) { + if (Character.toLowerCase(attrib.charAt(i)) != state.charAt(i)) continue outer; + } + if (state.charAt(alen) != '=') continue; + for (int i = 0; i < vlen; i++) { + if (Character.toLowerCase(value.charAt(i)) != state.charAt(alen + 1 + i)) continue outer; + } + return true; } return false; }