From 7980c7dbe5abc0d606ff55331f7ab53fe3bc2272 Mon Sep 17 00:00:00 2001 From: Marvos Date: Mon, 21 Oct 2019 21:32:49 +0200 Subject: [PATCH] Implemented functionality to resume full renders by typing "dynmap fullrender resume " or "dynmap fullrender resume :". At the start of the render, existing map tiles are loaded from storage and their location info saved in a set. Before each tile is rendered, the set is checked if it contains the tile about to be rendered. If so, the tile is skipped. Information about skipped tiles is output in the periodic tile update message. --- .../src/main/java/org/dynmap/DynmapCore.java | 15 ++- .../src/main/java/org/dynmap/MapManager.java | 118 ++++++++++++++---- .../java/org/dynmap/storage/MapStorage.java | 9 ++ .../storage/MapStorageBaseTileEnumCB.java | 12 ++ .../storage/MapStorageTileSearchEndCB.java | 10 ++ .../storage/filetree/FileTreeMapStorage.java | 39 +++++- .../storage/mariadb/MariaDBMapStorage.java | 39 +++++- .../dynmap/storage/mysql/MySQLMapStorage.java | 39 +++++- .../storage/sqllte/SQLiteMapStorage.java | 41 +++++- 9 files changed, 278 insertions(+), 44 deletions(-) create mode 100644 DynmapCore/src/main/java/org/dynmap/storage/MapStorageBaseTileEnumCB.java create mode 100644 DynmapCore/src/main/java/org/dynmap/storage/MapStorageTileSearchEndCB.java diff --git a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java index f4259be6..eb9aeb0f 100644 --- a/DynmapCore/src/main/java/org/dynmap/DynmapCore.java +++ b/DynmapCore/src/main/java/org/dynmap/DynmapCore.java @@ -1042,7 +1042,9 @@ public class DynmapCore implements DynmapCommonAPI { new CommandInfo("dynmap", "render", "Renders the tile at your location."), new CommandInfo("dynmap", "fullrender", "Render all maps for entire world from your location."), new CommandInfo("dynmap", "fullrender", "", "Render all maps for world ."), - new CommandInfo("dynmap", "fullrender", ":", "Render map of world'."), + new CommandInfo("dynmap", "fullrender", ":", "Render map of world ."), + new CommandInfo("dynmap", "fullrender", "resume ", "Resume render of all maps for world . Skip already rendered tiles."), + new CommandInfo("dynmap", "fullrender", "resume :", "Resume render of map of world . Skip already rendered tiles."), new CommandInfo("dynmap", "radiusrender", "", "Render at least block radius from your location on all maps."), new CommandInfo("dynmap", "radiusrender", " ", "Render at least block radius from your location on map ."), new CommandInfo("dynmap", "radiusrender", " ", "Render at least block radius from location , on world ."), @@ -1315,7 +1317,7 @@ public class DynmapCore implements DynmapCommonAPI { loc = new DynmapLocation(w.getName(), x, 64, z); } if(loc != null) - mapManager.renderFullWorld(loc, sender, mapname, true); + mapManager.renderFullWorld(loc, sender, mapname, true, false); } else if (c.equals("hide")) { if (args.length == 1) { if(player != null && checkPlayerPermission(sender,"hide.self")) { @@ -1343,7 +1345,12 @@ public class DynmapCore implements DynmapCommonAPI { } else if (c.equals("fullrender") && checkPlayerPermission(sender,"fullrender")) { String map = null; if (args.length > 1) { + boolean resume = false; for (int i = 1; i < args.length; i++) { + if (args[i].equalsIgnoreCase("resume")) { + resume = true; + continue; + } int dot = args[i].indexOf(":"); DynmapWorld w; String wname = args[i]; @@ -1358,7 +1365,7 @@ public class DynmapCore implements DynmapCommonAPI { loc = w.center; else loc = w.getSpawnLocation(); - mapManager.renderFullWorld(loc,sender, map, false); + mapManager.renderFullWorld(loc,sender, map, false, resume); } else sender.sendMessage("World '" + wname + "' not defined/loaded"); @@ -1368,7 +1375,7 @@ public class DynmapCore implements DynmapCommonAPI { if(args.length > 1) map = args[1]; if(loc != null) - mapManager.renderFullWorld(loc, sender, map, false); + mapManager.renderFullWorld(loc, sender, map, false, false); } else { sender.sendMessage("World name is required"); } diff --git a/DynmapCore/src/main/java/org/dynmap/MapManager.java b/DynmapCore/src/main/java/org/dynmap/MapManager.java index 77d9633d..5fbed325 100644 --- a/DynmapCore/src/main/java/org/dynmap/MapManager.java +++ b/DynmapCore/src/main/java/org/dynmap/MapManager.java @@ -22,6 +22,7 @@ import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -32,6 +33,10 @@ import org.dynmap.debug.Debug; import org.dynmap.exporter.OBJExport; import org.dynmap.hdmap.HDMapManager; import org.dynmap.renderer.DynmapBlockState; +import org.dynmap.storage.MapStorage; +import org.dynmap.storage.MapStorageBaseTileEnumCB; +import org.dynmap.storage.MapStorageTileSearchEndCB; +import org.dynmap.storage.MapStorageTile; import org.dynmap.utils.MapChunkCache; import org.dynmap.utils.Polygon; import org.dynmap.utils.TileFlags; @@ -67,7 +72,7 @@ public class MapManager { private boolean tpspauseupdaterenders = false; private boolean tpspausefullrenders = false; private boolean tpspausezoomout = false; - + private boolean did_start = false; private int zoomout_period = DEFAULT_ZOOMOUT_PERIOD; /* Zoom-out tile processing period, in seconds */ @@ -233,6 +238,7 @@ public class MapManager { LinkedList renderQueue = null; MapTile tile0 = null; int rendercnt = 0; + int skipcnt = 0; DynmapCommandSender sender; String player; long timeaccum; @@ -246,21 +252,51 @@ public class MapManager { boolean shutdown = false; boolean pausedforworld = false; boolean updaterender = false; + boolean resume = false; boolean quiet = false; String mapname; AtomicLong total_render_ns = new AtomicLong(0L); AtomicInteger rendercalls = new AtomicInteger(0); long lastPendingSaveTS = 0; // Timestamp of last pending state save (msec) + HashSet storedTileIds = new HashSet<>(); /* Full world, all maps render */ - FullWorldRenderState(DynmapWorld dworld, DynmapLocation l, DynmapCommandSender sender, String mapname, boolean updaterender) { + FullWorldRenderState(DynmapWorld dworld, DynmapLocation l, DynmapCommandSender sender, String mapname, boolean updaterender, boolean resume) { this(dworld, l, sender, mapname, -1); if(updaterender) { rendertype = RENDERTYPE_UPDATERENDER; this.updaterender = true; } - else + else { rendertype = RENDERTYPE_FULLRENDER; + } + this.resume = resume; + + final CountDownLatch latch = new CountDownLatch(1); + + if (resume) { // if resume render + final MapStorage ms = world.getMapStorage(); + ms.enumMapBaseTiles(world, map, new MapStorageBaseTileEnumCB() { + @Override + public void tileFound(MapStorageTile tile, MapType.ImageEncoding enc) { + String tileId = String.format("%s_%s_%d_%d", tile.world.getName(), tile.map.getName(), tile.x, tile.y); + //sender.sendMessage("Tile found: " + tileId); + storedTileIds.add(tileId); + } + }, new MapStorageTileSearchEndCB() { + @Override + public void searchEnded() { + latch.countDown(); + } + }); + + try { + latch.await(10, TimeUnit.SECONDS); + } catch (InterruptedException e) { + e.printStackTrace(); + sender.sendMessage(e.toString()); + } + } } /* Full world, all maps render, with optional render radius */ @@ -481,13 +517,22 @@ public class MapManager { if(rndcalls == 0) rndcalls = 1; double rendtime = total_render_ns.doubleValue() * 0.000001 / rndcalls; if(activemapcnt > 1) { - sendMessage(String.format("%s of maps [%s] of '%s' completed - %d tiles rendered each (%.2f msec/map-tile, %.2f msec per render)", - rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime)); + if (skipcnt > 1) + sendMessage(String.format("%s of maps [%s] of '%s' completed - %d tiles rendered each (%.2f msec/map-tile, %.2f msec per render) (%d tiles skipped)", + rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime, skipcnt)); + else + sendMessage(String.format("%s of maps [%s] of '%s' completed - %d tiles rendered each (%.2f msec/map-tile, %.2f msec per render)", + rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime)); } else { - sendMessage(String.format("%s of map '%s' of '%s' completed - %d tiles rendered (%.2f msec/map-tile, %.2f msec per render)", - rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime)); + if (skipcnt > 1) + sendMessage(String.format("%s of map '%s' of '%s' completed - %d tiles rendered (%.2f msec/map-tile, %.2f msec per render) (%d tiles skipped)", + rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime, skipcnt)); + else + sendMessage(String.format("%s of map '%s' of '%s' completed - %d tiles rendered (%.2f msec/map-tile, %.2f msec per render)", + rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime)); } + skipcnt = 0; /* Now, if fullrender, use the render bitmap to purge obsolete tiles */ if(rendertype.equals(RENDERTYPE_FULLRENDER)) { if(activemapcnt == 1) { @@ -698,19 +743,37 @@ public class MapManager { chunks_read[cs.ordinal()].addAndGet(cache.getChunksLoaded(cs)); chunks_read_times[cs.ordinal()].addAndGet(cache.getTotalRuntimeNanos(cs)); } + + boolean skipTile = false; + if (resume) { + String tileId = String.format("%s_%s_%d_%d", tile.world.getName(), map.getName(), tile.tileOrdinalX(), tile.tileOrdinalY()); + skipTile = storedTileIds.contains(tileId); + } + if(tile0 != null) { /* Single tile? */ - if(cache.isEmpty() == false) - tile.render(cache, null); + if(cache.isEmpty() == false) { + if (skipTile) { + skipcnt++; + } else { + tile.render(cache, null); + } + } } else { /* Remove tile from tile queue, since we're processing it already */ tileQueue.remove(tile); /* 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(); - boolean upd = tile.render(cache, mapname); - total_render_ns.addAndGet(System.nanoTime()-rt0); - rendercalls.incrementAndGet(); + boolean upd; + if (skipTile) { + upd = false; + skipcnt++; + } else { + long rt0 = System.nanoTime(); + upd = tile.render(cache, mapname); + total_render_ns.addAndGet(System.nanoTime()-rt0); + rendercalls.incrementAndGet(); + } synchronized(lock) { rendered.setFlag(tile.tileOrdinalX(), tile.tileOrdinalY(), true); if(upd || (!updaterender)) { /* If updated or not an update render */ @@ -733,12 +796,22 @@ public class MapManager { if (rndcalls == 0) rndcalls = 1; double rendtime = total_render_ns.doubleValue() * 0.000001 / rndcalls; double msecpertile = (double)timeaccum / (double)rendercnt / (double)activemapcnt; - if(activemapcnt > 1) - sendMessage(String.format("%s of maps [%s] of '%s' in progress - %d tiles rendered each (%.2f msec/map-tile, %.2f msec per render)", - rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime)); - else - sendMessage(String.format("%s of map '%s' of '%s' in progress - %d tiles rendered (%.2f msec/tile, %.2f msec per render)", - rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime)); + if(activemapcnt > 1) { + if (skipcnt > 1) + sendMessage(String.format("%s of maps [%s] of '%s' in progress - %d tiles rendered each (%.2f msec/map-tile, %.2f msec per render) (%d tiles skipped)", + rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime, skipcnt)); + else + sendMessage(String.format("%s of maps [%s] of '%s' in progress - %d tiles rendered each (%.2f msec/map-tile, %.2f msec per render)", + rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime)); + } else { + if (skipcnt > 1) + sendMessage(String.format("%s of map '%s' of '%s' in progress - %d tiles rendered (%.2f msec/tile, %.2f msec per render) (%d tiles skipped)", + rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime, skipcnt)); + else + sendMessage(String.format("%s of map '%s' of '%s' in progress - %d tiles rendered (%.2f msec/tile, %.2f msec per render)", + rendertype, activemaps, world.getName(), rendercnt, msecpertile, rendtime)); + } + skipcnt = 0; } } } @@ -751,6 +824,7 @@ public class MapManager { public void cancelRender() { cancelled = true; + storedTileIds.clear(); } public void shutdownRender() { @@ -968,7 +1042,7 @@ public class MapManager { tileQueue.start(); } - void renderFullWorld(DynmapLocation l, DynmapCommandSender sender, String mapname, boolean update) { + void renderFullWorld(DynmapLocation l, DynmapCommandSender sender, String mapname, boolean update, boolean resume) { DynmapWorld world = getWorld(l.world); if (world == null) { sender.sendMessage("Could not render: world '" + l.world + "' not defined in configuration."); @@ -982,7 +1056,7 @@ public class MapManager { sender.sendMessage(rndr.rendertype + " of world '" + wname + "' already active."); return; } - rndr = new FullWorldRenderState(world,l,sender, mapname, update); /* Make new activation record */ + rndr = new FullWorldRenderState(world,l,sender, mapname, update, resume); /* Make new activation record */ active_renders.put(wname, rndr); /* Add to active table */ } /* Schedule first tile to be worked */ @@ -990,6 +1064,8 @@ public class MapManager { if(update) sender.sendMessage("Update render starting on world '" + wname + "'..."); + else if (resume) + sender.sendMessage("Full render resuming on world '" + wname + "'..."); else sender.sendMessage("Full render starting on world '" + wname + "'..."); } diff --git a/DynmapCore/src/main/java/org/dynmap/storage/MapStorage.java b/DynmapCore/src/main/java/org/dynmap/storage/MapStorage.java index e2962287..a54b8450 100644 --- a/DynmapCore/src/main/java/org/dynmap/storage/MapStorage.java +++ b/DynmapCore/src/main/java/org/dynmap/storage/MapStorage.java @@ -86,6 +86,15 @@ public abstract class MapStorage { */ public abstract void enumMapTiles(DynmapWorld world, MapType map, MapStorageTileEnumCB cb); + /** + * Enumerate existing map tiles, matching given constraints, with zoom at 0 + * @param world - specific world + * @param map - specific map (if non-null) + * @param cbBase - callback to receive matching tiles + * @param cbEnd - callback to receive end-of-search event + */ + public abstract void enumMapBaseTiles(DynmapWorld world, MapType map, MapStorageBaseTileEnumCB cbBase, MapStorageTileSearchEndCB cbEnd); + /** * Purge existing map tiles, matching given constraints * @param world - specific world diff --git a/DynmapCore/src/main/java/org/dynmap/storage/MapStorageBaseTileEnumCB.java b/DynmapCore/src/main/java/org/dynmap/storage/MapStorageBaseTileEnumCB.java new file mode 100644 index 00000000..814cc64f --- /dev/null +++ b/DynmapCore/src/main/java/org/dynmap/storage/MapStorageBaseTileEnumCB.java @@ -0,0 +1,12 @@ +package org.dynmap.storage; + +import org.dynmap.MapType.ImageEncoding; + +public interface MapStorageBaseTileEnumCB { + /** + * Callback for base (non-zoomed) tile enumeration calls + * @param tile - tile found + * @param enc - image encoding + */ + public void tileFound(MapStorageTile tile, ImageEncoding enc); +} diff --git a/DynmapCore/src/main/java/org/dynmap/storage/MapStorageTileSearchEndCB.java b/DynmapCore/src/main/java/org/dynmap/storage/MapStorageTileSearchEndCB.java new file mode 100644 index 00000000..8367a3f6 --- /dev/null +++ b/DynmapCore/src/main/java/org/dynmap/storage/MapStorageTileSearchEndCB.java @@ -0,0 +1,10 @@ +package org.dynmap.storage; + +import org.dynmap.MapType.ImageEncoding; + +public interface MapStorageTileSearchEndCB { + /** + * Callback for end of tile enumeration calls + */ + public void searchEnded(); +} diff --git a/DynmapCore/src/main/java/org/dynmap/storage/filetree/FileTreeMapStorage.java b/DynmapCore/src/main/java/org/dynmap/storage/filetree/FileTreeMapStorage.java index 93ab4912..f21b0460 100644 --- a/DynmapCore/src/main/java/org/dynmap/storage/filetree/FileTreeMapStorage.java +++ b/DynmapCore/src/main/java/org/dynmap/storage/filetree/FileTreeMapStorage.java @@ -22,6 +22,8 @@ import org.dynmap.debug.Debug; import org.dynmap.storage.MapStorage; import org.dynmap.storage.MapStorageTile; import org.dynmap.storage.MapStorageTileEnumCB; +import org.dynmap.storage.MapStorageBaseTileEnumCB; +import org.dynmap.storage.MapStorageTileSearchEndCB; import org.dynmap.utils.BufferInputStream; import org.dynmap.utils.BufferOutputStream; @@ -295,9 +297,13 @@ public class FileTreeMapStorage extends MapStorage { } - private void processEnumMapTiles(DynmapWorld world, MapType map, File base, ImageVariant var, MapStorageTileEnumCB cb) { + private void processEnumMapTiles(DynmapWorld world, MapType map, File base, ImageVariant var, MapStorageTileEnumCB cb, MapStorageBaseTileEnumCB cbBase, MapStorageTileSearchEndCB cbEnd) { File bdir = new File(base, map.getPrefix() + var.variantSuffix); - if (bdir.isDirectory() == false) return; + if (bdir.isDirectory() == false) { + if(cbEnd != null) + cbEnd.searchEnded(); + return; + } LinkedList dirs = new LinkedList(); // List to traverse dirs.add(bdir); // Directory for map @@ -343,7 +349,10 @@ public class FileTreeMapStorage extends MapStorage { int y = Integer.parseInt(coord[1]); // Invoke callback MapStorageTile t = new StorageTile(world, map, x, y, zoom, var); - cb.tileFound(t, fmt); + if(cb != null) + cb.tileFound(t, fmt); + if(cbBase != null && t.zoom == 0) + cbBase.tileFound(t, fmt); t.cleanup(); } catch (NumberFormatException nfx) { } @@ -351,6 +360,9 @@ public class FileTreeMapStorage extends MapStorage { } } } + if(cbEnd != null) { + cbEnd.searchEnded(); + } } @Override @@ -367,7 +379,26 @@ public class FileTreeMapStorage extends MapStorage { for (MapType mt : mtlist) { ImageVariant[] vars = mt.getVariants(); for (ImageVariant var : vars) { - processEnumMapTiles(world, mt, base, var, cb); + processEnumMapTiles(world, mt, base, var, cb, null, null); + } + } + } + + @Override + public void enumMapBaseTiles(DynmapWorld world, MapType map, MapStorageBaseTileEnumCB cbBase, MapStorageTileSearchEndCB cbEnd) { + File base = new File(baseTileDir, world.getName()); // Get base directory for world + List mtlist; + + if (map != null) { + mtlist = Collections.singletonList(map); + } + else { // Else, add all directories under world directory (for maps) + mtlist = new ArrayList(world.maps); + } + for (MapType mt : mtlist) { + ImageVariant[] vars = mt.getVariants(); + for (ImageVariant var : vars) { + processEnumMapTiles(world, mt, base, var, null, cbBase, cbEnd); } } } diff --git a/DynmapCore/src/main/java/org/dynmap/storage/mariadb/MariaDBMapStorage.java b/DynmapCore/src/main/java/org/dynmap/storage/mariadb/MariaDBMapStorage.java index 78618bb4..0dd6ad53 100644 --- a/DynmapCore/src/main/java/org/dynmap/storage/mariadb/MariaDBMapStorage.java +++ b/DynmapCore/src/main/java/org/dynmap/storage/mariadb/MariaDBMapStorage.java @@ -25,6 +25,8 @@ import org.dynmap.PlayerFaces.FaceType; import org.dynmap.storage.MapStorage; import org.dynmap.storage.MapStorageTile; import org.dynmap.storage.MapStorageTileEnumCB; +import org.dynmap.storage.MapStorageBaseTileEnumCB; +import org.dynmap.storage.MapStorageTileSearchEndCB; import org.dynmap.utils.BufferInputStream; import org.dynmap.utils.BufferOutputStream; @@ -608,7 +610,7 @@ public class MariaDBMapStorage extends MapStorage { @Override public void enumMapTiles(DynmapWorld world, MapType map, - MapStorageTileEnumCB cb) { + MapStorageTileEnumCB cb) { List mtlist; if (map != null) { @@ -620,15 +622,36 @@ public class MariaDBMapStorage extends MapStorage { for (MapType mt : mtlist) { ImageVariant[] vars = mt.getVariants(); for (ImageVariant var : vars) { - processEnumMapTiles(world, mt, var, cb); + processEnumMapTiles(world, mt, var, cb, null, null); } } } - private void processEnumMapTiles(DynmapWorld world, MapType map, ImageVariant var, MapStorageTileEnumCB cb) { + @Override + public void enumMapBaseTiles(DynmapWorld world, MapType map, MapStorageBaseTileEnumCB cbBase, MapStorageTileSearchEndCB cbEnd) { + List mtlist; + + if (map != null) { + mtlist = Collections.singletonList(map); + } + else { // Else, add all directories under world directory (for maps) + mtlist = new ArrayList(world.maps); + } + for (MapType mt : mtlist) { + ImageVariant[] vars = mt.getVariants(); + for (ImageVariant var : vars) { + processEnumMapTiles(world, mt, var, null, cbBase, cbEnd); + } + } + } + private void processEnumMapTiles(DynmapWorld world, MapType map, ImageVariant var, MapStorageTileEnumCB cb, MapStorageBaseTileEnumCB cbBase, MapStorageTileSearchEndCB cbEnd) { Connection c = null; boolean err = false; Integer mapkey = getMapKey(world, map, var); - if (mapkey == null) return; + if (mapkey == null) { + if(cbEnd != null) + cbEnd.searchEnded(); + return; + } try { c = getConnection(); // Query tiles for given mapkey @@ -636,9 +659,15 @@ public class MariaDBMapStorage extends MapStorage { ResultSet rs = stmt.executeQuery("SELECT x,y,zoom,Format FROM " + tableTiles + " WHERE MapID=" + mapkey + ";"); while (rs.next()) { StorageTile st = new StorageTile(world, map, rs.getInt("x"), rs.getInt("y"), rs.getInt("zoom"), var); - cb.tileFound(st, MapType.ImageEncoding.fromOrd(rs.getInt("Format"))); + final MapType.ImageEncoding encoding = MapType.ImageEncoding.fromOrd(rs.getInt("Format")); + if(cb != null) + cb.tileFound(st, encoding); + if(cbBase != null && st.zoom == 0) + cbBase.tileFound(st, encoding); st.cleanup(); } + if(cbEnd != null) + cbEnd.searchEnded(); rs.close(); stmt.close(); } catch (SQLException x) { diff --git a/DynmapCore/src/main/java/org/dynmap/storage/mysql/MySQLMapStorage.java b/DynmapCore/src/main/java/org/dynmap/storage/mysql/MySQLMapStorage.java index 3ec02063..0800695e 100644 --- a/DynmapCore/src/main/java/org/dynmap/storage/mysql/MySQLMapStorage.java +++ b/DynmapCore/src/main/java/org/dynmap/storage/mysql/MySQLMapStorage.java @@ -25,6 +25,8 @@ import org.dynmap.PlayerFaces.FaceType; import org.dynmap.storage.MapStorage; import org.dynmap.storage.MapStorageTile; import org.dynmap.storage.MapStorageTileEnumCB; +import org.dynmap.storage.MapStorageBaseTileEnumCB; +import org.dynmap.storage.MapStorageTileSearchEndCB; import org.dynmap.utils.BufferInputStream; import org.dynmap.utils.BufferOutputStream; @@ -610,7 +612,7 @@ public class MySQLMapStorage extends MapStorage { @Override public void enumMapTiles(DynmapWorld world, MapType map, - MapStorageTileEnumCB cb) { + MapStorageTileEnumCB cb) { List mtlist; if (map != null) { @@ -622,15 +624,36 @@ public class MySQLMapStorage extends MapStorage { for (MapType mt : mtlist) { ImageVariant[] vars = mt.getVariants(); for (ImageVariant var : vars) { - processEnumMapTiles(world, mt, var, cb); + processEnumMapTiles(world, mt, var, cb, null, null); } } } - private void processEnumMapTiles(DynmapWorld world, MapType map, ImageVariant var, MapStorageTileEnumCB cb) { + @Override + public void enumMapBaseTiles(DynmapWorld world, MapType map, MapStorageBaseTileEnumCB cbBase, MapStorageTileSearchEndCB cbEnd) { + List mtlist; + + if (map != null) { + mtlist = Collections.singletonList(map); + } + else { // Else, add all directories under world directory (for maps) + mtlist = new ArrayList(world.maps); + } + for (MapType mt : mtlist) { + ImageVariant[] vars = mt.getVariants(); + for (ImageVariant var : vars) { + processEnumMapTiles(world, mt, var, null, cbBase, cbEnd); + } + } + } + private void processEnumMapTiles(DynmapWorld world, MapType map, ImageVariant var, MapStorageTileEnumCB cb, MapStorageBaseTileEnumCB cbBase, MapStorageTileSearchEndCB cbEnd) { Connection c = null; boolean err = false; Integer mapkey = getMapKey(world, map, var); - if (mapkey == null) return; + if (mapkey == null) { + if(cbEnd != null) + cbEnd.searchEnded(); + return; + } try { c = getConnection(); // Query tiles for given mapkey @@ -638,9 +661,15 @@ public class MySQLMapStorage extends MapStorage { ResultSet rs = stmt.executeQuery("SELECT x,y,zoom,Format FROM " + tableTiles + " WHERE MapID=" + mapkey + ";"); while (rs.next()) { StorageTile st = new StorageTile(world, map, rs.getInt("x"), rs.getInt("y"), rs.getInt("zoom"), var); - cb.tileFound(st, MapType.ImageEncoding.fromOrd(rs.getInt("Format"))); + final MapType.ImageEncoding encoding = MapType.ImageEncoding.fromOrd(rs.getInt("Format")); + if(cb != null) + cb.tileFound(st, encoding); + if(cbBase != null && st.zoom == 0) + cbBase.tileFound(st, encoding); st.cleanup(); } + if(cbEnd != null) + cbEnd.searchEnded(); rs.close(); stmt.close(); } catch (SQLException x) { diff --git a/DynmapCore/src/main/java/org/dynmap/storage/sqllte/SQLiteMapStorage.java b/DynmapCore/src/main/java/org/dynmap/storage/sqllte/SQLiteMapStorage.java index 3b613164..7e99cc2b 100644 --- a/DynmapCore/src/main/java/org/dynmap/storage/sqllte/SQLiteMapStorage.java +++ b/DynmapCore/src/main/java/org/dynmap/storage/sqllte/SQLiteMapStorage.java @@ -23,6 +23,8 @@ import org.dynmap.PlayerFaces.FaceType; import org.dynmap.storage.MapStorage; import org.dynmap.storage.MapStorageTile; import org.dynmap.storage.MapStorageTileEnumCB; +import org.dynmap.storage.MapStorageBaseTileEnumCB; +import org.dynmap.storage.MapStorageTileSearchEndCB; import org.dynmap.utils.BufferInputStream; import org.dynmap.utils.BufferOutputStream; @@ -541,7 +543,7 @@ public class SQLiteMapStorage extends MapStorage { @Override public void enumMapTiles(DynmapWorld world, MapType map, - MapStorageTileEnumCB cb) { + MapStorageTileEnumCB cb) { List mtlist; if (map != null) { @@ -553,15 +555,38 @@ public class SQLiteMapStorage extends MapStorage { for (MapType mt : mtlist) { ImageVariant[] vars = mt.getVariants(); for (ImageVariant var : vars) { - processEnumMapTiles(world, mt, var, cb); + processEnumMapTiles(world, mt, var, cb, null, null); } } } - private void processEnumMapTiles(DynmapWorld world, MapType map, ImageVariant var, MapStorageTileEnumCB cb) { + + @Override + public void enumMapBaseTiles(DynmapWorld world, MapType map, MapStorageBaseTileEnumCB cbBase, MapStorageTileSearchEndCB cbEnd) { + List mtlist; + + if (map != null) { + mtlist = Collections.singletonList(map); + } + else { // Else, add all directories under world directory (for maps) + mtlist = new ArrayList(world.maps); + } + for (MapType mt : mtlist) { + ImageVariant[] vars = mt.getVariants(); + for (ImageVariant var : vars) { + processEnumMapTiles(world, mt, var, null, cbBase, cbEnd); + } + } + } + + private void processEnumMapTiles(DynmapWorld world, MapType map, ImageVariant var, MapStorageTileEnumCB cb, MapStorageBaseTileEnumCB cbBase, MapStorageTileSearchEndCB cbEnd) { Connection c = null; boolean err = false; Integer mapkey = getMapKey(world, map, var); - if (mapkey == null) return; + if (mapkey == null) { + if(cbEnd != null) + cbEnd.searchEnded(); + return; + } try { c = getConnection(); // Query tiles for given mapkey @@ -570,9 +595,15 @@ public class SQLiteMapStorage extends MapStorage { ResultSet rs = doExecuteQuery(stmt, "SELECT x,y,zoom,Format FROM Tiles WHERE MapID=" + mapkey + ";"); while (rs.next()) { StorageTile st = new StorageTile(world, map, rs.getInt("x"), rs.getInt("y"), rs.getInt("zoom"), var); - cb.tileFound(st, MapType.ImageEncoding.fromOrd(rs.getInt("Format"))); + final MapType.ImageEncoding encoding = MapType.ImageEncoding.fromOrd(rs.getInt("Format")); + if(cb != null) + cb.tileFound(st, encoding); + if(cbBase != null && st.zoom == 0) + cbBase.tileFound(st, encoding); st.cleanup(); } + if(cbEnd != null) + cbEnd.searchEnded(); rs.close(); stmt.close(); } catch (SQLException x) {