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 else
return null; return null;
} }
public static void reset() {
cache.clear();
}
} }

View File

@ -15,19 +15,22 @@ public class DynmapPlayerChatListener extends PlayerListener {
@Override @Override
public void onPlayerChat(PlayerChatEvent event) { public void onPlayerChat(PlayerChatEvent event) {
if(event.isCancelled()) return; 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().getDisplayName(), event.getMessage(),
event.getPlayer().getName())); event.getPlayer().getName()));
} }
@Override @Override
public void onPlayerJoin(PlayerJoinEvent event) { 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 @Override
public void onPlayerQuit(PlayerQuitEvent event) { 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 @Override
public void onEnable() { public void onEnable() {
/* Start with clean events */
events = new Events();
permissions = NijikokunPermissions.create(getServer(), "dynmap"); permissions = NijikokunPermissions.create(getServer(), "dynmap");
if (permissions == null) if (permissions == null)
permissions = new OpPermissions(new String[] { "fullrender", "reload" }); permissions = new OpPermissions(new String[] { "fullrender", "reload" });
@ -303,6 +306,7 @@ public class DynmapPlugin extends JavaPlugin {
if (mapManager != null) { if (mapManager != null) {
mapManager.stopRendering(); mapManager.stopRendering();
mapManager = null;
} }
if (webServer != null) { if (webServer != null) {

View File

@ -7,7 +7,7 @@ import org.bukkit.World;
import org.bukkit.Location; import org.bukkit.Location;
import org.dynmap.debug.Debug; import org.dynmap.debug.Debug;
import org.dynmap.kzedmap.KzedMap; 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.FileLockManager;
import org.dynmap.utils.MapChunkCache; import org.dynmap.utils.MapChunkCache;
@ -361,7 +361,7 @@ public class DynmapWorld {
Debug.debug("processZoomFile(" + pd.baseprefix + "," + zf.getPath() + "," + tx + "," + ty + ")"); Debug.debug("processZoomFile(" + pd.baseprefix + "," + zf.getPath() + "," + tx + "," + ty + ")");
int width = 128, height = 128; int width = 128, height = 128;
BufferedImage zIm = null; BufferedImage zIm = null;
KzedBufferedImage kzIm = null; DynmapBufferedImage kzIm = null;
int[] argb = new int[width*height]; int[] argb = new int[width*height];
int step = pd.stepsize << pd.zoomlevel; int step = pd.stepsize << pd.zoomlevel;
int ztx = tx; int ztx = tx;
@ -370,7 +370,7 @@ public class DynmapWorld {
ty = ty - (pd.neg_step_y?step:0); /* Adjust for negative step */ ty = ty - (pd.neg_step_y?step:0); /* Adjust for negative step */
/* create image buffer */ /* create image buffer */
kzIm = KzedMap.allocateBufferedImage(width, height); kzIm = DynmapBufferedImage.allocateBufferedImage(width, height);
zIm = kzIm.buf_img; zIm = kzIm.buf_img;
for(int i = 0; i < 4; i++) { for(int i = 0; i < 4; i++) {
@ -434,7 +434,7 @@ public class DynmapWorld {
} }
} finally { } finally {
FileLockManager.releaseWriteLock(zf); 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) { protected void webChat(String name, String message) {
// TODO: Change null to something meaningful. // TODO: Change null to something meaningful.
plugin.mapManager.pushUpdate(new Client.ChatMessage("web", null, name, message, null)); 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); ChatEvent event = new ChatEvent("web", name, message);
plugin.events.trigger("webchat", event); plugin.events.trigger("webchat", event);
} }

View File

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

View File

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

View File

@ -44,17 +44,20 @@ public class SimpleWebChatComponent extends Component {
@Override @Override
public void onPlayerChat(PlayerChatEvent event) { public void onPlayerChat(PlayerChatEvent event) {
if(event.isCancelled()) return; 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 @Override
public void onPlayerJoin(PlayerJoinEvent event) { 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 @Override
public void onPlayerQuit(PlayerQuitEvent event) { 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.MapType.MapStep;
import org.dynmap.debug.Debug; import org.dynmap.debug.Debug;
import org.dynmap.kzedmap.KzedMap; 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.FileLockManager;
import org.dynmap.utils.MapChunkCache; import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator; import org.dynmap.utils.MapIterator;
@ -134,12 +134,12 @@ public class FlatMap extends MapType {
Color rslt = new Color(); Color rslt = new Color();
int[] pixel = new int[4]; int[] pixel = new int[4];
int[] pixel_day = null; 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; int[] argb_buf = im.argb_buf;
KzedBufferedImage im_day = null; DynmapBufferedImage im_day = null;
int[] argb_buf_day = null; int[] argb_buf_day = null;
if(night_and_day) { 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; argb_buf_day = im_day.argb_buf;
pixel_day = new int[4]; pixel_day = new int[4];
} }
@ -300,7 +300,7 @@ public class FlatMap extends MapType {
} }
} finally { } finally {
FileLockManager.releaseWriteLock(outputFile); FileLockManager.releaseWriteLock(outputFile);
KzedMap.freeBufferedImage(im); DynmapBufferedImage.freeBufferedImage(im);
} }
MapManager.mapman.updateStatistics(tile, null, true, tile_update, !rendered); MapManager.mapman.updateStatistics(tile, null, true, tile_update, !rendered);
@ -332,7 +332,7 @@ public class FlatMap extends MapType {
} }
} finally { } finally {
FileLockManager.releaseWriteLock(dayfile); FileLockManager.releaseWriteLock(dayfile);
KzedMap.freeBufferedImage(im_day); DynmapBufferedImage.freeBufferedImage(im_day);
} }
MapManager.mapman.updateStatistics(tile, "day", true, tile_update, !rendered); MapManager.mapman.updateStatistics(tile, "day", true, tile_update, !rendered);
} }

View File

@ -265,6 +265,11 @@ public class HDBlockModels {
* Load models * Load models
*/ */
public static void loadModels(File datadir) { 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 */ /* Load block models */
InputStream in = TexturePack.class.getResourceAsStream("/models.txt"); InputStream in = TexturePack.class.getResourceAsStream("/models.txt");
if(in != null) { if(in != null) {

View File

@ -26,8 +26,8 @@ import org.dynmap.debug.Debug;
import org.dynmap.utils.MapIterator.BlockStep; import org.dynmap.utils.MapIterator.BlockStep;
import org.dynmap.hdmap.TexturePack.BlockTransparency; import org.dynmap.hdmap.TexturePack.BlockTransparency;
import org.dynmap.hdmap.TexturePack.HDTextureMap; import org.dynmap.hdmap.TexturePack.HDTextureMap;
import org.dynmap.kzedmap.KzedMap.KzedBufferedImage;
import org.dynmap.kzedmap.KzedMap; import org.dynmap.kzedmap.KzedMap;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.FileLockManager; import org.dynmap.utils.FileLockManager;
import org.dynmap.utils.MapChunkCache; import org.dynmap.utils.MapChunkCache;
import org.dynmap.utils.MapIterator; import org.dynmap.utils.MapIterator;
@ -836,8 +836,8 @@ public class IsoHDPerspective implements HDPerspective {
/* Check if nether world */ /* Check if nether world */
boolean isnether = tile.getWorld().getEnvironment() == Environment.NETHER; boolean isnether = tile.getWorld().getEnvironment() == Environment.NETHER;
/* Create buffered image for each */ /* Create buffered image for each */
KzedBufferedImage im[] = new KzedBufferedImage[numshaders]; DynmapBufferedImage im[] = new DynmapBufferedImage[numshaders];
KzedBufferedImage dayim[] = new KzedBufferedImage[numshaders]; DynmapBufferedImage dayim[] = new DynmapBufferedImage[numshaders];
int[][] argb_buf = new int[numshaders][]; int[][] argb_buf = new int[numshaders][];
int[][] day_argb_buf = new int[numshaders][]; int[][] day_argb_buf = new int[numshaders][];
@ -848,10 +848,10 @@ public class IsoHDPerspective implements HDPerspective {
need_emittedlightlevel = true; need_emittedlightlevel = true;
if(shader.isSkyLightLevelNeeded() || lighting.isSkyLightLevelNeeded()) if(shader.isSkyLightLevelNeeded() || lighting.isSkyLightLevelNeeded())
need_skylightlevel = true; need_skylightlevel = true;
im[i] = KzedMap.allocateBufferedImage(tileWidth, tileHeight); im[i] = DynmapBufferedImage.allocateBufferedImage(tileWidth, tileHeight);
argb_buf[i] = im[i].argb_buf; argb_buf[i] = im[i].argb_buf;
if(lighting.isNightAndDayEnabled()) { if(lighting.isNightAndDayEnabled()) {
dayim[i] = KzedMap.allocateBufferedImage(tileWidth, tileHeight); dayim[i] = DynmapBufferedImage.allocateBufferedImage(tileWidth, tileHeight);
day_argb_buf[i] = dayim[i].argb_buf; day_argb_buf[i] = dayim[i].argb_buf;
} }
} }
@ -931,7 +931,7 @@ public class IsoHDPerspective implements HDPerspective {
} }
} finally { } finally {
FileLockManager.releaseWriteLock(f); FileLockManager.releaseWriteLock(f);
KzedMap.freeBufferedImage(im[i]); DynmapBufferedImage.freeBufferedImage(im[i]);
} }
MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, !rendered[i]); MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, !rendered[i]);
/* Handle day image, if needed */ /* Handle day image, if needed */
@ -964,7 +964,7 @@ public class IsoHDPerspective implements HDPerspective {
} }
} finally { } finally {
FileLockManager.releaseWriteLock(f); FileLockManager.releaseWriteLock(f);
KzedMap.freeBufferedImage(dayim[i]); DynmapBufferedImage.freeBufferedImage(dayim[i]);
} }
MapManager.mapman.updateStatistics(tile, prefix, true, tile_update, !rendered[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.Color;
import org.dynmap.DynmapPlugin; import org.dynmap.DynmapPlugin;
import org.dynmap.Log; import org.dynmap.Log;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.MapIterator.BlockStep; import org.dynmap.utils.MapIterator.BlockStep;
import org.dynmap.kzedmap.KzedMap; import org.dynmap.kzedmap.KzedMap;
import org.dynmap.utils.MapIterator; 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); 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); ImageIO.write(img, "png", f);
} }
@ -561,6 +562,8 @@ public class TexturePack {
* Load texture pack mappings * Load texture pack mappings
*/ */
public static void loadTextureMapping(File datadir) { 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 */ /* Initialize map with blank map for all entries */
HDTextureMap.initializeTable(); HDTextureMap.initializeTable();
/* Load block models */ /* Load block models */

View File

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

View File

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

View File

@ -19,6 +19,7 @@ import org.dynmap.MapManager;
import org.dynmap.MapTile; import org.dynmap.MapTile;
import org.dynmap.MapType; import org.dynmap.MapType;
import org.dynmap.MapType.MapStep; import org.dynmap.MapType.MapStep;
import org.dynmap.utils.DynmapBufferedImage;
import org.dynmap.utils.MapChunkCache; import org.dynmap.utils.MapChunkCache;
import org.json.simple.JSONObject; import org.json.simple.JSONObject;
import java.awt.image.DataBufferInt; import java.awt.image.DataBufferInt;
@ -50,20 +51,6 @@ public class KzedMap extends MapType {
MapTileRenderer[] renderers; MapTileRenderer[] renderers;
private boolean isbigmap; 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) { public KzedMap(ConfigurationNode configuration) {
Log.verboseinfo("Loading renderers for map '" + getClass().toString() + "'..."); Log.verboseinfo("Loading renderers for map '" + getClass().toString() + "'...");
List<MapTileRenderer> renderers = configuration.<MapTileRenderer>createInstances("renderers", new Class<?>[0], new Object[0]); 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); 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() { public boolean isBiomeDataNeeded() {
for(MapTileRenderer r : renderers) { for(MapTileRenderer r : renderers) {
@ -382,21 +323,4 @@ public class KzedMap extends MapType {
renderer.buildClientConfiguration(worldObject, world, this); 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.net.SocketAddress;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet; import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.SortedMap; import java.util.SortedMap;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -28,6 +29,9 @@ public class HttpServer extends Thread {
private boolean check_banned_ips; private boolean check_banned_ips;
public SortedMap<String, HttpHandler> handlers = new TreeMap<String, HttpHandler>(Collections.reverseOrder()); 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) { public HttpServer(InetAddress bindAddress, int port, boolean check_banned_ips) {
this.bindAddress = bindAddress; this.bindAddress = bindAddress;
@ -62,6 +66,9 @@ public class HttpServer extends Thread {
} }
HttpServerConnection requestThread = new HttpServerConnection(socket, this); HttpServerConnection requestThread = new HttpServerConnection(socket, this);
synchronized(lock) {
active_connections.add(requestThread);
}
requestThread.start(); requestThread.start();
} catch (IOException e) { } catch (IOException e) {
if(listeningThread != null) /* Only report this if we didn't initiate the shutdown */ 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.close();
sock = null; 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) { } catch (IOException e) {
Log.warning("Exception while closing socket for webserver shutdown", 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 = new HashSet<String>();
private HashSet<String> banned_ips_notified = new HashSet<String>(); private HashSet<String> banned_ips_notified = new HashSet<String>();
private long last_loaded = 0; private long last_loaded = 0;

View File

@ -19,13 +19,13 @@ import java.net.InetSocketAddress;
public class HttpServerConnection extends Thread { public class HttpServerConnection extends Thread {
protected static final Logger log = Logger.getLogger("Minecraft"); 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 requestHeaderLine = Pattern.compile("^(\\S+)\\s+(\\S+)\\s+HTTP/(.+)$");
private static Pattern requestHeaderField = Pattern.compile("^([^:]+):\\s*(.+)$"); private static Pattern requestHeaderField = Pattern.compile("^([^:]+):\\s*(.+)$");
private Socket socket; private Socket socket;
private HttpServer server; private HttpServer server;
private boolean do_shutdown;
private PrintStream printOut; private PrintStream printOut;
private StringWriter sw = new StringWriter(); private StringWriter sw = new StringWriter();
@ -35,6 +35,7 @@ public class HttpServerConnection extends Thread {
public HttpServerConnection(Socket socket, HttpServer server) { public HttpServerConnection(Socket socket, HttpServer server) {
this.socket = socket; this.socket = socket;
this.server = server; this.server = server;
do_shutdown = false;
} }
private final static void readLine(InputStream in, StringWriter sw) throws IOException { private final static void readLine(InputStream in, StringWriter sw) throws IOException {
@ -232,8 +233,10 @@ public class HttpServerConnection extends Thread {
} catch (IOException e) { } catch (IOException e) {
} catch (Exception e) { } catch (Exception e) {
Log.severe("Exception while handling request: ", e); if(!do_shutdown) {
e.printStackTrace(); Log.severe("Exception while handling request: ", e);
e.printStackTrace();
}
} finally { } finally {
if (socket != null) { if (socket != null) {
try { try {
@ -241,6 +244,18 @@ public class HttpServerConnection extends Thread {
} catch (IOException ex) { } 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 worldName = match.group(1);
String timeKey = match.group(2); 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) { if (dynmapWorld == null || dynmapWorld.world == null) {
response.status = WorldNotFound; response.status = WorldNotFound;
return; return;

View File

@ -16,10 +16,7 @@ import org.dynmap.web.HttpStatus;
public abstract class FileHandler implements HttpHandler { public abstract class FileHandler implements HttpHandler {
protected static final Logger log = Logger.getLogger("Minecraft"); 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 LinkedList<byte[]> bufferpool = new LinkedList<byte[]>();
private Object lock = new Object(); private Object lock = new Object();
private static final int MAX_FREE_IN_POOL = 2; 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 { public class SendMessageHandler implements HttpHandler {
protected static final Logger log = Logger.getLogger("Minecraft"); protected static final Logger log = Logger.getLogger("Minecraft");
protected static final String LOG_PREFIX = "[dynmap] ";
private static final JSONParser parser = new JSONParser(); private static final JSONParser parser = new JSONParser();
public Event<Message> onMessageReceived = new Event<SendMessageHandler.Message>(); public Event<Message> onMessageReceived = new Event<SendMessageHandler.Message>();