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>(); public Map<String, DynmapWorld> inactiveworlds = new HashMap<String, DynmapWorld>();
private BukkitScheduler scheduler; private BukkitScheduler scheduler;
private DynmapPlugin plug_in; private DynmapPlugin plug_in;
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>();
@ -96,22 +94,14 @@ public class MapManager {
else { /* Else, single tile render */ else { /* Else, single tile render */
tile = tile0; tile = tile0;
} }
DynmapChunk[] requiredChunks = tile.getMap().getRequiredChunks(tile); DynmapChunk[] requiredChunks = tile.getMap().getRequiredChunks(tile);
LinkedList<DynmapChunk> loadedChunks = new LinkedList<DynmapChunk>(); MapChunkCache cache = new MapChunkCache(world.world, requiredChunks);
World w = world.world; 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? */ if(tile0 != null) { /* Single tile? */
render(tile); /* Just render */ render(cache, tile); /* Just render */
} }
else { else {
if (render(tile)) { if (render(cache, tile)) {
found.remove(tile); found.remove(tile);
rendered.add(tile); rendered.add(tile);
for (MapTile adjTile : map.getAdjecentTiles(tile)) { for (MapTile adjTile : map.getAdjecentTiles(tile)) {
@ -129,22 +119,7 @@ public class MapManager {
} }
} }
/* And unload what we loaded */ /* And unload what we loaded */
while (!loadedChunks.isEmpty()) { cache.unloadChunks();
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);
}
if(tile0 == null) { /* fullrender */ 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));
@ -159,11 +134,8 @@ public class MapManager {
this.tileQueue = new AsynchronousQueue<MapTile>(new Handler<MapTile>() { this.tileQueue = new AsynchronousQueue<MapTile>(new Handler<MapTile>() {
@Override @Override
public void handle(MapTile t) { public void handle(MapTile t) {
if(do_sync_render) scheduler.scheduleSyncDelayedTask(plug_in,
scheduler.scheduleSyncDelayedTask(plug_in, new FullWorldRenderState(t), 1);
new FullWorldRenderState(t), 1);
else
render(t);
} }
}, (int) (configuration.getDouble("renderinterval", 0.5) * 1000)); }, (int) (configuration.getDouble("renderinterval", 0.5) * 1000));
@ -175,9 +147,7 @@ public class MapManager {
} }
}, 10); }, 10);
do_timesliced_render = configuration.getBoolean("timeslicerender", true);
timeslice_interval = configuration.getDouble("timesliceinterval", 0.5); timeslice_interval = configuration.getDouble("timesliceinterval", 0.5);
do_sync_render = configuration.getBoolean("renderonsync", true);
for(ConfigurationNode worldConfiguration : configuration.getNodes("worlds")) { for(ConfigurationNode worldConfiguration : configuration.getNodes("worlds")) {
String worldName = worldConfiguration.getString("name"); String worldName = worldConfiguration.getString("name");
@ -219,78 +189,17 @@ public class MapManager {
Log.severe("Could not render: world '" + l.getWorld().getName() + "' not defined in configuration."); Log.severe("Could not render: world '" + l.getWorld().getName() + "' not defined in configuration.");
return; return;
} }
if(do_timesliced_render) { String wname = l.getWorld().getName();
String wname = l.getWorld().getName(); FullWorldRenderState rndr = active_renders.get(wname);
FullWorldRenderState rndr = active_renders.get(wname); if(rndr != null) {
if(rndr != null) { Log.info("Full world render of world '" + wname + "' already active.");
Log.info("Full world render of world '" + wname + "' already active.");
return;
}
rndr = new FullWorldRenderState(world,l); /* Make new activation record */
active_renders.put(wname, rndr); /* Add to active table */
/* 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; return;
} }
World w = world.world; rndr = new FullWorldRenderState(world,l); /* Make new activation record */
active_renders.put(wname, rndr); /* Add to active table */
Log.info("Full render starting on world '" + w.getName() + "'..."); /* Schedule first tile to be worked */
for (MapType map : world.maps) { scheduler.scheduleSyncDelayedTask(plug_in, rndr, (int)(timeslice_interval*20));
int requiredChunkCount = 200; Log.info("Full render starting on world '" + wname + "' (timesliced)...");
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) { public void activateWorld(World w) {
@ -337,8 +246,8 @@ public class MapManager {
writeQueue.stop(); writeQueue.stop();
} }
public boolean render(MapTile tile) { public boolean render(MapChunkCache cache, MapTile tile) {
boolean result = tile.getMap().render(tile, getTileFile(tile)); boolean result = tile.getMap().render(cache, tile, getTileFile(tile));
//Do update after async file write //Do update after async file write
return result; return result;
@ -387,6 +296,6 @@ public class MapManager {
} }
public boolean doSyncRender() { 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 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.MapTile;
import org.dynmap.MapType; import org.dynmap.MapType;
import org.dynmap.debug.Debug; import org.dynmap.debug.Debug;
import org.dynmap.MapChunkCache;
public class FlatMap extends MapType { public class FlatMap extends MapType {
private String prefix; private String prefix;
@ -73,7 +74,7 @@ public class FlatMap extends MapType {
} }
@Override @Override
public boolean render(MapTile tile, File outputFile) { public boolean render(MapChunkCache cache, MapTile tile, File outputFile) {
FlatMapTile t = (FlatMapTile) tile; FlatMapTile t = (FlatMapTile) tile;
World w = t.getWorld(); World w = t.getWorld();
boolean isnether = (w.getEnvironment() == Environment.NETHER) && (maximumHeight == 127); boolean isnether = (w.getEnvironment() == Environment.NETHER) && (maximumHeight == 127);
@ -93,16 +94,16 @@ public class FlatMap extends MapType {
if(isnether) { if(isnether) {
/* Scan until we hit air */ /* Scan until we hit air */
my = 127; my = 127;
while((blockType = w.getBlockTypeIdAt(mx, my, mz)) != 0) { while((blockType = cache.getBlockTypeID(mx, my, mz)) != 0) {
my--; my--;
if(my < 0) { /* Solid - use top */ if(my < 0) { /* Solid - use top */
my = 127; my = 127;
blockType = w.getBlockTypeIdAt(mx, my, mz); blockType = cache.getBlockTypeID(mx, my, mz);
break; break;
} }
} }
if(blockType == 0) { /* Hit air - now find non-air */ 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--; my--;
if(my < 0) { if(my < 0) {
my = 0; my = 0;
@ -112,14 +113,14 @@ public class FlatMap extends MapType {
} }
} }
else { else {
my = w.getHighestBlockYAt(mx, mz) - 1; my = cache.getHighestBlockYAt(mx, mz) - 1;
if(my > maximumHeight) my = maximumHeight; if(my > maximumHeight) my = maximumHeight;
blockType = w.getBlockTypeIdAt(mx, my, mz); blockType = cache.getBlockTypeID(mx, my, mz);
} }
byte data = 0; byte data = 0;
Color[] colors = colorScheme.colors[blockType]; Color[] colors = colorScheme.colors[blockType];
if(colorScheme.datacolors[blockType] != null) { if(colorScheme.datacolors[blockType] != null) {
data = w.getBlockAt(mx, my, mz).getData(); data = cache.getBlockData(mx, my, mz);
colors = colorScheme.datacolors[blockType][data]; colors = colorScheme.datacolors[blockType][data];
} }
if (colors == null) if (colors == null)

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,10 @@
package org.dynmap.kzedmap; package org.dynmap.kzedmap;
import java.io.File; import java.io.File;
import org.dynmap.MapChunkCache;
public interface MapTileRenderer { public interface MapTileRenderer {
String getName(); 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.io.File;
import java.util.Map; import java.util.Map;
import org.dynmap.MapChunkCache;
public class ZoomedTileRenderer { public class ZoomedTileRenderer {
public ZoomedTileRenderer(Map<String, Object> configuration) { 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 */ return; /* Doing this in Default render, since image already loaded */
} }
} }