diff --git a/src/main/java/org/dynmap/ChatQueue.java b/src/main/java/org/dynmap/ChatQueue.java deleted file mode 100644 index d0fb1df4..00000000 --- a/src/main/java/org/dynmap/ChatQueue.java +++ /dev/null @@ -1,63 +0,0 @@ -package org.dynmap; - -import java.util.ArrayList; -import java.util.LinkedList; - -import org.bukkit.event.player.PlayerChatEvent; - -public class ChatQueue { - - public class ChatMessage { - public long time; - public String playerName; - public String message; - - public ChatMessage(PlayerChatEvent event) { - time = System.currentTimeMillis(); - playerName = event.getPlayer().getName(); - message = event.getMessage(); - } - } - - /* a list of recent chat message */ - private LinkedList messageQueue; - - /* remember up to this old chat messages (ms) */ - private static final int maxChatAge = 120000; - - public ChatQueue() { - messageQueue = new LinkedList(); - } - - /* put a chat message in the queue */ - public void pushChatMessage(PlayerChatEvent event) { - synchronized (MapManager.lock) { - messageQueue.add(new ChatMessage(event)); - } - } - - public ChatMessage[] getChatMessages(long cutoff) { - - ArrayList queue = new ArrayList(); - ArrayList updateList = new ArrayList(); - queue.addAll(messageQueue); - - long now = System.currentTimeMillis(); - long deadline = now - maxChatAge; - - synchronized (MapManager.lock) { - - for (ChatMessage message : queue) { - if (message.time < deadline) { - messageQueue.remove(message); - } else if (message.time >= cutoff) { - updateList.add(message); - } - } - } - ChatMessage[] messages = new ChatMessage[updateList.size()]; - updateList.toArray(messages); - return messages; - } - -} diff --git a/src/main/java/org/dynmap/Client.java b/src/main/java/org/dynmap/Client.java new file mode 100644 index 00000000..e18f582c --- /dev/null +++ b/src/main/java/org/dynmap/Client.java @@ -0,0 +1,43 @@ +package org.dynmap; + +public class Client { + public static class Update { + public long timestamp; + public long servertime; + public Player[] players; + public Object[] updates; + } + + public static class Player { + public String type = "player"; + public String name; + public double x, y, z; + + public Player(String name, double x, double y, double z) { + this.name = name; + this.x = x; + this.y = y; + this.z = z; + } + } + + public static class ChatMessage { + public String type = "chat"; + public String playerName; + public String message; + + public ChatMessage(String playerName, String message) { + this.playerName = playerName; + this.message = message; + } + } + + public static class Tile { + public String type = "tile"; + public String name; + + public Tile(String name) { + this.name = name; + } + } +} diff --git a/src/main/java/org/dynmap/DynmapPlayerListener.java b/src/main/java/org/dynmap/DynmapPlayerListener.java index b11bc4a3..e643b984 100644 --- a/src/main/java/org/dynmap/DynmapPlayerListener.java +++ b/src/main/java/org/dynmap/DynmapPlayerListener.java @@ -58,6 +58,6 @@ public class DynmapPlayerListener extends PlayerListener { * Relevant event details */ public void onPlayerChat(PlayerChatEvent event) { - mgr.addChatEvent(event); + mgr.updateQueue.pushUpdate(new Client.ChatMessage(event.getPlayer().getName(), event.getMessage())); } } \ No newline at end of file diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index fc2fed32..227284fa 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -8,9 +8,9 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.logging.Logger; + import org.bukkit.Location; import org.bukkit.World; -import org.bukkit.event.player.PlayerChatEvent; import org.bukkit.util.config.ConfigurationNode; import org.dynmap.debug.Debugger; @@ -21,7 +21,7 @@ public class MapManager extends Thread { private Debugger debugger; private MapType[] maps; public StaleQueue staleQueue; - public ChatQueue chatQueue; + public UpdateQueue updateQueue; public PlayerList playerList; /* lock for our data structures */ @@ -65,7 +65,7 @@ public class MapManager extends Thread { this.world = world; this.debugger = debugger; this.staleQueue = new StaleQueue(); - this.chatQueue = new ChatQueue(); + this.updateQueue = new UpdateQueue(); tileDirectory = combinePaths(DynmapPlugin.dataRoot, configuration.getString("tilespath", "web/tiles")); webDirectory = combinePaths(DynmapPlugin.dataRoot, configuration.getString("webpath", "web")); @@ -110,7 +110,7 @@ public class MapManager extends Thread { debugger.debug("renderQueue: " + renderQueue.size() + "/" + found.size()); if (map.render(tile)) { found.remove(tile); - staleQueue.onTileUpdated(tile); + updateQueue.pushUpdate(new Client.Tile(tile.getName())); for (MapTile adjTile : map.getAdjecentTiles(tile)) { if (!(found.contains(adjTile) || map.isRendered(adjTile))) { found.add(adjTile); @@ -244,7 +244,7 @@ public class MapManager extends Thread { debugger.debug("Rendering tile " + t + "..."); boolean isNonEmptyTile = t.getMap().render(t); - staleQueue.onTileUpdated(t); + updateQueue.pushUpdate(new Client.Tile(t.getName())); if (isNonEmptyTile) handleFullMapRender(t); @@ -281,8 +281,4 @@ public class MapManager extends Thread { debugger.debug("Invalidating tile " + tile.getName()); staleQueue.pushStaleTile(tile); } - - public void addChatEvent(PlayerChatEvent event) { - chatQueue.pushChatMessage(event); - } } diff --git a/src/main/java/org/dynmap/StaleQueue.java b/src/main/java/org/dynmap/StaleQueue.java index 2d7eddc0..26172607 100644 --- a/src/main/java/org/dynmap/StaleQueue.java +++ b/src/main/java/org/dynmap/StaleQueue.java @@ -1,10 +1,7 @@ package org.dynmap; -import java.util.ArrayList; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedList; -import java.util.ListIterator; import java.util.NoSuchElementException; import java.util.Set; @@ -13,16 +10,9 @@ public class StaleQueue { private LinkedList staleTilesQueue; private Set staleTiles; - /* this list stores the tile updates */ - public LinkedList tileUpdates = null; - - /* remember up to this old tile updates (ms) */ - private static final int maxTileAge = 60000; - public StaleQueue() { staleTilesQueue = new LinkedList(); staleTiles = new HashSet(); - tileUpdates = new LinkedList(); } public int size() { @@ -57,47 +47,4 @@ public class StaleQueue { } } } - - public void onTileUpdated(MapTile t) { - long now = System.currentTimeMillis(); - long deadline = now - maxTileAge; - synchronized (MapManager.lock) { - ListIterator it = tileUpdates.listIterator(0); - while (it.hasNext()) { - TileUpdate tu = it.next(); - if (tu.at < deadline || tu.tile == t) - it.remove(); - } - tileUpdates.addLast(new TileUpdate(now, t)); - } - } - - private ArrayList tmpupdates = new ArrayList(); - - public TileUpdate[] getTileUpdates(long cutoff) { - long now = System.currentTimeMillis(); - long deadline = now - maxTileAge; - TileUpdate[] updates; - synchronized (MapManager.lock) { - tmpupdates.clear(); - Iterator it = tileUpdates.descendingIterator(); - while (it.hasNext()) { - TileUpdate tu = it.next(); - if (tu.at >= cutoff) { // Tile is new. - tmpupdates.add(tu); - } else if (tu.at < deadline) { // Tile is too old, removing this - // one (will eventually - // decrease). - it.remove(); - break; - } else { // Tile is old, but not old enough for removal. - break; - } - } - updates = new TileUpdate[tmpupdates.size()]; - tmpupdates.toArray(updates); - } - return updates; - } - } diff --git a/src/main/java/org/dynmap/TileUpdate.java b/src/main/java/org/dynmap/TileUpdate.java deleted file mode 100644 index 689cab9d..00000000 --- a/src/main/java/org/dynmap/TileUpdate.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.dynmap; - -/* this class stores a tile update */ - -public class TileUpdate { - public long at; - public MapTile tile; - - public TileUpdate(long at, MapTile tile) { - this.at = at; - this.tile = tile; - } -} diff --git a/src/main/java/org/dynmap/UpdateQueue.java b/src/main/java/org/dynmap/UpdateQueue.java new file mode 100644 index 00000000..bddcb098 --- /dev/null +++ b/src/main/java/org/dynmap/UpdateQueue.java @@ -0,0 +1,70 @@ +package org.dynmap; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.ListIterator; + +public class UpdateQueue { + public Object lock = new Object(); + private LinkedList updateQueue = new LinkedList(); + + private static final int maxUpdateAge = 120000; + + public void pushUpdate(Object obj) { + long now = System.currentTimeMillis(); + long deadline = now - maxUpdateAge; + synchronized (lock) { + ListIterator i = updateQueue.listIterator(0); + while (i.hasNext()) { + Update u = i.next(); + if (u.time < deadline || u.obj == obj) + i.remove(); + } + updateQueue.addLast(new Update(now, obj)); + } + } + + private ArrayList tmpupdates = new ArrayList(); + + public Object[] getUpdatedObjects(long since) { + long now = System.currentTimeMillis(); + long deadline = now - maxUpdateAge; + Object[] updates; + synchronized (lock) { + tmpupdates.clear(); + Iterator it = updateQueue.descendingIterator(); + while (it.hasNext()) { + Update u = it.next(); + if (u.time >= since) { + // Tile is new. + tmpupdates.add(u.obj); + } else if (u.time < deadline) { + // Tile is too old, removing this one (will eventually decrease). + it.remove(); + break; + } else { + // Tile is old, but not old enough for removal. + break; + } + } + + // Reverse output. + updates = new Object[tmpupdates.size()]; + for (int i = 0; i < updates.length; i++) { + updates[i] = tmpupdates.get(updates.length-1-i); + } + } + return updates; + } + + public class Update { + public long time; + public Object obj; + + public Update(long time, Object obj) { + this.time = time; + this.obj = obj; + } + } +} diff --git a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java index b56ccd66..565a4c2d 100644 --- a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java @@ -2,6 +2,7 @@ package org.dynmap.kzedmap; import java.awt.Color; import java.util.Map; + import org.bukkit.World; import org.dynmap.debug.Debugger; diff --git a/src/main/java/org/dynmap/kzedmap/KzedMap.java b/src/main/java/org/dynmap/kzedmap/KzedMap.java index eebf393a..155f0790 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMap.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMap.java @@ -11,6 +11,7 @@ import java.util.List; import java.util.Map; import java.util.Scanner; import java.util.logging.Logger; + import org.bukkit.Location; import org.bukkit.World; import org.dynmap.DynmapChunk; diff --git a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java index 2872169a..6b6da8c3 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java @@ -1,6 +1,7 @@ package org.dynmap.kzedmap; import java.util.logging.Logger; + import org.dynmap.MapTile; public class KzedMapTile extends MapTile { diff --git a/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java index 749bb877..af2e353e 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java +++ b/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java @@ -1,6 +1,7 @@ package org.dynmap.kzedmap; import java.awt.image.BufferedImage; + import org.dynmap.MapTile; public class KzedZoomedMapTile extends MapTile { diff --git a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java index d616042a..b9e4ce1f 100644 --- a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java @@ -6,7 +6,9 @@ import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.util.Map; + import javax.imageio.ImageIO; + import org.dynmap.debug.Debugger; public class ZoomedTileRenderer { diff --git a/src/main/java/org/dynmap/web/HttpServerConnection.java b/src/main/java/org/dynmap/web/HttpServerConnection.java index cff0779f..a758d66d 100644 --- a/src/main/java/org/dynmap/web/HttpServerConnection.java +++ b/src/main/java/org/dynmap/web/HttpServerConnection.java @@ -61,15 +61,16 @@ public class HttpServerConnection extends Thread { sb.append(response.statusCode); sb.append(" "); sb.append(response.statusMessage); - sb.append("\n"); + sb.append("\r\n"); for (Entry field : response.fields.entrySet()) { sb.append(field.getKey()); sb.append(": "); sb.append(field.getValue()); - sb.append("\n"); + sb.append("\r\n"); } - sb.append("\n"); + sb.append("\r\n"); o.write(sb.toString().getBytes()); + o.flush(); } public void run() { @@ -84,34 +85,41 @@ public class HttpServerConnection extends Thread { // TODO: Optimize HttpHandler-finding by using a real path-aware // tree. - HttpResponse response = null; + HttpHandler handler = null; + String relativePath = null; for (Entry entry : server.handlers.entrySet()) { String key = entry.getKey(); boolean directoryHandler = key.endsWith("/"); if (directoryHandler && request.path.startsWith(entry.getKey()) || !directoryHandler && request.path.equals(entry.getKey())) { - String path = request.path.substring(entry.getKey().length()); - - response = new HttpResponse(socket.getOutputStream()); - entry.getValue().handle(path, request, response); + relativePath = request.path.substring(entry.getKey().length()); + handler = entry.getValue(); break; } } - if (response != null) { - if (response.fields.get("Content-Length") == null) { - response.fields.put("Content-Length", "0"); - OutputStream out = response.getBody(); - if (out != null) { - out.close(); - } - } + if (handler == null) { + socket.close(); + return; + } - String connection = response.fields.get("Connection"); - if (connection == null || connection.equals("close")) { - socket.close(); - return; - } - } else { + HttpResponse response = new HttpResponse(socket.getOutputStream()); + + try { + handler.handle(relativePath, request, response); + } catch (Exception e) { + log.log(Level.SEVERE, "HttpHandler '" + handler + "' has thown an exception", e); + e.printStackTrace(); + socket.close(); + return; + } + + if (response.fields.get("Content-Length") == null) { + response.fields.put("Content-Length", "0"); + /* OutputStream out = */response.getBody(); + } + + String connection = response.fields.get("Connection"); + if (connection != null && connection.equals("close")) { socket.close(); return; } diff --git a/src/main/java/org/dynmap/web/handlers/ClientUpdateHandler.java b/src/main/java/org/dynmap/web/handlers/ClientUpdateHandler.java index ab983d06..a17a612f 100644 --- a/src/main/java/org/dynmap/web/handlers/ClientUpdateHandler.java +++ b/src/main/java/org/dynmap/web/handlers/ClientUpdateHandler.java @@ -6,57 +6,52 @@ import java.util.Date; import org.bukkit.World; import org.bukkit.entity.Player; -import org.dynmap.ChatQueue; +import org.dynmap.Client; import org.dynmap.MapManager; import org.dynmap.PlayerList; -import org.dynmap.TileUpdate; import org.dynmap.web.HttpHandler; import org.dynmap.web.HttpRequest; import org.dynmap.web.HttpResponse; +import org.dynmap.web.Json; public class ClientUpdateHandler implements HttpHandler { private MapManager mapManager; private PlayerList playerList; private World world; + public ClientUpdateHandler(MapManager mapManager, PlayerList playerList, World world) { this.mapManager = mapManager; this.playerList = playerList; this.world = world; } + @Override public void handle(String path, HttpRequest request, HttpResponse response) throws IOException { - int current = (int) (System.currentTimeMillis() / 1000); + long current = System.currentTimeMillis(); long cutoff = 0; if (path.length() > 0) { try { - cutoff = ((long) Integer.parseInt(path)) * 1000; + cutoff = Long.parseLong(path); } catch (NumberFormatException e) { } } - - StringBuilder sb = new StringBuilder(); - long relativeTime = world.getTime() % 24000; - sb.append(current + " " + relativeTime + "\n"); - + + Client.Update update = new Client.Update(); + update.timestamp = current; + update.servertime = world.getTime() % 24000; + + Player[] players = playerList.getVisiblePlayers(); - for (Player player : players) { - sb.append("player " + player.getName() + " " + player.getLocation().getX() + " " + player.getLocation().getY() + " " + player.getLocation().getZ() + "\n"); + update.players = new Client.Player[players.length]; + for(int i=0;i + diff --git a/web/map.js b/web/map.js index 95941d86..352471f7 100644 --- a/web/map.js +++ b/web/map.js @@ -198,65 +198,42 @@ DynMap.prototype = { // TODO: is there a better place for this? this.cleanPopups(); - $.ajax({ - url: me.options.updateUrl + me.lasttimestamp, - success: function(res) { - if (!res) { - me.alertbox - .text('Invalid response') - .show(); - } - + $.getJSON(me.options.updateUrl + me.lasttimestamp, function(update) { me.alertbox.hide(); - var rows = res.split('\n'); - var row = splitArgs(rows[0], 'timestamp', 'servertime'); - delete rows[0]; - - me.lasttimestamp = row.timestamp; - me.clock.setTime(getMinecraftTime(row.servertime)); + + me.lasttimestamp = update.timestamp; + me.clock.setTime(getMinecraftTime(update.servertime)); var typeVisibleMap = {}; var newmarkers = {}; - for(var rowIndex in rows) { - var line = rows[rowIndex]; - row = splitArgs(line, 'type', 'name', 'posx', 'posy', 'posz'); + $.each(update.players, function(index, player) { + var mi = { + id: 'player_' + player.name, + text: player.name, + type: 'player', + position: me.map.getProjection().fromWorldToLatLng(parseFloat(player.x), parseFloat(player.y), parseFloat(player.z)), + visible: true + }; - if (!row.type) continue; - - swtch(row.type, { + me.updateMarker(mi); + newmarkers[mi.id] = mi; + }); + + $.each(update.updates, function(index, update) { + swtch(update.type, { tile: function() { - me.onTileUpdated(row.name); - } - , chat: function() { + me.onTileUpdated(update.name); + }, + chat: function() { if (!me.options.showchatballoons) return; - var chats = line.split(' '); - var message = ''; - for (var chatIndex = 2; chatIndex < chats.length; chatIndex++) - { - if (chatIndex > 2) message = message + " "; - message = message + chats[chatIndex]; - } - if (message.length > 0) - { - me.onPlayerChat(row.name, message); - } + me.onPlayerChat(update.playerName, update.message); } - }, - function() { - var mi = { - id: row.type + '_' + row.name, - text: row.name, - type: row.type, - position: me.map.getProjection().fromWorldToLatLng(parseFloat(row.posx), parseFloat(row.posy), parseFloat(row.posz)), - visible: true - }; - - me.updateMarker(mi); - newmarkers[mi.id] = mi; + }, function(type) { + console.log('Unknown type ', value, '!'); }); - } + }); for(var m in me.markers) { var marker = me.markers[m]; @@ -269,14 +246,13 @@ DynMap.prototype = { } } setTimeout(function() { me.update(); }, me.options.updaterate); - }, - error: function(request, statusText, ex) { + }, function(request, statusText, ex) { me.alertbox .text('Could not update map') .show(); setTimeout(function() { me.update(); }, me.options.updaterate); } - }); + ); }, cleanPopups: function() { var POPUP_LIFE = 8000;