diff --git a/configuration.txt b/configuration.txt index fee06e63..4d7bd1d2 100755 --- a/configuration.txt +++ b/configuration.txt @@ -15,6 +15,7 @@ webserver-bindaddress: 0.0.0.0 # The TCP-port the webserver will listen on. webserver-port: 8123 +# The maptypes Dynmap will use to render. maps: - class: org.dynmap.kzedmap.KzedMap renderers: @@ -27,7 +28,14 @@ web: # Interval the browser should poll for updates. updaterate: 2000 + showchatballoons: true + showplayerfacesonmap: true + showplayerfacesinmenu: true + + # The name of the map shown when opening Dynmap's page (must be in menu). defaultmap: defaultmap + + # The maps shown in the menu. shownmaps: - type: KzedMapType name: defaultmap diff --git a/src/main/java/org/dynmap/web/WebServerRequest.java b/src/main/java/org/dynmap/web/WebServerRequest.java index 0837e3ca..c6a0f860 100644 --- a/src/main/java/org/dynmap/web/WebServerRequest.java +++ b/src/main/java/org/dynmap/web/WebServerRequest.java @@ -8,6 +8,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.net.Socket; +import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.LinkedHashMap; @@ -21,7 +22,6 @@ import org.dynmap.ChatQueue; import org.dynmap.MapManager; import org.dynmap.PlayerList; import org.dynmap.TileUpdate; -import org.dynmap.ChatQueue.ChatMessage; import org.dynmap.debug.Debugger; public class WebServerRequest extends Thread { @@ -112,11 +112,14 @@ public class WebServerRequest extends Thread { public String stringifyJson(Object o) { if (o == null) { return "null"; + } else if (o instanceof Boolean) { + return ((Boolean)o) ? "true" : "false"; } else if (o instanceof String) { return "\"" + o + "\""; } else if (o instanceof Integer || o instanceof Long || o instanceof Float || o instanceof Double) { return o.toString(); } else if (o instanceof LinkedHashMap) { + @SuppressWarnings("unchecked") LinkedHashMap m = (LinkedHashMap)o; StringBuilder sb = new StringBuilder(); sb.append("{"); @@ -131,6 +134,17 @@ public class WebServerRequest extends Thread { } sb.append("}"); return sb.toString(); + } else if (o instanceof ArrayList) { + @SuppressWarnings("unchecked") + ArrayList l = (ArrayList)o; + StringBuilder sb = new StringBuilder(); + int count = 0; + for(int i=0;i colors; - MapTileRenderer[] renderers; - ZoomedTileRenderer zoomrenderer; - - public KzedMap(MapManager manager, World world, Debugger debugger, Map configuration) { - super(manager, world, debugger); - if (colors == null) { - colors = loadColorSet("colors.txt"); + return new google.maps.LatLng(lat, lng); } +}; + +function KzedMapType(configuration) { $.extend(this, configuration); } +KzedMapType.prototype = $.extend(new DynMapType(), { + constructor: KzedMapType, + projection: new KzedProjection(), + tileSize: new google.maps.Size(128, 128), + minZoom: 0, + maxZoom: 3, + prefix: null, + getTile: function(coord, zoom, doc) { + var tileDebugText = null; + var tileSize = 128; + var tileName; + var imgSize; + var offset = {x: 0, y: 0}; - renderers = loadRenderers(configuration); - zoomrenderer = new ZoomedTileRenderer(debugger, configuration); - } - - private MapTileRenderer[] loadRenderers(Map configuration) { - List configuredRenderers = (List)configuration.get("renderers"); - ArrayList renderers = new ArrayList(); - for(Object configuredRendererObj : configuredRenderers) { - try { - @SuppressWarnings("unchecked") - Map configuredRenderer = (Map)configuredRendererObj; - String typeName = (String)configuredRenderer.get("class"); - log.info("Loading renderer '" + typeName.toString() + "'..."); - Class mapTypeClass = Class.forName(typeName); - Constructor constructor = mapTypeClass.getConstructor(Debugger.class, Map.class); - MapTileRenderer mapTileRenderer = (MapTileRenderer)constructor.newInstance(getDebugger(), configuredRenderer); - renderers.add(mapTileRenderer); - } catch (Exception e) { - getDebugger().error("Error loading renderer", e); - } - } - MapTileRenderer[] result = new MapTileRenderer[renderers.size()]; - renderers.toArray(result); - return result; - } - - @Override - public void touch(Location l) { - int x = l.getBlockX(); - int y = l.getBlockY(); - int z = l.getBlockZ(); + var debugred; + var debugblue; - int dx = x - anchorx; - int dy = y - anchory; - int dz = z - anchorz; - int px = dx + dz; - int py = dx - dz - dy; - - int tx = tilex(px); - int ty = tiley(py); - - invalidateTile(tx, ty); - - boolean ledge = tilex(px - 4) != tx; - boolean tedge = tiley(py - 4) != ty; - boolean redge = tilex(px + 4) != tx; - boolean bedge = tiley(py + 4) != ty; - - if(ledge) invalidateTile(tx - tileWidth, ty); - if(redge) invalidateTile(tx + tileWidth, ty); - if(tedge) invalidateTile(tx, ty - tileHeight); - if(bedge) invalidateTile(tx, ty + tileHeight); - - if(ledge && tedge) invalidateTile(tx - tileWidth, ty - tileHeight); - if(ledge && bedge) invalidateTile(tx - tileWidth, ty + tileHeight); - if(redge && tedge) invalidateTile(tx + tileWidth, ty - tileHeight); - if(redge && bedge) invalidateTile(tx + tileWidth, ty + tileHeight); - } - - public void invalidateTile(int px, int py) { - for(MapTileRenderer renderer : renderers) { - invalidateTile(new KzedMapTile(this, renderer, px, py)); - } - } - - @Override - public void render(MapTile tile) { - if (tile instanceof KzedZoomedMapTile) { - zoomrenderer.render((KzedZoomedMapTile)tile, getMapManager().tileDirectory.getAbsolutePath()); - } else if (tile instanceof KzedMapTile) { - ((KzedMapTile)tile).renderer.render((KzedMapTile)tile, getMapManager().tileDirectory.getAbsolutePath()); - } - } - - /* tile X for position x */ - static int tilex(int x) - { - if(x < 0) - return x - (tileWidth + (x % tileWidth)); - else - return x - (x % tileWidth); - } - - /* tile Y for position y */ - static int tiley(int y) - { - if(y < 0) - return y - (tileHeight + (y % tileHeight)); - else - return y - (y % tileHeight); - } - - /* zoomed-out tile X for tile position x */ - static int ztilex(int x) - { - if(x < 0) - return x + x % zTileWidth; - else - return x - (x % zTileWidth); - } - - /* zoomed-out tile Y for tile position y */ - static int ztiley(int y) - { - if(y < 0) - return y + y % zTileHeight; - //return y - (zTileHeight + (y % zTileHeight)); - else - return y - (y % zTileHeight); - } - - /* - // regenerate the entire map, starting at position - public void regenerate(int x, int y, int z) - { - int dx = x - anchorx; - int dy = y - anchory; - int dz = z - anchorz; - int px = dx + dz; - int py = dx - dz - dy; - - int tx = tilex(px); - int ty = tiley(py); - - MapTile first = getTileByPosition(tx, ty); - - Vector open = new Vector(); - open.add(first); - - while(open.size() > 0) { - MapTile t = open.remove(open.size() - 1); - if(t.stale) continue; - int h = world.getHighestBlockYAt(t.mx, t.mz); - - log.info("walking: " + t.mx + ", " + t.mz + ", h = " + h); - if(h < 1) - continue; - - pushStaleTile(t); - - open.add(getTileByPosition(t.px + tileWidth, t.py)); - open.add(getTileByPosition(t.px - tileWidth, t.py)); - open.add(getTileByPosition(t.px, t.py + tileHeight)); - open.add(getTileByPosition(t.px, t.py - tileHeight)); - } - } - - // regenerate all zoom tiles, starting at position - public void regenerateZoom(int x, int y, int z) - { - int dx = x - anchorx; - int dy = y - anchory; - int dz = z - anchorz; - int px = dx + dz; - int py = dx - dz - dy; - - int fzpx = ztilex(tilex(px)); - int fzpy = ztiley(tiley(py)); - - class Pair implements Comparator { - public int x; - public int y; - public Pair(int x, int y) - { - this.x = x; - this.y = y; - } - - public int hashCode() - { - return (x << 16) ^ y; - } - - public boolean equals(Object o) - { - Pair p = (Pair) o; - return x == p.x && y == p.y; - } - - public int compare(Object o1, Object o2) - { - Pair p1 = (Pair) o1; - Pair p2 = (Pair) o2; - if(p1.x < p1.x) return -1; - if(p1.x > p1.x) return 1; - if(p1.y < p1.y) return -1; - if(p1.y > p1.y) return 1; - return 0; - } - } - - HashSet visited = new HashSet(); - Vector open = new Vector(); - - Pair fp = new Pair(fzpx, fzpy); - open.add(fp); - visited.add(fp); - - while(open.size() > 0) { - Pair p = open.remove(open.size() - 1); - - int zpx = p.x; - int zpy = p.y; - - log.info("Regenerating zoom tile " + zpx + "," + zpy); - - int g = regenZoomTile(zpx, zpy); - - if(g > 0) { - Pair[] np = new Pair[4]; - np[0] = new Pair(zpx-zTileWidth, zpy); - np[1] = new Pair(zpx+zTileWidth, zpy); - np[2] = new Pair(zpx, zpy-zTileHeight); - np[3] = new Pair(zpx, zpy+zTileHeight); - - for(int i=0; i<4; i++) { - if(!visited.contains(np[i])) { - visited.add(np[i]); - open.add(np[i]); - } - } - } - } - } - - // regenerate zoom-out tile - // returns number of valid subtiles - public int regenZoomTile(int zpx, int zpy) - { - int px1 = zpx + tileWidth; - int py1 = zpy; - int px2 = zpx; - int py2 = py1 + tileHeight; - - MapTile t1 = getTileByPosition(px1, py1); - MapTile t2 = getTileByPosition(px2, py1); - MapTile t3 = getTileByPosition(px1, py2); - MapTile t4 = getTileByPosition(px2, py2); - - BufferedImage im1 = t1.loadTile(this); - BufferedImage im2 = t2.loadTile(this); - BufferedImage im3 = t3.loadTile(this); - BufferedImage im4 = t4.loadTile(this); - - BufferedImage zIm = new BufferedImage(MapManager.tileWidth, MapManager.tileHeight, BufferedImage.TYPE_INT_RGB); - WritableRaster zr = zIm.getRaster(); - Graphics2D g2 = zIm.createGraphics(); - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - - int scw = tileWidth / 2; - int sch = tileHeight / 2; - - int good = 0; - - if(im1 != null) { - g2.drawImage(im1, 0, 0, scw, sch, null); - good ++; - } - - if(im2 != null) { - g2.drawImage(im2, scw, 0, scw, sch, null); - good ++; - } - - if(im3 != null) { - g2.drawImage(im3, 0, sch, scw, sch, null); - good ++; - } - - if(im4 != null) { - g2.drawImage(im4, scw, sch, scw, sch, null); - good ++; - } - - if(good == 0) { - return 0; - } - - String zPath = t1.getZoomPath(this); - // save zoom-out tile - try { - File file = new File(zPath); - ImageIO.write(zIm, "png", file); - log.info("regenZoomTile saved zoom-out tile at " + zPath); - } catch(IOException e) { - log.log(Level.SEVERE, "Failed to save zoom-out tile: " + zPath, e); - } catch(java.lang.NullPointerException e) { - log.log(Level.SEVERE, "Failed to save zoom-out tile (NullPointerException): " + zPath, e); - } - - return good; - } - */ - - public java.util.Map loadColorSet(String colorsetpath) { - java.util.Map colors = new HashMap(); - - InputStream stream; - - try { - /* load colorset */ - File cfile = new File(colorsetpath); - if (cfile.isFile()) { - getDebugger().debug("Loading colors from '" + colorsetpath + "'..."); - stream = new FileInputStream(cfile); - } else { - getDebugger().debug("Loading colors from jar..."); - stream = KzedMap.class.getResourceAsStream("/colors.txt"); - } + if (zoom == 0) { + // Most zoomed out tiles. - Scanner scanner = new Scanner(stream); - int nc = 0; - while(scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.startsWith("#") || line.equals("")) { - continue; - } + tileSize = 128; + imgSize = tileSize; + tileName = 'z' + this.prefix + '_' + (-coord.x * tileSize*2) + '_' + (coord.y * tileSize*2); + } else { + // Other zoom levels. + tileSize = 128; + + // Helper functions. + var floor = Math.floor; + var div = function(x,y){return floor(x/y);} + var mod = function(x,y){return ((x%y)+y)%y;}; - String[] split = line.split("\t"); - if (split.length < 17) { - continue; - } - - Integer id = new Integer(split[0]); - - Color[] c = new Color[4]; - - /* store colors by raycast sequence number */ - c[0] = new Color(Integer.parseInt(split[1]), Integer.parseInt(split[2]), Integer.parseInt(split[3]), Integer.parseInt(split[4])); - c[3] = new Color(Integer.parseInt(split[5]), Integer.parseInt(split[6]), Integer.parseInt(split[7]), Integer.parseInt(split[8])); - c[1] = new Color(Integer.parseInt(split[9]), Integer.parseInt(split[10]), Integer.parseInt(split[11]), Integer.parseInt(split[12])); - c[2] = new Color(Integer.parseInt(split[13]), Integer.parseInt(split[14]), Integer.parseInt(split[15]), Integer.parseInt(split[16])); - - colors.put(id, c); - nc += 1; + // Split the image up in ... segments (1*1 for zoom 1, 2*2 for zoom 2, 4*4 for zoom 3, etc). + var segments = Math.pow(2,zoom-1); + imgSize = segments*tileSize; + + // Calculate the location relative to the world of this segment. + var mapcoord = {x: div(coord.x,segments)*tileSize, y: div(coord.y,segments)*tileSize}; + // Calculate the location relative to the image of this segment. + offset = {x: mod(coord.x,segments)*-tileSize, y: mod(coord.y,segments)*-tileSize}; + + // The next couple of lines are somewhat of a hack, but makes it faster to render zoomed in tiles: + tileSize = imgSize; + + if (offset.x == 0 && offset.y == 0) { + tileName = this.prefix + '_' + (-mapcoord.x) + '_' + mapcoord.y; } - scanner.close(); - } catch(Exception e) { - getDebugger().error("Could not load colors", e); - return null; + offset = {x: 0, y: 0}; + // The next line is not: + //tileName = this.prefix + '_' + (-mapcoord.x) + '_' + mapcoord.y; } - return colors; + var img; + var tile = $('
') + .addClass('tile') + .css({ + overflow: 'hidden', + width: tileSize + 'px', + height: tileSize + 'px' + }); + if (tileDebugText) { + $('') + .text(tileDebugText) + .css({ + position: 'absolute', + color: 'red' + }) + .appendTo(tile); + } + if (tileName) { + img = $('') + .attr('src', this.dynmap.getTileUrl(tileName)) + .error(function() { img.hide(); }) + .css({ + width: imgSize + 'px', + height: imgSize + 'px', + borderStyle: 'none', + marginLeft: offset.x + 'px', + marginTop: offset.y + 'px' + }) + .appendTo(tile); + this.dynmap.registerTile(this, tileName, img); + } else { + this.dynmap.unregisterTile(this, tileName); + } + return tile.get(0); } -} +}); \ No newline at end of file diff --git a/web/map.js b/web/map.js index 60c83542..596412a4 100644 --- a/web/map.js +++ b/web/map.js @@ -68,11 +68,11 @@ MinecraftClock.prototype = { }; function DynMap(options) { - var self = this; - self.options = options; - $.getJSON(self.options.updateUrl + 'configuration', function(configuration) { - self.configure(configuration); - self.initialize(); + var me = this; + me.options = options; + $.getJSON(me.options.updateUrl + 'configuration', function(configuration) { + me.configure(configuration); + me.initialize(); }) } DynMap.prototype = { @@ -83,14 +83,12 @@ DynMap.prototype = { lasttimestamp: '0', followingPlayer: '', configure: function(configuration) { - var self = this; - $.extend(this.options, configuration); - if (!self.options.maps) self.options.maps = {}; - $.each(self.options.shownmaps, function(index, mapentry) { + var me = this; + $.extend(me.options, configuration); + if (!me.options.maps) me.options.maps = {}; + $.each(me.options.shownmaps, function(index, mapentry) { var mapconstructor = eval(mapentry.type); - var mapname = mapentry.name; - var mapconfiguration = mapentry; - self.options.maps[mapname] = new mapconstructor(mapconfiguration); + me.options.maps[mapentry.name] = new mapconstructor(mapentry); }); }, initialize: function() { @@ -192,7 +190,7 @@ DynMap.prototype = { .addClass('alertbox') .appendTo(container); - setTimeout(function() { me.update(); }, me.options.updateRate); + setTimeout(function() { me.update(); }, me.options.updaterate); }, update: function() { var me = this; @@ -231,6 +229,8 @@ DynMap.prototype = { me.onTileUpdated(row.name); } , chat: function() { + if (!me.options.showchatballoons) + return; var chats = line.split(' '); var message = ''; for (var chatIndex = 2; chatIndex < chats.length; chatIndex++) @@ -268,13 +268,13 @@ DynMap.prototype = { delete me.markers[m]; } } - setTimeout(function() { me.update(); }, me.options.updateRate); + setTimeout(function() { me.update(); }, me.options.updaterate); }, error: function(request, statusText, ex) { me.alertbox .text('Could not update map') .show(); - setTimeout(function() { me.update(); }, me.options.updateRate); + setTimeout(function() { me.update(); }, me.options.updaterate); } }); }, @@ -369,13 +369,14 @@ DynMap.prototype = { .append($('') .addClass('playerName') .text(mi.text)) - - getMinecraftHead(mi.text, 32, function(head) { - $(head) - .addClass('playerIcon') - .prependTo(div); - playerImage.remove(); - }); + if (me.options.showplayerfacesonmap) { + getMinecraftHead(mi.text, 32, function(head) { + $(head) + .addClass('playerIcon') + .prependTo(div); + playerImage.remove(); + }); + } }; } var marker = new CustomMarker(mi.position, map, contentfun, mi); @@ -404,11 +405,13 @@ DynMap.prototype = { .click(function(e) { map.panTo(markers[mi.id].getPosition()); }) ); - getMinecraftHead(mi.text, 16, function(head) { - marker.playerRow.icon = $(head) - .addClass('playerIcon') - .appendTo(marker.playerIconContainer); - }); + if (me.options.showplayerfacesinmenu) { + getMinecraftHead(mi.text, 16, function(head) { + marker.playerRow.icon = $(head) + .addClass('playerIcon') + .appendTo(marker.playerIconContainer); + }); + } me.playerlist.append(marker.playerRow); }