Improve handling of /reload, clean up dead code

This commit is contained in:
Mike Primm 2011-07-24 23:23:24 -05:00
parent 3ddce85f89
commit 737bcb98d9
23 changed files with 279 additions and 338 deletions

View File

@ -1,111 +0,0 @@
package org.dynmap;
import java.util.HashMap;
public class Cache<K, V> {
private final int size;
private int len;
private CacheNode head;
private CacheNode tail;
private class CacheNode {
public CacheNode prev;
public CacheNode next;
public K key;
public V value;
public CacheNode(K key, V value) {
this.key = key;
this.value = value;
prev = null;
next = null;
}
public void unlink() {
if (prev == null) {
head = next;
} else {
prev.next = next;
}
if (next == null) {
tail = prev;
} else {
next.prev = prev;
}
prev = null;
next = null;
len--;
}
public void append() {
if (tail == null) {
head = this;
tail = this;
} else {
tail.next = this;
prev = tail;
tail = this;
}
len++;
}
}
private HashMap<K, CacheNode> map;
public Cache(int size) {
this.size = size;
len = 0;
head = null;
tail = null;
map = new HashMap<K, CacheNode>();
}
/*
* returns value for key, if key exists in the cache otherwise null
*/
public V get(K key) {
CacheNode n = map.get(key);
if (n == null)
return null;
return n.value;
}
/*
* puts a new key-value pair in the cache if the key existed already, the
* value is updated, and the old value is returned if the key didn't exist,
* it is added; the oldest value (now pushed out of the cache) may be
* returned, or null if the cache isn't yet full
*/
public V put(K key, V value) {
CacheNode n = map.get(key);
if (n == null) {
V ret = null;
if (len >= size) {
CacheNode first = head;
first.unlink();
map.remove(first.key);
ret = first.value;
}
CacheNode add = new CacheNode(key, value);
add.append();
map.put(key, add);
return ret;
} else {
n.unlink();
V old = n.value;
n.value = value;
n.append();
return old;
}
}
}

View File

@ -235,4 +235,7 @@ public class ColorScheme {
else
return null;
}
public static void reset() {
cache.clear();
}
}

View File

@ -15,19 +15,22 @@ public class DynmapPlayerChatListener extends PlayerListener {
@Override
public void onPlayerChat(PlayerChatEvent event) {
if(event.isCancelled()) return;
plugin.mapManager.pushUpdate(new Client.ChatMessage("player", "",
if(plugin.mapManager != null)
plugin.mapManager.pushUpdate(new Client.ChatMessage("player", "",
event.getPlayer().getDisplayName(), event.getMessage(),
event.getPlayer().getName()));
}
@Override
public void onPlayerJoin(PlayerJoinEvent event) {
plugin.mapManager.pushUpdate(new Client.PlayerJoinMessage(event.getPlayer().getDisplayName(), event.getPlayer().getName()));
if(plugin.mapManager != null)
plugin.mapManager.pushUpdate(new Client.PlayerJoinMessage(event.getPlayer().getDisplayName(), event.getPlayer().getName()));
}
@Override
public void onPlayerQuit(PlayerQuitEvent event) {
plugin.mapManager.pushUpdate(new Client.PlayerQuitMessage(event.getPlayer().getDisplayName(), event.getPlayer().getName()));
if(plugin.mapManager != null)
plugin.mapManager.pushUpdate(new Client.PlayerQuitMessage(event.getPlayer().getDisplayName(), event.getPlayer().getName()));
}
}

View File

@ -176,6 +176,9 @@ public class DynmapPlugin extends JavaPlugin {
@Override
public void onEnable() {
/* Start with clean events */
events = new Events();
permissions = NijikokunPermissions.create(getServer(), "dynmap");
if (permissions == null)
permissions = new OpPermissions(new String[] { "fullrender", "reload" });
@ -303,6 +306,7 @@ public class DynmapPlugin extends JavaPlugin {
if (mapManager != null) {
mapManager.stopRendering();
mapManager = null;
}
if (webServer != null) {

View File

@ -7,7 +7,7 @@ import org.bukkit.World;
import org.bukkit.Location;
import org.dynmap.debug.Debug;
import org.dynmap.kzedmap.KzedMap;
import org.dynmap.kzedmap.KzedMap.KzedBufferedImage;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.FileLockManager;
import org.dynmap.utils.MapChunkCache;
@ -361,7 +361,7 @@ public class DynmapWorld {
Debug.debug("processZoomFile(" + pd.baseprefix + "," + zf.getPath() + "," + tx + "," + ty + ")");
int width = 128, height = 128;
BufferedImage zIm = null;
KzedBufferedImage kzIm = null;
DynmapBufferedImage kzIm = null;
int[] argb = new int[width*height];
int step = pd.stepsize << pd.zoomlevel;
int ztx = tx;
@ -370,7 +370,7 @@ public class DynmapWorld {
ty = ty - (pd.neg_step_y?step:0); /* Adjust for negative step */
/* create image buffer */
kzIm = KzedMap.allocateBufferedImage(width, height);
kzIm = DynmapBufferedImage.allocateBufferedImage(width, height);
zIm = kzIm.buf_img;
for(int i = 0; i < 4; i++) {
@ -434,7 +434,7 @@ public class DynmapWorld {
}
} finally {
FileLockManager.releaseWriteLock(zf);
KzedMap.freeBufferedImage(kzIm);
DynmapBufferedImage.freeBufferedImage(kzIm);
}
}
}

View File

@ -47,7 +47,7 @@ public class InternalClientUpdateComponent extends ClientUpdateComponent {
protected void webChat(String name, String message) {
// TODO: Change null to something meaningful.
plugin.mapManager.pushUpdate(new Client.ChatMessage("web", null, name, message, null));
Log.info(plugin.configuration.getString("webprefix", "\u00A72[WEB] ") + name + ": " + plugin.configuration.getString("websuffix", "\u00A7f") + message);
Log.info(unescapeString(plugin.configuration.getString("webprefix", "\u00A72[WEB] ")) + name + ": " + unescapeString(plugin.configuration.getString("websuffix", "\u00A7f")) + message);
ChatEvent event = new ChatEvent("web", name, message);
plugin.events.trigger("webchat", event);
}

View File

@ -22,8 +22,6 @@ import static org.dynmap.JSONUtils.*;
import java.nio.charset.Charset;
public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
protected TimerTask task;
protected Timer timer;
protected long jsonInterval;
protected long currentTimestamp = 0;
protected long lastTimestamp = 0;
@ -39,7 +37,7 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
final boolean allowwebchat = configuration.getBoolean("allowwebchat", false);
jsonInterval = (long)(configuration.getFloat("writeinterval", 1) * 1000);
hidewebchatip = configuration.getBoolean("hidewebchatip", false);
task = new TimerTask() {
MapManager.scheduleDelayedJob(new Runnable() {
@Override
public void run() {
currentTimestamp = System.currentTimeMillis();
@ -48,10 +46,9 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
handleWebChat();
}
lastTimestamp = currentTimestamp;
}
};
timer = new Timer();
timer.scheduleAtFixedRate(task, jsonInterval, jsonInterval);
MapManager.scheduleDelayedJob(this, jsonInterval);
}}, jsonInterval);
plugin.events.addListener("buildclientconfiguration", new Event.Listener<JSONObject>() {
@Override
public void triggered(JSONObject t) {
@ -84,6 +81,7 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
return new File(plugin.getDataFolder(), webpath.toString());
}
private static final int RETRY_LIMIT = 5;
protected void writeConfiguration() {
File outputFile;
File outputTempFile;
@ -91,17 +89,27 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
plugin.events.trigger("buildclientconfiguration", clientConfiguration);
outputFile = getStandaloneFile("dynmap_config.json");
outputTempFile = getStandaloneFile("dynmap_config.json.new");
try {
FileOutputStream fos = new FileOutputStream(outputTempFile);
fos.write(clientConfiguration.toJSONString().getBytes("UTF-8"));
fos.close();
outputFile.delete();
outputTempFile.renameTo(outputFile);
} catch (FileNotFoundException ex) {
Log.severe("Exception while writing JSON-configuration-file.", ex);
} catch (IOException ioe) {
Log.severe("Exception while writing JSON-configuration-file.", ioe);
int retrycnt = 0;
boolean done = false;
while(!done) {
try {
FileOutputStream fos = new FileOutputStream(outputTempFile);
fos.write(clientConfiguration.toJSONString().getBytes("UTF-8"));
fos.close();
outputFile.delete();
outputTempFile.renameTo(outputFile);
done = true;
} catch (IOException ioe) {
if(retrycnt < RETRY_LIMIT) {
try { Thread.sleep(20 * (1 << retrycnt)); } catch (InterruptedException ix) {}
retrycnt++;
}
else {
Log.severe("Exception while writing JSON-configuration-file.", ioe);
done = true;
}
}
}
}
@ -120,16 +128,26 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
outputFile = getStandaloneFile("dynmap_" + world.getName() + ".json");
outputTempFile = getStandaloneFile("dynmap_" + world.getName() + ".json.new");
try {
FileOutputStream fos = new FileOutputStream(outputTempFile);
fos.write(Json.stringifyJson(update).getBytes("UTF-8"));
fos.close();
outputFile.delete();
outputTempFile.renameTo(outputFile);
} catch (FileNotFoundException ex) {
Log.severe("Exception while writing JSON-file.", ex);
} catch (IOException ioe) {
Log.severe("Exception while writing JSON-file.", ioe);
int retrycnt = 0;
boolean done = false;
while(!done) {
try {
FileOutputStream fos = new FileOutputStream(outputTempFile);
fos.write(Json.stringifyJson(update).getBytes("UTF-8"));
fos.close();
outputFile.delete();
outputTempFile.renameTo(outputFile);
done = true;
} catch (IOException ioe) {
if(retrycnt < RETRY_LIMIT) {
try { Thread.sleep(20 * (1 << retrycnt)); } catch (InterruptedException ix) {}
retrycnt++;
}
else {
Log.severe("Exception while writing JSON-file.", ioe);
done = true;
}
}
}
plugin.events.<ClientUpdateEvent>trigger("clientupdatewritten", clientUpdate);
}
@ -179,7 +197,7 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
protected void webChat(String name, String message) {
// TODO: Change null to something meaningful.
plugin.mapManager.pushUpdate(new Client.ChatMessage("web", null, name, message, null));
Log.info(plugin.configuration.getString("webprefix", "\u00A2[WEB] ") + name + ": " + plugin.configuration.getString("websuffix", "\u00A7f") + message);
Log.info(unescapeString(plugin.configuration.getString("webprefix", "\u00A2[WEB] ")) + name + ": " + unescapeString(plugin.configuration.getString("websuffix", "\u00A7f")) + message);
ChatEvent event = new ChatEvent("web", name, message);
plugin.events.trigger("webchat", event);
}
@ -187,6 +205,5 @@ public class JsonFileClientUpdateComponent extends ClientUpdateComponent {
@Override
public void dispose() {
super.dispose();
timer.cancel();
}
}

View File

@ -1,7 +1,6 @@
package org.dynmap;
import java.io.File;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@ -12,8 +11,6 @@ import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ScheduledFuture;
@ -21,7 +18,6 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import org.bukkit.ChunkSnapshot;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.scheduler.BukkitScheduler;
@ -29,7 +25,6 @@ import org.bukkit.command.CommandSender;
import org.dynmap.DynmapWorld.AutoGenerateOption;
import org.dynmap.debug.Debug;
import org.dynmap.hdmap.HDMapManager;
import org.dynmap.utils.LRULinkedHashMap;
import org.dynmap.utils.LegacyMapChunkCache;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.NewMapChunkCache;
@ -66,7 +61,7 @@ public class MapManager {
public SnapshotCache sscache;
/* Thread pool for processing renders */
private DynmapScheduledThreadPoolExecutor renderpool;
private DynmapScheduledThreadPoolExecutor render_pool;
private static final int POOL_SIZE = 3;
private HashMap<String, MapStats> mapstats = new HashMap<String, MapStats>();
@ -102,6 +97,9 @@ public class MapManager {
DynmapScheduledThreadPoolExecutor() {
super(POOL_SIZE);
this.setThreadFactory(new OurThreadFactory());
/* Set shutdown policy to stop everything */
setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
}
protected void afterExecute(Runnable r, Throwable x) {
@ -337,10 +335,10 @@ public class MapManager {
if(tile0 == null) { /* fullrender */
long tend = System.currentTimeMillis();
if(timeslice_int > (tend-tstart)) { /* We were fast enough */
renderpool.schedule(this, timeslice_int - (tend-tstart), TimeUnit.MILLISECONDS);
scheduleDelayedJob(this, timeslice_int - (tend-tstart));
}
else { /* Schedule to run ASAP */
renderpool.execute(this);
scheduleDelayedJob(this, 0);
}
}
else {
@ -360,7 +358,7 @@ public class MapManager {
boolean isday = new_servertime >= 0 && new_servertime < 13700;
w.servertime = new_servertime;
if(wasday != isday) {
MapManager.mapman.pushUpdate(w.world, new Client.DayNight(isday));
pushUpdate(w.world, new Client.DayNight(isday));
}
}
return 0;
@ -371,7 +369,7 @@ public class MapManager {
} catch (Exception ix) {
Log.severe(ix);
}
renderpool.schedule(this, 5, TimeUnit.SECONDS);
scheduleDelayedJob(this, 5000);
}
}
@ -382,7 +380,7 @@ public class MapManager {
for(DynmapWorld w : wl) {
w.freshenZoomOutFiles();
}
renderpool.schedule(this, zoomout_period, TimeUnit.SECONDS);
scheduleDelayedJob(this, zoomout_period*1000);
Debug.debug("DoZoomOutProcessing finished");
}
}
@ -390,6 +388,8 @@ public class MapManager {
public MapManager(DynmapPlugin plugin, ConfigurationNode configuration) {
plug_in = plugin;
mapman = this;
/* Clear color scheme */
ColorScheme.reset();
/* Initialize HD map manager */
hdmapman = new HDMapManager();
hdmapman.loadHDShaders(plugin);
@ -400,7 +400,7 @@ public class MapManager {
this.tileQueue = new AsynchronousQueue<MapTile>(new Handler<MapTile>() {
@Override
public void handle(MapTile t) {
renderpool.execute(new FullWorldRenderState(t));
scheduleDelayedJob(new FullWorldRenderState(t), 0);
}
}, (int) (configuration.getDouble("renderinterval", 0.5) * 1000));
@ -441,7 +441,8 @@ public class MapManager {
active_renders.put(wname, rndr); /* Add to active table */
}
/* Schedule first tile to be worked */
renderpool.execute(rndr);
scheduleDelayedJob(rndr, 0);
sender.sendMessage("Full render starting on world '" + wname + "'...");
}
@ -463,7 +464,7 @@ public class MapManager {
active_renders.put(wname, rndr); /* Add to active table */
}
/* Schedule first tile to be worked */
renderpool.execute(rndr);
scheduleDelayedJob(rndr, 0);
sender.sendMessage("Render of " + radius + " block radius starting on world '" + wname + "'...");
}
@ -583,16 +584,31 @@ public class MapManager {
tileQueue.push(tile);
}
public static void scheduleDelayedJob(Runnable job, long delay_in_msec) {
if((mapman != null) && (mapman.render_pool != null)) {
if(delay_in_msec > 0)
mapman.render_pool.schedule(job, delay_in_msec, TimeUnit.MILLISECONDS);
else
mapman.render_pool.execute(job);
}
}
public void startRendering() {
tileQueue.start();
renderpool = new DynmapScheduledThreadPoolExecutor();
renderpool.schedule(new DoZoomOutProcessing(), 60000, TimeUnit.MILLISECONDS);
renderpool.schedule(new CheckWorldTimes(), 5, TimeUnit.SECONDS);
render_pool = new DynmapScheduledThreadPoolExecutor();
scheduleDelayedJob(new DoZoomOutProcessing(), 60000);
scheduleDelayedJob(new CheckWorldTimes(), 5000);
}
public void stopRendering() {
renderpool.shutdown();
render_pool.shutdown();
try {
render_pool.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException ix) {
}
tileQueue.stop();
mapman = null;
hdmapman = null;
}
private HashMap<World, File> worldTileDirectories = new HashMap<World, File>();

View File

@ -44,17 +44,20 @@ public class SimpleWebChatComponent extends Component {
@Override
public void onPlayerChat(PlayerChatEvent event) {
if(event.isCancelled()) return;
plugin.mapManager.pushUpdate(new Client.ChatMessage("player", "", event.getPlayer().getDisplayName(), event.getMessage(), event.getPlayer().getName()));
if(plugin.mapManager != null)
plugin.mapManager.pushUpdate(new Client.ChatMessage("player", "", event.getPlayer().getDisplayName(), event.getMessage(), event.getPlayer().getName()));
}
@Override
public void onPlayerJoin(PlayerJoinEvent event) {
plugin.mapManager.pushUpdate(new Client.PlayerJoinMessage(event.getPlayer().getDisplayName(), event.getPlayer().getName()));
if(plugin.mapManager != null)
plugin.mapManager.pushUpdate(new Client.PlayerJoinMessage(event.getPlayer().getDisplayName(), event.getPlayer().getName()));
}
@Override
public void onPlayerQuit(PlayerQuitEvent event) {
plugin.mapManager.pushUpdate(new Client.PlayerQuitMessage(event.getPlayer().getDisplayName(), event.getPlayer().getName()));
if(plugin.mapManager != null)
plugin.mapManager.pushUpdate(new Client.PlayerQuitMessage(event.getPlayer().getDisplayName(), event.getPlayer().getName()));
}
}

View File

@ -1,50 +0,0 @@
package org.dynmap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.NoSuchElementException;
import java.util.Set;
public class StaleQueue {
/* a list of MapTiles to be updated */
private LinkedList<MapTile> staleTilesQueue;
private Set<MapTile> staleTiles;
public StaleQueue() {
staleTilesQueue = new LinkedList<MapTile>();
staleTiles = new HashSet<MapTile>();
}
public int size() {
return staleTilesQueue.size();
}
/* put a MapTile that needs to be regenerated on the list of stale tiles */
public boolean pushStaleTile(MapTile m) {
synchronized (MapManager.lock) {
if (staleTiles.add(m)) {
staleTilesQueue.addLast(m);
return true;
}
return false;
}
}
/*
* get next MapTile that needs to be regenerated, or null the mapTile is
* removed from the list of stale tiles!
*/
public MapTile popStaleTile() {
synchronized (MapManager.lock) {
try {
MapTile t = staleTilesQueue.removeFirst();
if (!staleTiles.remove(t)) {
// This should never happen.
}
return t;
} catch (NoSuchElementException e) {
return null;
}
}
}
}

View File

@ -26,7 +26,7 @@ import org.dynmap.MapType;
import org.dynmap.MapType.MapStep;
import org.dynmap.debug.Debug;
import org.dynmap.kzedmap.KzedMap;
import org.dynmap.kzedmap.KzedMap.KzedBufferedImage;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.FileLockManager;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
@ -134,12 +134,12 @@ public class FlatMap extends MapType {
Color rslt = new Color();
int[] pixel = new int[4];
int[] pixel_day = null;
KzedBufferedImage im = KzedMap.allocateBufferedImage(t.size, t.size);
DynmapBufferedImage im = DynmapBufferedImage.allocateBufferedImage(t.size, t.size);
int[] argb_buf = im.argb_buf;
KzedBufferedImage im_day = null;
DynmapBufferedImage im_day = null;
int[] argb_buf_day = null;
if(night_and_day) {
im_day = KzedMap.allocateBufferedImage(t.size, t.size);
im_day = DynmapBufferedImage.allocateBufferedImage(t.size, t.size);
argb_buf_day = im_day.argb_buf;
pixel_day = new int[4];
}
@ -300,7 +300,7 @@ public class FlatMap extends MapType {
}
} finally {
FileLockManager.releaseWriteLock(outputFile);
KzedMap.freeBufferedImage(im);
DynmapBufferedImage.freeBufferedImage(im);
}
MapManager.mapman.updateStatistics(tile, null, true, tile_update, !rendered);
@ -332,7 +332,7 @@ public class FlatMap extends MapType {
}
} finally {
FileLockManager.releaseWriteLock(dayfile);
KzedMap.freeBufferedImage(im_day);
DynmapBufferedImage.freeBufferedImage(im_day);
}
MapManager.mapman.updateStatistics(tile, "day", true, tile_update, !rendered);
}

View File

@ -265,6 +265,11 @@ public class HDBlockModels {
* Load models
*/
public static void loadModels(File datadir) {
/* Reset models-by-ID-Data cache */
models_by_id_data.clear();
/* Reset scaled models by scale cache */
scaled_models_by_scale.clear();
/* Load block models */
InputStream in = TexturePack.class.getResourceAsStream("/models.txt");
if(in != null) {

View File

@ -26,8 +26,8 @@ import org.dynmap.debug.Debug;
import org.dynmap.utils.MapIterator.BlockStep;
import org.dynmap.hdmap.TexturePack.BlockTransparency;
import org.dynmap.hdmap.TexturePack.HDTextureMap;
import org.dynmap.kzedmap.KzedMap.KzedBufferedImage;
import org.dynmap.kzedmap.KzedMap;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.FileLockManager;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
@ -836,8 +836,8 @@ public class IsoHDPerspective implements HDPerspective {
/* Check if nether world */
boolean isnether = tile.getWorld().getEnvironment() == Environment.NETHER;
/* Create buffered image for each */
KzedBufferedImage im[] = new KzedBufferedImage[numshaders];
KzedBufferedImage dayim[] = new KzedBufferedImage[numshaders];
DynmapBufferedImage im[] = new DynmapBufferedImage[numshaders];
DynmapBufferedImage dayim[] = new DynmapBufferedImage[numshaders];
int[][] argb_buf = new int[numshaders][];
int[][] day_argb_buf = new int[numshaders][];
@ -848,10 +848,10 @@ public class IsoHDPerspective implements HDPerspective {
need_emittedlightlevel = true;
if(shader.isSkyLightLevelNeeded() || lighting.isSkyLightLevelNeeded())
need_skylightlevel = true;
im[i] = KzedMap.allocateBufferedImage(tileWidth, tileHeight);
im[i] = DynmapBufferedImage.allocateBufferedImage(tileWidth, tileHeight);
argb_buf[i] = im[i].argb_buf;
if(lighting.isNightAndDayEnabled()) {
dayim[i] = KzedMap.allocateBufferedImage(tileWidth, tileHeight);
dayim[i] = DynmapBufferedImage.allocateBufferedImage(tileWidth, tileHeight);
day_argb_buf[i] = dayim[i].argb_buf;
}
}
@ -931,7 +931,7 @@ public class IsoHDPerspective implements HDPerspective {
}
} finally {
FileLockManager.releaseWriteLock(f);
KzedMap.freeBufferedImage(im[i]);
DynmapBufferedImage.freeBufferedImage(im[i]);
}
MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, !rendered[i]);
/* Handle day image, if needed */
@ -964,7 +964,7 @@ public class IsoHDPerspective implements HDPerspective {
}
} finally {
FileLockManager.releaseWriteLock(f);
KzedMap.freeBufferedImage(dayim[i]);
DynmapBufferedImage.freeBufferedImage(dayim[i]);
}
MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, !rendered[i]);
}

View File

@ -22,6 +22,7 @@ import javax.imageio.ImageIO;
import org.dynmap.Color;
import org.dynmap.DynmapPlugin;
import org.dynmap.Log;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.MapIterator.BlockStep;
import org.dynmap.kzedmap.KzedMap;
import org.dynmap.utils.MapIterator;
@ -553,7 +554,7 @@ public class TexturePack {
System.arraycopy(terrain_argb[i],native_scale*y,outbuf,((i>>4)*native_scale+y)*terrain_width + (i & 0xF)*native_scale, native_scale);
}
}
BufferedImage img = KzedMap.createBufferedImage(outbuf, terrain_width, terrain_height);
BufferedImage img = DynmapBufferedImage.createBufferedImage(outbuf, terrain_width, terrain_height);
ImageIO.write(img, "png", f);
}
@ -561,6 +562,8 @@ public class TexturePack {
* Load texture pack mappings
*/
public static void loadTextureMapping(File datadir) {
/* Start clean with texture packs - need to be loaded after mapping */
packs.clear();
/* Initialize map with blank map for all entries */
HDTextureMap.initializeTable();
/* Load block models */

View File

@ -230,12 +230,13 @@ public class HeroChatHandler {
hcchannels.contains(c.getNick())) {
if(cce.isSentByPlayer()) { /* Player message? */
org.bukkit.entity.Player p = plugin.getServer().getPlayer(cce.getSource());
if(p != null)
if((p != null) && (plugin.mapManager != null)) {
plugin.mapManager.pushUpdate(new Client.ChatMessage("player",
c.getNick(),
p.getDisplayName(),
cce.getMessage(),
p.getName()));
}
}
}
}

View File

@ -20,7 +20,7 @@ import org.dynmap.DynmapWorld;
import org.dynmap.MapManager;
import org.dynmap.TileHashManager;
import org.dynmap.debug.Debug;
import org.dynmap.kzedmap.KzedMap.KzedBufferedImage;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.FileLockManager;
import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator;
@ -119,15 +119,15 @@ public class DefaultTileRenderer implements MapTileRenderer {
public boolean render(MapChunkCache cache, KzedMapTile tile, File outputFile) {
World world = tile.getWorld();
boolean isnether = (world.getEnvironment() == Environment.NETHER);
KzedBufferedImage im = KzedMap.allocateBufferedImage(KzedMap.tileWidth, KzedMap.tileHeight);
KzedBufferedImage zim = KzedMap.allocateBufferedImage(KzedMap.tileWidth/2, KzedMap.tileHeight/2);
DynmapBufferedImage im = DynmapBufferedImage.allocateBufferedImage(KzedMap.tileWidth, KzedMap.tileHeight);
DynmapBufferedImage zim = DynmapBufferedImage.allocateBufferedImage(KzedMap.tileWidth/2, KzedMap.tileHeight/2);
boolean isempty = true;
KzedBufferedImage im_day = null;
KzedBufferedImage zim_day = null;
DynmapBufferedImage im_day = null;
DynmapBufferedImage zim_day = null;
if(night_and_day) {
im_day = KzedMap.allocateBufferedImage(KzedMap.tileWidth, KzedMap.tileHeight);
zim_day = KzedMap.allocateBufferedImage(KzedMap.tileWidth/2, KzedMap.tileHeight/2);
im_day = DynmapBufferedImage.allocateBufferedImage(KzedMap.tileWidth, KzedMap.tileHeight);
zim_day = DynmapBufferedImage.allocateBufferedImage(KzedMap.tileWidth/2, KzedMap.tileHeight/2);
}
int ix = KzedMap.anchorx + tile.px / 2 + tile.py / 2 - ((127-maximumHeight)/2);
@ -255,9 +255,9 @@ public class DefaultTileRenderer implements MapTileRenderer {
}
private void doFileWrites(final File fname, final KzedMapTile mtile,
final KzedBufferedImage img, final KzedBufferedImage img_day,
final DynmapBufferedImage img, final DynmapBufferedImage img_day,
final KzedZoomedMapTile zmtile, final File zoomFile,
final KzedBufferedImage zimg, final KzedBufferedImage zimg_day, boolean rendered) {
final DynmapBufferedImage zimg, final DynmapBufferedImage zimg_day, boolean rendered) {
/* Get coordinates of zoomed tile */
int ox = (mtile.px == zmtile.getTileX())?0:KzedMap.tileWidth/2;
@ -288,7 +288,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
}
} finally {
FileLockManager.releaseWriteLock(fname);
KzedMap.freeBufferedImage(img);
DynmapBufferedImage.freeBufferedImage(img);
}
MapManager.mapman.updateStatistics(mtile, null, true, updated_fname, !rendered);
@ -318,7 +318,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
}
} finally {
FileLockManager.releaseWriteLock(dfname);
KzedMap.freeBufferedImage(img_day);
DynmapBufferedImage.freeBufferedImage(img_day);
}
MapManager.mapman.updateStatistics(mtile, "day", true, updated_dfname, !rendered);
}
@ -337,7 +337,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
}
} finally {
FileLockManager.releaseWriteLock(zoomFile);
KzedMap.freeBufferedImage(zimg);
DynmapBufferedImage.freeBufferedImage(zimg);
}
MapManager.mapman.updateStatistics(zmtile, null, true, ztile_updated, !rendered);
@ -355,16 +355,16 @@ public class DefaultTileRenderer implements MapTileRenderer {
}
} finally {
FileLockManager.releaseWriteLock(zoomFile_day);
KzedMap.freeBufferedImage(zimg_day);
DynmapBufferedImage.freeBufferedImage(zimg_day);
}
MapManager.mapman.updateStatistics(zmtile, "day", true, ztile_updated, !rendered);
}
}
private void saveZoomedTile(final KzedZoomedMapTile zmtile, final File zoomFile,
final KzedBufferedImage zimg, int ox, int oy, String subkey) {
final DynmapBufferedImage zimg, int ox, int oy, String subkey) {
BufferedImage zIm = null;
KzedBufferedImage kzIm = null;
DynmapBufferedImage kzIm = null;
try {
zIm = ImageIO.read(zoomFile);
} catch (IOException e) {
@ -374,7 +374,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
boolean zIm_allocated = false;
if (zIm == null) {
/* create new one */
kzIm = KzedMap.allocateBufferedImage(KzedMap.tileWidth, KzedMap.tileHeight);
kzIm = DynmapBufferedImage.allocateBufferedImage(KzedMap.tileWidth, KzedMap.tileHeight);
zIm = kzIm.buf_img;
zIm_allocated = true;
Debug.debug("New zoom-out tile created " + zmtile.getFilename());
@ -399,7 +399,7 @@ public class DefaultTileRenderer implements MapTileRenderer {
}
if(zIm_allocated)
KzedMap.freeBufferedImage(kzIm);
DynmapBufferedImage.freeBufferedImage(kzIm);
else
zIm.flush();

View File

@ -19,6 +19,7 @@ import org.dynmap.MapManager;
import org.dynmap.MapTile;
import org.dynmap.MapType;
import org.dynmap.MapType.MapStep;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.MapChunkCache;
import org.json.simple.JSONObject;
import java.awt.image.DataBufferInt;
@ -50,20 +51,6 @@ public class KzedMap extends MapType {
MapTileRenderer[] renderers;
private boolean isbigmap;
/* BufferedImage with direct access to its ARGB-formatted data buffer */
public static class KzedBufferedImage {
public BufferedImage buf_img;
public int[] argb_buf;
public int width;
public int height;
}
/* BufferedImage cache - we use the same things a lot... */
private static Object lock = new Object();
private static HashMap<Long, LinkedList<KzedBufferedImage>> imgcache =
new HashMap<Long, LinkedList<KzedBufferedImage>>(); /* Indexed by resolution - X<<32+Y */
private static final int CACHE_LIMIT = 10;
public KzedMap(ConfigurationNode configuration) {
Log.verboseinfo("Loading renderers for map '" + getClass().toString() + "'...");
List<MapTileRenderer> renderers = configuration.<MapTileRenderer>createInstances("renderers", new Class<?>[0], new Object[0]);
@ -267,52 +254,6 @@ public class KzedMap extends MapType {
return y - (y % zTileHeight);
}
/**
* Allocate buffered image from pool, if possible
* @param x - x dimension
* @param y - y dimension
*/
public static KzedBufferedImage allocateBufferedImage(int x, int y) {
KzedBufferedImage img = null;
synchronized(lock) {
long k = (x<<16) + y;
LinkedList<KzedBufferedImage> ll = imgcache.get(k);
if(ll != null) {
img = ll.poll();
}
}
if(img != null) { /* Got it - reset it for use */
Arrays.fill(img.argb_buf, 0);
}
else {
img = new KzedBufferedImage();
img.width = x;
img.height = y;
img.argb_buf = new int[x*y];
}
img.buf_img = createBufferedImage(img.argb_buf, img.width, img.height);
return img;
}
/**
* Return buffered image to pool
*/
public static void freeBufferedImage(KzedBufferedImage img) {
img.buf_img.flush();
img.buf_img = null; /* Toss bufferedimage - seems to hold on to other memory */
synchronized(lock) {
long k = (img.width<<16) + img.height;
LinkedList<KzedBufferedImage> ll = imgcache.get(k);
if(ll == null) {
ll = new LinkedList<KzedBufferedImage>();
imgcache.put(k, ll);
}
if(ll.size() < CACHE_LIMIT) {
ll.add(img);
img = null;
}
}
}
public boolean isBiomeDataNeeded() {
for(MapTileRenderer r : renderers) {
@ -382,21 +323,4 @@ public class KzedMap extends MapType {
renderer.buildClientConfiguration(worldObject, world, this);
}
}
/* ARGB band masks */
private static final int [] band_masks = {0xFF0000, 0xFF00, 0xff, 0xff000000};
/**
* Build BufferedImage from provided ARGB array and dimensions
*/
public static BufferedImage createBufferedImage(int[] argb_buf, int w, int h) {
/* Create integer-base data buffer */
DataBuffer db = new DataBufferInt (argb_buf, w*h);
/* Create writable raster */
WritableRaster raster = Raster.createPackedRaster(db, w, h, w, band_masks, null);
/* RGB color model */
ColorModel color_model = ColorModel.getRGBdefault ();
/* Return buffered image */
return new BufferedImage (color_model, raster, false, null);
}
}

View File

@ -0,0 +1,88 @@
package org.dynmap.utils;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
public class DynmapBufferedImage {
public BufferedImage buf_img;
public int[] argb_buf;
public int width;
public int height;
/* BufferedImage cache - we use the same things a lot... */
private static Object lock = new Object();
private static HashMap<Long, LinkedList<DynmapBufferedImage>> imgcache =
new HashMap<Long, LinkedList<DynmapBufferedImage>>(); /* Indexed by resolution - X<<32+Y */
private static final int CACHE_LIMIT = 10;
/**
* Allocate buffered image from pool, if possible
* @param x - x dimension
* @param y - y dimension
*/
public static DynmapBufferedImage allocateBufferedImage(int x, int y) {
DynmapBufferedImage img = null;
synchronized(lock) {
long k = (x<<16) + y;
LinkedList<DynmapBufferedImage> ll = imgcache.get(k);
if(ll != null) {
img = ll.poll();
}
}
if(img != null) { /* Got it - reset it for use */
Arrays.fill(img.argb_buf, 0);
}
else {
img = new DynmapBufferedImage();
img.width = x;
img.height = y;
img.argb_buf = new int[x*y];
}
img.buf_img = createBufferedImage(img.argb_buf, img.width, img.height);
return img;
}
/**
* Return buffered image to pool
*/
public static void freeBufferedImage(DynmapBufferedImage img) {
img.buf_img.flush();
img.buf_img = null; /* Toss bufferedimage - seems to hold on to other memory */
synchronized(lock) {
long k = (img.width<<16) + img.height;
LinkedList<DynmapBufferedImage> ll = imgcache.get(k);
if(ll == null) {
ll = new LinkedList<DynmapBufferedImage>();
imgcache.put(k, ll);
}
if(ll.size() < CACHE_LIMIT) {
ll.add(img);
img = null;
}
}
}
/* ARGB band masks */
private static final int [] band_masks = {0xFF0000, 0xFF00, 0xff, 0xff000000};
/**
* Build BufferedImage from provided ARGB array and dimensions
*/
public static BufferedImage createBufferedImage(int[] argb_buf, int w, int h) {
/* Create integer-base data buffer */
DataBuffer db = new DataBufferInt (argb_buf, w*h);
/* Create writable raster */
WritableRaster raster = Raster.createPackedRaster(db, w, h, w, band_masks, null);
/* RGB color model */
ColorModel color_model = ColorModel.getRGBdefault ();
/* Return buffered image */
return new BufferedImage (color_model, raster, false, null);
}
}

View File

@ -11,6 +11,7 @@ import java.net.Socket;
import java.net.SocketAddress;
import java.util.Collections;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.logging.Logger;
@ -28,6 +29,9 @@ public class HttpServer extends Thread {
private boolean check_banned_ips;
public SortedMap<String, HttpHandler> handlers = new TreeMap<String, HttpHandler>(Collections.reverseOrder());
private Object lock = new Object();
private HashSet<HttpServerConnection> active_connections = new HashSet<HttpServerConnection>();
public HttpServer(InetAddress bindAddress, int port, boolean check_banned_ips) {
this.bindAddress = bindAddress;
@ -62,6 +66,9 @@ public class HttpServer extends Thread {
}
HttpServerConnection requestThread = new HttpServerConnection(socket, this);
synchronized(lock) {
active_connections.add(requestThread);
}
requestThread.start();
} catch (IOException e) {
if(listeningThread != null) /* Only report this if we didn't initiate the shutdown */
@ -83,11 +90,25 @@ public class HttpServer extends Thread {
sock.close();
sock = null;
}
/* And kill off the active connections */
HashSet<HttpServerConnection> sc;
synchronized(lock) {
sc = new HashSet<HttpServerConnection>(active_connections);
}
for(HttpServerConnection c : sc) {
c.shutdownConnection();
}
} catch (IOException e) {
Log.warning("Exception while closing socket for webserver shutdown", e);
}
}
public void connectionEnded(HttpServerConnection c) {
synchronized(lock) {
active_connections.remove(c);
}
}
private HashSet<String> banned_ips = new HashSet<String>();
private HashSet<String> banned_ips_notified = new HashSet<String>();
private long last_loaded = 0;

View File

@ -19,13 +19,13 @@ import java.net.InetSocketAddress;
public class HttpServerConnection extends Thread {
protected static final Logger log = Logger.getLogger("Minecraft");
protected static final String LOG_PREFIX = "[dynmap] ";
private static Pattern requestHeaderLine = Pattern.compile("^(\\S+)\\s+(\\S+)\\s+HTTP/(.+)$");
private static Pattern requestHeaderField = Pattern.compile("^([^:]+):\\s*(.+)$");
private Socket socket;
private HttpServer server;
private boolean do_shutdown;
private PrintStream printOut;
private StringWriter sw = new StringWriter();
@ -35,6 +35,7 @@ public class HttpServerConnection extends Thread {
public HttpServerConnection(Socket socket, HttpServer server) {
this.socket = socket;
this.server = server;
do_shutdown = false;
}
private final static void readLine(InputStream in, StringWriter sw) throws IOException {
@ -232,8 +233,10 @@ public class HttpServerConnection extends Thread {
} catch (IOException e) {
} catch (Exception e) {
Log.severe("Exception while handling request: ", e);
e.printStackTrace();
if(!do_shutdown) {
Log.severe("Exception while handling request: ", e);
e.printStackTrace();
}
} finally {
if (socket != null) {
try {
@ -241,6 +244,18 @@ public class HttpServerConnection extends Thread {
} catch (IOException ex) {
}
}
server.connectionEnded(this);
}
}
public void shutdownConnection() {
try {
do_shutdown = true;
if(socket != null) {
socket.close();
}
join(); /* Wait for thread to die */
} catch (IOException iox) {
} catch (InterruptedException ix) {
}
}
}

View File

@ -38,7 +38,10 @@ public class ClientUpdateHandler implements HttpHandler {
String worldName = match.group(1);
String timeKey = match.group(2);
DynmapWorld dynmapWorld = plugin.mapManager.getWorld(worldName);
DynmapWorld dynmapWorld = null;
if(plugin.mapManager != null) {
dynmapWorld = plugin.mapManager.getWorld(worldName);
}
if (dynmapWorld == null || dynmapWorld.world == null) {
response.status = WorldNotFound;
return;

View File

@ -16,10 +16,7 @@ import org.dynmap.web.HttpStatus;
public abstract class FileHandler implements HttpHandler {
protected static final Logger log = Logger.getLogger("Minecraft");
protected static final String LOG_PREFIX = "[dynmap] ";
//BUG-this breaks re-entrancy of this handler, which is called from multiple threads (one per request)
//private byte[] readBuffer = new byte[40960];
//Replace with pool of buffers
private LinkedList<byte[]> bufferpool = new LinkedList<byte[]>();
private Object lock = new Object();
private static final int MAX_FREE_IN_POOL = 2;

View File

@ -18,7 +18,6 @@ import org.json.simple.parser.JSONParser;
public class SendMessageHandler implements HttpHandler {
protected static final Logger log = Logger.getLogger("Minecraft");
protected static final String LOG_PREFIX = "[dynmap] ";
private static final JSONParser parser = new JSONParser();
public Event<Message> onMessageReceived = new Event<SendMessageHandler.Message>();