diff --git a/MapListener.java b/MapListener.java index bda55853..9891f3a6 100644 --- a/MapListener.java +++ b/MapListener.java @@ -1,8 +1,12 @@ +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; import java.util.logging.Logger; public class MapListener extends PluginListener { private static final Logger log = Logger.getLogger("Minecraft"); private MapManager mgr; + private ArrayList markers; public MapListener(MapManager mgr) { @@ -71,6 +75,65 @@ public class MapListener extends PluginListener { mgr.debugPlayer = null; return true; } + + if(split[0].equals("/addmarker")) { + if(split.length < 2) + { + player.sendMessage("Map> " + Colors.Red + "Usage: /map_addmarker [name]"); + } + else + { + if (mgr.addMapMarker(player, split[1], player.getX(), player.getY(), player.getZ())) + { + player.sendMessage("Map> " + Colors.White + "Marker \"" + split[1] + "\" added successfully"); + } + } + return true; + } + + if(split[0].equals("/removemarker")) { + if(split.length < 2) + { + player.sendMessage("Map> " + Colors.Red + "Usage: /map_removemarker [name]"); + } + else + { + if (mgr.removeMapMarker(player, split[1])) + { + player.sendMessage("Map> " + Colors.White + "Marker \"" + split[1] + "\" removed successfully"); + } + } + return true; + } + + if(split[0].equals("/listmarkers")) { + String msg = ""; + Collection values = mgr.markers.values(); + Iterator it = values.iterator(); + while(it.hasNext()) + { + MapMarker marker = it.next(); + String line = " - " + marker.name + " (" + marker.owner + ")\t"; + msg += line; + } + player.sendMessage("" + Colors.White + msg); + return true; + } + + if(split[0].equals("/tpmarker")) { + if(split.length < 2) + { + player.sendMessage("Map> " + Colors.Red + "Usage: /map_tpmarker [name]"); + } + else + { + if (mgr.teleportToMapMarker(player, split[1])) + { + //player.sendMessage("Map> " + Colors.White + ""); + } + } + return true; + } return false; } diff --git a/MapManager.java b/MapManager.java index f3b49aca..5b3a5000 100644 --- a/MapManager.java +++ b/MapManager.java @@ -1,17 +1,23 @@ +import java.awt.Color; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.Iterator; -import java.util.logging.Logger; -import java.util.logging.Level; -import java.util.Scanner; import java.util.LinkedList; import java.util.ListIterator; import java.util.NoSuchElementException; +import java.util.Scanner; import java.util.Vector; - -import java.io.File; -import java.io.IOException; - -import java.awt.Color; +import java.util.logging.Level; +import java.util.logging.Logger; public class MapManager extends Thread { protected static final Logger log = Logger.getLogger("Minecraft"); @@ -46,6 +52,12 @@ public class MapManager extends Thread { /* path to image tile directory */ public String tilepath = "tiles/"; + /* path to markers file */ + public String markerpath = "markers.csv"; + + /* port to run web server on */ + public int serverport = 8123; + /* time to pause between rendering tiles (ms) */ public int renderWait = 500; @@ -57,6 +69,9 @@ public class MapManager extends Thread { /* map debugging mode (send debugging messages to this player) */ public String debugPlayer = null; + + /* hashmap of markers */ + public HashMap markers = null; public void debug(String msg) { @@ -66,7 +81,7 @@ public class MapManager extends Thread { if(p == null) return; p.sendMessage("Map> " + Colors.Red + msg); } - + public MapManager() { /* load configuration */ @@ -76,6 +91,8 @@ public class MapManager extends Thread { try { tilepath = properties.getString("map-tilepath", "tiles/"); colorsetpath = properties.getString("map-colorsetpath", "colors.txt"); + markerpath = properties.getString("map-markerpath", "markers.csv"); + serverport = Integer.parseInt(properties.getString("map-serverport", "8123")); } catch(Exception ex) { log.log(Level.SEVERE, "Exception while reading properties for dynamic map", ex); } @@ -83,9 +100,8 @@ public class MapManager extends Thread { tileStore = new HashMap(); staleTiles = new LinkedList(); tileUpdates = new LinkedList(); - - - + + markers = new HashMap(); } /* tile X for position x */ @@ -114,6 +130,8 @@ public class MapManager extends Thread { /* load colorset */ File cfile = new File(colorsetpath); + loadMapMarkers(); + try { Scanner scanner = new Scanner(cfile); int nc = 0; @@ -389,4 +407,182 @@ public class MapManager extends Thread { open.add(getTileByPosition(t.px, t.py - tileHeight)); } } + + /* adds a marker to the map */ + public boolean addMapMarker(Player player, String name, double px, double py, double pz) + { + if (markers.containsKey(name)) + { + player.sendMessage("Map> " + Colors.Red + "Marker \"" + name + "\" already exists."); + return false; + } + + MapMarker marker = new MapMarker(); + marker.name = name; + marker.owner = player.getName(); + marker.px = px; + marker.py = py; + marker.pz = pz; + markers.put(name, marker); + + try + { + saveMapMarkers(); + return true; + } + catch(IOException e) + { + log.log(Level.SEVERE, "Failed to save markers.csv", e); + } + + return false; + } + + /* removes a marker from the map */ + public boolean removeMapMarker(Player player, String name) + { + if (markers.containsKey(name)) + { + MapMarker marker = markers.get(name); + if (marker.owner.equalsIgnoreCase(player.getName())) + { + markers.remove(name); + + try + { + saveMapMarkers(); + return true; + } + catch(IOException e) + { + log.log(Level.SEVERE, "Failed to save markers.csv", e); + } + } + else + { + player.sendMessage("Map> " + Colors.Red + "Marker \"" + name + "\" does not belong to you."); + } + } + else + { + player.sendMessage("Map> " + Colors.Red + "Marker \"" + name + "\" does not exist."); + } + + return false; + } + + /* teleports a user to a marker */ + public boolean teleportToMapMarker(Player player, String name) + { + if (markers.containsKey(name)) + { + MapMarker marker = markers.get(name); + + player.teleportTo(marker.px, marker.py, marker.pz, 0, 0); + } + else + { + player.sendMessage("Map> " + Colors.Red + "Marker \"" + name + "\" does not exist."); + } + + return false; + } + + /* load the map marker file */ + private void loadMapMarkers() + { + Scanner scanner = null; + try + { + scanner = new Scanner(new FileInputStream(markerpath), "UTF-8"); + while (scanner.hasNextLine()) + { + String line = scanner.nextLine(); + String[] values = line.split(","); + MapMarker marker = new MapMarker(); + marker.name = values[0]; + marker.owner = values[1]; + marker.px = Double.parseDouble(values[2]); + marker.py = Double.parseDouble(values[3]); + marker.pz = Double.parseDouble(values[4]); + markers.put(marker.name, marker); + } + } + catch(FileNotFoundException e) + { + log.log(Level.SEVERE, "markers.csv not found", e); + } + finally + { + if (scanner != null) scanner.close(); + } + } + + /* save the map marker file */ + private void saveMapMarkers() throws IOException + { + Writer out = null; + try + { + out = new OutputStreamWriter(new FileOutputStream(markerpath), "UTF-8"); + Collection values = markers.values(); + Iterator it = values.iterator(); + while(it.hasNext()) + { + MapMarker marker = it.next(); + String line = marker.name + "," + marker.owner + "," + marker.px + "," + marker.py + "," + marker.pz + "\r\n"; + out.write(line); + } + } + catch(UnsupportedEncodingException e) + { + log.log(Level.SEVERE, "Unsupported encoding", e); + } + catch(FileNotFoundException e) + { + log.log(Level.SEVERE, "markers.csv not found", e); + } + finally + { + if (out != null) out.close(); + } + } + + /* load the warps file (doesn't work with SQL data source) */ + /* find a way to load this from the server itself, loading the file each time isn't good */ + public ArrayList loadWarps() + { + ArrayList warps = new ArrayList(); + Scanner scanner = null; + + try + { + scanner = new Scanner(new FileInputStream("warps.txt"), "UTF-8"); + while (scanner.hasNextLine()) + { + String line = scanner.nextLine(); + String[] values = line.split(":"); + Warp warp = new Warp(); + warp.Name = values[0]; + warp.Location = new Location( + Double.parseDouble(values[1]), + Double.parseDouble(values[2]), + Double.parseDouble(values[3]), + Float.parseFloat(values[4]), + Float.parseFloat(values[5]) + ); + warps.add(warp); + } + } + catch(FileNotFoundException e) + { + + } + finally + { + if (scanner != null) scanner.close(); + } + + return warps; + } } diff --git a/MapMarker.java b/MapMarker.java new file mode 100644 index 00000000..f1dab5c2 --- /dev/null +++ b/MapMarker.java @@ -0,0 +1,4 @@ +public class MapMarker { + public double px, py, pz; + public String name, owner; +} diff --git a/MapTile.java b/MapTile.java index 91980ae0..0717d1c3 100644 --- a/MapTile.java +++ b/MapTile.java @@ -118,8 +118,10 @@ public class MapTile { String path = getPath(mgr); File file = new File(path); ImageIO.write(im, "png", file); - - //log.info("Saved tile: " + path); + } catch(java.lang.NullPointerException e) { + // IOException is not enough, a NullPointerException often occurs due to this issue + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=5034864 + log.log(Level.SEVERE, "Failed to save tile (NullPointerException): " + getPath(mgr), e); } catch(IOException e) { log.log(Level.SEVERE, "Failed to save tile: " + getPath(mgr), e); } diff --git a/WebServer.java b/WebServer.java index e84bdff9..4d3bdec5 100644 --- a/WebServer.java +++ b/WebServer.java @@ -20,7 +20,7 @@ public class WebServer extends Thread { sock = new ServerSocket(port, 5, InetAddress.getByName("127.0.0.1")); running = true; start(); - log.info("map WebServer started"); + log.info("map WebServer started on port " + port); } public void run() diff --git a/WebServerRequest.java b/WebServerRequest.java index 685bd42c..bdaaac00 100644 --- a/WebServerRequest.java +++ b/WebServerRequest.java @@ -69,7 +69,20 @@ public class WebServerRequest extends Thread { for(Player player : etc.getServer().getPlayerList()) { sb.append(player.getName() + " " + player.getX() + " " + player.getY() + " " + player.getZ() + "\n"); } + + for(MapMarker marker : mgr.markers.values()) + { + sb.append(marker.name + " marker " + marker.owner + " " + marker.px + " " + marker.py + " " + marker.pz + "\n"); + } + // TODO: Find a way to load the warps from the server. Currently loading the from the flatfile over and over... + ArrayList warps = mgr.loadWarps(); + + for(Warp warp : warps) + { + sb.append(warp.Name + " warp unknown " + warp.Location.x + " " + warp.Location.y + " " + warp.Location.z + "\n"); + } + synchronized(mgr.lock) { for(TileUpdate tu : mgr.tileUpdates) { if(tu.at >= cutoff) { diff --git a/build.bat b/build.bat new file mode 100644 index 00000000..2046edbf --- /dev/null +++ b/build.bat @@ -0,0 +1,9 @@ +@ECHO OFF + +CD E:\Users\Fescen9\workspace\DynamicMap\branches\fescen9 +del *.class +del ..\..\..\map.jar +javac *.java -cp ..\..\..\Minecraft_Mod.jar;..\..\..\minecraft_server.jar +jar cvf ..\..\..\map.jar *.class + +PAUSE \ No newline at end of file diff --git a/clean.bat b/clean.bat new file mode 100644 index 00000000..02c0434b --- /dev/null +++ b/clean.bat @@ -0,0 +1,5 @@ +@ECHO OFF + +CD E:\Users\Fescen9\workspace\DynamicMap\branches\fescen9 +del *.class +del ..\..\..\map.jar \ No newline at end of file diff --git a/map.java b/map.java index 0eeddfa9..84ed9ef2 100644 --- a/map.java +++ b/map.java @@ -17,7 +17,7 @@ public class map extends Plugin { mgr.startManager(); try { - server = new WebServer(8123, mgr); + server = new WebServer(mgr.serverport, mgr); } catch(IOException e) { log.info("position failed to start WebServer (IOException)"); } @@ -48,5 +48,9 @@ public class map extends Plugin { etc.getInstance().addCommand("/map_regen", " - regenerate entire map"); etc.getInstance().addCommand("/map_debug", " - send map debugging messages"); etc.getInstance().addCommand("/map_nodebug", " - disable map debugging messages"); + etc.getInstance().addCommand("/addmarker", " [name] - adds a named marker to the map"); + etc.getInstance().addCommand("/removemarker", " [name] - removes a named marker to the map"); + etc.getInstance().addCommand("/listmarkers", " - list all named markers"); + etc.getInstance().addCommand("/tpmarker", " [name] - teleport to a named marker"); } } diff --git a/web/index.html b/web/index.html index 4d967389..405374d7 100644 --- a/web/index.html +++ b/web/index.html @@ -1,733 +1,25 @@ - - - - + + + + + + Minecraft Dynamic Map + + + + + + - -
-
???
+ + +
+
on +
[Connecting]
+
+ diff --git a/web/map.js b/web/map.js index 42a5b462..a2324b3d 100644 --- a/web/map.js +++ b/web/map.js @@ -1,3 +1,14 @@ +var setup = { + //Web based path to your tiles. Example: tileUrl: 'http://mydomain.com/myminecraftmap/', + tileUrl: 'http://gorfyhome.com/minecraft/', + //Web based path to your updates. Example: updateUrl: 'http://mydomain.com/myminecraftmap/up/', + updateUrl: 'http:/gorfyhome.com/minecraft/up/', + //Seconds the map should poll for updates. (Seconds) * 1000. The default is 2000 (every 2 seconds). + updateRate: 2000 +}; + +/* THERE SHOULD BE NO NEED FOR MANUAL CONFIGURATION BEYOND THIS POINT */ + /** * This constructor creates a label and associates it with a marker. * It is for the private use of the MarkerWithLabel class. @@ -7,10 +18,10 @@ */ function MarkerLabel_(marker) { this.marker_ = marker; - + this.labelDiv_ = document.createElement("div"); this.labelDiv_.style.cssText = "position: absolute; overflow: hidden;"; - + // Set up the DIV for handling mouse events in the label. This DIV forms a transparent veil // in the "overlayMouseTarget" pane, a veil that covers just the label. This is done so that // events can be captured even if the label is in the shadow of a google.maps.InfoWindow. @@ -18,10 +29,10 @@ function MarkerLabel_(marker) { this.eventDiv_ = document.createElement("div"); this.eventDiv_.style.cssText = this.labelDiv_.style.cssText; } - + // MarkerLabel_ inherits from OverlayView: MarkerLabel_.prototype = new google.maps.OverlayView(); - + /** * Adds the DIV representing the label to the DOM. This method is called * automatically when the marker's setMap method is called. @@ -35,7 +46,7 @@ MarkerLabel_.prototype.onAdd = function () { var cSavedZIndex; var cLatOffset, cLngOffset; var cIgnoreClick; - + // Stops all processing of an event. // var cAbortEvent = function (e) { @@ -47,10 +58,10 @@ MarkerLabel_.prototype.onAdd = function () { e.stopPropagation(); } }; - + this.getPanes().overlayImage.appendChild(this.labelDiv_); this.getPanes().overlayMouseTarget.appendChild(this.eventDiv_); - + this.listeners_ = [ google.maps.event.addDomListener(document, "mouseup", function (mEvent) { if (cDraggingInProgress) { @@ -145,7 +156,7 @@ MarkerLabel_.prototype.onAdd = function () { }) ]; }; - + /** * Removes the DIV for the label from the DOM. It also removes all event handlers. * This method is called automatically when the marker's setMap(null) @@ -156,13 +167,13 @@ MarkerLabel_.prototype.onRemove = function () { var i; this.labelDiv_.parentNode.removeChild(this.labelDiv_); this.eventDiv_.parentNode.removeChild(this.eventDiv_); - + // Remove event listeners: for (i = 0; i < this.listeners_.length; i++) { google.maps.event.removeListener(this.listeners_[i]); } }; - + /** * Draws the label on the map. * @private @@ -172,7 +183,7 @@ MarkerLabel_.prototype.draw = function () { this.setTitle(); this.setStyles(); }; - + /** * Sets the content of the label. * The content can be plain text or an HTML DOM node. @@ -189,7 +200,7 @@ MarkerLabel_.prototype.setContent = function () { this.eventDiv_.appendChild(content); } }; - + /** * Sets the content of the tool tip for the label. It is * always set to be the same as for the marker itself. @@ -198,7 +209,7 @@ MarkerLabel_.prototype.setContent = function () { MarkerLabel_.prototype.setTitle = function () { this.eventDiv_.title = this.marker_.getTitle() || ""; }; - + /** * Sets the style of the label by setting the style sheet and applying * other specific styles requested. @@ -206,11 +217,11 @@ MarkerLabel_.prototype.setTitle = function () { */ MarkerLabel_.prototype.setStyles = function () { var i, labelStyle; - + // Apply style values from the style sheet defined in the labelClass parameter: this.labelDiv_.className = this.marker_.get("labelClass"); this.eventDiv_.className = this.labelDiv_.className; - + // Clear existing inline style values: this.labelDiv_.style.cssText = ""; this.eventDiv_.style.cssText = ""; @@ -224,7 +235,7 @@ MarkerLabel_.prototype.setStyles = function () { } this.setMandatoryStyles(); }; - + /** * Sets the mandatory styles to the DIV representing the label as well as to the * associated event DIV. This includes setting the DIV position, zIndex, and visibility. @@ -237,7 +248,7 @@ MarkerLabel_.prototype.setMandatoryStyles = function () { if (typeof this.labelDiv_.style.opacity !== "undefined") { this.labelDiv_.style.filter = "alpha(opacity=" + (this.labelDiv_.style.opacity * 100) + ")"; } - + this.eventDiv_.style.position = this.labelDiv_.style.position; this.eventDiv_.style.overflow = this.labelDiv_.style.overflow; this.eventDiv_.style.opacity = 0.01; // Don't use 0; DIV won't be clickable on MSIE @@ -247,7 +258,7 @@ MarkerLabel_.prototype.setMandatoryStyles = function () { this.setPosition(); // This also updates zIndex, if necessary. this.setVisible(); }; - + /** * Sets the anchor point of the label. * @private @@ -259,7 +270,7 @@ MarkerLabel_.prototype.setAnchor = function () { this.eventDiv_.style.marginLeft = -anchor.x + "px"; this.eventDiv_.style.marginTop = -anchor.y + "px"; }; - + /** * Sets the position of the label. The zIndex is also updated, if necessary. * @private @@ -271,10 +282,10 @@ MarkerLabel_.prototype.setPosition = function () { this.labelDiv_.style.top = position.y + "px"; this.eventDiv_.style.left = this.labelDiv_.style.left; this.eventDiv_.style.top = this.labelDiv_.style.top; - + this.setZIndex(); }; - + /** * Sets the zIndex of the label. If the marker's zIndex property has not been defined, the zIndex * of the label is set to the vertical coordinate of the label. This is in keeping with the default @@ -291,7 +302,7 @@ MarkerLabel_.prototype.setZIndex = function () { this.eventDiv_.style.zIndex = this.labelDiv_.style.zIndex; } }; - + /** * Sets the visibility of the label. The label is visible only if the marker itself is * visible (i.e., its visible property is true) and the labelVisible property is true. @@ -305,7 +316,7 @@ MarkerLabel_.prototype.setVisible = function () { } this.eventDiv_.style.display = this.labelDiv_.style.display; }; - + /** * @name MarkerWithLabelOptions * @class This class represents the optional parameter passed to the {@link MarkerWithLabel} constructor. @@ -359,29 +370,29 @@ function MarkerWithLabel(opt_options) { if (typeof opt_options.labelVisible === "undefined") { opt_options.labelVisible = true; } - + this.label = new MarkerLabel_(this); // Bind the label to the marker - + // Call the parent constructor. It calls Marker.setValues to initialize, so all // the new parameters are conveniently saved and can be accessed with get/set. // Marker.set triggers a property changed event (called "propertyname_changed") // that the marker label listens for in order to react to state changes. google.maps.Marker.apply(this, arguments); } - + // MarkerWithLabel inherits from Marker: MarkerWithLabel.prototype = new google.maps.Marker(); - + MarkerWithLabel.prototype.setMap = function (theMap) { // Call the inherited function... google.maps.Marker.prototype.setMap.apply(this, arguments); - + // ... then deal with the label: this.label.setMap(theMap); }; - - - + + + /* generic function for making an XMLHttpRequest * url: request URL * func: callback function for success @@ -395,16 +406,16 @@ MarkerWithLabel.prototype.setMap = function (theMap) { function makeRequest(url, func, type, fail, post, contenttype) { var http_request = false; - + type = typeof(type) != 'undefined' ? type : 'text'; fail = typeof(fail) != 'undefined' ? fail : function() { }; - + if(window.XMLHttpRequest) { http_request = new XMLHttpRequest(); } else if(window.ActiveXObject) { http_request = new ActiveXObject("Microsoft.XMLHTTP"); } - + if(type == 'text') { http_request.onreadystatechange = function() { if(http_request.readyState == 4) { @@ -418,15 +429,13 @@ function makeRequest(url, func, type, fail, post, contenttype) } else { http_request.onreadystatechange = function() { if(http_request.readyState == 4) { - if(http_request.status == 200) { - func(http_request.responseXML); - } else { + if(http_request.status == 200) { func(http_request.responseXML); } else { fail(http_request); } } } } - + if(typeof(post) != 'undefined') { http_request.open('POST', url, true); if(typeof(contenttype) != 'undefined') @@ -437,48 +446,281 @@ function makeRequest(url, func, type, fail, post, contenttype) http_request.send(null); } } - -var markers = new Array(); - -function initActive() -{ - playerUpdate(); -} - -function playerUpdate() -{ - makeRequest('/pos', function(res) { - var rows = res.split('\n'); - var loggedin = new Array(); - for(var line in rows) { - var p = rows[line].split(' '); - loggedin[p[0]] = 1; - if(p[0] == '') continue; - if(p[0] in markers) { - var m = markers[p[0]]; - var converted = fromWorldToLatLng(p[1], p[2], p[3]); - m.setPosition(converted); - } else { - var converted = fromWorldToLatLng(p[1], p[2], p[3]); - var marker = new MarkerWithLabel({ - position: converted, - map: map, - labelContent: p[0], - labelAnchor: new google.maps.Point(-10, 30), - labelClass: "labels" - }); - - markers[p[0]] = marker; - } + + + var config = { + tileUrl: setup.tileUrl, + updateUrl: setup.updateUrl, + tileWidth: 128, + tileHeight: 128, + updateRate: setup.updateRate, + zoomSize: [ 32, 128, 256 ] + }; + + function MCMapProjection() { + } + + MCMapProjection.prototype.fromLatLngToPoint = function(latLng) { + var x = (latLng.lng() * config.tileWidth)|0; + var y = (latLng.lat() * config.tileHeight)|0; + return new google.maps.Point(x, y); + }; + + MCMapProjection.prototype.fromPointToLatLng = function(point) { + var lng = point.x / config.tileWidth; + var lat = point.y / config.tileHeight; + return new google.maps.LatLng(lat, lng); + }; + + function fromWorldToLatLng(x, y, z) + { + var dx = +x; + var dy = +y - 127; + var dz = +z; + var px = dx + dz; + var py = dx - dz - dy; + + var lng = -px / config.tileWidth / 2 + 0.5; + var lat = py / config.tileHeight / 2; + + return new google.maps.LatLng(lat, lng); + } + + function mcMapType() { + } + + var tileDict = new Array(); + var lastSeen = new Array(); + + function tileUrl(tile, always) { + if(always) { + var now = new Date(); + return config.tileUrl + 't_' + tile + '.png?' + now.getTime(); + } else if(tile in lastSeen) { + return config.tileUrl + 't_' + tile + '.png?' + lastSeen[tile]; + } else { + return config.tileUrl + 't_' + tile + '.png?0'; } - - for(var m in markers) { - if(!(m in loggedin)) { - markers[m].setMap(null); - delete markers[m]; - } + } + + function imgSubst(tile) { + if(!(tile in tileDict)) + return; + + var src = tileUrl(tile); + var t = tileDict[tile]; + t.src = src; + t.style.display = ''; + t.onerror = function() { + setTimeout(function() { + t.src = tileUrl(tile, 1); + }, 1000); + t.onerror = ''; } + } + + mcMapType.prototype.tileSize = new google.maps.Size(config.tileWidth, config.tileHeight); + mcMapType.prototype.minZoom = 1; + mcMapType.prototype.maxZoom = 2; + mcMapType.prototype.getTile = function(coord, zoom, doc) { + var img = doc.createElement('IMG'); + + img.onerror = function() { img.style.display = 'none'; } + + img.style.width = config.zoomSize[zoom] + 'px'; + img.style.height = config.zoomSize[zoom] + 'px'; + img.style.borderStyle = 'none'; + + var tilename = (- coord.x * config.tileWidth) + '_' + coord.y * config.tileHeight; + tileDict[tilename] = img; + + var url = tileUrl(tilename); + img.src = url; + //img.style.background = 'url(' + url + ')'; + //img.innerHTML = '' + tilename + ''; + + return img; + } + + var markers = new Array(); + var lasttimestamp = 0; + var followPlayer = ''; + + var lst; + var plistbtn; + var lstopen = true; + var oldplayerlst = '[Connecting]'; + + function mapUpdate() + { + makeRequest(config.updateUrl + lasttimestamp, function(res) { + var rows = res.split('\n'); + var loggedin = new Array(); + + lasttimestamp = rows[0]; + delete rows[0]; + + var playerlst = '' + + for(var line in rows) { + var p = rows[line].split(' '); + loggedin[p[0]] = 1; + if(p[0] == '') continue; + + if(p.length == 4) { + if(playerlst != '') playerlst += '
'; + playerlst += ' ' + p[0] + ''; + + if(p[0] == followPlayer) { + map.setCenter(fromWorldToLatLng(p[1], p[2], p[3])); + } + + if(p[0] in markers) { + var m = markers[p[0]]; + var converted = fromWorldToLatLng(p[1], p[2], p[3]); + m.setPosition(converted); + } else { + var converted = fromWorldToLatLng(p[1], p[2], p[3]); + var marker = new MarkerWithLabel({ + position: converted, + map: map, + labelContent: p[0], + labelAnchor: new google.maps.Point(-14, 10), + labelClass: "labels", + clickable: false, + flat: true, + icon: new google.maps.MarkerImage('marker.png', new google.maps.Size(28, 28), new google.maps.Point(0, 0), new google.maps.Point(14, 14)) + }); + + markers[p[0]] = marker; + } + } else if(p.length == 6) { + if(p[0] in markers) { + var m = markers[p[0]]; + var converted = fromWorldToLatLng(p[2], p[3], p[4]); + m.setPosition(converted); + } else { + var image = 'sign.png'; - setTimeout(playerUpdate, 2000); - }, 'text', function() { alert('failed to get position data'); } ); -} + if (p[1] == 'warp') + image = 'watch.png'; + else if (p[1] == 'home') + image = 'list_on.png'; + else if (p[1] == 'spawn') + image = 'list_on.png'; + + var converted = fromWorldToLatLng(p[2], p[3], p[4]); + var marker = new MarkerWithLabel({ + position: converted, + map: map, + labelContent: p[0], + labelAnchor: new google.maps.Point(-14, 10), + labelClass: "labels", + clickable: false, + flat: true, + icon: new google.maps.MarkerImage(image, new google.maps.Size(28, 28), new google.maps.Point(0, 0), new google.maps.Point(14, 14)) + }); + + markers[p[0]] = marker; + } + } else if(p.length == 1) { + lastSeen[p[0]] = lasttimestamp; + imgSubst(p[0]); + } + } + + if(playerlst != oldplayerlst) { + oldplayerlst = playerlst; + lst.innerHTML = playerlst; + } + + for(var m in markers) { + if(!(m in loggedin)) { + markers[m].setMap(null); + delete markers[m]; + } + } + + setTimeout(mapUpdate, config.updateRate); + }, 'text', function() { alert('failed to get update data'); } ); + } + + window.onload = function initialize() { + lst = document.getElementById('lst'); + plistbtn = document.getElementById('plistbtn'); + var mapOptions = { + zoom: 1, + center: new google.maps.LatLng(0, 1), + navigationControl: true, + navigationControlOptions: { + style: google.maps.NavigationControlStyle.SMALL + }, + scaleControl: false, + mapTypeControl: false, + streetViewControl: false, + mapTypeId: 'mcmap' + }; + map = new google.maps.Map(document.getElementById("mcmap"), mapOptions); + mapType = new mcMapType(); + mapType.projection = new MCMapProjection(); + map.zoom_changed = function() { + mapType.tileSize = new google.maps.Size(config.zoomSize[map.zoom], config.zoomSize[map.zoom]); + }; + + google.maps.event.addListener(map, 'dragstart', function(mEvent) { + plfollow(''); + }); + + map.dragstart = plfollow(''); + map.mapTypes.set('mcmap', mapType); + map.setMapTypeId('mcmap'); + mapUpdate(); + } + + function plistopen() { + if(lstopen) { + lstopen = false; + lst.style.display = 'none'; + lst.style.visibility = 'hidden'; + plistbtn.src = 'list_off.png'; + } else { + lstopen = true; + lst.style.display = ''; + lst.style.visibility = ''; + plistbtn.src = 'list_on.png'; + } + } + + function plclick(name) { + if(name in markers) { + if(name != followPlayer) plfollow(''); + map.setCenter(markers[name].getPosition()); + } + } + + function plfollow(name) { + var icon; + + if(followPlayer == name) { + icon = document.getElementById('icon_' + followPlayer); + if(icon) icon.src = 'follow_off.png'; + followPlayer = ''; + return; + } + + if(followPlayer) { + icon = document.getElementById('icon_' + followPlayer); + if(icon) icon.src = 'follow_off.png'; + followPlayer = ''; + } + + if(!name) return; + + icon = document.getElementById('icon_' + name); + if(icon) icon.src = 'follow_on.png'; + followPlayer = name; + + if(name in markers) { + map.setCenter(markers[name].getPosition()); + } + } diff --git a/web/sign.png b/web/sign.png new file mode 100644 index 00000000..8d8573ce Binary files /dev/null and b/web/sign.png differ diff --git a/web/style.css b/web/style.css new file mode 100644 index 00000000..eae2880f --- /dev/null +++ b/web/style.css @@ -0,0 +1,42 @@ +html { height: 100% } +body { height: 100%; margin: 0px; padding: 0px ; background-color: #000; } +#mcmap { width:100%; height: 100% } +#plist { + position: absolute; + top: 8px; + right: 8px; + border: 1px solid #808080; + background: #000; + opacity: 0.6; + padding: 2px; +} +#lst { + clear:both; + font-family: sans-serif; + font-size: 8pt; + color: white; +} +#plistbtn { + float:right; +} +#link { + position: absolute; + bottom: 4px; + left: 80px; + border: 1px solid #808080; + background: #000; + opacity: 0.6; + padding: 2px; + font-size: 8pt; + color: #ddd; +} +a, a:visited { + color: white; + text-decoration: none; +} +.labels { + font-size: 10pt; + font-family: sans-serif; + white-space: nowrap; + color: white; +} diff --git a/web/watch.png b/web/watch.png new file mode 100644 index 00000000..8ed89e7f Binary files /dev/null and b/web/watch.png differ