mirror of
https://github.com/webbukkit/dynmap.git
synced 2025-02-17 20:31:37 +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>();
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
@ -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;
|
||||||
|
@ -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();
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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 */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user