Prototype of chunk snapshot support - pre-Bukkit API (reflection

based, with fallback to existing APIs)
This commit is contained in:
Mike Primm 2011-05-20 01:38:49 -05:00
parent 26f4f7d994
commit 3365a96565
11 changed files with 361 additions and 137 deletions

View File

@ -0,0 +1,108 @@
package org.dynmap;
/**
* Represents a static, thread-safe snapshot of chunk of blocks
* Purpose is to allow clean, efficient copy of a chunk data to be made, and then handed off for processing in another thread (e.g. map rendering)
*/
public class CraftChunkSnapshot {
private final int x, z;
private final byte[] buf; /* Flat buffer in uncompressed chunk file format */
private static final int BLOCKDATA_OFF = 32768;
private static final int BLOCKLIGHT_OFF = BLOCKDATA_OFF + 16384;
private static final int SKYLIGHT_OFF = BLOCKLIGHT_OFF + 16384;
/**
* Constructor
*/
CraftChunkSnapshot(int x, int z, byte[] buf) {
this.x = x;
this.z = z;
this.buf = buf;
}
/**
* Gets the X-coordinate of this chunk
*
* @return X-coordinate
*/
public int getX() {
return x;
}
/**
* Gets the Z-coordinate of this chunk
*
* @return Z-coordinate
*/
public int getZ() {
return z;
}
/**
* Get block type for block at corresponding coordinate in the chunk
*
* @param x 0-15
* @param y 0-127
* @param z 0-15
* @return 0-255
*/
public int getBlockTypeId(int x, int y, int z) {
return buf[x << 11 | z << 7 | y] & 255;
}
/**
* Get block data for block at corresponding coordinate in the chunk
*
* @param x 0-15
* @param y 0-127
* @param z 0-15
* @return 0-15
*/
public int getBlockData(int x, int y, int z) {
int off = ((x << 10) | (z << 6) | (y >> 1)) + BLOCKDATA_OFF;
return ((y & 1) == 0) ? (buf[off] & 0xF) : ((buf[off] >> 4) & 0xF);
}
/**
* Get sky light level for block at corresponding coordinate in the chunk
*
* @param x 0-15
* @param y 0-127
* @param z 0-15
* @return 0-15
*/
public int getBlockSkyLight(int x, int y, int z) {
int off = ((x << 10) | (z << 6) | (y >> 1)) + SKYLIGHT_OFF;
return ((y & 1) == 0) ? (buf[off] & 0xF) : ((buf[off] >> 4) & 0xF);
}
/**
* Get light level emitted by block at corresponding coordinate in the chunk
*
* @param x 0-15
* @param y 0-127
* @param z 0-15
* @return 0-15
*/
public int getBlockEmittedLight(int x, int y, int z) {
int off = ((x << 10) | (z << 6) | (y >> 1)) + BLOCKLIGHT_OFF;
return ((y & 1) == 0) ? (buf[off] & 0xF) : ((buf[off] >> 4) & 0xF);
}
public int getHighestBlockYAt(int x, int z) {
int off = x << 11 | z << 7 | 127;
int i;
for(i = 127; (i >= 0); i--, off--) {
if(buf[off] != 0) {
if(i < 127) i++;
break;
}
}
return i;
}
}

View File

@ -0,0 +1,198 @@
package org.dynmap;
import java.lang.reflect.Method;
import java.util.LinkedList;
import org.bukkit.World;
import org.bukkit.Chunk;
import org.bukkit.entity.Entity;
/**
* Container for managing chunks, as well as abstracting the different methods we may
* handle chunk data (existing chunk loading, versus upcoming chunk snapshots)
*
*/
public class MapChunkCache {
private World w;
private static Method getchunkdata = null;
private static Method gethandle = null;
private static boolean initialized = false;
private int x_min, x_max, z_min, z_max;
private int x_dim;
private CraftChunkSnapshot[] snaparray; /* Index = (x-x_min) + ((z-z_min)*x_dim) */
private LinkedList<DynmapChunk> loadedChunks = new LinkedList<DynmapChunk>();
/**
* Create chunk cache container
* @param w - world
* @param x_min - minimum chunk x coordinate
* @param z_min - minimum chunk z coordinate
* @param x_max - maximum chunk x coordinate
* @param z_max - maximum chunk z coordinate
*/
@SuppressWarnings({ "unchecked" })
public MapChunkCache(World w, DynmapChunk[] chunks) {
/* Compute range */
if(chunks.length == 0) {
this.x_min = 0;
this.x_max = 0;
this.z_min = 0;
this.z_max = 0;
x_dim = 1;
}
else {
x_min = x_max = chunks[0].x;
z_min = z_max = chunks[0].z;
for(int i = 1; i < chunks.length; i++) {
if(chunks[i].x > x_max)
x_max = chunks[i].x;
if(chunks[i].x < x_min)
x_min = chunks[i].x;
if(chunks[i].z > z_max)
z_max = chunks[i].z;
if(chunks[i].z < z_min)
z_min = chunks[i].z;
}
x_dim = x_max - x_min + 1;
}
this.w = w;
if(!initialized) {
try {
Class c = Class.forName("net.minecraft.server.Chunk");
getchunkdata = c.getDeclaredMethod("a", new Class[] { byte[].class, int.class,
int.class, int.class, int.class, int.class, int.class, int.class });
c = Class.forName("org.bukkit.craftbukkit.CraftChunk");
gethandle = c.getDeclaredMethod("getHandle", new Class[0]);
} catch (ClassNotFoundException cnfx) {
} catch (NoSuchMethodException nsmx) {
}
initialized = true;
if(gethandle != null)
Log.info("Chunk snapshot support enabled");
else
Log.info("Chunk snapshot support disabled");
}
if(gethandle != null) { /* We can use caching */
snaparray = new CraftChunkSnapshot[x_dim * (z_max-z_min+1)];
}
if(snaparray != null) {
// Load the required chunks.
for (DynmapChunk chunk : chunks) {
boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z);
boolean didload = w.loadChunk(chunk.x, chunk.z, false);
/* If it did load, make cache of it */
if(didload) {
Chunk c = w.getChunkAt(chunk.x, chunk.z);
try {
Object cc = gethandle.invoke(c);
byte[] buf = new byte[32768 + 16384 + 16384 + 16384]; /* Get big enough buffer for whole chunk */
getchunkdata.invoke(cc, buf, 0, 0, 0, 16, 128, 16, 0);
snaparray[(chunk.x-x_min) + (chunk.z - z_min)*x_dim] =
new CraftChunkSnapshot(chunk.x, chunk.z, buf);
} catch (Exception x) {
}
}
if ((!wasLoaded) && didload) {
/* It looks like bukkit "leaks" entities - they don't get removed from the world-level table
* when chunks are unloaded but not saved - removing them seems to do the trick */
Chunk cc = w.getChunkAt(chunk.x, chunk.z);
if(cc != null) {
for(Entity e: cc.getEntities())
e.remove();
}
/* Since we only remember ones we loaded, and we're synchronous, no player has
* moved, so it must be safe (also prevent chunk leak, which appears to happen
* because isChunkInUse defined "in use" as being within 256 blocks of a player,
* 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) */
w.unloadChunk(chunk.x, chunk.z, false, false);
}
}
}
else { /* Else, load and keep them loaded for now */
// Load the required chunks.
for (DynmapChunk chunk : chunks) {
boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z);
boolean didload = w.loadChunk(chunk.x, chunk.z, false);
if ((!wasLoaded) && didload)
loadedChunks.add(chunk);
}
}
}
/**
* Unload chunks
*/
public void unloadChunks() {
if(snaparray != null) {
for(int i = 0; i < snaparray.length; i++) {
snaparray[i] = null;
}
}
else {
while (!loadedChunks.isEmpty()) {
DynmapChunk c = loadedChunks.pollFirst();
/* It looks like bukkit "leaks" entities - they don't get removed from the world-level table
* when chunks are unloaded but not saved - removing them seems to do the trick */
Chunk cc = w.getChunkAt(c.x, c.z);
if(cc != null) {
for(Entity e: cc.getEntities())
e.remove();
}
/* Since we only remember ones we loaded, and we're synchronous, no player has
* moved, so it must be safe (also prevent chunk leak, which appears to happen
* because isChunkInUse defined "in use" as being within 256 blocks of a player,
* 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) */
w.unloadChunk(c.x, c.z, false, false);
}
}
}
/**
* Get block ID at coordinates
*/
public int getBlockTypeID(int x, int y, int z) {
if(snaparray != null) {
CraftChunkSnapshot ss = snaparray[((x>>4) - x_min) + ((z>>4) - z_min) * x_dim];
if(ss == null)
return 0;
else
return ss.getBlockTypeId(x & 0xF, y, z & 0xF);
}
else {
return w.getBlockTypeIdAt(x, y, z);
}
}
/**
* Get block data at coordiates
*/
public byte getBlockData(int x, int y, int z) {
if(snaparray != null) {
CraftChunkSnapshot ss = snaparray[((x>>4) - x_min) + ((z>>4) - z_min) * x_dim];
if(ss == null)
return 0;
else
return (byte)ss.getBlockData(x & 0xF, y, z & 0xF);
}
else {
return w.getBlockAt(x, y, z).getData();
}
}
/* Get highest block Y
*
*/
public int getHighestBlockYAt(int x, int z) {
if(snaparray != null) {
CraftChunkSnapshot ss = snaparray[((x>>4) - x_min) + ((z>>4) - z_min) * x_dim];
if(ss == null) {
return 0;
}
else
return ss.getHighestBlockYAt(x & 0xF, z & 0xF);
}
else {
return w.getHighestBlockYAt(x, z);
}
}
}

View File

@ -21,9 +21,7 @@ public class MapManager {
public Map<String, DynmapWorld> inactiveworlds = new HashMap<String, DynmapWorld>();
private BukkitScheduler scheduler;
private DynmapPlugin plug_in;
private boolean do_timesliced_render = false;
private double timeslice_interval = 0.0;
private boolean do_sync_render = false; /* Do incremental renders on sync thread too */
/* Which timesliced renders are active */
private HashMap<String, FullWorldRenderState> active_renders = new HashMap<String, FullWorldRenderState>();
@ -96,22 +94,14 @@ public class MapManager {
else { /* Else, single tile render */
tile = tile0;
}
DynmapChunk[] requiredChunks = tile.getMap().getRequiredChunks(tile);
LinkedList<DynmapChunk> loadedChunks = new LinkedList<DynmapChunk>();
MapChunkCache cache = new MapChunkCache(world.world, requiredChunks);
World w = world.world;
// Load the required chunks.
for (DynmapChunk chunk : requiredChunks) {
boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z);
boolean didload = w.loadChunk(chunk.x, chunk.z, false);
if ((!wasLoaded) && didload)
loadedChunks.add(chunk);
}
if(tile0 != null) { /* Single tile? */
render(tile); /* Just render */
render(cache, tile); /* Just render */
}
else {
if (render(tile)) {
if (render(cache, tile)) {
found.remove(tile);
rendered.add(tile);
for (MapTile adjTile : map.getAdjecentTiles(tile)) {
@ -129,22 +119,7 @@ public class MapManager {
}
}
/* And unload what we loaded */
while (!loadedChunks.isEmpty()) {
DynmapChunk c = loadedChunks.pollFirst();
/* It looks like bukkit "leaks" entities - they don't get removed from the world-level table
* when chunks are unloaded but not saved - removing them seems to do the trick */
Chunk cc = w.getChunkAt(c.x, c.z);
if(cc != null) {
for(Entity e: cc.getEntities())
e.remove();
}
/* Since we only remember ones we loaded, and we're synchronous, no player has
* moved, so it must be safe (also prevent chunk leak, which appears to happen
* because isChunkInUse defined "in use" as being within 256 blocks of a player,
* 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) */
w.unloadChunk(c.x, c.z, false, false);
}
cache.unloadChunks();
if(tile0 == null) { /* fullrender */
/* Schedule the next tile to be worked */
scheduler.scheduleSyncDelayedTask(plug_in, this, (int)(timeslice_interval*20));
@ -159,11 +134,8 @@ public class MapManager {
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), 1);
else
render(t);
}
}, (int) (configuration.getDouble("renderinterval", 0.5) * 1000));
@ -175,9 +147,7 @@ public class MapManager {
}
}, 10);
do_timesliced_render = configuration.getBoolean("timeslicerender", true);
timeslice_interval = configuration.getDouble("timesliceinterval", 0.5);
do_sync_render = configuration.getBoolean("renderonsync", true);
for(ConfigurationNode worldConfiguration : configuration.getNodes("worlds")) {
String worldName = worldConfiguration.getString("name");
@ -219,7 +189,6 @@ public class MapManager {
Log.severe("Could not render: world '" + l.getWorld().getName() + "' not defined in configuration.");
return;
}
if(do_timesliced_render) {
String wname = l.getWorld().getName();
FullWorldRenderState rndr = active_renders.get(wname);
if(rndr != null) {
@ -231,66 +200,6 @@ public class MapManager {
/* Schedule first tile to be worked */
scheduler.scheduleSyncDelayedTask(plug_in, rndr, (int)(timeslice_interval*20));
Log.info("Full render starting on world '" + wname + "' (timesliced)...");
return;
}
World w = world.world;
Log.info("Full render starting on world '" + w.getName() + "'...");
for (MapType map : world.maps) {
int requiredChunkCount = 200;
HashSet<MapTile> found = new HashSet<MapTile>();
HashSet<MapTile> rendered = new HashSet<MapTile>();
LinkedList<MapTile> renderQueue = new LinkedList<MapTile>();
LinkedList<DynmapChunk> loadedChunks = new LinkedList<DynmapChunk>();
for (MapTile tile : map.getTiles(l)) {
if (!found.contains(tile)) {
found.add(tile);
renderQueue.add(tile);
}
}
while (!renderQueue.isEmpty()) {
MapTile tile = renderQueue.pollFirst();
DynmapChunk[] requiredChunks = tile.getMap().getRequiredChunks(tile);
if (requiredChunks.length > requiredChunkCount)
requiredChunkCount = requiredChunks.length;
// Unload old chunks.
while (loadedChunks.size() >= requiredChunkCount - requiredChunks.length) {
DynmapChunk c = loadedChunks.pollFirst();
w.unloadChunk(c.x, c.z, false, true);
}
// Load the required chunks.
for (DynmapChunk chunk : requiredChunks) {
boolean wasLoaded = w.isChunkLoaded(chunk.x, chunk.z);
w.loadChunk(chunk.x, chunk.z, false);
if (!wasLoaded)
loadedChunks.add(chunk);
}
if (render(tile)) {
found.remove(tile);
rendered.add(tile);
for (MapTile adjTile : map.getAdjecentTiles(tile)) {
if (!found.contains(adjTile) && !rendered.contains(adjTile)) {
found.add(adjTile);
renderQueue.add(adjTile);
}
}
}
found.remove(tile);
}
// Unload remaining chunks to clean-up.
while (!loadedChunks.isEmpty()) {
DynmapChunk c = loadedChunks.pollFirst();
w.unloadChunk(c.x, c.z, false, true);
}
}
Log.info("Full render finished.");
}
public void activateWorld(World w) {
@ -337,8 +246,8 @@ public class MapManager {
writeQueue.stop();
}
public boolean render(MapTile tile) {
boolean result = tile.getMap().render(tile, getTileFile(tile));
public boolean render(MapChunkCache cache, MapTile tile) {
boolean result = tile.getMap().render(cache, tile, getTileFile(tile));
//Do update after async file write
return result;
@ -387,6 +296,6 @@ public class MapManager {
}
public boolean doSyncRender() {
return do_sync_render;
return true;
}
}

View File

@ -13,5 +13,5 @@ public abstract class MapType {
public abstract DynmapChunk[] getRequiredChunks(MapTile tile);
public abstract boolean render(MapTile tile, File outputFile);
public abstract boolean render(MapChunkCache cache, MapTile tile, File outputFile);
}

View File

@ -19,6 +19,7 @@ import org.dynmap.MapManager;
import org.dynmap.MapTile;
import org.dynmap.MapType;
import org.dynmap.debug.Debug;
import org.dynmap.MapChunkCache;
public class FlatMap extends MapType {
private String prefix;
@ -73,7 +74,7 @@ public class FlatMap extends MapType {
}
@Override
public boolean render(MapTile tile, File outputFile) {
public boolean render(MapChunkCache cache, MapTile tile, File outputFile) {
FlatMapTile t = (FlatMapTile) tile;
World w = t.getWorld();
boolean isnether = (w.getEnvironment() == Environment.NETHER) && (maximumHeight == 127);
@ -93,16 +94,16 @@ public class FlatMap extends MapType {
if(isnether) {
/* Scan until we hit air */
my = 127;
while((blockType = w.getBlockTypeIdAt(mx, my, mz)) != 0) {
while((blockType = cache.getBlockTypeID(mx, my, mz)) != 0) {
my--;
if(my < 0) { /* Solid - use top */
my = 127;
blockType = w.getBlockTypeIdAt(mx, my, mz);
blockType = cache.getBlockTypeID(mx, my, mz);
break;
}
}
if(blockType == 0) { /* Hit air - now find non-air */
while((blockType = w.getBlockTypeIdAt(mx, my, mz)) == 0) {
while((blockType = cache.getBlockTypeID(mx, my, mz)) == 0) {
my--;
if(my < 0) {
my = 0;
@ -112,14 +113,14 @@ public class FlatMap extends MapType {
}
}
else {
my = w.getHighestBlockYAt(mx, mz) - 1;
my = cache.getHighestBlockYAt(mx, mz) - 1;
if(my > maximumHeight) my = maximumHeight;
blockType = w.getBlockTypeIdAt(mx, my, mz);
blockType = cache.getBlockTypeID(mx, my, mz);
}
byte data = 0;
Color[] colors = colorScheme.colors[blockType];
if(colorScheme.datacolors[blockType] != null) {
data = w.getBlockAt(mx, my, mz).getData();
data = cache.getBlockData(mx, my, mz);
colors = colorScheme.datacolors[blockType][data];
}
if (colors == null)

View File

@ -1,6 +1,7 @@
package org.dynmap.kzedmap;
import org.bukkit.World;
import org.dynmap.MapChunkCache;
import org.dynmap.Color;
import org.dynmap.ConfigurationNode;
@ -11,14 +12,15 @@ public class CaveTileRenderer extends DefaultTileRenderer {
}
@Override
protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result) {
protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result,
MapChunkCache cache) {
boolean air = true;
result.setTransparent();
for (;;) {
if (y < 0)
return;
int id = world.getBlockTypeIdAt(x, y, z);
int id = cache.getBlockTypeID(x, y, z);
if(isnether) { /* Make ceiling into air in nether */
if(id != 0)
id = 0;

View File

@ -18,6 +18,7 @@ import org.dynmap.ColorScheme;
import org.dynmap.ConfigurationNode;
import org.dynmap.MapManager;
import org.dynmap.debug.Debug;
import org.dynmap.MapChunkCache;
public class DefaultTileRenderer implements MapTileRenderer {
protected static final Color translucent = new Color(0, 0, 0, 0);
@ -44,7 +45,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
colorScheme = ColorScheme.getScheme((String)configuration.get("colorscheme"));
}
public boolean render(KzedMapTile tile, File outputFile) {
public boolean render(MapChunkCache cache, KzedMapTile tile, File outputFile) {
World world = tile.getWorld();
boolean isnether = (world.getEnvironment() == Environment.NETHER);
BufferedImage im = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB);
@ -73,8 +74,8 @@ public class DefaultTileRenderer implements MapTileRenderer {
jz = iz;
for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) {
scan(world, jx, iy, jz, 0, isnether, c1);
scan(world, jx, iy, jz, 2, isnether, c2);
scan(world, jx, iy, jz, 0, isnether, c1, cache);
scan(world, jx, iy, jz, 2, isnether, c2, cache);
if(c1.isTransparent() == false) {
rgb[0] = c1.getRed(); rgb[1] = c1.getGreen(); rgb[2] = c1.getBlue();
r.setPixel(x, y, rgb);
@ -97,10 +98,10 @@ public class DefaultTileRenderer implements MapTileRenderer {
jz = iz - 1;
for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) {
scan(world, jx, iy, jz, 2, isnether, c1);
scan(world, jx, iy, jz, 2, isnether, c1, cache);
jx++;
jz++;
scan(world, jx, iy, jz, 0, isnether, c2);
scan(world, jx, iy, jz, 0, isnether, c2, cache);
if(c1.isTransparent() == false) {
rgb[0] = c1.getRed(); rgb[1] = c1.getGreen(); rgb[2] = c1.getBlue();
r.setPixel(x, y, rgb);
@ -210,13 +211,14 @@ public class DefaultTileRenderer implements MapTileRenderer {
}
protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result) {
protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result,
MapChunkCache cache) {
result.setTransparent();
for (;;) {
if (y < 0) {
return;
}
int id = world.getBlockTypeIdAt(x, y, z);
int id = cache.getBlockTypeID(x, y, z);
byte data = 0;
if(isnether) { /* Make bedrock ceiling into air in nether */
if(id != 0) {
@ -230,7 +232,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
isnether = false;
}
if(colorScheme.datacolors[id] != null) { /* If data colored */
data = world.getBlockAt(x, y, z).getData();
data = cache.getBlockData(x, y, z);
}
switch (seq) {
case 0:
@ -270,7 +272,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
}
/* this block is transparent, so recurse */
scan(world, x, y, z, seq, isnether, result);
scan(world, x, y, z, seq, isnether, result, cache);
int cr = c.getRed();
int cg = c.getGreen();

View File

@ -1,6 +1,7 @@
package org.dynmap.kzedmap;
import java.util.HashSet;
import org.dynmap.MapChunkCache;
import java.util.List;
import org.bukkit.World;
@ -19,14 +20,15 @@ public class HighlightTileRenderer extends DefaultTileRenderer {
}
@Override
protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result) {
protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result,
MapChunkCache cache) {
result.setTransparent();
for (;;) {
if (y < 0) {
break;
}
int id = world.getBlockTypeIdAt(x, y, z);
int id = cache.getBlockTypeID(x, y, z);
if(isnether) { /* Make bedrock ceiling into air in nether */
if(id != 0) {
/* Remember first color we see, in case we wind up solid */
@ -40,7 +42,7 @@ public class HighlightTileRenderer extends DefaultTileRenderer {
}
byte data = 0;
if(colorScheme.datacolors[id] != null) { /* If data colored */
data = world.getBlockAt(x, y, z).getData();
data = cache.getBlockData(x, y, z);
}
switch (seq) {

View File

@ -12,6 +12,7 @@ import org.dynmap.DynmapChunk;
import org.dynmap.Log;
import org.dynmap.MapTile;
import org.dynmap.MapType;
import org.dynmap.MapChunkCache;
public class KzedMap extends MapType {
protected static final Logger log = Logger.getLogger("Minecraft");
@ -203,12 +204,12 @@ public class KzedMap extends MapType {
}
@Override
public boolean render(MapTile tile, File outputFile) {
public boolean render(MapChunkCache cache, MapTile tile, File outputFile) {
if (tile instanceof KzedZoomedMapTile) {
zoomrenderer.render((KzedZoomedMapTile) tile, outputFile);
zoomrenderer.render(cache, (KzedZoomedMapTile) tile, outputFile);
return true;
} else if (tile instanceof KzedMapTile) {
return ((KzedMapTile) tile).renderer.render((KzedMapTile) tile, outputFile);
return ((KzedMapTile) tile).renderer.render(cache, (KzedMapTile) tile, outputFile);
}
return false;
}

View File

@ -1,9 +1,10 @@
package org.dynmap.kzedmap;
import java.io.File;
import org.dynmap.MapChunkCache;
public interface MapTileRenderer {
String getName();
boolean render(KzedMapTile tile, File outputFile);
boolean render(MapChunkCache cache, KzedMapTile tile, File outputFile);
}

View File

@ -2,12 +2,12 @@ package org.dynmap.kzedmap;
import java.io.File;
import java.util.Map;
import org.dynmap.MapChunkCache;
public class ZoomedTileRenderer {
public ZoomedTileRenderer(Map<String, Object> configuration) {
}
public void render(final KzedZoomedMapTile zt, final File outputPath) {
public void render(MapChunkCache cache, final KzedZoomedMapTile zt, final File outputPath) {
return; /* Doing this in Default render, since image already loaded */
}
}