Change autogenerate-to-visibilitylimits to have options for map-only (temporary generated chunks) versus permanent (world growing)

Fix exception catching code in thread pool
This commit is contained in:
Mike Primm 2011-06-25 16:01:06 -05:00
parent 6dac7f0689
commit 39281188bc
7 changed files with 90 additions and 20 deletions

View File

@ -315,9 +315,9 @@ worlds:
# # Use hidestyle to control how hidden-but-existing chunks are to be rendered (air=empty air (same as ungenerated), stone=a flat stone plain, ocean=a flat ocean) # # Use hidestyle to control how hidden-but-existing chunks are to be rendered (air=empty air (same as ungenerated), stone=a flat stone plain, ocean=a flat ocean)
# hidestyle: stone # hidestyle: stone
# # Use 'autogenerate-to-visibilitylimits: true' to choose to force the generation of ungenerated chunks while rendering maps on this world, for any chunks within the defined # # Use 'autogenerate-to-visibilitylimits: true' to choose to force the generation of ungenerated chunks while rendering maps on this world, for any chunks within the defined
# # visibilitylimits (limits must be set). This will result in initializing all game world areas within the visible ranges that have not yet been initialized. # # visibilitylimits (limits must be set). The three options here are: none (default - no autogenerate), map-only (temporarily generate chunks for map, but don't save them (no world change),
# # Note: BE SURE YOU WANT TO DO THIS - there isn't a good way to "ungenerate" terrain chunks once generated (although tools like WorldEdit can regenerate them) # # permanent (generate and save chunks - this permanently adds the chunks to the world, as if a player had visited them - BE SURE THIS IS WHAT YOU WANT)
# autogenerate-to-visibilitylimits: false # autogenerate-to-visibilitylimits: map-only
# Use 'template: mycustomtemplate' to use the properties specified in the template 'mycustomtemplate' to this world. Default it is set to the environment-name (normal or nether). # Use 'template: mycustomtemplate' to use the properties specified in the template 'mycustomtemplate' to this world. Default it is set to the environment-name (normal or nether).
# template: mycustomtemplate # template: mycustomtemplate
# Rest of comes from template - uncomment to tailor for world specifically # Rest of comes from template - uncomment to tailor for world specifically

View File

@ -53,6 +53,7 @@ public class DynmapPlugin extends JavaPlugin {
/* Flag to let code know that we're doing reload - make sure we don't double-register event handlers */ /* Flag to let code know that we're doing reload - make sure we don't double-register event handlers */
public boolean is_reload = false; public boolean is_reload = false;
private boolean generate_only = false; private boolean generate_only = false;
private static boolean ignore_chunk_loads = false; /* Flat to keep us from processing our own chunk loads */
public static File dataDirectory; public static File dataDirectory;
public static File tilesDirectory; public static File tilesDirectory;
@ -248,6 +249,8 @@ public class DynmapPlugin extends JavaPlugin {
WorldListener renderTrigger = new WorldListener() { WorldListener renderTrigger = new WorldListener() {
@Override @Override
public void onChunkLoad(ChunkLoadEvent event) { public void onChunkLoad(ChunkLoadEvent event) {
if(ignore_chunk_loads)
return;
if(generate_only) { if(generate_only) {
if(!isNewChunk(event)) if(!isNewChunk(event))
return; return;
@ -494,4 +497,8 @@ public class DynmapPlugin extends JavaPlugin {
public String getWebPath() { public String getWebPath() {
return configuration.getString("webpath", "web"); return configuration.getString("webpath", "web");
} }
public static void setIgnoreChunkLoads(boolean ignore) {
ignore_chunk_loads = ignore;
}
} }

View File

@ -21,13 +21,18 @@ import java.util.HashSet;
import javax.imageio.ImageIO; import javax.imageio.ImageIO;
public class DynmapWorld { public class DynmapWorld {
public enum AutoGenerateOption {
NONE,
FORMAPONLY,
PERMANENT
}
public World world; public World world;
public List<MapType> maps = new ArrayList<MapType>(); public List<MapType> maps = new ArrayList<MapType>();
public UpdateQueue updates = new UpdateQueue(); public UpdateQueue updates = new UpdateQueue();
public ConfigurationNode configuration; public ConfigurationNode configuration;
public List<Location> seedloc; public List<Location> seedloc;
public List<MapChunkCache.VisibilityLimit> visibility_limits; public List<MapChunkCache.VisibilityLimit> visibility_limits;
public boolean do_autogenerate; public AutoGenerateOption do_autogenerate;
public MapChunkCache.HiddenChunkStyle hiddenchunkstyle; public MapChunkCache.HiddenChunkStyle hiddenchunkstyle;
public int servertime; public int servertime;
public boolean sendposition; public boolean sendposition;

View File

@ -10,6 +10,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
@ -17,6 +18,7 @@ import org.bukkit.Location;
import org.bukkit.World; import org.bukkit.World;
import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.scheduler.BukkitScheduler;
import org.bukkit.command.CommandSender; import org.bukkit.command.CommandSender;
import org.dynmap.DynmapWorld.AutoGenerateOption;
import org.dynmap.debug.Debug; import org.dynmap.debug.Debug;
import org.dynmap.utils.LegacyMapChunkCache; import org.dynmap.utils.LegacyMapChunkCache;
import org.dynmap.utils.MapChunkCache; import org.dynmap.utils.MapChunkCache;
@ -94,6 +96,33 @@ public class MapManager {
x.printStackTrace(); x.printStackTrace();
} }
} }
@Override
public void execute(final Runnable r) {
final Runnable rr = r;
super.execute(new Runnable() {
public void run() {
try {
r.run();
} catch (Exception x) {
Log.severe("Exception during render job: " + r);
x.printStackTrace();
}
}
});
}
@Override
public ScheduledFuture<?> schedule(final Runnable command, long delay, TimeUnit unit) {
return super.schedule(new Runnable() {
public void run() {
try {
command.run();
} catch (Exception x) {
Log.severe("Exception during render job: " + command);
x.printStackTrace();
}
}
}, delay, unit);
}
} }
/* This always runs on render pool threads - no bukkit calls from here */ /* This always runs on render pool threads - no bukkit calls from here */
private class FullWorldRenderState implements Runnable { private class FullWorldRenderState implements Runnable {
@ -391,10 +420,19 @@ public class MapManager {
dynmapWorld.seedloc.add(new Location(w, (lim.x0+lim.x1)/2, 64, (lim.z0+lim.z1)/2)); dynmapWorld.seedloc.add(new Location(w, (lim.x0+lim.x1)/2, 64, (lim.z0+lim.z1)/2));
} }
} }
dynmapWorld.do_autogenerate = worldConfiguration.getBoolean("autogenerate-to-visibilitylimits", false); String autogen = worldConfiguration.getString("autogenerate-to-visibilitylimits", "none");
if(dynmapWorld.do_autogenerate && (dynmapWorld.visibility_limits == null)) { if(autogen.equals("permanent")) {
Log.info("Warning: Automatic world generation to visible limits option requires that visiblelimits be set - option disabled"); dynmapWorld.do_autogenerate = AutoGenerateOption.PERMANENT;
dynmapWorld.do_autogenerate = false; }
else if(autogen.equals("map-only")) {
dynmapWorld.do_autogenerate = AutoGenerateOption.FORMAPONLY;
}
else {
dynmapWorld.do_autogenerate = AutoGenerateOption.NONE;
}
if((dynmapWorld.do_autogenerate != AutoGenerateOption.NONE) && (dynmapWorld.visibility_limits == null)) {
Log.info("Warning: Automatic world generation to visible limits option requires that visibitylimits be set - option disabled");
dynmapWorld.do_autogenerate = AutoGenerateOption.NONE;
} }
String hiddenchunkstyle = worldConfiguration.getString("hidestyle", "stone"); String hiddenchunkstyle = worldConfiguration.getString("hidestyle", "stone");
if(hiddenchunkstyle.equals("air")) if(hiddenchunkstyle.equals("air"))

View File

@ -11,6 +11,8 @@ import org.bukkit.Chunk;
import org.bukkit.block.Biome; import org.bukkit.block.Biome;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.dynmap.DynmapChunk; import org.dynmap.DynmapChunk;
import org.dynmap.DynmapPlugin;
import org.dynmap.DynmapWorld;
import org.dynmap.Log; import org.dynmap.Log;
import java.util.List; import java.util.List;
@ -28,7 +30,9 @@ public class LegacyMapChunkCache implements MapChunkCache {
private World w; private World w;
private List<DynmapChunk> chunks; private List<DynmapChunk> chunks;
private ListIterator<DynmapChunk> iterator; private ListIterator<DynmapChunk> iterator;
private boolean do_generate; private DynmapWorld.AutoGenerateOption generateopt;
private boolean do_generate = false;
private boolean do_save = false;
private boolean isempty = true; private boolean isempty = true;
private int x_min, x_max, z_min, z_max; private int x_min, x_max, z_min, z_max;
@ -263,6 +267,7 @@ public class LegacyMapChunkCache implements MapChunkCache {
if(iterator == null) if(iterator == null)
iterator = chunks.listIterator(); iterator = chunks.listIterator();
DynmapPlugin.setIgnoreChunkLoads(true);
// Load the required chunks. // Load the required chunks.
while((cnt < max_to_load) && iterator.hasNext()) { while((cnt < max_to_load) && iterator.hasNext()) {
DynmapChunk chunk = iterator.next(); DynmapChunk chunk = iterator.next();
@ -278,7 +283,7 @@ public class LegacyMapChunkCache implements MapChunkCache {
} }
} }
boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z); boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z);
boolean didload = w.loadChunk(chunk.x, chunk.z, do_generate && vis); boolean didload = w.loadChunk(chunk.x, chunk.z, false);
boolean didgenerate = false; boolean didgenerate = false;
/* If we didn't load, and we're supposed to generate, do it */ /* If we didn't load, and we're supposed to generate, do it */
if((!didload) && do_generate && vis) if((!didload) && do_generate && vis)
@ -325,7 +330,7 @@ public class LegacyMapChunkCache implements MapChunkCache {
* while the actual in-use chunk area for a player where the chunks are managed * while the actual in-use chunk area for a player where the chunks are managed
* 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).
* Also, if we did generate it, need to save it */ * Also, if we did generate it, need to save it */
w.unloadChunk(chunk.x, chunk.z, didgenerate, false); w.unloadChunk(chunk.x, chunk.z, didgenerate && do_save, false);
/* And pop preserved chunk - this is a bad leak in Bukkit for map traversals like us */ /* And pop preserved chunk - this is a bad leak in Bukkit for map traversals like us */
try { try {
if(poppreservedchunk != null) if(poppreservedchunk != null)
@ -337,6 +342,8 @@ public class LegacyMapChunkCache implements MapChunkCache {
} }
cnt++; cnt++;
} }
DynmapPlugin.setIgnoreChunkLoads(false);
/* If done, finish table */ /* If done, finish table */
if(iterator.hasNext() == false) { if(iterator.hasNext() == false) {
isempty = true; isempty = true;
@ -456,12 +463,14 @@ public class LegacyMapChunkCache implements MapChunkCache {
/** /**
* Set autogenerate - must be done after at least one visible range has been set * Set autogenerate - must be done after at least one visible range has been set
*/ */
public void setAutoGenerateVisbileRanges(boolean do_generate) { public void setAutoGenerateVisbileRanges(DynmapWorld.AutoGenerateOption generateopt) {
if(do_generate && ((visible_limits == null) || (visible_limits.size() == 0))) { if((generateopt != DynmapWorld.AutoGenerateOption.NONE) && ((visible_limits == null) || (visible_limits.size() == 0))) {
Log.severe("Cannot setAutoGenerateVisibleRanges() without visible ranges defined"); Log.severe("Cannot setAutoGenerateVisibleRanges() without visible ranges defined");
return; return;
} }
this.do_generate = do_generate; this.generateopt = generateopt;
this.do_generate = (generateopt != DynmapWorld.AutoGenerateOption.NONE);
this.do_save = (generateopt == DynmapWorld.AutoGenerateOption.PERMANENT);
} }
@Override @Override
public boolean setChunkDataTypes(boolean blockdata, boolean biome, boolean highestblocky, boolean rawbiome) { public boolean setChunkDataTypes(boolean blockdata, boolean biome, boolean highestblocky, boolean rawbiome) {

View File

@ -4,6 +4,7 @@ import org.bukkit.block.Biome;
import java.util.List; import java.util.List;
import org.dynmap.DynmapChunk; import org.dynmap.DynmapChunk;
import org.dynmap.DynmapWorld;
public interface MapChunkCache { public interface MapChunkCache {
public enum HiddenChunkStyle { public enum HiddenChunkStyle {
@ -94,5 +95,5 @@ public interface MapChunkCache {
/** /**
* Set autogenerate - must be done after at least one visible range has been set * Set autogenerate - must be done after at least one visible range has been set
*/ */
public void setAutoGenerateVisbileRanges(boolean do_generate); public void setAutoGenerateVisbileRanges(DynmapWorld.AutoGenerateOption do_generate);
} }

View File

@ -12,7 +12,10 @@ import org.bukkit.block.Biome;
import org.bukkit.entity.Entity; import org.bukkit.entity.Entity;
import org.bukkit.ChunkSnapshot; import org.bukkit.ChunkSnapshot;
import org.dynmap.DynmapChunk; import org.dynmap.DynmapChunk;
import org.dynmap.DynmapPlugin;
import org.dynmap.DynmapWorld;
import org.dynmap.Log; import org.dynmap.Log;
import org.dynmap.MapManager;
/** /**
* Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread * Container for managing chunks - dependent upon using chunk snapshots, since rendering is off server thread
@ -31,7 +34,9 @@ public class NewMapChunkCache implements MapChunkCache {
private boolean biome, biomeraw, highesty, blockdata; private boolean biome, biomeraw, highesty, blockdata;
private HiddenChunkStyle hidestyle = HiddenChunkStyle.FILL_AIR; private HiddenChunkStyle hidestyle = HiddenChunkStyle.FILL_AIR;
private List<VisibilityLimit> visible_limits = null; private List<VisibilityLimit> visible_limits = null;
private DynmapWorld.AutoGenerateOption generateopt;
private boolean do_generate = false; private boolean do_generate = false;
private boolean do_save = false;
private boolean isempty = true; private boolean isempty = true;
private ChunkSnapshot[] snaparray; /* Index = (x-x_min) + ((z-z_min)*x_dim) */ private ChunkSnapshot[] snaparray; /* Index = (x-x_min) + ((z-z_min)*x_dim) */
@ -284,6 +289,7 @@ public class NewMapChunkCache implements MapChunkCache {
if(iterator == null) if(iterator == null)
iterator = chunks.listIterator(); iterator = chunks.listIterator();
DynmapPlugin.setIgnoreChunkLoads(true);
// Load the required chunks. // Load the required chunks.
while((cnt < max_to_load) && iterator.hasNext()) { while((cnt < max_to_load) && iterator.hasNext()) {
DynmapChunk chunk = iterator.next(); DynmapChunk chunk = iterator.next();
@ -298,7 +304,7 @@ public class NewMapChunkCache implements MapChunkCache {
} }
} }
boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z); boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z);
boolean didload = w.loadChunk(chunk.x, chunk.z, do_generate && vis); boolean didload = w.loadChunk(chunk.x, chunk.z, false);
boolean didgenerate = false; boolean didgenerate = false;
/* If we didn't load, and we're supposed to generate, do it */ /* If we didn't load, and we're supposed to generate, do it */
if((!didload) && do_generate && vis) if((!didload) && do_generate && vis)
@ -348,7 +354,7 @@ public class NewMapChunkCache implements MapChunkCache {
* while the actual in-use chunk area for a player where the chunks are managed * while the actual in-use chunk area for a player where the chunks are managed
* 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).
* Also, if we did generate it, need to save it */ * Also, if we did generate it, need to save it */
w.unloadChunk(chunk.x, chunk.z, didgenerate, false); w.unloadChunk(chunk.x, chunk.z, didgenerate && do_save, false);
/* And pop preserved chunk - this is a bad leak in Bukkit for map traversals like us */ /* And pop preserved chunk - this is a bad leak in Bukkit for map traversals like us */
try { try {
if(poppreservedchunk != null) if(poppreservedchunk != null)
@ -359,6 +365,8 @@ public class NewMapChunkCache implements MapChunkCache {
} }
cnt++; cnt++;
} }
DynmapPlugin.setIgnoreChunkLoads(false);
if(iterator.hasNext() == false) { /* If we're done */ if(iterator.hasNext() == false) { /* If we're done */
isempty = true; isempty = true;
/* Fill missing chunks with empty dummy chunk */ /* Fill missing chunks with empty dummy chunk */
@ -457,12 +465,14 @@ public class NewMapChunkCache implements MapChunkCache {
/** /**
* Set autogenerate - must be done after at least one visible range has been set * Set autogenerate - must be done after at least one visible range has been set
*/ */
public void setAutoGenerateVisbileRanges(boolean do_generate) { public void setAutoGenerateVisbileRanges(DynmapWorld.AutoGenerateOption generateopt) {
if(do_generate && ((visible_limits == null) || (visible_limits.size() == 0))) { if((generateopt != DynmapWorld.AutoGenerateOption.NONE) && ((visible_limits == null) || (visible_limits.size() == 0))) {
Log.severe("Cannot setAutoGenerateVisibleRanges() without visible ranges defined"); Log.severe("Cannot setAutoGenerateVisibleRanges() without visible ranges defined");
return; return;
} }
this.do_generate = do_generate; this.generateopt = generateopt;
this.do_generate = (generateopt != DynmapWorld.AutoGenerateOption.NONE);
this.do_save = (generateopt == DynmapWorld.AutoGenerateOption.PERMANENT);
} }
/** /**
* Add visible area limit - can be called more than once * Add visible area limit - can be called more than once