mirror of https://github.com/webbukkit/dynmap.git
675 lines
27 KiB
Java
675 lines
27 KiB
Java
package org.dynmap;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.LinkedHashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import org.dynmap.MapType.ImageEncoding;
|
|
import org.dynmap.hdmap.TexturePack;
|
|
import org.dynmap.storage.MapStorage;
|
|
import org.dynmap.storage.MapStorageTile;
|
|
import org.dynmap.utils.DynmapBufferedImage;
|
|
import org.dynmap.utils.ImageIOManager;
|
|
import org.dynmap.utils.MapChunkCache;
|
|
import org.dynmap.utils.RectangleVisibilityLimit;
|
|
import org.dynmap.utils.RoundVisibilityLimit;
|
|
import org.dynmap.utils.TileFlags;
|
|
import org.dynmap.utils.VisibilityLimit;
|
|
import org.dynmap.utils.Polygon;
|
|
|
|
import java.awt.image.BufferedImage;
|
|
import java.io.IOException;
|
|
import java.util.concurrent.*;
|
|
|
|
public abstract class DynmapWorld {
|
|
public List<MapType> maps = new ArrayList<MapType>();
|
|
public List<MapTypeState> mapstate = new ArrayList<MapTypeState>();
|
|
|
|
public UpdateQueue updates = new UpdateQueue();
|
|
public DynmapLocation center;
|
|
public List<DynmapLocation> seedloc; /* All seed location - both direct and based on visibility limits */
|
|
private List<DynmapLocation> seedloccfg; /* Configured full render seeds only */
|
|
|
|
public List<VisibilityLimit> visibility_limits;
|
|
public List<VisibilityLimit> hidden_limits;
|
|
public MapChunkCache.HiddenChunkStyle hiddenchunkstyle;
|
|
public int servertime;
|
|
public boolean sendposition;
|
|
public boolean sendhealth;
|
|
public boolean showborder;
|
|
private int extrazoomoutlevels; /* Number of additional zoom out levels to generate */
|
|
private boolean cancelled;
|
|
private final String wname;
|
|
private final int hashcode;
|
|
private final String raw_wname;
|
|
private String title;
|
|
public int tileupdatedelay;
|
|
private boolean is_enabled;
|
|
boolean is_protected; /* If true, user needs 'dynmap.world.<worldid>' privilege to see world */
|
|
protected int[] brightnessTable = new int[16]; // 0-256 scaled brightness table
|
|
|
|
private MapStorage storage; // Storage handler for this world's maps
|
|
|
|
/* World height data */
|
|
public int worldheight; // really maxY+1
|
|
public int minY;
|
|
public int sealevel;
|
|
|
|
/* used for storing the amount of tiles processed with last zoom render */
|
|
private long lastZoomRenderTileCount = 0;
|
|
|
|
protected void updateWorldHeights(int worldheight, int minY, int sealevel) {
|
|
this.worldheight = worldheight;
|
|
this.minY = minY;
|
|
this.sealevel = sealevel;
|
|
}
|
|
|
|
protected DynmapWorld(String wname, int worldheight, int sealevel) {
|
|
this(wname, worldheight, sealevel, 0);
|
|
}
|
|
protected DynmapWorld(String wname, int worldheight, int sealevel, int miny) {
|
|
this.raw_wname = wname;
|
|
this.wname = normalizeWorldName(wname);
|
|
this.hashcode = this.wname.hashCode();
|
|
this.title = wname;
|
|
this.worldheight = worldheight;
|
|
this.minY = miny;
|
|
this.sealevel = sealevel;
|
|
/* Generate default brightness table for surface world */
|
|
for (int i = 0; i <= 15; ++i) {
|
|
float f1 = 1.0F - (float)i / 15.0F;
|
|
setBrightnessTableEntry(i, ((1.0F - f1) / (f1 * 3.0F + 1.0F)));
|
|
}
|
|
}
|
|
protected void setBrightnessTableEntry(int level, float value) {
|
|
if ((level < 0) || (level > 15)) return;
|
|
this.brightnessTable[level] = (int)(256.0 * value);
|
|
if (this.brightnessTable[level] > 256) this.brightnessTable[level] = 256;
|
|
if (this.brightnessTable[level] < 0) this.brightnessTable[level] = 0;
|
|
}
|
|
/**
|
|
* Get world's brightness table
|
|
* @return table
|
|
*/
|
|
public int[] getBrightnessTable() {
|
|
return brightnessTable;
|
|
}
|
|
|
|
public void setExtraZoomOutLevels(int lvl) {
|
|
extrazoomoutlevels = lvl;
|
|
}
|
|
public int getExtraZoomOutLevels() { return extrazoomoutlevels; }
|
|
|
|
public void enqueueZoomOutUpdate(MapStorageTile tile) {
|
|
MapTypeState mts = getMapState(tile.map);
|
|
if (mts != null) {
|
|
mts.setZoomOutInv(tile.x, tile.y, tile.zoom);
|
|
}
|
|
}
|
|
/**
|
|
* Constructs a thread pool executor that can be used for zoom out processing
|
|
* @return a new thread pool executor
|
|
*/
|
|
private ThreadPoolExecutor getZoomThreadPool(int capacity){
|
|
ThreadPoolExecutor executor = new ThreadPoolExecutor(
|
|
1,
|
|
1,
|
|
0L,
|
|
TimeUnit.MILLISECONDS,
|
|
new ArrayBlockingQueue<>(capacity)
|
|
);
|
|
executor.setThreadFactory(r -> {
|
|
Thread t = new Thread(r);
|
|
t.setDaemon(true);
|
|
t.setPriority(Thread.MIN_PRIORITY);
|
|
t.setName("Dynmap Zoom Render Thread");
|
|
return t;
|
|
});
|
|
//if the queue is full, we try to put it into it again, until queue is at a level where it can take the request
|
|
//This ensures that we will not load too many zoom tiles to memory
|
|
executor.setRejectedExecutionHandler((r, internalExecutor) -> {
|
|
try {
|
|
internalExecutor.getQueue().put( r );
|
|
} catch (InterruptedException e) {
|
|
e.printStackTrace();
|
|
}
|
|
});
|
|
|
|
return executor;
|
|
}
|
|
|
|
/**
|
|
* Calculates the maximum parallel Zoom Render threads that are allowed to run. Minimum is 1
|
|
* Calculates based on mapmanagers Active Render Thread Count and Parallelrender count
|
|
* @param maxParallel the maximum allowed parallel zoom render threads
|
|
* @return the number of Zoom render Threads that are allowed to be running
|
|
*/
|
|
private int getZoomThreadCount(int maxParallel){
|
|
int maxThreadCount;
|
|
int currentActive = MapManager.mapman.getActivePoolThreadCount();
|
|
maxThreadCount = maxParallel - currentActive;
|
|
if(maxThreadCount < 1){
|
|
maxThreadCount = 1;
|
|
}
|
|
return maxThreadCount;
|
|
}
|
|
|
|
/**
|
|
* Adjusts the Maximum Threads for a Threadpool Executor
|
|
* @param executor the Executor to change
|
|
* @param maxParallel
|
|
*/
|
|
private void adjustZoomThreadCount(ThreadPoolExecutor executor, int maxParallel){
|
|
int newMaxPool = getZoomThreadCount(maxParallel);
|
|
if(executor.getMaximumPoolSize() != newMaxPool){
|
|
//we adjust the thread count for zoom rendering based on unused render threads
|
|
//the more render threads are not doing anything - the more zoom out rendering we get
|
|
executor.setMaximumPoolSize(newMaxPool);
|
|
}
|
|
}
|
|
|
|
public void freshenZoomOutFiles(int maxParallel) {
|
|
if(maxParallel < 1){
|
|
maxParallel = 1; //ensure that we have at least 1 thread for zoom rendering
|
|
}
|
|
int maxQueueSize = 2*maxParallel;
|
|
ThreadPoolExecutor executor = getZoomThreadPool(maxQueueSize);
|
|
MapTypeState.ZoomOutCoord c = new MapTypeState.ZoomOutCoord();
|
|
long start1 = System.currentTimeMillis();
|
|
long tileCounter = 0;
|
|
|
|
for (MapTypeState mts : mapstate) {
|
|
if (cancelled) {
|
|
break;
|
|
}
|
|
MapType mt = mts.type;
|
|
MapType.ImageVariant var[] = mt.getVariants();
|
|
mts.startZoomOutIter(); // Start iterator
|
|
while (mts.nextZoomOutInv(c)) {
|
|
if (cancelled) {
|
|
break;
|
|
}
|
|
for (int varIdx = 0; varIdx < var.length; varIdx++) {
|
|
if (cancelled) {
|
|
break;
|
|
}
|
|
tileCounter++;
|
|
MapStorageTile tile = storage.getTile(this, mt, c.x, c.y, c.zoomlevel, var[varIdx]);
|
|
final MapTypeState finalMts = mts;
|
|
final MapStorageTile finalTile = tile;
|
|
final int finalVarIdx = varIdx;
|
|
|
|
adjustZoomThreadCount(executor, maxParallel);
|
|
executor.execute(() -> {
|
|
processZoomFile(finalMts, finalTile, finalVarIdx == 0);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
long end1 = System.currentTimeMillis();
|
|
long duration = end1-start1;
|
|
double perTile = (double) duration / (double) tileCounter;
|
|
executor.shutdown();
|
|
if(tileCounter > 0 ){
|
|
Log.info(String.format("Zoom Processing %s - %d tiles (%.2f msec/tile, %.2fs per render)", getName(), tileCounter, perTile, (double) duration / 1000));
|
|
}else if(tileCounter == 0 && lastZoomRenderTileCount > 0){
|
|
Log.info(String.format("Zoom Processing %s - Completed", getName()));
|
|
}
|
|
lastZoomRenderTileCount = tileCounter;
|
|
try {
|
|
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
|
|
} catch (InterruptedException e) {
|
|
Log.severe(e);
|
|
}
|
|
}
|
|
|
|
public void cancelZoomOutFreshen() {
|
|
cancelled = true;
|
|
}
|
|
|
|
public void activateZoomOutFreshen() {
|
|
cancelled = false;
|
|
}
|
|
|
|
private static final int[] stepseq = { 3, 1, 2, 0 };
|
|
|
|
private void processZoomFile(MapTypeState mts, MapStorageTile tile, boolean firstVariant) {
|
|
long mostRecentTimestamp = 0;
|
|
int step = 1 << tile.zoom;
|
|
MapStorageTile ztile = tile.getZoomOutTile();
|
|
int width = mts.tileSize, height = mts.tileSize;
|
|
BufferedImage zIm = null;
|
|
DynmapBufferedImage kzIm = null;
|
|
boolean blank = true;
|
|
int[] argb = new int[width*height];
|
|
int tx = ztile.x;
|
|
int ty = ztile.y;
|
|
ty = ty - step; /* Adjust for negative step */
|
|
|
|
/* create image buffer */
|
|
kzIm = DynmapBufferedImage.allocateBufferedImage(width, height);
|
|
zIm = kzIm.buf_img;
|
|
for(int i = 0; i < 4; i++) {
|
|
boolean doblit = true;
|
|
int tx1 = tx + step * (1 & stepseq[i]);
|
|
int ty1 = ty + step * (stepseq[i] >> 1);
|
|
MapStorageTile tile1 = storage.getTile(this, tile.map, tx1, ty1, tile.zoom, tile.var);
|
|
if (tile1 == null) continue;
|
|
tile1.getReadLock();
|
|
if (firstVariant) { // We're handling this one - but only clear on first variant (so that we don't miss updates later)
|
|
mts.clearZoomOutInv(tile1.x, tile1.y, tile1.zoom);
|
|
}
|
|
try {
|
|
MapStorageTile.TileRead tr = tile1.read();
|
|
if (tr != null) {
|
|
BufferedImage im = null;
|
|
try {
|
|
im = ImageIOManager.imageIODecode(tr);
|
|
// Only consider the timestamp when the tile exists and isn't broken
|
|
mostRecentTimestamp = Math.max(mostRecentTimestamp, tr.lastModified);
|
|
} catch (IOException iox) {
|
|
// Broken file - zap it
|
|
tile1.delete();
|
|
}
|
|
if((im != null) && (im.getWidth() >= width) && (im.getHeight() >= height)) {
|
|
int iwidth = im.getWidth();
|
|
int iheight = im.getHeight();
|
|
if(iwidth > iheight) iwidth = iheight;
|
|
|
|
if ((iwidth == width) && (iheight == height)) {
|
|
im.getRGB(0, 0, width, height, argb, 0, width); /* Read data */
|
|
im.flush();
|
|
/* Do binlinear scale to width/2 x height/2 */
|
|
int off = 0;
|
|
for(int y = 0; y < height; y += 2) {
|
|
off = y*width;
|
|
for(int x = 0; x < width; x += 2, off += 2) {
|
|
int p0 = argb[off];
|
|
int p1 = argb[off+1];
|
|
int p2 = argb[off+width];
|
|
int p3 = argb[off+width+1];
|
|
int alpha = ((p0 >> 24) & 0xFF) + ((p1 >> 24) & 0xFF) + ((p2 >> 24) & 0xFF) + ((p3 >> 24) & 0xFF);
|
|
int red = ((p0 >> 16) & 0xFF) + ((p1 >> 16) & 0xFF) + ((p2 >> 16) & 0xFF) + ((p3 >> 16) & 0xFF);
|
|
int green = ((p0 >> 8) & 0xFF) + ((p1 >> 8) & 0xFF) + ((p2 >> 8) & 0xFF) + ((p3 >> 8) & 0xFF);
|
|
int blue = (p0 & 0xFF) + (p1 & 0xFF) + (p2 & 0xFF) + (p3 & 0xFF);
|
|
argb[off>>1] = (((alpha>>2)&0xFF)<<24) | (((red>>2)&0xFF)<<16) | (((green>>2)&0xFF)<<8) | ((blue>>2)&0xFF);
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
int[] buf = new int[iwidth * iwidth];
|
|
im.getRGB(0, 0, iwidth, iwidth, buf, 0, iwidth);
|
|
im.flush();
|
|
TexturePack.scaleTerrainPNGSubImage(iwidth, width/2, buf, argb);
|
|
/* blit scaled rendered tile onto zoom-out tile */
|
|
zIm.setRGB(((i>>1) != 0)?0:width/2, (i & 1) * height/2, width/2, height/2, argb, 0, width/2);
|
|
doblit = false;
|
|
}
|
|
blank = false;
|
|
}
|
|
else {
|
|
if (tile1.map.getImageFormat().getEncoding() == ImageEncoding.JPG) {
|
|
Arrays.fill(argb, tile1.map.getBackgroundARGB(tile1.var));
|
|
}
|
|
else {
|
|
Arrays.fill(argb, 0);
|
|
}
|
|
tile1.delete(); // Delete unusable tile
|
|
}
|
|
}
|
|
else {
|
|
if (tile1.map.getImageFormat().getEncoding() == ImageEncoding.JPG) {
|
|
Arrays.fill(argb, tile1.map.getBackgroundARGB(tile1.var));
|
|
}
|
|
else {
|
|
Arrays.fill(argb, 0);
|
|
}
|
|
}
|
|
} finally {
|
|
tile1.releaseReadLock();
|
|
}
|
|
/* blit scaled rendered tile onto zoom-out tile */
|
|
if(doblit) {
|
|
zIm.setRGB(((i>>1) != 0)?0:width/2, (i & 1) * height/2, width/2, height/2, argb, 0, width);
|
|
}
|
|
}
|
|
ztile.getWriteLock();
|
|
try {
|
|
MapManager mm = MapManager.mapman;
|
|
if(mm == null)
|
|
return;
|
|
long crc = MapStorage.calculateImageHashCode(kzIm.argb_buf, 0, kzIm.argb_buf.length); /* Get hash of tile */
|
|
if(blank) {
|
|
if (ztile.exists()) {
|
|
ztile.delete();
|
|
MapManager.mapman.pushUpdate(this, new Client.Tile(ztile.getURI()));
|
|
enqueueZoomOutUpdate(ztile);
|
|
}
|
|
}
|
|
else /* if (!ztile.matchesHashCode(crc)) */ {
|
|
ztile.write(crc, zIm, (mostRecentTimestamp == 0)? System.currentTimeMillis() : mostRecentTimestamp);
|
|
MapManager.mapman.pushUpdate(this, new Client.Tile(ztile.getURI()));
|
|
enqueueZoomOutUpdate(ztile);
|
|
}
|
|
} finally {
|
|
ztile.releaseWriteLock();
|
|
DynmapBufferedImage.freeBufferedImage(kzIm);
|
|
}
|
|
}
|
|
/* Get world name */
|
|
public String getName() {
|
|
return wname;
|
|
}
|
|
/* Test if world is nether */
|
|
public abstract boolean isNether();
|
|
|
|
/* Get world spawn location */
|
|
public abstract DynmapLocation getSpawnLocation();
|
|
|
|
public int hashCode() {
|
|
return this.hashcode;
|
|
}
|
|
/* Get world time */
|
|
public abstract long getTime();
|
|
/* World is storming */
|
|
public abstract boolean hasStorm();
|
|
/* World is thundering */
|
|
public abstract boolean isThundering();
|
|
/* World is loaded */
|
|
public abstract boolean isLoaded();
|
|
/* Set world unloaded */
|
|
public abstract void setWorldUnloaded();
|
|
/* Get light level of block */
|
|
public abstract int getLightLevel(int x, int y, int z);
|
|
/* Get highest Y coord of given location */
|
|
public abstract int getHighestBlockYAt(int x, int z);
|
|
/* Test if sky light level is requestable */
|
|
public abstract boolean canGetSkyLightLevel();
|
|
/* Return sky light level */
|
|
public abstract int getSkyLightLevel(int x, int y, int z);
|
|
/**
|
|
* Get world environment ID (lower case - normal, the_end, nether)
|
|
* @return environment ID
|
|
*/
|
|
public abstract String getEnvironment();
|
|
/**
|
|
* Get map chunk cache for world
|
|
* @param chunks - list of chunks to load
|
|
* @return cache
|
|
*/
|
|
public abstract MapChunkCache getChunkCache(List<DynmapChunk> chunks);
|
|
|
|
/**
|
|
* Get title for world
|
|
* @return title
|
|
*/
|
|
public String getTitle() {
|
|
return title;
|
|
}
|
|
/**
|
|
* Get center location
|
|
* @return center
|
|
*/
|
|
public DynmapLocation getCenterLocation() {
|
|
if(center != null)
|
|
return center;
|
|
else
|
|
return getSpawnLocation();
|
|
}
|
|
|
|
/* Load world configuration from configuration node */
|
|
public boolean loadConfiguration(DynmapCore core, ConfigurationNode worldconfig) {
|
|
is_enabled = worldconfig.getBoolean("enabled", false);
|
|
if (!is_enabled) {
|
|
return false;
|
|
}
|
|
title = worldconfig.getString("title", title);
|
|
ConfigurationNode ctr = worldconfig.getNode("center");
|
|
int mid_y = (worldheight + minY)/2;
|
|
if(ctr != null)
|
|
center = new DynmapLocation(wname, ctr.getDouble("x", 0.0), ctr.getDouble("y", mid_y), ctr.getDouble("z", 0));
|
|
else
|
|
center = null;
|
|
List<ConfigurationNode> loclist = worldconfig.getNodes("fullrenderlocations");
|
|
seedloc = new ArrayList<DynmapLocation>();
|
|
seedloccfg = new ArrayList<DynmapLocation>();
|
|
servertime = (int)(getTime() % 24000);
|
|
sendposition = worldconfig.getBoolean("sendposition", true);
|
|
sendhealth = worldconfig.getBoolean("sendhealth", true);
|
|
showborder = worldconfig.getBoolean("showborder", true);
|
|
is_protected = worldconfig.getBoolean("protected", false);
|
|
setExtraZoomOutLevels(worldconfig.getInteger("extrazoomout", 0));
|
|
setTileUpdateDelay(worldconfig.getInteger("tileupdatedelay", -1));
|
|
storage = core.getDefaultMapStorage();
|
|
if(loclist != null) {
|
|
for(ConfigurationNode loc : loclist) {
|
|
DynmapLocation lx = new DynmapLocation(wname, loc.getDouble("x", 0), loc.getDouble("y", mid_y), loc.getDouble("z", 0));
|
|
seedloc.add(lx); /* Add to both combined and configured seed list */
|
|
seedloccfg.add(lx);
|
|
}
|
|
}
|
|
/* Build maps */
|
|
maps.clear();
|
|
Log.verboseinfo("Loading maps of world '" + wname + "'...");
|
|
for(MapType map : worldconfig.<MapType>createInstances("maps", new Class<?>[] { DynmapCore.class }, new Object[] { core })) {
|
|
if(map.getName() != null) {
|
|
maps.add(map);
|
|
}
|
|
}
|
|
/* Rebuild map state list - match on indexes */
|
|
mapstate.clear();
|
|
for(MapType map : maps) {
|
|
MapTypeState ms = new MapTypeState(this, map);
|
|
ms.setInvalidatePeriod(map.getTileUpdateDelay(this));
|
|
mapstate.add(ms);
|
|
}
|
|
Log.info("Loaded " + maps.size() + " maps of world '" + wname + "'.");
|
|
/* Load visibility limits, if any are defined */
|
|
List<ConfigurationNode> vislimits = worldconfig.getNodes("visibilitylimits");
|
|
if(vislimits != null) {
|
|
visibility_limits = new ArrayList<VisibilityLimit>();
|
|
for(ConfigurationNode vis : vislimits) {
|
|
VisibilityLimit lim;
|
|
if (vis.containsKey("r")) { /* It is round visibility limit */
|
|
int x_center = vis.getInteger("x", 0);
|
|
int z_center = vis.getInteger("z", 0);
|
|
int radius = vis.getInteger("r", 0);
|
|
lim = new RoundVisibilityLimit(x_center, z_center, radius);
|
|
}
|
|
else { /* Rectangle visibility limit */
|
|
int x0 = vis.getInteger("x0", 0);
|
|
int x1 = vis.getInteger("x1", 0);
|
|
int z0 = vis.getInteger("z0", 0);
|
|
int z1 = vis.getInteger("z1", 0);
|
|
lim = new RectangleVisibilityLimit(x0, z0, x1, z1);
|
|
}
|
|
visibility_limits.add(lim);
|
|
/* Also, add a seed location for the middle of each visible area */
|
|
seedloc.add(new DynmapLocation(wname, lim.xCenter(), 64, lim.zCenter()));
|
|
}
|
|
}
|
|
/* Load hidden limits, if any are defined */
|
|
List<ConfigurationNode> hidelimits = worldconfig.getNodes("hiddenlimits");
|
|
if(hidelimits != null) {
|
|
hidden_limits = new ArrayList<VisibilityLimit>();
|
|
for(ConfigurationNode vis : hidelimits) {
|
|
VisibilityLimit lim;
|
|
if (vis.containsKey("r")) { /* It is round visibility limit */
|
|
int x_center = vis.getInteger("x", 0);
|
|
int z_center = vis.getInteger("z", 0);
|
|
int radius = vis.getInteger("r", 0);
|
|
lim = new RoundVisibilityLimit(x_center, z_center, radius);
|
|
}
|
|
else { /* Rectangle visibility limit */
|
|
int x0 = vis.getInteger("x0", 0);
|
|
int x1 = vis.getInteger("x1", 0);
|
|
int z0 = vis.getInteger("z0", 0);
|
|
int z1 = vis.getInteger("z1", 0);
|
|
lim = new RectangleVisibilityLimit(x0, z0, x1, z1);
|
|
}
|
|
hidden_limits.add(lim);
|
|
}
|
|
}
|
|
String hiddenchunkstyle = worldconfig.getString("hidestyle", "stone");
|
|
this.hiddenchunkstyle = MapChunkCache.HiddenChunkStyle.fromValue(hiddenchunkstyle);
|
|
if (this.hiddenchunkstyle == null) this.hiddenchunkstyle = MapChunkCache.HiddenChunkStyle.FILL_STONE_PLAIN;
|
|
|
|
return true;
|
|
}
|
|
/*
|
|
* Make configuration node for saving world
|
|
*/
|
|
public ConfigurationNode saveConfiguration() {
|
|
ConfigurationNode node = new ConfigurationNode();
|
|
/* Add name and title */
|
|
node.put("name", wname);
|
|
node.put("title", getTitle());
|
|
node.put("enabled", is_enabled);
|
|
node.put("protected", is_protected);
|
|
node.put("showborder", showborder);
|
|
if(tileupdatedelay > 0) {
|
|
node.put("tileupdatedelay", tileupdatedelay);
|
|
}
|
|
/* Add center */
|
|
if(center != null) {
|
|
ConfigurationNode c = new ConfigurationNode();
|
|
c.put("x", center.x);
|
|
c.put("y", center.y);
|
|
c.put("z", center.z);
|
|
node.put("center", c.entries);
|
|
}
|
|
/* Add seed locations, if any */
|
|
if(seedloccfg.size() > 0) {
|
|
ArrayList<Map<String,Object>> locs = new ArrayList<Map<String,Object>>();
|
|
for(int i = 0; i < seedloccfg.size(); i++) {
|
|
DynmapLocation dl = seedloccfg.get(i);
|
|
ConfigurationNode ll = new ConfigurationNode();
|
|
ll.put("x", dl.x);
|
|
ll.put("y", dl.y);
|
|
ll.put("z", dl.z);
|
|
locs.add(ll.entries);
|
|
}
|
|
node.put("fullrenderlocations", locs);
|
|
}
|
|
/* Add flags */
|
|
node.put("sendposition", sendposition);
|
|
node.put("sendhealth", sendhealth);
|
|
node.put("extrazoomout", extrazoomoutlevels);
|
|
/* Save visibility limits, if defined */
|
|
if(visibility_limits != null) {
|
|
ArrayList<Map<String,Object>> lims = new ArrayList<Map<String,Object>>();
|
|
for(int i = 0; i < visibility_limits.size(); i++) {
|
|
VisibilityLimit lim = visibility_limits.get(i);
|
|
LinkedHashMap<String, Object> lv = new LinkedHashMap<String,Object>();
|
|
if (lim instanceof RectangleVisibilityLimit) {
|
|
RectangleVisibilityLimit rect_lim = (RectangleVisibilityLimit) lim;
|
|
lv.put("x0", rect_lim.x_min);
|
|
lv.put("z0", rect_lim.z_min);
|
|
lv.put("x1", rect_lim.x_max);
|
|
lv.put("z1", rect_lim.z_max);
|
|
}
|
|
else {
|
|
RoundVisibilityLimit round_lim = (RoundVisibilityLimit) lim;
|
|
lv.put("x", round_lim.x_center);
|
|
lv.put("z", round_lim.z_center);
|
|
lv.put("r", round_lim.radius);
|
|
}
|
|
lims.add(lv);
|
|
}
|
|
node.put("visibilitylimits", lims);
|
|
}
|
|
/* Save hidden limits, if defined */
|
|
if(hidden_limits != null) {
|
|
ArrayList<Map<String,Object>> lims = new ArrayList<Map<String,Object>>();
|
|
for(int i = 0; i < hidden_limits.size(); i++) {
|
|
VisibilityLimit lim = hidden_limits.get(i);
|
|
LinkedHashMap<String, Object> lv = new LinkedHashMap<String,Object>();
|
|
if (lim instanceof RectangleVisibilityLimit) {
|
|
RectangleVisibilityLimit rect_lim = (RectangleVisibilityLimit) lim;
|
|
lv.put("x0", rect_lim.x_min);
|
|
lv.put("z0", rect_lim.z_min);
|
|
lv.put("x1", rect_lim.x_max);
|
|
lv.put("z1", rect_lim.z_max);
|
|
}
|
|
else {
|
|
RoundVisibilityLimit round_lim = (RoundVisibilityLimit) lim;
|
|
lv.put("x", round_lim.x_center);
|
|
lv.put("z", round_lim.z_center);
|
|
lv.put("r", round_lim.radius);
|
|
}
|
|
lims.add(lv);
|
|
}
|
|
node.put("hiddenlimits", lims);
|
|
}
|
|
/* Handle hide style */
|
|
node.put("hidestyle", hiddenchunkstyle.getValue());
|
|
/* Handle map settings */
|
|
ArrayList<Map<String,Object>> mapinfo = new ArrayList<Map<String,Object>>();
|
|
for(MapType mt : maps) {
|
|
ConfigurationNode mnode = mt.saveConfiguration();
|
|
mapinfo.add(mnode);
|
|
}
|
|
node.put("maps", mapinfo);
|
|
|
|
return node;
|
|
}
|
|
public boolean isEnabled() {
|
|
return is_enabled;
|
|
}
|
|
public void setTitle(String title) {
|
|
this.title = title;
|
|
}
|
|
public static String normalizeWorldName(String n) {
|
|
return (n != null)?n.replace('/', '-').replace('[', '_').replace(']', '_'):null;
|
|
}
|
|
public String getRawName() {
|
|
return raw_wname;
|
|
}
|
|
public boolean isProtected() {
|
|
return is_protected;
|
|
}
|
|
public int getTileUpdateDelay() {
|
|
if(tileupdatedelay > 0)
|
|
return tileupdatedelay;
|
|
else
|
|
return MapManager.mapman.getDefTileUpdateDelay();
|
|
}
|
|
public void setTileUpdateDelay(int time_sec) {
|
|
tileupdatedelay = time_sec;
|
|
}
|
|
public static void doInitialScan(boolean doscan) {
|
|
}
|
|
// Return number of chunks found (-1 if not implemented)
|
|
public int getChunkMap(TileFlags map) {
|
|
return -1;
|
|
}
|
|
// Get map state for given map
|
|
public MapTypeState getMapState(MapType m) {
|
|
for (int i = 0; i < this.maps.size(); i++) {
|
|
MapType mt = this.maps.get(i);
|
|
if (mt == m) {
|
|
return this.mapstate.get(i);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public void purgeTree() {
|
|
storage.purgeMapTiles(this, null);
|
|
}
|
|
|
|
public void purgeMap(MapType mt) {
|
|
storage.purgeMapTiles(this, mt);
|
|
}
|
|
|
|
public MapStorage getMapStorage() {
|
|
return storage;
|
|
}
|
|
|
|
public Polygon getWorldBorder() {
|
|
return null;
|
|
}
|
|
}
|