Add render statistics, support for tile hashcodes to stop non-updates
This commit is contained in:
parent
4b30fff8a7
commit
d393ccf6e9
11 changed files with 418 additions and 115 deletions
152
src/main/java/org/dynmap/TileHashManager.java
Normal file
152
src/main/java/org/dynmap/TileHashManager.java
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
package org.dynmap;
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.io.IOException;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
/**
|
||||
* Image hash code manager - used to reduce compression and notification of updated tiles that do not actually yield new content
|
||||
*
|
||||
*/
|
||||
public class TileHashManager {
|
||||
private File tiledir; /* Base tile directory */
|
||||
private boolean enabled;
|
||||
|
||||
/**
|
||||
* Each tile hash file is a 32x32 tile grid, with each file having a CRC32 hash code generated from its pre-compression frame buffer
|
||||
*/
|
||||
private static class TileHashFile {
|
||||
final String key;
|
||||
final String subtype;
|
||||
final int x; /* minimum tile coordinate / 32 */
|
||||
final int y; /* minimum tile coordinate / 32 */
|
||||
private File hf;
|
||||
TileHashFile(String key, String subtype, int x, int y) {
|
||||
this.key = key;
|
||||
if(subtype != null)
|
||||
this.subtype = subtype;
|
||||
else
|
||||
this.subtype = "";
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if(!(o instanceof TileHashFile))
|
||||
return false;
|
||||
TileHashFile fo = (TileHashFile)o;
|
||||
return (x == fo.x) && (y == fo.y) && key.equals(fo.key) && (subtype.equals(fo.subtype));
|
||||
}
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return key.hashCode() ^ subtype.hashCode() ^ (x << 16) ^ y;
|
||||
}
|
||||
|
||||
public File getHashFile(File tiledir) {
|
||||
if(hf == null)
|
||||
hf = new File(tiledir, key + (subtype.equals("")?"":("." + subtype)) + "_" + x + "_" + y + ".hash");
|
||||
return hf;
|
||||
}
|
||||
/* Write to file */
|
||||
public void writeToFile(File tiledir, byte[] crcbuf) {
|
||||
try {
|
||||
RandomAccessFile fd = new RandomAccessFile(getHashFile(tiledir), "rw");
|
||||
fd.seek(0);
|
||||
fd.write(crcbuf);
|
||||
fd.close();
|
||||
} catch (IOException iox) {
|
||||
Log.severe("Error writing hash file - " + getHashFile(tiledir).getPath());
|
||||
}
|
||||
}
|
||||
|
||||
/* Read from file */
|
||||
public void readFromFile(File tiledir, byte[] crcbuf) {
|
||||
try {
|
||||
RandomAccessFile fd = new RandomAccessFile(getHashFile(tiledir), "r");
|
||||
fd.seek(0);
|
||||
fd.read(crcbuf);
|
||||
fd.close();
|
||||
} catch (IOException iox) {
|
||||
Arrays.fill(crcbuf, (byte)0xFF);
|
||||
writeToFile(tiledir, crcbuf);
|
||||
}
|
||||
}
|
||||
/* Read CRC */
|
||||
public long getCRC(int tx, int ty, byte[] crcbuf) {
|
||||
int off = (128 * (ty & 0x1F)) + (4 * (tx & 0x1F));
|
||||
long crc = 0;
|
||||
for(int i = 0; i < 4; i++)
|
||||
crc = (crc << 8) + (0xFF & (int)crcbuf[off+i]);
|
||||
return crc;
|
||||
}
|
||||
/* Set CRC */
|
||||
public void setCRC(int tx, int ty, byte[] crcbuf, long crc) {
|
||||
int off = (128 * (ty & 0x1F)) + (4 * (tx & 0x1F));
|
||||
for(int i = 0; i < 4; i++)
|
||||
crcbuf[off+i] = (byte)((crc >> ((3-i)*8)) & 0xFF);
|
||||
}
|
||||
}
|
||||
private Object lock = new Object();
|
||||
private LinkedHashMap<TileHashFile, byte[]> tilehash = new LinkedHashMap<TileHashFile, byte[]>(16, (float) 0.75, true);
|
||||
private CRC32 crc32 = new CRC32();
|
||||
|
||||
public TileHashManager(File tileroot, boolean enabled) {
|
||||
tiledir = tileroot;
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
/* Read cached hashcode for given tile */
|
||||
public long getImageHashCode(String key, String subtype, int tx, int ty) {
|
||||
if(!enabled) {
|
||||
return -1; /* Return value that never matches */
|
||||
}
|
||||
TileHashFile thf = new TileHashFile(key, subtype, tx >> 5, ty >> 5);
|
||||
synchronized(lock) {
|
||||
byte[] crcbuf = tilehash.get(thf); /* See if we have it cached */
|
||||
if(crcbuf == null) { /* If not in cache, load it */
|
||||
crcbuf = new byte[32*32*4]; /* Get our space */
|
||||
Arrays.fill(crcbuf, (byte)0xFF); /* Fill with -1 */
|
||||
tilehash.put(thf, crcbuf); /* Add to cache */
|
||||
thf.readFromFile(tiledir, crcbuf);
|
||||
}
|
||||
return thf.getCRC(tx & 0x1F, ty & 0x1F, crcbuf);
|
||||
}
|
||||
}
|
||||
/* Calculate hash code for given buffer */
|
||||
public long calculateTileHash(int[] newbuf) {
|
||||
if(!enabled) {
|
||||
return 0; /* Return value that doesn't match */
|
||||
}
|
||||
synchronized(lock) {
|
||||
/* Calculate CRC-32 for buffer */
|
||||
crc32.reset();
|
||||
for(int i = 0; i < newbuf.length; i++) {
|
||||
int v = newbuf[i];
|
||||
crc32.update(0xFF & v);
|
||||
crc32.update(0xFF & (v >> 8));
|
||||
crc32.update(0xFF & (v >> 16));
|
||||
crc32.update(0xFF & (v >> 24));
|
||||
}
|
||||
return crc32.getValue();
|
||||
}
|
||||
}
|
||||
/* Update hashcode for given tile */
|
||||
public void updateHashCode(String key, String subtype, int tx, int ty, long newcrc) {
|
||||
if(!enabled)
|
||||
return;
|
||||
synchronized(lock) {
|
||||
/* Now, find and check existing value */
|
||||
TileHashFile thf = new TileHashFile(key, subtype, tx >> 5, ty >> 5);
|
||||
byte[] crcbuf = tilehash.get(thf); /* See if we have it cached */
|
||||
if(crcbuf == null) { /* If not in cache, load it */
|
||||
crcbuf = new byte[32*32*4]; /* Get our space */
|
||||
tilehash.put(thf, crcbuf); /* Add to cache */
|
||||
thf.readFromFile(tiledir, crcbuf);
|
||||
}
|
||||
thf.setCRC(tx & 0x1F, ty & 0x1F, crcbuf, newcrc); /* Update field */
|
||||
thf.writeToFile(tiledir, crcbuf); /* And write it out */
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue