From 1548ae72d68ec95756388351737616eca25ad5c5 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 21 Sep 2011 23:18:23 -0500 Subject: [PATCH] Add support for saving/restoring of pending tile updates on server stops/start --- .../java/org/dynmap/AsynchronousQueue.java | 12 +++++ src/main/java/org/dynmap/MapManager.java | 53 +++++++++++++++++++ src/main/java/org/dynmap/MapTile.java | 27 ++++++++++ src/main/java/org/dynmap/MapType.java | 1 + src/main/java/org/dynmap/flat/FlatMap.java | 22 ++++++++ src/main/java/org/dynmap/hdmap/HDMapTile.java | 18 +++++++ .../java/org/dynmap/kzedmap/KzedMapTile.java | 31 +++++++++++ .../org/dynmap/kzedmap/KzedZoomedMapTile.java | 6 +++ src/main/resources/configuration.txt | 3 ++ 9 files changed, 173 insertions(+) diff --git a/src/main/java/org/dynmap/AsynchronousQueue.java b/src/main/java/org/dynmap/AsynchronousQueue.java index d4c54757..de6bb44c 100644 --- a/src/main/java/org/dynmap/AsynchronousQueue.java +++ b/src/main/java/org/dynmap/AsynchronousQueue.java @@ -1,7 +1,9 @@ package org.dynmap; +import java.util.ArrayList; import java.util.HashSet; import java.util.LinkedList; +import java.util.List; import java.util.NoSuchElementException; import java.util.Set; @@ -45,6 +47,16 @@ public class AsynchronousQueue { return set.size(); } + public List popAll() { + List s; + synchronized(lock) { + s = new ArrayList(queue); + queue.clear(); + set.clear(); + } + return s; + } + public void start() { synchronized (lock) { thread = new Thread(new Runnable() { diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index eb43f5d4..b817b9ae 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -45,6 +45,7 @@ public class MapManager { private int max_chunk_loads_per_tick = DEFAULT_CHUNKS_PER_TICK; private int parallelrendercnt = 0; private int progressinterval = 100; + private boolean saverestorepending = true; private int zoomout_period = DEFAULT_ZOOMOUT_PERIOD; /* Zoom-out tile processing period, in seconds */ /* Which fullrenders are active */ @@ -501,6 +502,7 @@ public class MapManager { parallelrendercnt = configuration.getInteger("parallelrendercnt", 0); progressinterval = configuration.getInteger("progressloginterval", 100); if(progressinterval < 100) progressinterval = 100; + saverestorepending = configuration.getBoolean("saverestorepending", true); this.tileQueue = new AsynchronousQueue( new Handler() { @@ -708,6 +710,54 @@ public class MapManager { } worldsLookup.put(w.getName(), dynmapWorld); plug_in.events.trigger("worldactivated", dynmapWorld); + /* Now, restore any pending renders for this world */ + if(saverestorepending) + loadPending(dynmapWorld); + } + + private void loadPending(DynmapWorld w) { + File f = new File(plug_in.getDataFolder(), w.world.getName() + ".pending"); + if(f.exists()) { + org.bukkit.util.config.Configuration saved = new org.bukkit.util.config.Configuration(f); + saved.load(); + ConfigurationNode cn = new ConfigurationNode(saved); + /* Get the saved tile definitions */ + List tiles = cn.getNodes("tiles"); + if(tiles != null) { + int cnt = 0; + for(ConfigurationNode tile : tiles) { + MapTile mt = MapTile.restoreTile(w, tile); /* Restore tile, if possible */ + if(mt != null) { + invalidateTile(mt); + cnt++; + } + } + if(cnt > 0) + Log.info("Loaded " + cnt + " pending tile renders for world '" + w.world.getName()); + } + f.delete(); /* And clean it up */ + } + } + + private void savePending() { + List mt = tileQueue.popAll(); + for(DynmapWorld w : worlds) { + File f = new File(plug_in.getDataFolder(), w.world.getName() + ".pending"); + org.bukkit.util.config.Configuration saved = new org.bukkit.util.config.Configuration(f); + ArrayList savedtiles = new ArrayList(); + for(MapTile tile : mt) { + if(tile.getDynmapWorld() != w) continue; + ConfigurationNode tilenode = tile.saveTile(); + if(tilenode != null) { + savedtiles.add(tilenode); + } + } + if(savedtiles.size() > 0) { /* Something to save? */ + saved.setProperty("tiles", savedtiles); + saved.save(); + Log.info("Saved " + savedtiles.size() + " pending tile renders in world '" + w.world.getName()); + } + } } public int touch(Location l) { @@ -773,6 +823,9 @@ public class MapManager { tileQueue.stop(); mapman = null; hdmapman = null; + + if(saverestorepending) + savePending(); } private HashMap worldTileDirectories = new HashMap(); diff --git a/src/main/java/org/dynmap/MapTile.java b/src/main/java/org/dynmap/MapTile.java index e88e890c..30f9df25 100644 --- a/src/main/java/org/dynmap/MapTile.java +++ b/src/main/java/org/dynmap/MapTile.java @@ -1,6 +1,8 @@ package org.dynmap; import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.util.List; import org.bukkit.World; @@ -54,4 +56,29 @@ public abstract class MapTile { public abstract int tileOrdinalX(); public abstract int tileOrdinalY(); + public ConfigurationNode saveTile() { + ConfigurationNode cn = new ConfigurationNode(); + cn.put("class", this.getClass().getName()); + cn.put("data", saveTileData()); + return cn; + } + + protected abstract String saveTileData(); + + public static MapTile restoreTile(DynmapWorld w, ConfigurationNode node) { + String cn = node.getString("class"); + String dat = node.getString("data"); + if((cn == null) || (dat == null)) return null; + try { + Class cls = Class.forName(cn); + Constructor con = cls.getConstructor(DynmapWorld.class, String.class); + return (MapTile)con.newInstance(w, dat); + } catch (ClassNotFoundException cnfx) { + } catch (NoSuchMethodException nsmx) { + } catch (InvocationTargetException itx) { + } catch (IllegalAccessException iax) { + } catch (InstantiationException ix) { + } + return null; + } } diff --git a/src/main/java/org/dynmap/MapType.java b/src/main/java/org/dynmap/MapType.java index 619065b2..adb649e3 100644 --- a/src/main/java/org/dynmap/MapType.java +++ b/src/main/java/org/dynmap/MapType.java @@ -80,4 +80,5 @@ public abstract class MapType { * 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 f5320b3b..ce39e3ce 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -495,6 +495,28 @@ public class FlatMap extends MapType { this.size = size; } + public FlatMapTile(DynmapWorld world, String parm) throws Exception { + super(world); + + String[] parms = parm.split(","); + if(parms.length < 4) throw new Exception("wrong parameter count"); + this.x = Integer.parseInt(parms[0]); + this.y = Integer.parseInt(parms[1]); + this.size = Integer.parseInt(parms[2]); + for(MapType t : world.maps) { + if(t.getName().equals(parms[3]) && (t instanceof FlatMap)) { + this.map = (FlatMap)t; + break; + } + } + if(this.map == null) throw new Exception("invalid map"); + } + + @Override + protected String saveTileData() { + return String.format("%d,%d,%d,%s", x, y, size, map.getName()); + } + @Override public String getFilename() { if(fname == null) { diff --git a/src/main/java/org/dynmap/hdmap/HDMapTile.java b/src/main/java/org/dynmap/hdmap/HDMapTile.java index bbb87a18..a03f536b 100644 --- a/src/main/java/org/dynmap/hdmap/HDMapTile.java +++ b/src/main/java/org/dynmap/hdmap/HDMapTile.java @@ -7,6 +7,8 @@ import org.dynmap.MapType; import java.util.List; import org.dynmap.MapTile; +import org.dynmap.kzedmap.KzedMap; +import org.dynmap.kzedmap.MapTileRenderer; import org.dynmap.utils.MapChunkCache; public class HDMapTile extends MapTile { @@ -20,6 +22,22 @@ public class HDMapTile extends MapTile { this.ty = ty; } + public HDMapTile(DynmapWorld world, String parm) throws Exception { + super(world); + + String[] parms = parm.split(","); + if(parms.length < 3) throw new Exception("wrong parameter count"); + this.tx = Integer.parseInt(parms[0]); + this.ty = Integer.parseInt(parms[1]); + this.perspective = MapManager.mapman.hdmapman.perspectives.get(parms[2]); + if(this.perspective == null) throw new Exception("invalid perspective"); + } + + @Override + protected String saveTileData() { + return String.format("%d,%d,%s", tx, ty, perspective.getName()); + } + @Override public String getFilename() { return getFilename("hdmap", MapType.ImageFormat.FORMAT_PNG); diff --git a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java index aabe4bad..8afabf29 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java @@ -3,11 +3,13 @@ package org.dynmap.kzedmap; import org.dynmap.DynmapChunk; import org.dynmap.DynmapWorld; import org.dynmap.MapManager; +import org.dynmap.MapType; import java.io.File; import java.util.List; import org.dynmap.MapTile; +import org.dynmap.flat.FlatMap; import org.dynmap.utils.MapChunkCache; public class KzedMapTile extends MapTile { @@ -28,6 +30,35 @@ public class KzedMapTile extends MapTile { this.py = py; } + public KzedMapTile(DynmapWorld world, String parm) throws Exception { + super(world); + + String[] parms = parm.split(","); + if(parms.length < 4) throw new Exception("wrong parameter count"); + this.px = Integer.parseInt(parms[0]); + this.py = Integer.parseInt(parms[1]); + + for(MapType t : world.maps) { + if(t.getName().equals(parms[2]) && (t instanceof KzedMap)) { + this.map = (KzedMap)t; + break; + } + } + if(this.map == null) throw new Exception("invalid map"); + for(MapTileRenderer r : map.renderers) { + if(r.getName().equals(parms[3])) { + this.renderer = r; + break; + } + } + if(this.renderer == null) throw new Exception("invalid renderer"); + } + + @Override + protected String saveTileData() { + return String.format("%d,%d,%s,%s", px, py, map.getName(), renderer.getName()); + } + @Override public String getFilename() { if(fname == null) { diff --git a/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java index d1c95111..9e92f663 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java +++ b/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java @@ -5,6 +5,7 @@ import java.util.List; import org.dynmap.DynmapChunk; import org.dynmap.DynmapWorld; import org.dynmap.MapTile; +import org.dynmap.MapType; import org.dynmap.utils.MapChunkCache; public class KzedZoomedMapTile extends MapTile { @@ -41,6 +42,11 @@ public class KzedZoomedMapTile extends MapTile { super(world); this.originalTile = original; } + + @Override + protected String saveTileData() { + return originalTile.saveTileData(); + } public int getTileX() { return ztilex(originalTile.px + KzedMap.tileWidth); diff --git a/src/main/resources/configuration.txt b/src/main/resources/configuration.txt index 8e9a88ec..814bb27d 100644 --- a/src/main/resources/configuration.txt +++ b/src/main/resources/configuration.txt @@ -228,6 +228,9 @@ renderacceleratethreshold: 60 # How often to render tiles when backlog is above renderacceleratethreshold renderaccelerateinterval: 0.2 +# Save and restore pending tile renders - prevents their loss on server shutdown or /reload +saverestorepending: true + # Zoom-out tile update period - how often to scan for and process tile updates into zoom-out tiles (in seconds) zoomoutperiod: 30