mirror of
https://github.com/webbukkit/dynmap.git
synced 2024-11-24 19:25:15 +01:00
Prototype of chunk snapshot support - pre-Bukkit API (reflection
based, with fallback to existing APIs)
This commit is contained in:
parent
26f4f7d994
commit
3365a96565
108
src/main/java/org/dynmap/CraftChunkSnapshot.java
Normal file
108
src/main/java/org/dynmap/CraftChunkSnapshot.java
Normal 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;
|
||||
}
|
||||
}
|
198
src/main/java/org/dynmap/MapChunkCache.java
Normal file
198
src/main/java/org/dynmap/MapChunkCache.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
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<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.");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 */
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user