diff --git a/configuration.txt b/configuration.txt index 8f424fbf..806a62e6 100644 --- a/configuration.txt +++ b/configuration.txt @@ -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) # 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 - # # visibilitylimits (limits must be set). This will result in initializing all game world areas within the visible ranges that have not yet been initialized. - # # 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) - # autogenerate-to-visibilitylimits: false + # # 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), + # # 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: 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). # template: mycustomtemplate # Rest of comes from template - uncomment to tailor for world specifically diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index e349e7f3..e7687ae0 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -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 */ public boolean is_reload = 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 tilesDirectory; @@ -248,6 +249,8 @@ public class DynmapPlugin extends JavaPlugin { WorldListener renderTrigger = new WorldListener() { @Override public void onChunkLoad(ChunkLoadEvent event) { + if(ignore_chunk_loads) + return; if(generate_only) { if(!isNewChunk(event)) return; @@ -494,4 +497,8 @@ public class DynmapPlugin extends JavaPlugin { public String getWebPath() { return configuration.getString("webpath", "web"); } + + public static void setIgnoreChunkLoads(boolean ignore) { + ignore_chunk_loads = ignore; + } } diff --git a/src/main/java/org/dynmap/DynmapWorld.java b/src/main/java/org/dynmap/DynmapWorld.java index d9f449c3..7be330d3 100644 --- a/src/main/java/org/dynmap/DynmapWorld.java +++ b/src/main/java/org/dynmap/DynmapWorld.java @@ -21,13 +21,18 @@ import java.util.HashSet; import javax.imageio.ImageIO; public class DynmapWorld { + public enum AutoGenerateOption { + NONE, + FORMAPONLY, + PERMANENT + } public World world; public List maps = new ArrayList(); public UpdateQueue updates = new UpdateQueue(); public ConfigurationNode configuration; public List seedloc; public List visibility_limits; - public boolean do_autogenerate; + public AutoGenerateOption do_autogenerate; public MapChunkCache.HiddenChunkStyle hiddenchunkstyle; public int servertime; public boolean sendposition; diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index 659f7034..cfd0681e 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; import java.util.TreeSet; import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; @@ -17,6 +18,7 @@ import org.bukkit.Location; import org.bukkit.World; import org.bukkit.scheduler.BukkitScheduler; import org.bukkit.command.CommandSender; +import org.dynmap.DynmapWorld.AutoGenerateOption; import org.dynmap.debug.Debug; import org.dynmap.utils.LegacyMapChunkCache; import org.dynmap.utils.MapChunkCache; @@ -94,6 +96,33 @@ public class MapManager { 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 */ 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.do_autogenerate = worldConfiguration.getBoolean("autogenerate-to-visibilitylimits", false); - if(dynmapWorld.do_autogenerate && (dynmapWorld.visibility_limits == null)) { - Log.info("Warning: Automatic world generation to visible limits option requires that visiblelimits be set - option disabled"); - dynmapWorld.do_autogenerate = false; + String autogen = worldConfiguration.getString("autogenerate-to-visibilitylimits", "none"); + if(autogen.equals("permanent")) { + dynmapWorld.do_autogenerate = AutoGenerateOption.PERMANENT; + } + 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"); if(hiddenchunkstyle.equals("air")) diff --git a/src/main/java/org/dynmap/utils/LegacyMapChunkCache.java b/src/main/java/org/dynmap/utils/LegacyMapChunkCache.java index 653ceef9..c27c6012 100644 --- a/src/main/java/org/dynmap/utils/LegacyMapChunkCache.java +++ b/src/main/java/org/dynmap/utils/LegacyMapChunkCache.java @@ -11,6 +11,8 @@ import org.bukkit.Chunk; import org.bukkit.block.Biome; import org.bukkit.entity.Entity; import org.dynmap.DynmapChunk; +import org.dynmap.DynmapPlugin; +import org.dynmap.DynmapWorld; import org.dynmap.Log; import java.util.List; @@ -28,7 +30,9 @@ public class LegacyMapChunkCache implements MapChunkCache { private World w; private List chunks; private ListIterator iterator; - private boolean do_generate; + private DynmapWorld.AutoGenerateOption generateopt; + private boolean do_generate = false; + private boolean do_save = false; private boolean isempty = true; private int x_min, x_max, z_min, z_max; @@ -263,6 +267,7 @@ public class LegacyMapChunkCache implements MapChunkCache { if(iterator == null) iterator = chunks.listIterator(); + DynmapPlugin.setIgnoreChunkLoads(true); // Load the required chunks. while((cnt < max_to_load) && iterator.hasNext()) { DynmapChunk chunk = iterator.next(); @@ -278,7 +283,7 @@ public class LegacyMapChunkCache implements MapChunkCache { } } 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; /* If we didn't load, and we're supposed to generate, do it */ 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 * by the MC base server is 21x21 (or about a 160 block radius). * 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 */ try { if(poppreservedchunk != null) @@ -337,6 +342,8 @@ public class LegacyMapChunkCache implements MapChunkCache { } cnt++; } + DynmapPlugin.setIgnoreChunkLoads(false); + /* If done, finish table */ if(iterator.hasNext() == false) { 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 */ - public void setAutoGenerateVisbileRanges(boolean do_generate) { - if(do_generate && ((visible_limits == null) || (visible_limits.size() == 0))) { + public void setAutoGenerateVisbileRanges(DynmapWorld.AutoGenerateOption generateopt) { + if((generateopt != DynmapWorld.AutoGenerateOption.NONE) && ((visible_limits == null) || (visible_limits.size() == 0))) { Log.severe("Cannot setAutoGenerateVisibleRanges() without visible ranges defined"); return; } - this.do_generate = do_generate; + this.generateopt = generateopt; + this.do_generate = (generateopt != DynmapWorld.AutoGenerateOption.NONE); + this.do_save = (generateopt == DynmapWorld.AutoGenerateOption.PERMANENT); } @Override public boolean setChunkDataTypes(boolean blockdata, boolean biome, boolean highestblocky, boolean rawbiome) { diff --git a/src/main/java/org/dynmap/utils/MapChunkCache.java b/src/main/java/org/dynmap/utils/MapChunkCache.java index e917d12a..797566a6 100644 --- a/src/main/java/org/dynmap/utils/MapChunkCache.java +++ b/src/main/java/org/dynmap/utils/MapChunkCache.java @@ -4,6 +4,7 @@ import org.bukkit.block.Biome; import java.util.List; import org.dynmap.DynmapChunk; +import org.dynmap.DynmapWorld; public interface MapChunkCache { public enum HiddenChunkStyle { @@ -94,5 +95,5 @@ public interface MapChunkCache { /** * 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); } diff --git a/src/main/java/org/dynmap/utils/NewMapChunkCache.java b/src/main/java/org/dynmap/utils/NewMapChunkCache.java index c621e1dd..e2b9fb92 100644 --- a/src/main/java/org/dynmap/utils/NewMapChunkCache.java +++ b/src/main/java/org/dynmap/utils/NewMapChunkCache.java @@ -12,7 +12,10 @@ import org.bukkit.block.Biome; import org.bukkit.entity.Entity; import org.bukkit.ChunkSnapshot; import org.dynmap.DynmapChunk; +import org.dynmap.DynmapPlugin; +import org.dynmap.DynmapWorld; import org.dynmap.Log; +import org.dynmap.MapManager; /** * 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 HiddenChunkStyle hidestyle = HiddenChunkStyle.FILL_AIR; private List visible_limits = null; + private DynmapWorld.AutoGenerateOption generateopt; private boolean do_generate = false; + private boolean do_save = false; private boolean isempty = true; private ChunkSnapshot[] snaparray; /* Index = (x-x_min) + ((z-z_min)*x_dim) */ @@ -284,6 +289,7 @@ public class NewMapChunkCache implements MapChunkCache { if(iterator == null) iterator = chunks.listIterator(); + DynmapPlugin.setIgnoreChunkLoads(true); // Load the required chunks. while((cnt < max_to_load) && iterator.hasNext()) { DynmapChunk chunk = iterator.next(); @@ -298,7 +304,7 @@ public class NewMapChunkCache implements MapChunkCache { } } 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; /* If we didn't load, and we're supposed to generate, do it */ 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 * by the MC base server is 21x21 (or about a 160 block radius). * 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 */ try { if(poppreservedchunk != null) @@ -359,6 +365,8 @@ public class NewMapChunkCache implements MapChunkCache { } cnt++; } + DynmapPlugin.setIgnoreChunkLoads(false); + if(iterator.hasNext() == false) { /* If we're done */ isempty = true; /* 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 */ - public void setAutoGenerateVisbileRanges(boolean do_generate) { - if(do_generate && ((visible_limits == null) || (visible_limits.size() == 0))) { + public void setAutoGenerateVisbileRanges(DynmapWorld.AutoGenerateOption generateopt) { + if((generateopt != DynmapWorld.AutoGenerateOption.NONE) && ((visible_limits == null) || (visible_limits.size() == 0))) { Log.severe("Cannot setAutoGenerateVisibleRanges() without visible ranges defined"); 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