diff --git a/src/main/java/org/dynmap/CraftChunkSnapshot.java b/src/main/java/org/dynmap/CraftChunkSnapshot.java new file mode 100644 index 00000000..de080c69 --- /dev/null +++ b/src/main/java/org/dynmap/CraftChunkSnapshot.java @@ -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; + } +} diff --git a/src/main/java/org/dynmap/MapChunkCache.java b/src/main/java/org/dynmap/MapChunkCache.java new file mode 100644 index 00000000..bcd56715 --- /dev/null +++ b/src/main/java/org/dynmap/MapChunkCache.java @@ -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 loadedChunks = new LinkedList(); + + /** + * 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); + } + } +} diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index 413dbbc3..0a0496d5 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -21,9 +21,7 @@ public class MapManager { public Map inactiveworlds = new HashMap(); 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 active_renders = new HashMap(); @@ -96,22 +94,14 @@ public class MapManager { else { /* Else, single tile render */ tile = tile0; } - DynmapChunk[] requiredChunks = tile.getMap().getRequiredChunks(tile); - LinkedList loadedChunks = new LinkedList(); + 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(new Handler() { @Override public void handle(MapTile t) { - if(do_sync_render) - scheduler.scheduleSyncDelayedTask(plug_in, - new FullWorldRenderState(t), 1); - else - render(t); + scheduler.scheduleSyncDelayedTask(plug_in, + new FullWorldRenderState(t), 1); } }, (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,78 +189,17 @@ 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) { - 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)..."); - + String wname = l.getWorld().getName(); + FullWorldRenderState rndr = active_renders.get(wname); + if(rndr != null) { + Log.info("Full world render of world '" + wname + "' already active."); return; } - World w = world.world; - - Log.info("Full render starting on world '" + w.getName() + "'..."); - for (MapType map : world.maps) { - int requiredChunkCount = 200; - HashSet found = new HashSet(); - HashSet rendered = new HashSet(); - LinkedList renderQueue = new LinkedList(); - LinkedList loadedChunks = new LinkedList(); - - 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."); + 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)..."); } 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; } } diff --git a/src/main/java/org/dynmap/MapType.java b/src/main/java/org/dynmap/MapType.java index 006d1846..5aa5a35e 100644 --- a/src/main/java/org/dynmap/MapType.java +++ b/src/main/java/org/dynmap/MapType.java @@ -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); } diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index 59f30ae2..49a27c3a 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -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) diff --git a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java index d07f5713..59041e16 100644 --- a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java @@ -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; diff --git a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java index 963c5d39..f7d1efae 100644 --- a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java @@ -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(); diff --git a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java index d5314e54..2c379878 100644 --- a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java @@ -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) { diff --git a/src/main/java/org/dynmap/kzedmap/KzedMap.java b/src/main/java/org/dynmap/kzedmap/KzedMap.java index ab395aa7..0b11697b 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMap.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMap.java @@ -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; } diff --git a/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java b/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java index 7674990f..7ad8dd83 100644 --- a/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java @@ -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); } diff --git a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java index a0de1520..7e1d5d09 100644 --- a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java @@ -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 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 */ } }