Add queued update triggers for zoom-out updates - deals with file time

resolution issues on Linux (1 second is too long to be reliable)
This commit is contained in:
Mike Primm 2011-06-24 08:25:26 -05:00
parent d63db655d8
commit 214fec208d
5 changed files with 121 additions and 88 deletions

View File

@ -33,7 +33,7 @@ public class ClientConfigurationComponent extends Component {
s(wo, "center/y", wn.getFloat("center/y", 64.0f)); s(wo, "center/y", wn.getFloat("center/y", 64.0f));
s(wo, "center/z", wn.getFloat("center/z", 0.0f)); s(wo, "center/z", wn.getFloat("center/z", 0.0f));
s(wo, "bigworld", world.bigworld); s(wo, "bigworld", world.bigworld);
s(wo, "extrazoomout", world.extrazoomoutlevels); s(wo, "extrazoomout", world.getExtraZoomOutLevels());
a(t, "worlds", wo); a(t, "worlds", wo);
for(MapType mt : world.maps) { for(MapType mt : world.maps) {

View File

@ -16,6 +16,7 @@ import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
@ -31,8 +32,39 @@ public class DynmapWorld {
public boolean sendposition; public boolean sendposition;
public boolean sendhealth; public boolean sendhealth;
public boolean bigworld; /* If true, deeper directory hierarchy */ public boolean bigworld; /* If true, deeper directory hierarchy */
public int extrazoomoutlevels; /* Number of additional zoom out levels to generate */ private int extrazoomoutlevels; /* Number of additional zoom out levels to generate */
public File worldtilepath; public File worldtilepath;
private Object lock = new Object();
private HashSet<File> zoomoutupdates[];
@SuppressWarnings("unchecked")
public void setExtraZoomOutLevels(int lvl) {
extrazoomoutlevels = lvl;
zoomoutupdates = new HashSet[lvl];
for(int i = 0; i < lvl; i++)
zoomoutupdates[i] = new HashSet<File>();
}
public int getExtraZoomOutLevels() { return extrazoomoutlevels; }
public void enqueueZoomOutUpdate(File f) {
enqueueZoomOutUpdate(f, 0);
}
private void enqueueZoomOutUpdate(File f, int level) {
if(level >= extrazoomoutlevels)
return;
synchronized(lock) {
zoomoutupdates[level].add(f);
}
}
private boolean popQueuedUpdate(File f, int level) {
if(level >= extrazoomoutlevels)
return false;
synchronized(lock) {
return zoomoutupdates[level].remove(f);
}
}
private static class DirFilter implements FilenameFilter { private static class DirFilter implements FilenameFilter {
public boolean accept(File f, String n) { public boolean accept(File f, String n) {
@ -73,11 +105,40 @@ public class DynmapWorld {
String zfnprefix; String zfnprefix;
} }
/**
* File based scan - used for priming map after server startup
* @param zoomlevel
*/
public void freshenZoomOutFilesByLevel(int zoomlevel) { public void freshenZoomOutFilesByLevel(int zoomlevel) {
int cnt = 0; int cnt = 0;
Debug.debug("freshenZoomOutFiles(" + world.getName() + "," + zoomlevel + ")"); Debug.debug("freshenZoomOutFiles(" + world.getName() + "," + zoomlevel + ")");
if(worldtilepath.exists() == false) /* Quit if not found */ if(worldtilepath.exists() == false) /* Quit if not found */
return; return;
HashMap<String, PrefixData> maptab = buildPrefixData(zoomlevel);
if(bigworld) { /* If big world, next directories are map name specific */
DirFilter df = new DirFilter();
for(String pfx : maptab.keySet()) { /* Walk through prefixes, as directories */
PrefixData pd = maptab.get(pfx);
File dname = new File(worldtilepath, pfx);
/* Now, go through subdirectories under this one, and process them */
String[] subdir = dname.list(df);
if(subdir == null) continue;
for(String s : subdir) {
File sdname = new File(dname, s);
cnt += processZoomDirectory(sdname, pd);
}
}
}
else { /* Else, classic file layout */
for(String pfx : maptab.keySet()) { /* Walk through prefixes, as directories */
cnt += processZoomDirectory(worldtilepath, maptab.get(pfx));
}
}
Debug.debug("freshenZoomOutFiles(" + world.getName() + "," + zoomlevel + ") - done (" + cnt + " updated files)");
}
private HashMap<String, PrefixData> buildPrefixData(int zoomlevel) {
HashMap<String, PrefixData> maptab = new HashMap<String, PrefixData>(); HashMap<String, PrefixData> maptab = new HashMap<String, PrefixData>();
/* Build table of file prefixes and step sizes */ /* Build table of file prefixes and step sizes */
for(MapType mt : maps) { for(MapType mt : maps) {
@ -85,8 +146,8 @@ public class DynmapWorld {
int stepsize = mt.baseZoomFileStepSize(); int stepsize = mt.baseZoomFileStepSize();
boolean neg_step_x = false; boolean neg_step_x = false;
if(stepsize < 0) { if(stepsize < 0) {
stepsize = -stepsize; stepsize = -stepsize;
neg_step_x = true; neg_step_x = true;
} }
int[] stepseq = mt.zoomFileStepSequence(); int[] stepseq = mt.zoomFileStepSequence();
for(String p : pfx) { for(String p : pfx) {
@ -115,26 +176,7 @@ public class DynmapWorld {
maptab.put(p, pd); maptab.put(p, pd);
} }
} }
if(bigworld) { /* If big world, next directories are map name specific */ return maptab;
DirFilter df = new DirFilter();
for(String pfx : maptab.keySet()) { /* Walk through prefixes, as directories */
PrefixData pd = maptab.get(pfx);
File dname = new File(worldtilepath, pfx);
/* Now, go through subdirectories under this one, and process them */
String[] subdir = dname.list(df);
if(subdir == null) continue;
for(String s : subdir) {
File sdname = new File(dname, s);
cnt += processZoomDirectory(sdname, pd);
}
}
}
else { /* Else, classic file layout */
for(String pfx : maptab.keySet()) { /* Walk through prefixes, as directories */
cnt += processZoomDirectory(worldtilepath, maptab.get(pfx));
}
}
Debug.debug("freshenZoomOutFiles(" + world.getName() + "," + zoomlevel + ") - done (" + cnt + " updated files)");
} }
private static class ProcessTileRec { private static class ProcessTileRec {
@ -158,51 +200,13 @@ public class DynmapWorld {
if(files == null) if(files == null)
return 0; return 0;
for(String fn : files) { for(String fn : files) {
/* Build file object */ ProcessTileRec tr = processZoomFile(new File(dir, fn), pd);
File f = new File(dir, fn); if(tr != null) {
/* Parse filename to predict zoomed out file */ String zfpath = tr.zf.getPath();
fn = fn.substring(0, fn.lastIndexOf('.')); /* Strip off extension */ if(!toprocess.containsKey(zfpath)) {
String[] tok = fn.split("_"); /* Split by underscores */ toprocess.put(zfpath, tr);
int x = 0;
int y = 0;
boolean parsed = false;
if(tok.length >= 2) {
try {
x = Integer.parseInt(tok[tok.length-2]);
y = Integer.parseInt(tok[tok.length-1]);
parsed = true;
} catch (NumberFormatException nfx) {
} }
} }
if(!parsed)
continue;
if(pd.neg_step_x) x = -x;
if(x >= 0)
x = x - (x % (2*step));
else
x = x + (x % (2*step));
if(pd.neg_step_x) x = -x;
if(y >= 0)
y = y - (y % (2*step));
else
y = y + (y % (2*step));
/* Make name of corresponding zoomed tile */
String zfname = makeFilePath(pd, x, y, true);
File zf = new File(worldtilepath, zfname);
long fts = f.lastModified();
/* If zoom file exists and is older than our file, nothing to do */
if(zf.exists() && ((zf.lastModified() >= f.lastModified()) || checkIgnoreUpdate(f, fts))) {
continue;
}
String zfpath = zf.getPath();
if(!toprocess.containsKey(zfpath)) {
ProcessTileRec rec = new ProcessTileRec();
rec.zf = zf;
rec.x = x;
rec.y = y;
rec.zfname = zfname;
toprocess.put(zfpath, rec);
}
} }
int cnt = 0; int cnt = 0;
/* Do processing */ /* Do processing */
@ -214,6 +218,50 @@ public class DynmapWorld {
return cnt; return cnt;
} }
private ProcessTileRec processZoomFile(File f, PrefixData pd) {
int step = pd.stepsize << pd.zoomlevel;
String fn = f.getName();
/* Parse filename to predict zoomed out file */
fn = fn.substring(0, fn.lastIndexOf('.')); /* Strip off extension */
String[] tok = fn.split("_"); /* Split by underscores */
int x = 0;
int y = 0;
boolean parsed = false;
if(tok.length >= 2) {
try {
x = Integer.parseInt(tok[tok.length-2]);
y = Integer.parseInt(tok[tok.length-1]);
parsed = true;
} catch (NumberFormatException nfx) {
}
}
if(!parsed)
return null;
if(pd.neg_step_x) x = -x;
if(x >= 0)
x = x - (x % (2*step));
else
x = x + (x % (2*step));
if(pd.neg_step_x) x = -x;
if(y >= 0)
y = y - (y % (2*step));
else
y = y + (y % (2*step));
/* Make name of corresponding zoomed tile */
String zfname = makeFilePath(pd, x, y, true);
File zf = new File(worldtilepath, zfname);
/* If we're not updated, and zoom file exists and is older than our file, nothing to do */
if((!popQueuedUpdate(f, pd.zoomlevel)) && zf.exists() && (zf.lastModified() >= f.lastModified())) {
return null;
}
ProcessTileRec rec = new ProcessTileRec();
rec.zf = zf;
rec.x = x;
rec.y = y;
rec.zfname = zfname;
return rec;
}
private void processZoomTile(PrefixData pd, File dir, File zf, String zfname, int tx, int ty) { private void processZoomTile(PrefixData pd, File dir, File zf, String zfname, int tx, int ty) {
Debug.debug("processZoomFile(" + pd.baseprefix + "," + dir.getPath() + "," + zf.getPath() + "," + tx + "," + ty + ")"); Debug.debug("processZoomFile(" + pd.baseprefix + "," + dir.getPath() + "," + zf.getPath() + "," + tx + "," + ty + ")");
int width = 128, height = 128; int width = 128, height = 128;
@ -285,31 +333,12 @@ public class DynmapWorld {
} }
hashman.updateHashCode(key, null, tilex, tiley, crc); hashman.updateHashCode(key, null, tilex, tiley, crc);
MapManager.mapman.pushUpdate(this.world, new Client.Tile(zfname)); MapManager.mapman.pushUpdate(this.world, new Client.Tile(zfname));
enqueueZoomOutUpdate(zf, pd.zoomlevel+1);
} }
else { else {
zf.setLastModified(System.currentTimeMillis()); /* Touch the existing file */ zf.setLastModified(System.currentTimeMillis()); /* Touch the existing file */
// ignoreUpdate(zf); /* Remember to ignore this update */
} }
FileLockManager.releaseWriteLock(zf); FileLockManager.releaseWriteLock(zf);
KzedMap.freeBufferedImage(kzIm); KzedMap.freeBufferedImage(kzIm);
} }
private HashMap<String, Long> ignore_upd = new HashMap<String, Long>();
private void ignoreUpdate(File f) {
long ts = f.lastModified();
ignore_upd.put(f.getPath(), ts);
}
/**
* Check if ignore for file at current timestamp - return true if so
*/
private boolean checkIgnoreUpdate(File f, long ts) {
String fn = f.getPath();
Long its = ignore_upd.get(fn);
if((its != null) && (ts <= its.longValue())) {
return true;
}
ignore_upd.remove(fn); /* If newer, stop ignoring */
return false;
}
} }

View File

@ -361,7 +361,7 @@ public class MapManager {
dynmapWorld.sendposition = worldConfiguration.getBoolean("sendposition", true); dynmapWorld.sendposition = worldConfiguration.getBoolean("sendposition", true);
dynmapWorld.sendhealth = worldConfiguration.getBoolean("sendhealth", true); dynmapWorld.sendhealth = worldConfiguration.getBoolean("sendhealth", true);
dynmapWorld.bigworld = worldConfiguration.getBoolean("bigworld", false); dynmapWorld.bigworld = worldConfiguration.getBoolean("bigworld", false);
dynmapWorld.extrazoomoutlevels = worldConfiguration.getInteger("extrazoomout", 0); dynmapWorld.setExtraZoomOutLevels(worldConfiguration.getInteger("extrazoomout", 0));
dynmapWorld.worldtilepath = new File(plug_in.tilesDirectory, w.getName()); dynmapWorld.worldtilepath = new File(plug_in.tilesDirectory, w.getName());
if(loclist != null) { if(loclist != null) {
for(ConfigurationNode loc : loclist) { for(ConfigurationNode loc : loclist) {

View File

@ -289,6 +289,7 @@ public class FlatMap extends MapType {
} }
MapManager.mapman.pushUpdate(tile.getWorld(), new Client.Tile(tile.getFilename())); MapManager.mapman.pushUpdate(tile.getWorld(), new Client.Tile(tile.getFilename()));
hashman.updateHashCode(tile.getKey(), null, t.x, t.y, crc); hashman.updateHashCode(tile.getKey(), null, t.x, t.y, crc);
tile.getDynmapWorld().enqueueZoomOutUpdate(outputFile);
tile_update = true; tile_update = true;
} }
else { else {
@ -316,6 +317,7 @@ public class FlatMap extends MapType {
} }
MapManager.mapman.pushUpdate(tile.getWorld(), new Client.Tile(tile.getDayFilename())); MapManager.mapman.pushUpdate(tile.getWorld(), new Client.Tile(tile.getDayFilename()));
hashman.updateHashCode(tile.getKey(), "day", t.x, t.y, crc); hashman.updateHashCode(tile.getKey(), "day", t.x, t.y, crc);
tile.getDynmapWorld().enqueueZoomOutUpdate(dayfile);
tile_update = true; tile_update = true;
} }
else { else {

View File

@ -317,6 +317,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
saveZoomedTile(zmtile, zoomFile, zimg, ox, oy, null); saveZoomedTile(zmtile, zoomFile, zimg, ox, oy, null);
MapManager.mapman.pushUpdate(zmtile.getWorld(), MapManager.mapman.pushUpdate(zmtile.getWorld(),
new Client.Tile(zmtile.getFilename())); new Client.Tile(zmtile.getFilename()));
zmtile.getDynmapWorld().enqueueZoomOutUpdate(zoomFile);
ztile_updated = true; ztile_updated = true;
} }
KzedMap.freeBufferedImage(zimg); KzedMap.freeBufferedImage(zimg);
@ -331,6 +332,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
saveZoomedTile(zmtile, zoomFile_day, zimg_day, ox, oy, "day"); saveZoomedTile(zmtile, zoomFile_day, zimg_day, ox, oy, "day");
MapManager.mapman.pushUpdate(zmtile.getWorld(), MapManager.mapman.pushUpdate(zmtile.getWorld(),
new Client.Tile(zmtile.getDayFilename())); new Client.Tile(zmtile.getDayFilename()));
zmtile.getDynmapWorld().enqueueZoomOutUpdate(zoomFile_day);
ztile_updated = true; ztile_updated = true;
} }
KzedMap.freeBufferedImage(zimg_day); KzedMap.freeBufferedImage(zimg_day);