diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 1be4a9f9..a9c1e11e 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -824,6 +824,7 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI { "fullrender", "cancelrender", "radiusrender", + "updaterender", "reload", "stats", "triggerstats", @@ -899,6 +900,32 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI { } if(loc != null) mapManager.renderWorldRadius(loc, sender, mapname, radius); + } else if(c.equals("updaterender") && checkPlayerPermission(sender,"updaterender")) { + String mapname = null; + Location loc = null; + if(args.length <= 3) { /* Just command, or command plus map */ + if(args.length > 2) + mapname = args[2]; + if (player != null) + loc = player.getLocation(); + else + sender.sendMessage("Command require if issued from console."); + } + else { /* */ + DynmapWorld w = mapManager.worldsLookup.get(args[1]); /* Look up world */ + if(w == null) { + sender.sendMessage("World '" + args[1] + "' not defined/loaded"); + } + double x = 0, z = 0; + x = Double.parseDouble(args[2]); + z = Double.parseDouble(args[3]); + if(args.length > 4) + mapname = args[4]; + if(w != null) + loc = new Location(w.world, x, 64.0, z); + } + if(loc != null) + mapManager.renderFullWorld(loc, sender, mapname, true); } else if (c.equals("hide")) { if (args.length == 1) { if(player != null && checkPlayerPermission(sender,"hide.self")) { @@ -938,7 +965,7 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI { if(w != null) { Location spawn = w.world.getSpawnLocation(); Location loc = new Location(w.world, w.configuration.getDouble("center/x", spawn.getX()), w.configuration.getDouble("center/y", spawn.getY()), w.configuration.getDouble("center/z", spawn.getZ())); - mapManager.renderFullWorld(loc,sender, map); + mapManager.renderFullWorld(loc,sender, map, false); } else sender.sendMessage("World '" + wname + "' not defined/loaded"); @@ -948,7 +975,7 @@ public class DynmapPlugin extends JavaPlugin implements DynmapAPI { if(args.length > 1) map = args[1]; if(loc != null) - mapManager.renderFullWorld(loc, sender, map); + mapManager.renderFullWorld(loc, sender, map, false); } else { sender.sendMessage("World name is required"); } diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index 9b03a33c..764a0661 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -177,6 +177,10 @@ public class MapManager { } } } + private static final String RENDERTYPE_FULLRENDER = "Full render"; + private static final String RENDERTYPE_RADIUSRENDER = "Radius render"; + private static final String RENDERTYPE_UPDATERENDER = "Update render"; + /* This always runs on render pool threads - no bukkit calls from here */ private class FullWorldRenderState implements Runnable { DynmapWorld world; /* Which world are we rendering */ @@ -197,14 +201,20 @@ public class MapManager { int cxmin, cxmax, czmin, czmax; String rendertype; boolean cancelled; + boolean updaterender = false; String mapname; AtomicLong total_render_ns = new AtomicLong(0L); AtomicInteger rendercalls = new AtomicInteger(0); /* Full world, all maps render */ - FullWorldRenderState(DynmapWorld dworld, Location l, CommandSender sender, String mapname) { + FullWorldRenderState(DynmapWorld dworld, Location l, CommandSender sender, String mapname, boolean updaterender) { this(dworld, l, sender, mapname, -1); - rendertype = "Full render"; + if(updaterender) { + rendertype = RENDERTYPE_UPDATERENDER; + this.updaterender = true; + } + else + rendertype = RENDERTYPE_FULLRENDER; } /* Full world, all maps render, with optional render radius */ @@ -218,14 +228,14 @@ public class MapManager { if(radius < 0) { cxmin = czmin = Integer.MIN_VALUE; cxmax = czmax = Integer.MAX_VALUE; - rendertype = "Full render"; + rendertype = RENDERTYPE_FULLRENDER; } else { cxmin = (l.getBlockX() - radius)>>4; czmin = (l.getBlockZ() - radius)>>4; cxmax = (l.getBlockX() + radius+15)>>4; czmax = (l.getBlockZ() + radius+15)>>4; - rendertype = "Radius render"; + rendertype = RENDERTYPE_RADIUSRENDER; } this.mapname = mapname; } @@ -291,6 +301,7 @@ public class MapManager { czmax = n.getInteger("czmax", 0); rendertype = n.getString("rendertype", ""); mapname = n.getString("mapname", null); + updaterender = rendertype.equals(RENDERTYPE_UPDATERENDER); sender = null; } @@ -372,7 +383,7 @@ public class MapManager { sendMessage(String.format("%s of map '%s' of '%s' completed - %d tiles rendered (%.2f msec/map-tile, %.2f msec per render)", rendertype, activemaps, world.world.getName(), rendercnt, msecpertile, rendtime)); /* Now, if fullrender, use the render bitmap to purge obsolete tiles */ - if(cxmin == Integer.MIN_VALUE) { + if(rendertype.equals(RENDERTYPE_FULLRENDER)) { if(activemapcnt == 1) { map.purgeOldTiles(world, rendered); } @@ -436,20 +447,22 @@ public class MapManager { renderQueue.add(mt); } } - /* Add spawn location too (helps with some worlds where 0,64,0 may not be generated */ - Location sloc = world.world.getSpawnLocation(); - for (MapTile mt : map.getTiles(sloc)) { - if (!found.getFlag(mt.tileOrdinalX(), mt.tileOrdinalY())) { - found.setFlag(mt.tileOrdinalX(), mt.tileOrdinalY(), true); - renderQueue.add(mt); + if(!updaterender) { /* Only add other seed points for fullrender */ + /* Add spawn location too (helps with some worlds where 0,64,0 may not be generated */ + Location sloc = world.world.getSpawnLocation(); + for (MapTile mt : map.getTiles(sloc)) { + if (!found.getFlag(mt.tileOrdinalX(), mt.tileOrdinalY())) { + found.setFlag(mt.tileOrdinalX(), mt.tileOrdinalY(), true); + renderQueue.add(mt); + } } - } - if(world.seedloc != null) { - for(Location seed : world.seedloc) { - for (MapTile mt : map.getTiles(seed)) { - if (!found.getFlag(mt.tileOrdinalX(),mt.tileOrdinalY())) { - found.setFlag(mt.tileOrdinalX(),mt.tileOrdinalY(), true); - renderQueue.add(mt); + if(world.seedloc != null) { + for(Location seed : world.seedloc) { + for (MapTile mt : map.getTiles(seed)) { + if (!found.getFlag(mt.tileOrdinalX(),mt.tileOrdinalY())) { + found.setFlag(mt.tileOrdinalX(),mt.tileOrdinalY(), true); + renderQueue.add(mt); + } } } } @@ -572,15 +585,18 @@ public class MapManager { /* Switch to not checking if rendered tile is blank - breaks us on skylands, where tiles can be nominally blank - just work off chunk cache empty */ if (cache.isEmpty() == false) { long rt0 = System.nanoTime(); - tile.render(cache, mapname); + boolean upd = tile.render(cache, mapname); total_render_ns.addAndGet(System.nanoTime()-rt0); rendercalls.incrementAndGet(); synchronized(lock) { rendered.setFlag(tile.tileOrdinalX(), tile.tileOrdinalY(), true); - for (MapTile adjTile : map.getAdjecentTiles(tile)) { - if (!found.getFlag(adjTile.tileOrdinalX(),adjTile.tileOrdinalY())) { - found.setFlag(adjTile.tileOrdinalX(), adjTile.tileOrdinalY(), true); - renderQueue.add(adjTile); + if(upd || (!updaterender)) { /* If updated or not an update render */ + /* Add adjacent unrendered tiles to queue */ + for (MapTile adjTile : map.getAdjecentTiles(tile)) { + if (!found.getFlag(adjTile.tileOrdinalX(),adjTile.tileOrdinalY())) { + found.setFlag(adjTile.tileOrdinalX(), adjTile.tileOrdinalY(), true); + renderQueue.add(adjTile); + } } } } @@ -718,7 +734,7 @@ public class MapManager { } } - void renderFullWorld(Location l, CommandSender sender, String mapname) { + void renderFullWorld(Location l, CommandSender sender, String mapname, boolean update) { DynmapWorld world = getWorld(l.getWorld().getName()); if (world == null) { sender.sendMessage("Could not render: world '" + l.getWorld().getName() + "' not defined in configuration."); @@ -732,13 +748,16 @@ public class MapManager { sender.sendMessage(rndr.rendertype + " of world '" + wname + "' already active."); return; } - rndr = new FullWorldRenderState(world,l,sender, mapname); /* Make new activation record */ + rndr = new FullWorldRenderState(world,l,sender, mapname, update); /* Make new activation record */ active_renders.put(wname, rndr); /* Add to active table */ } /* Schedule first tile to be worked */ scheduleDelayedJob(rndr, 0); - sender.sendMessage("Full render starting on world '" + wname + "'..."); + if(update) + sender.sendMessage("Update render starting on world '" + wname + "'..."); + else + sender.sendMessage("Full render starting on world '" + wname + "'..."); } void renderWorldRadius(Location l, CommandSender sender, String mapname, int radius) { diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index 0b00ffda..3fe144d5 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -155,7 +155,7 @@ public class FlatMap extends MapType { World w = t.getWorld(); boolean isnether = (w.getEnvironment() == Environment.NETHER) && (maximumHeight == 127); - boolean rendered = false; + boolean didwrite = false; Color rslt = new Color(); int[] pixel = new int[4]; int[] pixel_day = null; @@ -294,7 +294,6 @@ public class FlatMap extends MapType { rslt.setRGBA(pixel_day[0], pixel_day[1], pixel_day[2], pixel[3]); argb_buf_day[(t.size-y-1) + (x*t.size)] = rslt.getARGB(); } - rendered = true; } } /* Test to see if we're unchanged from older tile */ @@ -319,6 +318,7 @@ public class FlatMap extends MapType { hashman.updateHashCode(tile.getKey(prefix), null, t.x, t.y, crc); tile.getDynmapWorld().enqueueZoomOutUpdate(outputFile); tile_update = true; + didwrite = true; } else { Debug.debug("skipping image " + outputFile.getPath() + " - hash match"); @@ -327,7 +327,7 @@ public class FlatMap extends MapType { FileLockManager.releaseWriteLock(outputFile); DynmapBufferedImage.freeBufferedImage(im); } - MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, !rendered); + MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, true); /* If day too, handle it */ if(night_and_day) { @@ -350,6 +350,7 @@ public class FlatMap extends MapType { hashman.updateHashCode(tile.getKey(prefix), "day", t.x, t.y, crc); tile.getDynmapWorld().enqueueZoomOutUpdate(dayfile); tile_update = true; + didwrite = true; } else { Debug.debug("skipping image " + dayfile.getPath() + " - hash match"); @@ -359,10 +360,10 @@ public class FlatMap extends MapType { FileLockManager.releaseWriteLock(dayfile); DynmapBufferedImage.freeBufferedImage(im_day); } - MapManager.mapman.updateStatistics(tile, prefix+"_day", true, tile_update, !rendered); + MapManager.mapman.updateStatistics(tile, prefix+"_day", true, tile_update, true); } - return rendered; + return didwrite; } private void process_transparent(int[] pixel, int[] pixel_day, MapIterator mapiter) { int r = pixel[0], g = pixel[1], b = pixel[2], a = pixel[3]; diff --git a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java index c15b23f7..d948b3fb 100644 --- a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java +++ b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java @@ -1184,8 +1184,6 @@ public class IsoHDPerspective implements HDPerspective { File f = new File(tile.getDynmapWorld().worldtilepath, fname); FileLockManager.getWriteLock(f); try { - if(rendered[i]) - renderone = true; if((!f.exists()) || (crc != hashman.getImageHashCode(tile.getKey(prefix), null, tile.tx, tile.ty))) { /* Wrap buffer as buffered image */ Debug.debug("saving image " + f.getPath()); @@ -1202,6 +1200,7 @@ public class IsoHDPerspective implements HDPerspective { hashman.updateHashCode(tile.getKey(prefix), null, tile.tx, tile.ty, crc); tile.getDynmapWorld().enqueueZoomOutUpdate(f); tile_update = true; + renderone = true; } else { Debug.debug("skipping image " + f.getPath() + " - hash match"); @@ -1234,6 +1233,7 @@ public class IsoHDPerspective implements HDPerspective { hashman.updateHashCode(tile.getKey(prefix), "day", tile.tx, tile.ty, crc); tile.getDynmapWorld().enqueueZoomOutUpdate(f); tile_update = true; + renderone = true; } else { Debug.debug("skipping image " + f.getPath() + " - hash match"); diff --git a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java index 0f3ba68c..d41a6737 100644 --- a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java @@ -123,7 +123,6 @@ public class DefaultTileRenderer implements MapTileRenderer { boolean isnether = (world.getEnvironment() == Environment.NETHER); DynmapBufferedImage im = DynmapBufferedImage.allocateBufferedImage(KzedMap.tileWidth, KzedMap.tileHeight); DynmapBufferedImage zim = DynmapBufferedImage.allocateBufferedImage(KzedMap.tileWidth/2, KzedMap.tileHeight/2); - boolean isempty = true; DynmapBufferedImage im_day = null; DynmapBufferedImage zim_day = null; @@ -179,9 +178,7 @@ public class DefaultTileRenderer implements MapTileRenderer { argb_day[rowoff+x] = c1_day.getARGB(); argb_day[rowoff+x-1] = c2_day.getARGB(); } - - isempty = isempty && c1.isTransparent() && c2.isTransparent(); - + jx++; jz++; @@ -208,8 +205,6 @@ public class DefaultTileRenderer implements MapTileRenderer { argb_day[rowoff+x] = c1_day.getARGB(); argb_day[rowoff+x-1] = c2_day.getARGB(); } - - isempty = isempty && c1.isTransparent() && c2.isTransparent(); } y++; rowoff += KzedMap.tileWidth; @@ -227,9 +222,7 @@ public class DefaultTileRenderer implements MapTileRenderer { KzedZoomedMapTile zmtile = new KzedZoomedMapTile(tile.getDynmapWorld(), tile); File zoomFile = MapManager.mapman.getTileFile(zmtile); - doFileWrites(outputFile, tile, im, im_day, zmtile, zoomFile, zim, zim_day, !isempty); - - return !isempty; + return doFileWrites(outputFile, tile, im, im_day, zmtile, zoomFile, zim, zim_day); } private void doScaleWithBilinear(int[] argb, int[] zargb, int width, int height) { @@ -256,11 +249,12 @@ public class DefaultTileRenderer implements MapTileRenderer { } } - private void doFileWrites(final File fname, final KzedMapTile mtile, + private boolean doFileWrites(final File fname, final KzedMapTile mtile, final DynmapBufferedImage img, final DynmapBufferedImage img_day, final KzedZoomedMapTile zmtile, final File zoomFile, - final DynmapBufferedImage zimg, final DynmapBufferedImage zimg_day, boolean rendered) { - + final DynmapBufferedImage zimg, final DynmapBufferedImage zimg_day) { + boolean didwrite = false; + /* Get coordinates of zoomed tile */ int ox = (mtile.px == zmtile.getTileX())?0:KzedMap.tileWidth/2; int oy = (mtile.py == zmtile.getTileY())?0:KzedMap.tileHeight/2; @@ -287,12 +281,13 @@ public class DefaultTileRenderer implements MapTileRenderer { MapManager.mapman.pushUpdate(mtile.getWorld(), new Client.Tile(mtile.getFilename())); hashman.updateHashCode(mtile.getKey(prefix), null, tx, ty, crc); updated_fname = true; + didwrite = true; } } finally { FileLockManager.releaseWriteLock(fname); DynmapBufferedImage.freeBufferedImage(img); } - MapManager.mapman.updateStatistics(mtile, prefix, true, updated_fname, !rendered); + MapManager.mapman.updateStatistics(mtile, prefix, true, updated_fname, true); mtile.file = fname; @@ -317,12 +312,13 @@ public class DefaultTileRenderer implements MapTileRenderer { MapManager.mapman.pushUpdate(mtile.getWorld(), new Client.Tile(mtile.getDayFilename())); hashman.updateHashCode(mtile.getKey(prefix), "day", tx, ty, crc); updated_dfname = true; + didwrite = true; } } finally { FileLockManager.releaseWriteLock(dfname); DynmapBufferedImage.freeBufferedImage(img_day); } - MapManager.mapman.updateStatistics(mtile, prefix+"_day", true, updated_dfname, !rendered); + MapManager.mapman.updateStatistics(mtile, prefix+"_day", true, updated_dfname, true); } // Since we've already got the new tile, and we're on an async thread, just @@ -341,7 +337,7 @@ public class DefaultTileRenderer implements MapTileRenderer { FileLockManager.releaseWriteLock(zoomFile); DynmapBufferedImage.freeBufferedImage(zimg); } - MapManager.mapman.updateStatistics(zmtile, null, true, ztile_updated, !rendered); + MapManager.mapman.updateStatistics(zmtile, null, true, ztile_updated, true); if(zimg_day != null) { File zoomFile_day = new File(zmtile.getDynmapWorld().worldtilepath, zmtile.getDayFilename()); @@ -359,8 +355,9 @@ public class DefaultTileRenderer implements MapTileRenderer { FileLockManager.releaseWriteLock(zoomFile_day); DynmapBufferedImage.freeBufferedImage(zimg_day); } - MapManager.mapman.updateStatistics(zmtile, "day", true, ztile_updated, !rendered); + MapManager.mapman.updateStatistics(zmtile, "day", true, ztile_updated, true); } + return didwrite; } private void saveZoomedTile(final KzedZoomedMapTile zmtile, final File zoomFile, diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index 2a3cd74d..85f21926 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -19,6 +19,10 @@ commands: / radiusrender ## mapname - Render at least ## block radius from your location on map 'mapname' / radiusrender worldname x z ## - Render at least ## block radius from location x,z on world 'worldname' / radiusrender worldname x z ## mapname - Render at least ## block radius from location x,z on world 'worldname' on map 'mapname' + / updaterender - Render updates starting at your location on all maps. + / updaterender mapname - Render updates starting at your location on give map + / updaterender worldname x z - Render updates starting at location x,z on world 'worldname' for all maps + / updaterender worldname x z mapname - Render updates starting at location x,z on world 'worldname' for given map / cancelrender - Cancels any active renders on current world / cancelrender world - Cancels any active renders of world 'world' / stats - Show render statistics. @@ -78,6 +82,7 @@ permissions: dynmap.hide.others: true dynmap.fullrender: true dynmap.radiusrender: true + dynmap.updaterender: true dynmap.cancelrender: true dynmap.reload: true dynmap.stats: true @@ -126,6 +131,9 @@ permissions: dynmap.radiusrender: description: Allows /dynmap radiusrender default: op + dynmap.updaterender: + description: Allows /dynmap updaterender + default: op dynmap.cancelrender: description: Allows /dynmap cancelrender default: op