diff --git a/configuration.txt b/configuration.txt index f12252b5..20e94a60 100644 --- a/configuration.txt +++ b/configuration.txt @@ -75,7 +75,7 @@ worlds: renderers: - class: org.dynmap.kzedmap.DefaultTileRenderer prefix: nt - maximumheight: 64 + maximumheight: 127 colorscheme: default web: diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 0a950e1e..02383afb 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -115,6 +115,10 @@ public class DynmapPlugin extends JavaPlugin { } registerEvents(); + + /* Print version info */ + PluginDescriptionFile pdfFile = this.getDescription(); + log.info("[dynmap] version " + pdfFile.getVersion() + " is enabled" ); } public void loadWebserver() { @@ -157,9 +161,7 @@ public class DynmapPlugin extends JavaPlugin { } catch (IOException e) { log.severe("Failed to start WebServer on " + bindAddress + ":" + port + "!"); } - /* Print version info */ - PluginDescriptionFile pdfFile = this.getDescription(); - log.info("[dynmap] version " + pdfFile.getVersion() + " is enabled" ); + } public void onDisable() { diff --git a/src/main/java/org/dynmap/UpdateQueue.java b/src/main/java/org/dynmap/UpdateQueue.java index 368c987d..995a57c5 100644 --- a/src/main/java/org/dynmap/UpdateQueue.java +++ b/src/main/java/org/dynmap/UpdateQueue.java @@ -11,10 +11,11 @@ public class UpdateQueue { private static final int maxUpdateAge = 120000; - public synchronized void pushUpdate(Object obj) { - long now = System.currentTimeMillis(); - long deadline = now - maxUpdateAge; + public void pushUpdate(Object obj) { synchronized (lock) { + /* Do inside lock - prevent delay between time and actual work */ + long now = System.currentTimeMillis(); + long deadline = now - maxUpdateAge; ListIterator i = updateQueue.listIterator(0); while (i.hasNext()) { Update u = i.next(); @@ -27,11 +28,11 @@ public class UpdateQueue { private ArrayList tmpupdates = new ArrayList(); - public synchronized Object[] getUpdatedObjects(long since) { - long now = System.currentTimeMillis(); - long deadline = now - maxUpdateAge; + public Object[] getUpdatedObjects(long since) { Object[] updates; synchronized (lock) { + long now = System.currentTimeMillis(); + long deadline = now - maxUpdateAge; tmpupdates.clear(); Iterator it = updateQueue.descendingIterator(); while (it.hasNext()) { diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index acdc49c0..987c95f5 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -11,6 +11,7 @@ import javax.imageio.ImageIO; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.World.Environment; import org.dynmap.Client; import org.dynmap.ColorScheme; import org.dynmap.DynmapChunk; @@ -22,10 +23,17 @@ import org.dynmap.debug.Debug; public class FlatMap extends MapType { private String prefix; private ColorScheme colorScheme; - + private int maximumHeight = 127; + public FlatMap(Map configuration) { prefix = (String) configuration.get("prefix"); colorScheme = ColorScheme.getScheme((String) configuration.get("colorscheme")); + Object o = configuration.get("maximumheight"); + if (o != null) { + maximumHeight = Integer.parseInt(String.valueOf(o)); + if (maximumHeight > 127) + maximumHeight = 127; + } } @Override @@ -68,6 +76,7 @@ public class FlatMap extends MapType { public boolean render(MapTile tile, File outputFile) { FlatMapTile t = (FlatMapTile) tile; World w = t.getWorld(); + boolean isnether = (w.getEnvironment() == Environment.NETHER) && (maximumHeight == 127); boolean rendered = false; BufferedImage im = new BufferedImage(t.size, t.size, BufferedImage.TYPE_INT_RGB); @@ -79,17 +88,40 @@ public class FlatMap extends MapType { for (int y = 0; y < t.size; y++) { int mx = x + t.x * t.size; int mz = y + t.y * t.size; - int my = w.getHighestBlockYAt(mx, mz) - 1; - int blockType = w.getBlockTypeIdAt(mx, my, mz); - byte data = 0; - if(colorScheme.datacolors[blockType] != null) { /* If data colored */ - data = w.getBlockAt(mx, my, mz).getData(); + int my; + int blockType; + if(isnether) { + /* Scan until we hit air */ + my = 127; + while((blockType = w.getBlockTypeIdAt(mx, my, mz)) != 0) { + my--; + if(my < 0) { /* Solid - use top */ + my = 127; + blockType = w.getBlockTypeIdAt(mx, my, mz); + break; + } + } + if(blockType == 0) { /* Hit air - now find non-air */ + while((blockType = w.getBlockTypeIdAt(mx, my, mz)) == 0) { + my--; + if(my < 0) { + my = 0; + break; + } + } + } + } + else { + my = w.getHighestBlockYAt(mx, mz) - 1; + if(my > maximumHeight) my = maximumHeight; + blockType = w.getBlockTypeIdAt(mx, my, mz); + } + byte data = 0; + Color[] colors = colorScheme.colors[blockType]; + if(colorScheme.datacolors[blockType] != null) { + data = w.getBlockAt(mx, my, mz).getData(); + colors = colorScheme.datacolors[blockType][data]; } - Color[] colors; - if(data != 0) - colors = colorScheme.datacolors[blockType][data]; - else - colors = colorScheme.colors[blockType]; if (colors == null) continue; Color c = colors[0]; @@ -135,19 +167,19 @@ public class FlatMap extends MapType { final MapTile mtile = tile; final BufferedImage img = im; MapManager.mapman.enqueueImageWrite(new Runnable() { - public void run() { - Debug.debug("saving image " + fname.getPath()); - try { - ImageIO.write(img, "png", fname); - } catch (IOException e) { - Debug.error("Failed to save image: " + fname.getPath(), e); - } catch (java.lang.NullPointerException e) { - Debug.error("Failed to save image (NullPointerException): " + fname.getPath(), e); - } - img.flush(); - MapManager.mapman.pushUpdate(mtile.getWorld(), - new Client.Tile(mtile.getFilename())); - } + public void run() { + Debug.debug("saving image " + fname.getPath()); + try { + ImageIO.write(img, "png", fname); + } catch (IOException e) { + Debug.error("Failed to save image: " + fname.getPath(), e); + } catch (java.lang.NullPointerException e) { + Debug.error("Failed to save image (NullPointerException): " + fname.getPath(), e); + } + img.flush(); + MapManager.mapman.pushUpdate(mtile.getWorld(), + new Client.Tile(mtile.getFilename())); + } }); return rendered; diff --git a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java index afdb1db7..eab3d04d 100644 --- a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java @@ -12,7 +12,7 @@ public class CaveTileRenderer extends DefaultTileRenderer { } @Override - protected Color scan(World world, int x, int y, int z, int seq) { + protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { boolean air = true; for (;;) { @@ -20,6 +20,12 @@ public class CaveTileRenderer extends DefaultTileRenderer { return translucent; int id = world.getBlockTypeIdAt(x, y, z); + if(isnether) { /* Make ceiling into air in nether */ + if(id != 0) + id = 0; + else + isnether = false; + } switch (seq) { case 0: diff --git a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java index 3b486df2..2f332dc1 100644 --- a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java @@ -12,7 +12,9 @@ import java.util.Map; import javax.imageio.ImageIO; +import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.World.Environment; import org.dynmap.Client; import org.dynmap.ColorScheme; import org.dynmap.MapManager; @@ -28,25 +30,6 @@ public class DefaultTileRenderer implements MapTileRenderer { protected HashSet highlightBlocks = new HashSet(); protected Color highlightColor = new Color(255, 0, 0); - private static final Color[] woolshades = { - Color.WHITE, - Color.ORANGE, - Color.MAGENTA, - new Color(51,204,255), - Color.YELLOW, - new Color(102,255,102), - Color.PINK, - Color.GRAY, - Color.LIGHT_GRAY, - Color.CYAN, - new Color(255,0,255), - Color.BLUE, - new Color(102,51,51), - Color.GREEN, - Color.RED, - Color.BLACK - }; - @Override public String getName() { return name; @@ -65,6 +48,7 @@ public class DefaultTileRenderer implements MapTileRenderer { public boolean render(KzedMapTile tile, File outputFile) { World world = tile.getWorld(); + boolean isnether = (world.getEnvironment() == Environment.NETHER); BufferedImage im = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); WritableRaster r = im.getRaster(); @@ -74,6 +58,10 @@ public class DefaultTileRenderer implements MapTileRenderer { int iy = maximumHeight; int iz = KzedMap.anchorz + tile.px / 2 - tile.py / 2 + ((127-maximumHeight)/2); + /* Don't mess with existing height-clipped renders */ + if(maximumHeight < 127) + isnether = false; + int jx, jz; int x, y; @@ -84,8 +72,8 @@ public class DefaultTileRenderer implements MapTileRenderer { jz = iz; for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { - Color c1 = scan(world, jx, iy, jz, 0); - Color c2 = scan(world, jx, iy, jz, 2); + Color c1 = scan(world, jx, iy, jz, 0, isnether); + Color c2 = scan(world, jx, iy, jz, 2, isnether); isempty = isempty && c1 == translucent && c2 == translucent; r.setPixel(x, y, new int[] { c1.getRed(), @@ -107,10 +95,10 @@ public class DefaultTileRenderer implements MapTileRenderer { jz = iz - 1; for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { - Color c1 = scan(world, jx, iy, jz, 2); + Color c1 = scan(world, jx, iy, jz, 2, isnether); jx++; jz++; - Color c2 = scan(world, jx, iy, jz, 0); + Color c2 = scan(world, jx, iy, jz, 0, isnether); isempty = isempty && c1 == translucent && c2 == translucent; r.setPixel(x, y, new int[] { c1.getRed(), @@ -132,100 +120,112 @@ public class DefaultTileRenderer implements MapTileRenderer { final File fname = outputFile; final KzedMapTile mtile = tile; final BufferedImage img = im; - final KzedZoomedMapTile zmtile = new KzedZoomedMapTile(mtile.getWorld(), - (KzedMap) mtile.getMap(), mtile); - final File zoomFile = MapManager.mapman.getTileFile(zmtile); - - MapManager.mapman.enqueueImageWrite(new Runnable() { - public void run() { - doFileWrites(fname, mtile, img, zmtile, zoomFile); - } + final KzedZoomedMapTile zmtile = new KzedZoomedMapTile(mtile.getWorld(), + (KzedMap) mtile.getMap(), mtile); + final File zoomFile = MapManager.mapman.getTileFile(zmtile); + + MapManager.mapman.enqueueImageWrite(new Runnable() { + public void run() { + doFileWrites(fname, mtile, img, zmtile, zoomFile); + } }); return !isempty; } private void doFileWrites(final File fname, final KzedMapTile mtile, - final BufferedImage img, final KzedZoomedMapTile zmtile, final File zoomFile) { - Debug.debug("saving image " + fname.getPath()); - try { - ImageIO.write(img, "png", fname); - } catch (IOException e) { - Debug.error("Failed to save image: " + fname.getPath(), e); - } catch (java.lang.NullPointerException e) { - Debug.error("Failed to save image (NullPointerException): " + fname.getPath(), e); - } - mtile.file = fname; - // Since we've already got the new tile, and we're on an async thread, just - // make the zoomed tile here - int px = mtile.px; - int py = mtile.py; - int zpx = zmtile.getTileX(); - int zpy = zmtile.getTileY(); + final BufferedImage img, final KzedZoomedMapTile zmtile, final File zoomFile) { + Debug.debug("saving image " + fname.getPath()); + try { + ImageIO.write(img, "png", fname); + } catch (IOException e) { + Debug.error("Failed to save image: " + fname.getPath(), e); + } catch (java.lang.NullPointerException e) { + Debug.error("Failed to save image (NullPointerException): " + fname.getPath(), e); + } + mtile.file = fname; + // Since we've already got the new tile, and we're on an async thread, just + // make the zoomed tile here + int px = mtile.px; + int py = mtile.py; + int zpx = zmtile.getTileX(); + int zpy = zmtile.getTileY(); - /* scaled size */ - int scw = KzedMap.tileWidth / 2; - int sch = KzedMap.tileHeight / 2; + /* scaled size */ + int scw = KzedMap.tileWidth / 2; + int sch = KzedMap.tileHeight / 2; - /* origin in zoomed-out tile */ - int ox = 0; - int oy = 0; + /* origin in zoomed-out tile */ + int ox = 0; + int oy = 0; - if (zpx != px) - ox = scw; - if (zpy != py) - oy = sch; + if (zpx != px) + ox = scw; + if (zpy != py) + oy = sch; - BufferedImage zIm = null; - try { - zIm = ImageIO.read(zoomFile); - } catch (IOException e) { - } catch (IndexOutOfBoundsException e) { - } + BufferedImage zIm = null; + try { + zIm = ImageIO.read(zoomFile); + } catch (IOException e) { + } catch (IndexOutOfBoundsException e) { + } - if (zIm == null) { - /* create new one */ - zIm = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); - Debug.debug("New zoom-out tile created " + zmtile.getFilename()); - } else { - Debug.debug("Loaded zoom-out tile from " + zmtile.getFilename()); - } - - /* blit scaled rendered tile onto zoom-out tile */ - Graphics2D g2 = zIm.createGraphics(); - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.drawImage(img, ox, oy, scw, sch, null); + if (zIm == null) { + /* create new one */ + zIm = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); + Debug.debug("New zoom-out tile created " + zmtile.getFilename()); + } else { + Debug.debug("Loaded zoom-out tile from " + zmtile.getFilename()); + } + + /* blit scaled rendered tile onto zoom-out tile */ + Graphics2D g2 = zIm.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(img, ox, oy, scw, sch, null); - img.flush(); + img.flush(); - /* save zoom-out tile */ - - try { - ImageIO.write(zIm, "png", zoomFile); - Debug.debug("Saved zoom-out tile at " + zoomFile.getName()); - } catch (IOException e) { - Debug.error("Failed to save zoom-out tile: " + zoomFile.getName(), e); - } catch (java.lang.NullPointerException e) { - Debug.error("Failed to save zoom-out tile (NullPointerException): " + zoomFile.getName(), e); - } - zIm.flush(); - /* Push updates for both files.*/ - MapManager.mapman.pushUpdate(mtile.getWorld(), - new Client.Tile(mtile.getFilename())); - MapManager.mapman.pushUpdate(zmtile.getWorld(), - new Client.Tile(zmtile.getFilename())); + /* save zoom-out tile */ + + try { + ImageIO.write(zIm, "png", zoomFile); + Debug.debug("Saved zoom-out tile at " + zoomFile.getName()); + } catch (IOException e) { + Debug.error("Failed to save zoom-out tile: " + zoomFile.getName(), e); + } catch (java.lang.NullPointerException e) { + Debug.error("Failed to save zoom-out tile (NullPointerException): " + zoomFile.getName(), e); + } + zIm.flush(); + /* Push updates for both files.*/ + MapManager.mapman.pushUpdate(mtile.getWorld(), + new Client.Tile(mtile.getFilename())); + MapManager.mapman.pushUpdate(zmtile.getWorld(), + new Client.Tile(zmtile.getFilename())); } - protected Color scan(World world, int x, int y, int z, int seq) { + protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { + Color result = translucent; for (;;) { - if (y < 0) - return translucent; - + if (y < 0) { + return result; + } int id = world.getBlockTypeIdAt(x, y, z); byte data = 0; - if(colorScheme.datacolors[id] != null) { /* If data colored */ - data = world.getBlockAt(x, y, z).getData(); + if(isnether) { /* Make bedrock ceiling into air in nether */ + if(id != 0) { + /* Remember first color we see, in case we wind up solid */ + if(result == translucent) + if(colorScheme.colors[id] != null) + result = colorScheme.colors[id][seq]; + id = 0; + } + else + isnether = false; + } + if(colorScheme.datacolors[id] != null) { /* If data colored */ + data = world.getBlockAt(x, y, z).getData(); } switch (seq) { case 0: @@ -250,9 +250,9 @@ public class DefaultTileRenderer implements MapTileRenderer { } Color[] colors; if(data != 0) - colors = colorScheme.datacolors[id][data]; + colors = colorScheme.datacolors[id][data]; else - colors = colorScheme.colors[id]; + colors = colorScheme.colors[id]; if (colors != null) { Color c = colors[seq]; if (c.getAlpha() > 0) { @@ -263,7 +263,7 @@ public class DefaultTileRenderer implements MapTileRenderer { } /* this block is transparent, so recurse */ - Color bg = scan(world, x, y, z, seq); + Color bg = scan(world, x, y, z, seq, isnether); int cr = c.getRed(); int cg = c.getGreen(); diff --git a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java index dd639b70..247515af 100644 --- a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java @@ -23,17 +23,29 @@ public class HighlightTileRenderer extends DefaultTileRenderer { } @Override - protected Color scan(World world, int x, int y, int z, int seq) { + protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { Color result = translucent; + int top_nether_id = 0; for (;;) { if (y < 0) { break; } int id = world.getBlockTypeIdAt(x, y, z); + if(isnether) { /* Make bedrock ceiling into air in nether */ + if(id != 0) { + /* Remember first color we see, in case we wind up solid */ + if(result == translucent) + if(colorScheme.colors[id] != null) + result = colorScheme.colors[id][seq]; + id = 0; + } + else + isnether = false; + } byte data = 0; - if(colorScheme.datacolors[id] != null) { /* If data colored */ - data = world.getBlockAt(x, y, z).getData(); + if(colorScheme.datacolors[id] != null) { /* If data colored */ + data = world.getBlockAt(x, y, z).getData(); } switch (seq) { @@ -56,9 +68,9 @@ public class HighlightTileRenderer extends DefaultTileRenderer { if (id != 0) { Color[] colors; if(data != 0) - colors = colorScheme.datacolors[id][data]; + colors = colorScheme.datacolors[id][data]; else - colors = colorScheme.colors[id]; + colors = colorScheme.colors[id]; if (colors != null) { Color c = colors[seq]; diff --git a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java index 4d5dee8d..a0de1520 100644 --- a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java @@ -1,18 +1,8 @@ package org.dynmap.kzedmap; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.image.BufferedImage; import java.io.File; -import java.io.IOException; import java.util.Map; -import javax.imageio.ImageIO; - -import org.dynmap.Client; -import org.dynmap.MapManager; -import org.dynmap.debug.Debug; - public class ZoomedTileRenderer { public ZoomedTileRenderer(Map configuration) { } diff --git a/web/js/map.js b/web/js/map.js index 0ae78f17..fdd69d2b 100644 --- a/web/js/map.js +++ b/web/js/map.js @@ -386,7 +386,7 @@ DynMap.prototype = { swtch(update.type, { tile: function() { - me.onTileUpdated(update.name); + me.onTileUpdated(update.name,update.timestamp); }, playerjoin: function() { $(me).trigger('playerjoin', [ update.playerName ]); @@ -436,12 +436,12 @@ DynMap.prototype = { unregisterTile: function(mapType, tileName) { delete this.registeredTiles[tileName]; }, - onTileUpdated: function(tileName) { + onTileUpdated: function(tileName,timestamp) { var me = this; var tile = this.registeredTiles[tileName]; if (tile) { - tile.lastseen = this.lasttimestamp; + tile.lastseen = timestamp; tile.mapType.onTileUpdated(tile.tileElement, tileName); } },