Tinker with performance on some base data classes

This commit is contained in:
Mike Primm 2026-03-05 18:45:42 -05:00
parent 5a30ab0d3f
commit c6fb0be7e7
8 changed files with 172 additions and 119 deletions

View file

@ -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;
}
}

View file

@ -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<String, BiomeMap> biome_by_rl = new HashMap<String, BiomeMap>();
private static Map<String, BiomeMap> biome_by_rl = new HashMap<String, BiomeMap>(256);
// Tracks registered IDs for O(1) uniqueness checks during initialization
private static final HashSet<String> biome_ids = new HashSet<String>(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;

View file

@ -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];

View file

@ -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<String, Integer> worldIds = new HashMap<String, Integer>();
private int nextWorldId = 0;
private static class CacheRec {
Reference<ChunkCacheRec> ref;
}
@SuppressWarnings("serial")
public class CacheHashMap extends LinkedHashMap<String, CacheRec> {
public class CacheHashMap extends LinkedHashMap<Long, CacheRec> {
private int limit;
private IdentityHashMap<Reference<ChunkCacheRec>, String> reverselookup;
private IdentityHashMap<Reference<ChunkCacheRec>, Long> reverselookup;
public CacheHashMap(int lim) {
super(16, (float)0.75, true);
limit = lim;
reverselookup = new IdentityHashMap<Reference<ChunkCacheRec>, String>();
reverselookup = new IdentityHashMap<Reference<ChunkCacheRec>, Long>();
}
protected boolean removeEldestEntry(Map.Entry<String, CacheRec> last) {
protected boolean removeEldestEntry(Map.Entry<Long, CacheRec> 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<ChunkCacheRec>();
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<ChunkCacheRec>(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<? extends ChunkCacheRec> 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);
}

View file

@ -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));

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}