From bec1108fa15e41ec892f98eb7dd61316a8c432ac Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 22 Jun 2011 08:27:21 -0500 Subject: [PATCH 1/5] Settable zoom out code - incomplete prototype --- src/main/java/org/dynmap/DynmapWorld.java | 207 ++++++++++++++++++ src/main/java/org/dynmap/MapManager.java | 15 ++ src/main/java/org/dynmap/MapType.java | 11 +- src/main/java/org/dynmap/flat/FlatMap.java | 17 +- .../org/dynmap/kzedmap/CaveTileRenderer.java | 2 + .../dynmap/kzedmap/DefaultTileRenderer.java | 2 + src/main/java/org/dynmap/kzedmap/KzedMap.java | 15 ++ .../org/dynmap/kzedmap/MapTileRenderer.java | 1 + web/js/flatmap.js | 4 +- 9 files changed, 269 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/dynmap/DynmapWorld.java b/src/main/java/org/dynmap/DynmapWorld.java index 8933a129..708b9e4c 100644 --- a/src/main/java/org/dynmap/DynmapWorld.java +++ b/src/main/java/org/dynmap/DynmapWorld.java @@ -1,12 +1,25 @@ package org.dynmap; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import org.bukkit.World; import org.bukkit.Location; +import org.dynmap.debug.Debug; +import org.dynmap.kzedmap.KzedMap; +import org.dynmap.kzedmap.KzedMap.KzedBufferedImage; +import org.dynmap.utils.FileLockManager; import org.dynmap.utils.MapChunkCache; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.util.HashMap; + +import javax.imageio.ImageIO; + public class DynmapWorld { public World world; public List maps = new ArrayList(); @@ -19,4 +32,198 @@ public class DynmapWorld { public boolean sendposition; public boolean sendhealth; public boolean bigworld; /* If true, deeper directory hierarchy */ + public int extrazoomoutlevels; /* Number of additional zoom out levels to generate */ + + private static class DirFilter implements FilenameFilter { + public boolean accept(File f, String n) { + if(!n.equals("..") && !n.equals(".")) { + File fn = new File(f, n); + return fn.isDirectory(); + } + return false; + } + } + + private static class PNGFileFilter implements FilenameFilter { + String prefix; + public PNGFileFilter(String pre) { prefix = pre; } + public boolean accept(File f, String n) { + if(n.endsWith(".png") && n.startsWith(prefix)) { + File fn = new File(f, n); + return fn.isFile(); + } + return false; + } + } + + public void freshenZoomOutFiles(File tilepath) { + for(int i = 0; i < extrazoomoutlevels; i++) { + freshenZoomOutFilesByLevel(tilepath, i); + } + } + + public void freshenZoomOutFilesByLevel(File tilepath, int zoomlevel) { + Log.info("freshenZoomOutFiles(" + tilepath.getPath() + "," + zoomlevel + ")"); + File worldpath = new File(tilepath, world.getName()); /* Make path to our world */ + if(worldpath.exists() == false) /* Quit if not found */ + return; + HashMap maptab = new HashMap(); + /* Build table of file prefixes and step sizes */ + for(MapType mt : maps) { + List pfx = mt.baseZoomFilePrefixes(); + Integer step = mt.baseZoomFileStepSize(); + for(String p : pfx) { + maptab.put(p, step); + } + } + if(bigworld) { /* If big world, next directories are map name specific */ + DirFilter df = new DirFilter(); + for(String pfx : maptab.keySet()) { /* Walk thrugh prefixes, as directories */ + File dname = new File(worldpath, pfx); + /* Now, go through subdirectories under this one, and process them */ + String[] subdir = dname.list(df); + for(String s : subdir) { + File sdname = new File(dname, s); + /* Each middle tier directory is redundant - just go through them */ + String[] ssubdir = sdname.list(df); + for(String ss : ssubdir) { + File ssdname = new File(sdname, ss); + processZoomDirectory(ssdname, maptab.get(pfx), "", zoomlevel); + } + } + } + } + else { /* Else, classic file layout */ + for(String pfx : maptab.keySet()) { /* Walk thrugh prefixes, as directories */ + processZoomDirectory(worldpath, maptab.get(pfx), pfx + "_", zoomlevel); + } + } + } + + + private static class ProcessTileRec { + File zf; + int x, z; + } + private void processZoomDirectory(File dir, int stepsize, String prefix, int zoomlevel) { + Log.info("processZoomDirectory(" + dir.getPath() + "," + stepsize + "," + prefix + "," + zoomlevel + ")"); + String zoomprefix = "zzzzzzzzzzzzzzzzzzzz".substring(0, zoomlevel); + HashMap toprocess = new HashMap(); + if(prefix.equals("")) { + if(zoomlevel > 0) + prefix = zoomprefix + "_"; + } + else + prefix = zoomprefix + prefix; + zoomlevel++; + String[] files = dir.list(new PNGFileFilter(prefix)); + for(String fn : files) { + /* Build file object */ + File f = new File(dir, fn); + /* 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 z = 0; + boolean parsed = false; + if(tok.length >= 2) { + try { + x = Integer.parseInt(tok[tok.length-2]); + z = Integer.parseInt(tok[tok.length-1]); + parsed = true; + } catch (NumberFormatException nfx) { + } + } + if(!parsed) + continue; + if(x >= 0) + x = x - (x % (stepsize << zoomlevel)); + else + x = x + (x % (stepsize << zoomlevel)); + if(z >= 0) + z = z - (z % (stepsize << zoomlevel)); + else + z = z + (z % (stepsize << zoomlevel)); + File zf; + if(prefix.equals("")) + zf = new File(dir, "z_" + x + "_" + z + ".png"); + else + zf = new File(dir, "z" + prefix + x + "_" + z + ".png"); + /* If zoom file exists and is older than our file, nothing to do */ + if(zf.exists() && (zf.lastModified() >= f.lastModified())) { + continue; + } + String zfpath = zf.getPath(); + if(!toprocess.containsKey(zfpath)) { + ProcessTileRec rec = new ProcessTileRec(); + rec.zf = zf; + rec.x = x; + rec.z = z; + toprocess.put(zfpath, rec); + } + } + /* Do processing */ + for(ProcessTileRec s : toprocess.values()) { + processZoomTile(dir, s.zf, s.x, s.z, stepsize << (zoomlevel-1), prefix); + } + } + private void processZoomTile(File dir, File zf, int tx, int tz, int stepsize, String prefix) { + Log.info("processZoomFile(" + dir.getPath() + "," + zf.getPath() + "," + tx + "," + tz + "," + stepsize + "," + prefix); + int width = 128, height = 128; + BufferedImage zIm = null; + KzedBufferedImage kzIm = null; + int[] argb = new int[width*height]; + + /* create image buffer */ + kzIm = KzedMap.allocateBufferedImage(width, height); + zIm = kzIm.buf_img; + + for(int i = 0; i < 4; i++) { + File f = new File(dir, prefix + (tx + stepsize*(i>>1)) + "_" + (tz + stepsize*(i&1)) + ".png"); + if(f.exists()) { + BufferedImage im = null; + try { + im = ImageIO.read(f); + } catch (IOException e) { + } catch (IndexOutOfBoundsException e) { + } + if(im != null) { + im.getRGB(0, 0, width, height, argb, 0, width); /* Read data */ + im.flush(); + /* Do binlinear scale to 64x64 */ + Color c1 = new Color(); + for(int y = 0; y < height; y += 2) { + for(int x = 0; x < width; x += 2) { + int red = 0; + int green = 0; + int blue = 0; + int alpha = 0; + for(int yy = y; yy < y+2; yy++) { + for(int xx = x; xx < x+2; xx++) { + c1.setARGB(argb[(yy*width)+xx]); + red += c1.getRed(); + green += c1.getGreen(); + blue += c1.getBlue(); + alpha += c1.getAlpha(); + } + } + c1.setRGBA(red>>2, green>>2, blue>>2, alpha>>2); + argb[(y*width/2) + (x/2)] = c1.getARGB(); + } + } + /* blit scaled rendered tile onto zoom-out tile */ + zIm.setRGB(((i>>1) != 0)?0:width/2, (i & 1) * height/2, width/2, height/2, argb, 0, width); + } + } + } + try { + FileLockManager.imageIOWrite(zIm, "png", zf); + Debug.debug("Saved zoom-out tile at " + zf.getName()); + } catch (IOException e) { + Debug.error("Failed to save zoom-out tile: " + zf.getName(), e); + } catch (java.lang.NullPointerException e) { + Debug.error("Failed to save zoom-out tile (NullPointerException): " + zf.getName(), e); + } + KzedMap.freeBufferedImage(kzIm); + } } diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index be548053..2036e25b 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -261,6 +261,17 @@ public class MapManager { } } + private class DoZoomOutProcessing implements Runnable { + public void run() { + Log.info("DoZoomOutProcessing started"); + for(DynmapWorld w : worlds) { + w.freshenZoomOutFiles(plug_in.tilesDirectory); + } + renderpool.schedule(this, 60000, TimeUnit.MILLISECONDS); + Log.info("DoZoomOutProcessing finished"); + } + } + public MapManager(DynmapPlugin plugin, ConfigurationNode configuration) { plug_in = plugin; mapman = this; @@ -288,6 +299,7 @@ public class MapManager { scheduler.scheduleSyncRepeatingTask(plugin, new CheckWorldTimes(), 5*20, 5*20); /* Check very 5 seconds */ scheduler.scheduleSyncRepeatingTask(plugin, new ProcessChunkLoads(), 1, 1); /* Chunk loader task */ + } void renderFullWorld(Location l, CommandSender sender) { @@ -343,6 +355,8 @@ public class MapManager { dynmapWorld.sendposition = worldConfiguration.getBoolean("sendposition", true); dynmapWorld.sendhealth = worldConfiguration.getBoolean("sendhealth", true); dynmapWorld.bigworld = worldConfiguration.getBoolean("bigworld", false); + dynmapWorld.extrazoomoutlevels = worldConfiguration.getInteger("extrazoomout", 0); + if(loclist != null) { for(ConfigurationNode loc : loclist) { Location lx = new Location(w, loc.getDouble("x", 0), loc.getDouble("y", 64), loc.getDouble("z", 0)); @@ -422,6 +436,7 @@ public class MapManager { public void startRendering() { tileQueue.start(); renderpool = new DynmapScheduledThreadPoolExecutor(); + renderpool.schedule(new DoZoomOutProcessing(), 60000, TimeUnit.MILLISECONDS); } public void stopRendering() { diff --git a/src/main/java/org/dynmap/MapType.java b/src/main/java/org/dynmap/MapType.java index e5c0c979..f8cff80f 100644 --- a/src/main/java/org/dynmap/MapType.java +++ b/src/main/java/org/dynmap/MapType.java @@ -27,5 +27,14 @@ public abstract class MapType { public boolean isHightestBlockYDataNeeded() { return false; } public boolean isRawBiomeDataNeeded() { return false; } public boolean isBlockTypeDataNeeded() { return true; } - + + public abstract List baseZoomFilePrefixes(); + public abstract int baseZoomFileStepSize(); + public enum ZoomStepDirection { + POSITIVE_X_Y, + NEGATIVE_X_Y, + POSITIVE_X_NEGATIVE_Y, + NEGATIVE_X_POSITIVE_Y + } + public abstract ZoomStepDirection zoomFileStepDirection(); } diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index f97e9add..980c916b 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -22,8 +22,10 @@ import org.dynmap.MapManager; import org.dynmap.TileHashManager; import org.dynmap.MapTile; import org.dynmap.MapType; +import org.dynmap.MapType.ZoomStepDirection; import org.dynmap.debug.Debug; import org.dynmap.kzedmap.KzedMap; +import org.dynmap.kzedmap.MapTileRenderer; import org.dynmap.kzedmap.KzedMap.KzedBufferedImage; import org.dynmap.utils.FileLockManager; import org.dynmap.utils.MapChunkCache; @@ -406,6 +408,17 @@ public class FlatMap extends MapType { return prefix; } + public List baseZoomFilePrefixes() { + ArrayList s = new ArrayList(); + s.add(getName() + "_128"); + if(night_and_day) + s.add(getName()+"_day_128"); + return s; + } + + public int baseZoomFileStepSize() { return 1; } + + public ZoomStepDirection zoomFileStepDirection() { return ZoomStepDirection.POSITIVE_X_Y; } public static class FlatMapTile extends MapTile { FlatMap map; @@ -427,7 +440,7 @@ public class FlatMap extends MapType { public String getFilename() { if(fname == null) { if(world.bigworld) - fname = map.prefix + "/" + ((-(y+1))>>5) + "_" + (x>>5) + "/" + size + "_" + -(y+1) + "_" + x + ".png"; + fname = map.prefix + "_" + size + "/" + ((-(y+1))>>5) + "_" + (x>>5) + "/" + -(y+1) + "_" + x + ".png"; else fname = map.prefix + "_" + size + "_" + -(y+1) + "_" + x + ".png"; } @@ -437,7 +450,7 @@ public class FlatMap extends MapType { public String getDayFilename() { if(fname_day == null) { if(world.bigworld) - fname_day = map.prefix + "_day/" + ((-(y+1))>>5) + "_" + (x>>5) + "/" + size + "_" + -(y+1) + "_" + x + ".png"; + fname_day = map.prefix + "_day_" + size + "/" + ((-(y+1))>>5) + "_" + (x>>5) + "/" + -(y+1) + "_" + x + ".png"; else fname_day = map.prefix + "_day_" + size + "_" + -(y+1) + "_" + x + ".png"; } diff --git a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java index 67a02e71..7b540440 100644 --- a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java @@ -11,6 +11,8 @@ public class CaveTileRenderer extends DefaultTileRenderer { super(configuration); } + public boolean isNightAndDayEnabled() { return false; } + @Override protected void scan(World world, int seq, boolean isnether, final Color result, final Color result_day, MapIterator mapiter) { diff --git a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java index d5c068f7..07c01d12 100644 --- a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java @@ -48,6 +48,8 @@ public class DefaultTileRenderer implements MapTileRenderer { return name; } + public boolean isNightAndDayEnabled() { return night_and_day; } + public DefaultTileRenderer(ConfigurationNode configuration) { this.configuration = configuration; name = (String) configuration.get("prefix"); diff --git a/src/main/java/org/dynmap/kzedmap/KzedMap.java b/src/main/java/org/dynmap/kzedmap/KzedMap.java index 4963edb4..d13aa2bd 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMap.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMap.java @@ -17,6 +17,7 @@ import org.dynmap.Log; import org.dynmap.MapManager; import org.dynmap.MapTile; import org.dynmap.MapType; +import org.dynmap.MapType.ZoomStepDirection; import org.dynmap.utils.MapChunkCache; import org.json.simple.JSONObject; import java.awt.image.DataBufferInt; @@ -326,6 +327,20 @@ public class KzedMap extends MapType { return false; } + public List baseZoomFilePrefixes() { + ArrayList s = new ArrayList(); + for(MapTileRenderer r : renderers) { + s.add("z" + r.getName()); + if(r.isNightAndDayEnabled()) + s.add("z" + r.getName() + "_day"); + } + return s; + } + + public int baseZoomFileStepSize() { return zTileWidth; } + + public ZoomStepDirection zoomFileStepDirection() { return ZoomStepDirection.NEGATIVE_X_POSITIVE_Y; } + public String getName() { return "KzedMap"; } diff --git a/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java b/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java index 09390df7..01b1270d 100644 --- a/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java @@ -15,4 +15,5 @@ public interface MapTileRenderer { boolean isBiomeDataNeeded(); boolean isRawBiomeDataNeeded(); + boolean isNightAndDayEnabled(); } diff --git a/web/js/flatmap.js b/web/js/flatmap.js index 63d51d52..ef68cb59 100644 --- a/web/js/flatmap.js +++ b/web/js/flatmap.js @@ -30,8 +30,8 @@ FlatMapType.prototype = $.extend(new DynMapType(), { dnprefix = '_day'; if(this.dynmap.world.bigworld) - tileName = this.prefix + dnprefix + '/' + (coord.x >> 5) + '_' + (coord.y >> 5) + - '/128_' + coord.x + '_' + coord.y + '.png'; + tileName = this.prefix + dnprefix + '_128/' + (coord.x >> 5) + '_' + (coord.y >> 5) + + '/' + coord.x + '_' + coord.y + '.png'; else tileName = this.prefix + dnprefix + '_128_' + coord.x + '_' + coord.y + '.png'; imgSize = Math.pow(2, 7+zoom); From e7e4406e77980ad0e677d31fdea8d4a4bf4cdbbb Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 22 Jun 2011 13:56:21 -0500 Subject: [PATCH 2/5] Finish rest of zoom out core logic - still need to finish web UI --- configuration.txt | 13 +++++ src/main/java/org/dynmap/DynmapWorld.java | 54 +++++++++++-------- src/main/java/org/dynmap/MapManager.java | 16 ++++-- src/main/java/org/dynmap/MapType.java | 12 ++--- src/main/java/org/dynmap/flat/FlatMap.java | 6 +-- src/main/java/org/dynmap/kzedmap/KzedMap.java | 5 +- .../org/dynmap/regions/RegionHandler.java | 3 -- 7 files changed, 67 insertions(+), 42 deletions(-) diff --git a/configuration.txt b/configuration.txt index 5401e0a1..5f335235 100644 --- a/configuration.txt +++ b/configuration.txt @@ -73,6 +73,9 @@ display-whitelist: false # How often a tile gets rendered (in seconds). renderinterval: 1 +# Zoom-out tile update period - how often to scan for and process tile updates into zoom-out tiles (in seconds) +zoomoutperiod: 60 + # Tile hashing is used to minimize tile file updates when no changes have occurred - set to false to disable enabletilehash: true @@ -133,6 +136,8 @@ templates: enabled: true # # If bigworld set to true, use alternate directory layout better suited to large worlds # bigworld: true + # # Number of extra zoom-out levels for world (each level is twice as big as the previous one) + # extrazoomout: 3 center: x: 0 y: 64 @@ -209,6 +214,8 @@ templates: enabled: true # # If bigworld set to true, use alternate directory layout better suited to large worlds # bigworld: true + # # Number of extra zoom-out levels for world (each level is twice as big as the previous one) + # extrazoomout: 3 center: x: 0 y: 64 @@ -238,6 +245,8 @@ templates: enabled: true # # If bigworld set to true, use alternate directory layout better suited to large worlds # bigworld: true + # # Number of extra zoom-out levels for world (each level is twice as big as the previous one) + # extrazoomout: 3 center: x: 0 y: 64 @@ -312,6 +321,8 @@ worlds: # z: 0 # # If bigworld set to true, use alternate directory layout better suited to large worlds # bigworld: true + # # Number of extra zoom-out levels for world (each level is twice as big as the previous one) + # extrazoomout: 3 # maps: # - class: org.dynmap.flat.FlatMap # name: flat @@ -381,6 +392,8 @@ worlds: # x: 0 # y: 64 # z: 0 + # # Number of extra zoom-out levels for world (each level is twice as big as the previous one) + # extrazoomout: 3 # maps: # - class: org.dynmap.flat.FlatMap # name: flat diff --git a/src/main/java/org/dynmap/DynmapWorld.java b/src/main/java/org/dynmap/DynmapWorld.java index 708b9e4c..4afb3fc9 100644 --- a/src/main/java/org/dynmap/DynmapWorld.java +++ b/src/main/java/org/dynmap/DynmapWorld.java @@ -1,7 +1,6 @@ package org.dynmap; import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import org.bukkit.World; @@ -62,18 +61,25 @@ public class DynmapWorld { } } + private static class PrefixData { + int stepsize; + int[] stepseq; + } + public void freshenZoomOutFilesByLevel(File tilepath, int zoomlevel) { - Log.info("freshenZoomOutFiles(" + tilepath.getPath() + "," + zoomlevel + ")"); + Debug.debug("freshenZoomOutFiles(" + tilepath.getPath() + "," + zoomlevel + ")"); File worldpath = new File(tilepath, world.getName()); /* Make path to our world */ if(worldpath.exists() == false) /* Quit if not found */ return; - HashMap maptab = new HashMap(); + HashMap maptab = new HashMap(); /* Build table of file prefixes and step sizes */ for(MapType mt : maps) { + PrefixData pd = new PrefixData(); List pfx = mt.baseZoomFilePrefixes(); - Integer step = mt.baseZoomFileStepSize(); + pd.stepsize = mt.baseZoomFileStepSize(); + pd.stepseq = mt.zoomFileStepSequence(); for(String p : pfx) { - maptab.put(p, step); + maptab.put(p, pd); } } if(bigworld) { /* If big world, next directories are map name specific */ @@ -103,10 +109,10 @@ public class DynmapWorld { private static class ProcessTileRec { File zf; - int x, z; + int x, y; } - private void processZoomDirectory(File dir, int stepsize, String prefix, int zoomlevel) { - Log.info("processZoomDirectory(" + dir.getPath() + "," + stepsize + "," + prefix + "," + zoomlevel + ")"); + private void processZoomDirectory(File dir, PrefixData pd, String prefix, int zoomlevel) { + Debug.debug("processZoomDirectory(" + dir.getPath() + "," + pd.stepsize + "," + prefix + "," + zoomlevel + ")"); String zoomprefix = "zzzzzzzzzzzzzzzzzzzz".substring(0, zoomlevel); HashMap toprocess = new HashMap(); if(prefix.equals("")) { @@ -124,12 +130,12 @@ public class DynmapWorld { fn = fn.substring(0, fn.lastIndexOf('.')); /* Strip off extension */ String[] tok = fn.split("_"); /* Split by underscores */ int x = 0; - int z = 0; + int y = 0; boolean parsed = false; if(tok.length >= 2) { try { x = Integer.parseInt(tok[tok.length-2]); - z = Integer.parseInt(tok[tok.length-1]); + y = Integer.parseInt(tok[tok.length-1]); parsed = true; } catch (NumberFormatException nfx) { } @@ -137,18 +143,18 @@ public class DynmapWorld { if(!parsed) continue; if(x >= 0) - x = x - (x % (stepsize << zoomlevel)); + x = x - (x % (pd.stepsize << zoomlevel)); else - x = x + (x % (stepsize << zoomlevel)); - if(z >= 0) - z = z - (z % (stepsize << zoomlevel)); + x = x + (x % (pd.stepsize << zoomlevel)); + if(y >= 0) + y = y - (y % (pd.stepsize << zoomlevel)); else - z = z + (z % (stepsize << zoomlevel)); + y = y + (y % (pd.stepsize << zoomlevel)); File zf; if(prefix.equals("")) - zf = new File(dir, "z_" + x + "_" + z + ".png"); + zf = new File(dir, "z_" + x + "_" + y + ".png"); else - zf = new File(dir, "z" + prefix + x + "_" + z + ".png"); + zf = new File(dir, "z" + prefix + x + "_" + y + ".png"); /* If zoom file exists and is older than our file, nothing to do */ if(zf.exists() && (zf.lastModified() >= f.lastModified())) { continue; @@ -158,17 +164,17 @@ public class DynmapWorld { ProcessTileRec rec = new ProcessTileRec(); rec.zf = zf; rec.x = x; - rec.z = z; + rec.y = y; toprocess.put(zfpath, rec); } } /* Do processing */ for(ProcessTileRec s : toprocess.values()) { - processZoomTile(dir, s.zf, s.x, s.z, stepsize << (zoomlevel-1), prefix); + processZoomTile(dir, s.zf, s.x, s.y, pd.stepsize << (zoomlevel-1), prefix, pd.stepseq); } } - private void processZoomTile(File dir, File zf, int tx, int tz, int stepsize, String prefix) { - Log.info("processZoomFile(" + dir.getPath() + "," + zf.getPath() + "," + tx + "," + tz + "," + stepsize + "," + prefix); + private void processZoomTile(File dir, File zf, int tx, int ty, int stepsize, String prefix, int[] stepseq) { + Debug.debug("processZoomFile(" + dir.getPath() + "," + zf.getPath() + "," + tx + "," + ty + "," + stepsize + "," + prefix); int width = 128, height = 128; BufferedImage zIm = null; KzedBufferedImage kzIm = null; @@ -179,14 +185,16 @@ public class DynmapWorld { zIm = kzIm.buf_img; for(int i = 0; i < 4; i++) { - File f = new File(dir, prefix + (tx + stepsize*(i>>1)) + "_" + (tz + stepsize*(i&1)) + ".png"); + File f = new File(dir, prefix + (tx + stepsize*(1&stepseq[i])) + "_" + (ty + stepsize*(stepseq[i]>>1)) + ".png"); if(f.exists()) { BufferedImage im = null; + FileLockManager.getReadLock(f); try { im = ImageIO.read(f); } catch (IOException e) { } catch (IndexOutOfBoundsException e) { } + FileLockManager.releaseReadLock(f); if(im != null) { im.getRGB(0, 0, width, height, argb, 0, width); /* Read data */ im.flush(); @@ -216,6 +224,7 @@ public class DynmapWorld { } } } + FileLockManager.getWriteLock(zf); try { FileLockManager.imageIOWrite(zIm, "png", zf); Debug.debug("Saved zoom-out tile at " + zf.getName()); @@ -224,6 +233,7 @@ public class DynmapWorld { } catch (java.lang.NullPointerException e) { Debug.error("Failed to save zoom-out tile (NullPointerException): " + zf.getName(), e); } + FileLockManager.releaseWriteLock(zf); KzedMap.freeBufferedImage(kzIm); } } diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index 2036e25b..ce149601 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -26,13 +26,15 @@ public class MapManager { public AsynchronousQueue tileQueue; private static final int DEFAULT_CHUNKS_PER_TICK = 200; - + private static final int DEFAULT_ZOOMOUT_PERIOD = 60; public List worlds = new ArrayList(); public Map worldsLookup = new HashMap(); private BukkitScheduler scheduler; private DynmapPlugin plug_in; private long timeslice_int = 0; /* In milliseconds */ private int max_chunk_loads_per_tick = DEFAULT_CHUNKS_PER_TICK; + + private int zoomout_period = DEFAULT_ZOOMOUT_PERIOD; /* Zoom-out tile processing period, in seconds */ /* Which fullrenders are active */ private HashMap active_renders = new HashMap(); /* List of MapChunkCache requests to be processed */ @@ -263,12 +265,12 @@ public class MapManager { private class DoZoomOutProcessing implements Runnable { public void run() { - Log.info("DoZoomOutProcessing started"); + Debug.debug("DoZoomOutProcessing started"); for(DynmapWorld w : worlds) { - w.freshenZoomOutFiles(plug_in.tilesDirectory); + w.freshenZoomOutFiles(DynmapPlugin.tilesDirectory); } - renderpool.schedule(this, 60000, TimeUnit.MILLISECONDS); - Log.info("DoZoomOutProcessing finished"); + renderpool.schedule(this, zoomout_period, TimeUnit.SECONDS); + Debug.debug("DoZoomOutProcessing finished"); } } @@ -287,6 +289,10 @@ public class MapManager { timeslice_int = (long)(configuration.getDouble("timesliceinterval", 0.0) * 1000); max_chunk_loads_per_tick = configuration.getInteger("maxchunkspertick", DEFAULT_CHUNKS_PER_TICK); if(max_chunk_loads_per_tick < 5) max_chunk_loads_per_tick = 5; + /* Get zoomout processing periond in seconds */ + zoomout_period = configuration.getInteger("zoomoutperiod", DEFAULT_ZOOMOUT_PERIOD); + if(zoomout_period < 5) zoomout_period = 5; + scheduler = plugin.getServer().getScheduler(); hashman = new TileHashManager(DynmapPlugin.tilesDirectory, configuration.getBoolean("enabletilehash", true)); diff --git a/src/main/java/org/dynmap/MapType.java b/src/main/java/org/dynmap/MapType.java index f8cff80f..b0e92bbf 100644 --- a/src/main/java/org/dynmap/MapType.java +++ b/src/main/java/org/dynmap/MapType.java @@ -30,11 +30,9 @@ public abstract class MapType { public abstract List baseZoomFilePrefixes(); public abstract int baseZoomFileStepSize(); - public enum ZoomStepDirection { - POSITIVE_X_Y, - NEGATIVE_X_Y, - POSITIVE_X_NEGATIVE_Y, - NEGATIVE_X_POSITIVE_Y - } - public abstract ZoomStepDirection zoomFileStepDirection(); + /** + * Step sequence for creating zoomed file: first index is top-left, second top-right, third bottom-left, forth bottom-right + * Values correspond to tile X,Y (0), X+step,Y (1), X,Y+step (2), X+step,Y+step (3) + */ + public abstract int[] zoomFileStepSequence(); } diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index 980c916b..78e828e6 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -22,10 +22,8 @@ import org.dynmap.MapManager; import org.dynmap.TileHashManager; import org.dynmap.MapTile; import org.dynmap.MapType; -import org.dynmap.MapType.ZoomStepDirection; import org.dynmap.debug.Debug; import org.dynmap.kzedmap.KzedMap; -import org.dynmap.kzedmap.MapTileRenderer; import org.dynmap.kzedmap.KzedMap.KzedBufferedImage; import org.dynmap.utils.FileLockManager; import org.dynmap.utils.MapChunkCache; @@ -418,7 +416,9 @@ public class FlatMap extends MapType { public int baseZoomFileStepSize() { return 1; } - public ZoomStepDirection zoomFileStepDirection() { return ZoomStepDirection.POSITIVE_X_Y; } + private static final int[] stepseq = { 1, 3, 0, 2 }; + + public int[] zoomFileStepSequence() { return stepseq; } public static class FlatMapTile extends MapTile { FlatMap map; diff --git a/src/main/java/org/dynmap/kzedmap/KzedMap.java b/src/main/java/org/dynmap/kzedmap/KzedMap.java index d13aa2bd..4e12f3bf 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMap.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMap.java @@ -17,7 +17,6 @@ import org.dynmap.Log; import org.dynmap.MapManager; import org.dynmap.MapTile; import org.dynmap.MapType; -import org.dynmap.MapType.ZoomStepDirection; import org.dynmap.utils.MapChunkCache; import org.json.simple.JSONObject; import java.awt.image.DataBufferInt; @@ -339,7 +338,9 @@ public class KzedMap extends MapType { public int baseZoomFileStepSize() { return zTileWidth; } - public ZoomStepDirection zoomFileStepDirection() { return ZoomStepDirection.NEGATIVE_X_POSITIVE_Y; } + private static final int[] stepseq = { 0, 2, 1, 3 }; + + public int[] zoomFileStepSequence() { return stepseq; } public String getName() { return "KzedMap"; diff --git a/src/main/java/org/dynmap/regions/RegionHandler.java b/src/main/java/org/dynmap/regions/RegionHandler.java index 48aa9616..01ecd248 100644 --- a/src/main/java/org/dynmap/regions/RegionHandler.java +++ b/src/main/java/org/dynmap/regions/RegionHandler.java @@ -72,10 +72,7 @@ public class RegionHandler extends FileHandler { /* If not in list, remove it */ if(!idlist.contains(id)) { regionData.remove(id); - log.info("discard " + id); } - else - log.info("keep " + id); } } try { From 8e102446fe1b44e2dcbce23adca6e02db6949585 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 22 Jun 2011 16:43:41 -0500 Subject: [PATCH 3/5] First pass UI support for extra zoom out levels --- .../dynmap/ClientConfigurationComponent.java | 1 + web/js/flatmap.js | 35 ++++++++++++++----- web/js/kzedmaps.js | 26 ++++++++------ 3 files changed, 43 insertions(+), 19 deletions(-) diff --git a/src/main/java/org/dynmap/ClientConfigurationComponent.java b/src/main/java/org/dynmap/ClientConfigurationComponent.java index 6c795e79..aa54146f 100644 --- a/src/main/java/org/dynmap/ClientConfigurationComponent.java +++ b/src/main/java/org/dynmap/ClientConfigurationComponent.java @@ -33,6 +33,7 @@ public class ClientConfigurationComponent extends Component { s(wo, "center/y", wn.getFloat("center/y", 64.0f)); s(wo, "center/z", wn.getFloat("center/z", 0.0f)); s(wo, "bigworld", world.bigworld); + s(wo, "extrazoomout", world.extrazoomoutlevels); a(t, "worlds", wo); for(MapType mt : world.maps) { diff --git a/web/js/flatmap.js b/web/js/flatmap.js index ef68cb59..33b7ae79 100644 --- a/web/js/flatmap.js +++ b/web/js/flatmap.js @@ -28,13 +28,25 @@ FlatMapType.prototype = $.extend(new DynMapType(), { var dnprefix = ''; if(this.dynmap.map.mapTypes[this.dynmap.map.mapTypeId].nightandday && this.dynmap.serverday) dnprefix = '_day'; - - if(this.dynmap.world.bigworld) - tileName = this.prefix + dnprefix + '_128/' + (coord.x >> 5) + '_' + (coord.y >> 5) + - '/' + coord.x + '_' + coord.y + '.png'; - else - tileName = this.prefix + dnprefix + '_128_' + coord.x + '_' + coord.y + '.png'; - imgSize = Math.pow(2, 7+zoom); + var extrazoom = this.dynmap.world.extrazoomout; + if(zoom < extrazoom) { + var scale = 1 << (extrazoom-zoom); + var zprefix = "zzzzzzzzzzzz".substring(0, extrazoom-zoom); + if(this.dynmap.world.bigworld) + tileName = this.prefix + dnprefix + '_128/' + ((scale*coord.x) >> 5) + '_' + ((scale*coord.y) >> 5) + + '/' + zprefix + "_" + (scale*coord.x) + '_' + (scale*coord.y) + '.png'; + else + tileName = zprefix + this.prefix + dnprefix + '_128_' + (scale*coord.x) + '_' + (scale*coord.y) + '.png'; + imgSize = 128; + } + else { + if(this.dynmap.world.bigworld) + tileName = this.prefix + dnprefix + '_128/' + (coord.x >> 5) + '_' + (coord.y >> 5) + + '/' + coord.x + '_' + coord.y + '.png'; + else + tileName = this.prefix + dnprefix + '_128_' + coord.x + '_' + coord.y + '.png'; + imgSize = Math.pow(2, 7+zoom-extrazoom); + } var tile = $('
') .addClass('tile') .css({ @@ -58,7 +70,14 @@ FlatMapType.prototype = $.extend(new DynMapType(), { }, updateTileSize: function(zoom) { var size; - size = Math.pow(2, 7+zoom); + var extrazoom = this.dynmap.world.extrazoomout; + this.maxZoom = 3 + extrazoom; + if (zoom <= extrazoom) { + size = 128; + } + else { + size = Math.pow(2, 7+zoom-extrazoom); + } this.tileSize = new google.maps.Size(size, size); } }); diff --git a/web/js/kzedmaps.js b/web/js/kzedmaps.js index 6cda0d67..aa8c468d 100644 --- a/web/js/kzedmaps.js +++ b/web/js/kzedmaps.js @@ -45,25 +45,27 @@ KzedMapType.prototype = $.extend(new DynMapType(), { var dnprefix = ''; if(this.dynmap.map.mapTypes[this.dynmap.map.mapTypeId].nightandday && this.dynmap.serverday) - dnprefix = '_day'; - - if (zoom == 0) { + dnprefix = '_day'; + var extrazoom = this.dynmap.world.extrazoomout; + if (zoom <= extrazoom) { + var zpre = 'zzzzzzzzzzzzzzzz'.substring(0, extrazoom-zoom+1); // Most zoomed out tiles. tileSize = 128; - imgSize = tileSize; + imgSize = tileSize; + var tilescale = 2 << (extrazoom-zoom); if (this.dynmap.world.bigworld) { - tileName = 'z' + this.prefix + dnprefix + '/' + ((-coord.x * tileSize*2)>>12) + - '_' + ((coord.y * tileSize*2) >> 12) + '/' + - (-coord.x * tileSize*2) + '_' + (coord.y * tileSize*2) + '.png'; + tileName = zpre + this.prefix + dnprefix + '/' + ((-coord.x * tileSize*tilescale)>>12) + + '_' + ((coord.y * tileSize*tilescale) >> 12) + '/' + + (-coord.x * tileSize*tilescale) + '_' + (coord.y * tileSize*tilescale) + '.png'; } else { - tileName = 'z' + this.prefix + dnprefix + '_' + (-coord.x * tileSize*2) + '_' + (coord.y * tileSize*2) + '.png'; + tileName = zpre + this.prefix + dnprefix + '_' + (-coord.x * tileSize*tilescale) + '_' + (coord.y * tileSize*tilescale) + '.png'; } } else { // Other zoom levels. tileSize = 128; - imgSize = Math.pow(2, 6+zoom); + imgSize = Math.pow(2, 6+zoom-extrazoom); if(this.dynmap.world.bigworld) { tileName = this.prefix + dnprefix + '/' + ((-coord.x*tileSize) >> 12) + '_' + ((coord.y*tileSize)>>12) + '/' + @@ -109,10 +111,12 @@ KzedMapType.prototype = $.extend(new DynMapType(), { }, updateTileSize: function(zoom) { var size; - if (zoom == 0) { + var extrazoom = this.dynmap.world.extrazoomout; + this.maxZoom = 3 + extrazoom; + if (zoom <= extrazoom) { size = 128; } else { - size = Math.pow(2, 6+zoom); + size = Math.pow(2, 6+zoom-extrazoom); } this.tileSize = new google.maps.Size(size, size); } From 9db23444db82bbf0f2c9c2a3f27e6e3c7b2e9867 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 22 Jun 2011 17:24:12 -0500 Subject: [PATCH 4/5] Fix problem with coordinate offsets due to negative coordinate walk on kzedmaps --- src/main/java/org/dynmap/DynmapWorld.java | 20 +++++++++++++------ src/main/java/org/dynmap/kzedmap/KzedMap.java | 4 ++-- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/dynmap/DynmapWorld.java b/src/main/java/org/dynmap/DynmapWorld.java index 4afb3fc9..ed841caf 100644 --- a/src/main/java/org/dynmap/DynmapWorld.java +++ b/src/main/java/org/dynmap/DynmapWorld.java @@ -64,6 +64,7 @@ public class DynmapWorld { private static class PrefixData { int stepsize; int[] stepseq; + boolean neg_step_x; } public void freshenZoomOutFilesByLevel(File tilepath, int zoomlevel) { @@ -77,6 +78,10 @@ public class DynmapWorld { PrefixData pd = new PrefixData(); List pfx = mt.baseZoomFilePrefixes(); pd.stepsize = mt.baseZoomFileStepSize(); + if(pd.stepsize < 0) { + pd.stepsize = -pd.stepsize; + pd.neg_step_x = true; + } pd.stepseq = mt.zoomFileStepSequence(); for(String p : pfx) { maptab.put(p, pd); @@ -121,6 +126,7 @@ public class DynmapWorld { } else prefix = zoomprefix + prefix; + int step = pd.stepsize << zoomlevel; zoomlevel++; String[] files = dir.list(new PNGFileFilter(prefix)); for(String fn : files) { @@ -142,14 +148,16 @@ public class DynmapWorld { } if(!parsed) continue; - if(x >= 0) - x = x - (x % (pd.stepsize << zoomlevel)); + if(pd.neg_step_x) x = -x; + if(x >= 0) + x = x - (x % (2*step)); else - x = x + (x % (pd.stepsize << zoomlevel)); + x = x + (x % (2*step)); + if(pd.neg_step_x) x = -x; if(y >= 0) - y = y - (y % (pd.stepsize << zoomlevel)); + y = y - (y % (2*step)); else - y = y + (y % (pd.stepsize << zoomlevel)); + y = y + (y % (2*step)); File zf; if(prefix.equals("")) zf = new File(dir, "z_" + x + "_" + y + ".png"); @@ -170,7 +178,7 @@ public class DynmapWorld { } /* Do processing */ for(ProcessTileRec s : toprocess.values()) { - processZoomTile(dir, s.zf, s.x, s.y, pd.stepsize << (zoomlevel-1), prefix, pd.stepseq); + processZoomTile(dir, s.zf, s.x - (pd.neg_step_x?step:0), s.y, step, prefix, pd.stepseq); } } private void processZoomTile(File dir, File zf, int tx, int ty, int stepsize, String prefix, int[] stepseq) { diff --git a/src/main/java/org/dynmap/kzedmap/KzedMap.java b/src/main/java/org/dynmap/kzedmap/KzedMap.java index 4e12f3bf..85d2d757 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMap.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMap.java @@ -335,8 +335,8 @@ public class KzedMap extends MapType { } return s; } - - public int baseZoomFileStepSize() { return zTileWidth; } + /* Return negative to flag negative X walk */ + public int baseZoomFileStepSize() { return -zTileWidth; } private static final int[] stepseq = { 0, 2, 1, 3 }; From f722c6fdc82d23bad9aff68e32b8462ef1a546be Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Thu, 23 Jun 2011 00:53:56 -0500 Subject: [PATCH 5/5] Finish zoom out on normal and 'bigworld' mode, fix marker coordinates --- src/main/java/org/dynmap/DynmapWorld.java | 122 +++++++++++------- src/main/java/org/dynmap/MapManager.java | 4 +- src/main/java/org/dynmap/flat/FlatMap.java | 2 +- .../dynmap/kzedmap/DefaultTileRenderer.java | 12 +- .../org/dynmap/utils/FileLockManager.java | 1 + web/js/flatmap.js | 4 +- web/js/kzedmaps.js | 7 +- 7 files changed, 97 insertions(+), 55 deletions(-) diff --git a/src/main/java/org/dynmap/DynmapWorld.java b/src/main/java/org/dynmap/DynmapWorld.java index ed841caf..ffa3bea6 100644 --- a/src/main/java/org/dynmap/DynmapWorld.java +++ b/src/main/java/org/dynmap/DynmapWorld.java @@ -32,6 +32,7 @@ public class DynmapWorld { public boolean sendhealth; public boolean bigworld; /* If true, deeper directory hierarchy */ public int extrazoomoutlevels; /* Number of additional zoom out levels to generate */ + public File worldtilepath; private static class DirFilter implements FilenameFilter { public boolean accept(File f, String n) { @@ -55,9 +56,9 @@ public class DynmapWorld { } } - public void freshenZoomOutFiles(File tilepath) { + public void freshenZoomOutFiles() { for(int i = 0; i < extrazoomoutlevels; i++) { - freshenZoomOutFilesByLevel(tilepath, i); + freshenZoomOutFilesByLevel(i); } } @@ -65,70 +66,96 @@ public class DynmapWorld { int stepsize; int[] stepseq; boolean neg_step_x; + String baseprefix; + int zoomlevel; + String zoomprefix; + String fnprefix; + String zfnprefix; } - public void freshenZoomOutFilesByLevel(File tilepath, int zoomlevel) { - Debug.debug("freshenZoomOutFiles(" + tilepath.getPath() + "," + zoomlevel + ")"); - File worldpath = new File(tilepath, world.getName()); /* Make path to our world */ - if(worldpath.exists() == false) /* Quit if not found */ + public void freshenZoomOutFilesByLevel(int zoomlevel) { + int cnt = 0; + Debug.debug("freshenZoomOutFiles(" + world.getName() + "," + zoomlevel + ")"); + if(worldtilepath.exists() == false) /* Quit if not found */ return; HashMap maptab = new HashMap(); /* Build table of file prefixes and step sizes */ for(MapType mt : maps) { - PrefixData pd = new PrefixData(); List pfx = mt.baseZoomFilePrefixes(); - pd.stepsize = mt.baseZoomFileStepSize(); - if(pd.stepsize < 0) { - pd.stepsize = -pd.stepsize; - pd.neg_step_x = true; + int stepsize = mt.baseZoomFileStepSize(); + boolean neg_step_x = false; + if(stepsize < 0) { + stepsize = -stepsize; + neg_step_x = true; } - pd.stepseq = mt.zoomFileStepSequence(); + int[] stepseq = mt.zoomFileStepSequence(); for(String p : pfx) { + PrefixData pd = new PrefixData(); + pd.stepsize = stepsize; + pd.neg_step_x = neg_step_x; + pd.stepseq = stepseq; + pd.baseprefix = p; + pd.zoomlevel = zoomlevel; + pd.zoomprefix = "zzzzzzzzzzzz".substring(0, zoomlevel); + if(bigworld) { + if(zoomlevel > 0) { + pd.zoomprefix += "_"; + pd.zfnprefix = "z" + pd.zoomprefix; + } + else { + pd.zfnprefix = "z_"; + } + pd.fnprefix = pd.zoomprefix; + } + else { + pd.fnprefix = pd.zoomprefix + pd.baseprefix; + pd.zfnprefix = "z" + pd.fnprefix; + } + maptab.put(p, pd); } } if(bigworld) { /* If big world, next directories are map name specific */ DirFilter df = new DirFilter(); - for(String pfx : maptab.keySet()) { /* Walk thrugh prefixes, as directories */ - File dname = new File(worldpath, pfx); + 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); - /* Each middle tier directory is redundant - just go through them */ - String[] ssubdir = sdname.list(df); - for(String ss : ssubdir) { - File ssdname = new File(sdname, ss); - processZoomDirectory(ssdname, maptab.get(pfx), "", zoomlevel); - } + cnt += processZoomDirectory(sdname, pd); } } } else { /* Else, classic file layout */ - for(String pfx : maptab.keySet()) { /* Walk thrugh prefixes, as directories */ - processZoomDirectory(worldpath, maptab.get(pfx), pfx + "_", zoomlevel); + 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 { File zf; + String zfname; int x, y; } - private void processZoomDirectory(File dir, PrefixData pd, String prefix, int zoomlevel) { - Debug.debug("processZoomDirectory(" + dir.getPath() + "," + pd.stepsize + "," + prefix + "," + zoomlevel + ")"); - String zoomprefix = "zzzzzzzzzzzzzzzzzzzz".substring(0, zoomlevel); - HashMap toprocess = new HashMap(); - if(prefix.equals("")) { - if(zoomlevel > 0) - prefix = zoomprefix + "_"; - } + private String makeFilePath(PrefixData pd, int x, int y, boolean zoomed) { + if(bigworld) + return pd.baseprefix + "/" + ((x/pd.stepsize) >> 5) + "_" + ((y/pd.stepsize) >> 5) + "/" + (zoomed?pd.zfnprefix:pd.fnprefix) + x + "_" + y + ".png"; else - prefix = zoomprefix + prefix; - int step = pd.stepsize << zoomlevel; - zoomlevel++; - String[] files = dir.list(new PNGFileFilter(prefix)); + return (zoomed?pd.zfnprefix:pd.fnprefix) + "_" + x + "_" + y + ".png"; + } + private int processZoomDirectory(File dir, PrefixData pd) { + Debug.debug("processZoomDirectory(" + dir.getPath() + "," + pd.baseprefix + ")"); + HashMap toprocess = new HashMap(); + int step = pd.stepsize << pd.zoomlevel; + String[] files = dir.list(new PNGFileFilter(pd.fnprefix)); + if(files == null) + return 0; for(String fn : files) { /* Build file object */ File f = new File(dir, fn); @@ -158,11 +185,9 @@ public class DynmapWorld { y = y - (y % (2*step)); else y = y + (y % (2*step)); - File zf; - if(prefix.equals("")) - zf = new File(dir, "z_" + x + "_" + y + ".png"); - else - zf = new File(dir, "z" + prefix + x + "_" + y + ".png"); + /* Make name of corresponding zoomed tile */ + String zfname = makeFilePath(pd, x, y, true); + File zf = new File(worldtilepath, zfname); /* If zoom file exists and is older than our file, nothing to do */ if(zf.exists() && (zf.lastModified() >= f.lastModified())) { continue; @@ -173,27 +198,33 @@ public class DynmapWorld { rec.zf = zf; rec.x = x; rec.y = y; + rec.zfname = zfname; toprocess.put(zfpath, rec); } } + int cnt = 0; /* Do processing */ for(ProcessTileRec s : toprocess.values()) { - processZoomTile(dir, s.zf, s.x - (pd.neg_step_x?step:0), s.y, step, prefix, pd.stepseq); + processZoomTile(pd, dir, s.zf, s.zfname, s.x - (pd.neg_step_x?step:0), s.y); + cnt++; } + Debug.debug("processZoomDirectory(" + dir.getPath() + "," + pd.baseprefix + ") - done (" + cnt + " files)"); + return cnt; } - private void processZoomTile(File dir, File zf, int tx, int ty, int stepsize, String prefix, int[] stepseq) { - Debug.debug("processZoomFile(" + dir.getPath() + "," + zf.getPath() + "," + tx + "," + ty + "," + stepsize + "," + prefix); + 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 + ")"); int width = 128, height = 128; BufferedImage zIm = null; KzedBufferedImage kzIm = null; int[] argb = new int[width*height]; + int step = pd.stepsize << pd.zoomlevel; /* create image buffer */ kzIm = KzedMap.allocateBufferedImage(width, height); zIm = kzIm.buf_img; for(int i = 0; i < 4; i++) { - File f = new File(dir, prefix + (tx + stepsize*(1&stepseq[i])) + "_" + (ty + stepsize*(stepseq[i]>>1)) + ".png"); + File f = new File(worldtilepath, makeFilePath(pd, (tx + step*(1&pd.stepseq[i])), (ty + step*(pd.stepseq[i]>>1)), false)); if(f.exists()) { BufferedImage im = null; FileLockManager.getReadLock(f); @@ -234,8 +265,10 @@ public class DynmapWorld { } FileLockManager.getWriteLock(zf); try { + if(!zf.getParentFile().exists()) + zf.getParentFile().mkdirs(); FileLockManager.imageIOWrite(zIm, "png", zf); - Debug.debug("Saved zoom-out tile at " + zf.getName()); + Debug.debug("Saved zoom-out tile at " + zf.getPath()); } catch (IOException e) { Debug.error("Failed to save zoom-out tile: " + zf.getName(), e); } catch (java.lang.NullPointerException e) { @@ -243,5 +276,6 @@ public class DynmapWorld { } FileLockManager.releaseWriteLock(zf); KzedMap.freeBufferedImage(kzIm); + MapManager.mapman.pushUpdate(this.world, new Client.Tile(zfname)); } } diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index ce149601..88c68893 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -267,7 +267,7 @@ public class MapManager { public void run() { Debug.debug("DoZoomOutProcessing started"); for(DynmapWorld w : worlds) { - w.freshenZoomOutFiles(DynmapPlugin.tilesDirectory); + w.freshenZoomOutFiles(); } renderpool.schedule(this, zoomout_period, TimeUnit.SECONDS); Debug.debug("DoZoomOutProcessing finished"); @@ -362,7 +362,7 @@ public class MapManager { dynmapWorld.sendhealth = worldConfiguration.getBoolean("sendhealth", true); dynmapWorld.bigworld = worldConfiguration.getBoolean("bigworld", false); dynmapWorld.extrazoomoutlevels = worldConfiguration.getInteger("extrazoomout", 0); - + dynmapWorld.worldtilepath = new File(plug_in.tilesDirectory, w.getName()); if(loclist != null) { for(ConfigurationNode loc : loclist) { Location lx = new Location(w, loc.getDouble("x", 0), loc.getDouble("y", 64), loc.getDouble("z", 0)); diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index 78e828e6..2274feea 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -300,7 +300,7 @@ public class FlatMap extends MapType { /* If day too, handle it */ if(night_and_day) { - File dayfile = new File(outputFile.getParent(), tile.getDayFilename()); + File dayfile = new File(tile.getDynmapWorld().worldtilepath, tile.getDayFilename()); FileLockManager.getWriteLock(dayfile); crc = hashman.calculateTileHash(argb_buf_day); if((!dayfile.exists()) || (crc != hashman.getImageHashCode(tile.getKey(), "day", t.x, t.y))) { diff --git a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java index 07c01d12..b32101d4 100644 --- a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java @@ -284,7 +284,8 @@ public class DefaultTileRenderer implements MapTileRenderer { mtile.file = fname; boolean updated_dfname = false; - File dfname = new File(fname.getParent(), mtile.getDayFilename()); + + File dfname = new File(mtile.getDynmapWorld().worldtilepath, mtile.getDayFilename()); if(img_day != null) { FileLockManager.getWriteLock(dfname); crc = hashman.calculateTileHash(img.argb_buf); @@ -313,7 +314,7 @@ public class DefaultTileRenderer implements MapTileRenderer { boolean ztile_updated = false; FileLockManager.getWriteLock(zoomFile); if(updated_fname || (!zoomFile.exists())) { - saveZoomedTile(zmtile, zoomFile, zimg, ox, oy); + saveZoomedTile(zmtile, zoomFile, zimg, ox, oy, null); MapManager.mapman.pushUpdate(zmtile.getWorld(), new Client.Tile(zmtile.getFilename())); ztile_updated = true; @@ -323,11 +324,11 @@ public class DefaultTileRenderer implements MapTileRenderer { MapManager.mapman.updateStatistics(zmtile, null, true, ztile_updated, !rendered); if(zimg_day != null) { - File zoomFile_day = new File(zoomFile.getParent(), zmtile.getDayFilename()); + File zoomFile_day = new File(zmtile.getDynmapWorld().worldtilepath, zmtile.getDayFilename()); ztile_updated = false; FileLockManager.getWriteLock(zoomFile_day); if(updated_dfname || (!zoomFile_day.exists())) { - saveZoomedTile(zmtile, zoomFile_day, zimg_day, ox, oy); + saveZoomedTile(zmtile, zoomFile_day, zimg_day, ox, oy, "day"); MapManager.mapman.pushUpdate(zmtile.getWorld(), new Client.Tile(zmtile.getDayFilename())); ztile_updated = true; @@ -339,7 +340,7 @@ public class DefaultTileRenderer implements MapTileRenderer { } private void saveZoomedTile(final KzedZoomedMapTile zmtile, final File zoomFile, - final KzedBufferedImage zimg, int ox, int oy) { + final KzedBufferedImage zimg, int ox, int oy, String subkey) { BufferedImage zIm = null; KzedBufferedImage kzIm = null; try { @@ -374,6 +375,7 @@ public class DefaultTileRenderer implements MapTileRenderer { } catch (java.lang.NullPointerException e) { Debug.error("Failed to save zoom-out tile (NullPointerException): " + zoomFile.getName(), e); } + if(zIm_allocated) KzedMap.freeBufferedImage(kzIm); else diff --git a/src/main/java/org/dynmap/utils/FileLockManager.java b/src/main/java/org/dynmap/utils/FileLockManager.java index c3f4d679..a95b4f80 100644 --- a/src/main/java/org/dynmap/utils/FileLockManager.java +++ b/src/main/java/org/dynmap/utils/FileLockManager.java @@ -117,6 +117,7 @@ public class FileLockManager { while(!done) { try { ImageIO.write(img, type, fname); + fname.setLastModified(System.currentTimeMillis()); done = true; } catch (FileNotFoundException fnfx) { /* This seems to be what we get when file is locked by reader */ if(retrycnt < MAX_WRITE_RETRIES) { diff --git a/web/js/flatmap.js b/web/js/flatmap.js index 33b7ae79..67633b50 100644 --- a/web/js/flatmap.js +++ b/web/js/flatmap.js @@ -1,5 +1,6 @@ function FlatProjection() {} FlatProjection.prototype = { + extrazoom: 0, fromLatLngToPoint: function(latLng) { return new google.maps.Point(latLng.lat()*config.tileWidth, latLng.lng()*config.tileHeight); }, @@ -7,7 +8,7 @@ FlatProjection.prototype = { return new google.maps.LatLng(point.x/config.tileWidth, point.y/config.tileHeight); }, fromWorldToLatLng: function(x, y, z) { - return new google.maps.LatLng(-z / config.tileWidth, x / config.tileHeight); + return new google.maps.LatLng(-z / config.tileWidth / (1 << this.extrazoom), x / config.tileHeight / (1 << this.extrazoom)); } }; @@ -71,6 +72,7 @@ FlatMapType.prototype = $.extend(new DynMapType(), { updateTileSize: function(zoom) { var size; var extrazoom = this.dynmap.world.extrazoomout; + this.projection.extrazoom = extrazoom; this.maxZoom = 3 + extrazoom; if (zoom <= extrazoom) { size = 128; diff --git a/web/js/kzedmaps.js b/web/js/kzedmaps.js index aa8c468d..971af58e 100644 --- a/web/js/kzedmaps.js +++ b/web/js/kzedmaps.js @@ -1,5 +1,6 @@ function KzedProjection() {} KzedProjection.prototype = { + extrazoom: 0, fromLatLngToPoint: function(latLng) { var x = latLng.lng() * config.tileWidth; var y = latLng.lat() * config.tileHeight; @@ -18,9 +19,10 @@ KzedProjection.prototype = { var dz = +z; var px = dx + dz; var py = dx - dz - dy; + var scale = 2 << this.extrazoom; - var lng = -px / config.tileWidth / 2 + 0.5; - var lat = py / config.tileHeight / 2; + var lng = -px / config.tileWidth / scale + (1.0 / scale); + var lat = py / config.tileHeight / scale; return new google.maps.LatLng(lat, lng); } @@ -112,6 +114,7 @@ KzedMapType.prototype = $.extend(new DynMapType(), { updateTileSize: function(zoom) { var size; var extrazoom = this.dynmap.world.extrazoomout; + this.projection.extrazoom = extrazoom; this.maxZoom = 3 + extrazoom; if (zoom <= extrazoom) { size = 128;