Add support for saving/restoring of pending tile updates on server stops/start

This commit is contained in:
Mike Primm 2011-09-22 12:18:23 +08:00 committed by mikeprimm
parent 0627e2675f
commit 0704413829
9 changed files with 173 additions and 0 deletions

View File

@ -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<T> {
return set.size();
}
public List<T> popAll() {
List<T> s;
synchronized(lock) {
s = new ArrayList<T>(queue);
queue.clear();
set.clear();
}
return s;
}
public void start() {
synchronized (lock) {
thread = new Thread(new Runnable() {

View File

@ -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<MapTile>(
new Handler<MapTile>() {
@ -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<ConfigurationNode> 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<MapTile> 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<ConfigurationNode> savedtiles = new ArrayList<ConfigurationNode>();
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<World, File> worldTileDirectories = new HashMap<World, File>();

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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) {

View File

@ -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);

View File

@ -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) {

View File

@ -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);

View File

@ -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