Add option to run incremental renders on sync thread, using same chunk

logic as timesliced full render, in order to handle cases where
assumption that all chunks needed for incremental render are already
resident (which can be false if player telepots, move quickly,
multiple players are active, etc).
This commit is contained in:
Mike Primm 2011-05-10 11:09:25 +02:00 committed by FrozenCow
parent 88db6445f9
commit 0a1de1b18e
3 changed files with 117 additions and 84 deletions

View File

@ -6,6 +6,9 @@ display-whitelist: false
# How often a tile gets rendered (in seconds). # How often a tile gets rendered (in seconds).
renderinterval: 1 renderinterval: 1
# Do render on main thread - may generate more server load, but safer and fixes broken tiles
renderonsync: true
render-triggers: render-triggers:
# - playermove # - playermove
# - playerjoin # - playerjoin

View File

@ -33,60 +33,42 @@ public class MapManager {
private DynmapPlugin plug_in; private DynmapPlugin plug_in;
private boolean do_timesliced_render = false; private boolean do_timesliced_render = false;
private double timeslice_interval = 0.0; private double timeslice_interval = 0.0;
private boolean do_sync_render = false; /* Do incremental renders on sync thread too */
/* Which timesliced renders are active */ /* Which timesliced renders are active */
private HashMap<String, FullWorldRenderState> active_renders = new HashMap<String, FullWorldRenderState>(); private HashMap<String, FullWorldRenderState> active_renders = new HashMap<String, FullWorldRenderState>();
/* lock for our data structures */ /* lock for our data structures */
public static final Object lock = new Object(); public static final Object lock = new Object();
public MapManager(DynmapPlugin plugin, ConfigurationNode configuration) {
this.tileQueue = new AsynchronousQueue<MapTile>(new Handler<MapTile>() {
@Override
public void handle(MapTile t) {
render(t);
}
}, (int) (configuration.getDouble("renderinterval", 0.5) * 1000));
for(Object worldConfigurationObj : (List<?>)configuration.getProperty("worlds")) {
Map<?, ?> worldConfiguration = (Map<?, ?>)worldConfigurationObj;
String worldName = (String)worldConfiguration.get("name");
DynmapWorld world = new DynmapWorld();
if (worldConfiguration.get("maps") != null) {
for(MapType map : loadMapTypes((List<?>)worldConfiguration.get("maps"))) {
world.maps.add(map);
}
}
inactiveworlds.put(worldName, world);
World bukkitWorld = plugin.getServer().getWorld(worldName);
if (bukkitWorld != null)
activateWorld(bukkitWorld);
}
do_timesliced_render = configuration.getBoolean("timeslicerender", true);
timeslice_interval = configuration.getDouble("timesliceinterval", 0.5);
scheduler = plugin.getServer().getScheduler();
plug_in = plugin;
tileQueue.start();
}
private class FullWorldRenderState implements Runnable { private class FullWorldRenderState implements Runnable {
DynmapWorld world; /* Which world are we rendering */ DynmapWorld world; /* Which world are we rendering */
Location loc; /* Start location */ Location loc; /* Start location */
int map_index = -1; /* Which map are we on */ int map_index = -1; /* Which map are we on */
MapType map; MapType map;
HashSet<MapTile> found = new HashSet<MapTile>(); HashSet<MapTile> found = null;
HashSet<MapTile> rendered = new HashSet<MapTile>(); HashSet<MapTile> rendered = null;
LinkedList<MapTile> renderQueue = new LinkedList<MapTile>(); LinkedList<MapTile> renderQueue = null;
MapTile tile0 = null;
/* Full world, all maps render */
FullWorldRenderState(DynmapWorld dworld, Location l) { FullWorldRenderState(DynmapWorld dworld, Location l) {
world = dworld; world = dworld;
loc = l; loc = l;
found = new HashSet<MapTile>();
rendered = new HashSet<MapTile>();
renderQueue = new LinkedList<MapTile>();
}
/* Single tile render - used for incremental renders */
FullWorldRenderState(MapTile t) {
world = worlds.get(t.getWorld().getName());
tile0 = t;
} }
public void run() { public void run() {
MapTile tile; MapTile tile;
if(tile0 == null) { /* Not single tile render */
/* If render queue is empty, start next map */ /* If render queue is empty, start next map */
if(renderQueue.isEmpty()) { if(renderQueue.isEmpty()) {
found.clear(); found.clear();
@ -108,6 +90,10 @@ public class MapManager {
} }
} }
tile = renderQueue.pollFirst(); tile = renderQueue.pollFirst();
}
else { /* Else, single tile render */
tile = tile0;
}
DynmapChunk[] requiredChunks = tile.getMap().getRequiredChunks(tile); DynmapChunk[] requiredChunks = tile.getMap().getRequiredChunks(tile);
LinkedList<DynmapChunk> loadedChunks = new LinkedList<DynmapChunk>(); LinkedList<DynmapChunk> loadedChunks = new LinkedList<DynmapChunk>();
@ -119,7 +105,10 @@ public class MapManager {
if ((!wasLoaded) && didload) if ((!wasLoaded) && didload)
loadedChunks.add(chunk); loadedChunks.add(chunk);
} }
if(tile0 != null) { /* Single tile? */
render(tile); /* Just render */
}
else {
if (render(tile)) { if (render(tile)) {
found.remove(tile); found.remove(tile);
rendered.add(tile); rendered.add(tile);
@ -131,6 +120,7 @@ public class MapManager {
} }
} }
found.remove(tile); found.remove(tile);
}
/* And unload what we loaded */ /* And unload what we loaded */
while (!loadedChunks.isEmpty()) { while (!loadedChunks.isEmpty()) {
DynmapChunk c = loadedChunks.pollFirst(); DynmapChunk c = loadedChunks.pollFirst();
@ -148,10 +138,50 @@ public class MapManager {
* by the MC base server is 21x21 (or about a 160 block radius) */ * by the MC base server is 21x21 (or about a 160 block radius) */
w.unloadChunk(c.x, c.z, false, false); w.unloadChunk(c.x, c.z, false, false);
} }
if(tile0 == null) { /* fullrender */
/* Schedule the next tile to be worked */ /* Schedule the next tile to be worked */
scheduler.scheduleSyncDelayedTask(plug_in, this, (int)(timeslice_interval*20)); scheduler.scheduleSyncDelayedTask(plug_in, this, (int)(timeslice_interval*20));
} }
} }
}
public MapManager(DynmapPlugin plugin, ConfigurationNode configuration) {
this.tileQueue = new AsynchronousQueue<MapTile>(new Handler<MapTile>() {
@Override
public void handle(MapTile t) {
if(do_sync_render)
scheduler.scheduleSyncDelayedTask(plug_in,
new FullWorldRenderState(t), (int)(timeslice_interval*20));
else
render(t);
}
}, (int) (configuration.getDouble("renderinterval", 0.5) * 1000));
for(Object worldConfigurationObj : (List<?>)configuration.getProperty("worlds")) {
Map<?, ?> worldConfiguration = (Map<?, ?>)worldConfigurationObj;
String worldName = (String)worldConfiguration.get("name");
DynmapWorld world = new DynmapWorld();
if (worldConfiguration.get("maps") != null) {
for(MapType map : loadMapTypes((List<?>)worldConfiguration.get("maps"))) {
world.maps.add(map);
}
}
inactiveworlds.put(worldName, world);
World bukkitWorld = plugin.getServer().getWorld(worldName);
if (bukkitWorld != null)
activateWorld(bukkitWorld);
}
do_timesliced_render = configuration.getBoolean("timeslicerender", true);
timeslice_interval = configuration.getDouble("timesliceinterval", 0.5);
do_sync_render = configuration.getBoolean("renderonsync", true);
scheduler = plugin.getServer().getScheduler();
plug_in = plugin;
tileQueue.start();
}
void renderFullWorld(Location l) { void renderFullWorld(Location l) {