From af18b7390848e3486fb636f3577e0e6826b4c515 Mon Sep 17 00:00:00 2001 From: kzed Date: Sat, 4 Dec 2010 13:24:32 +0000 Subject: [PATCH] added fescen9 branch --- MapListener.java | 77 +++++ MapManager.java | 392 ++++++++++++++++++++++ MapTile.java | 191 +++++++++++ TileUpdate.java | 12 + WebServer.java | 53 +++ WebServerRequest.java | 97 ++++++ build.sh | 3 + clean.sh | 2 + colors.txt | 72 +++++ map.java | 52 +++ web/follow_off.png | Bin 0 -> 377 bytes web/follow_on.png | Bin 0 -> 376 bytes web/index.html | 733 ++++++++++++++++++++++++++++++++++++++++++ web/list_off.png | Bin 0 -> 351 bytes web/list_on.png | Bin 0 -> 360 bytes web/map.js | 484 ++++++++++++++++++++++++++++ web/marker.png | Bin 0 -> 620 bytes 17 files changed, 2168 insertions(+) create mode 100644 MapListener.java create mode 100644 MapManager.java create mode 100644 MapTile.java create mode 100644 TileUpdate.java create mode 100644 WebServer.java create mode 100644 WebServerRequest.java create mode 100755 build.sh create mode 100755 clean.sh create mode 100644 colors.txt create mode 100644 map.java create mode 100644 web/follow_off.png create mode 100644 web/follow_on.png create mode 100644 web/index.html create mode 100644 web/list_off.png create mode 100644 web/list_on.png create mode 100644 web/map.js create mode 100644 web/marker.png diff --git a/MapListener.java b/MapListener.java new file mode 100644 index 00000000..bda55853 --- /dev/null +++ b/MapListener.java @@ -0,0 +1,77 @@ +import java.util.logging.Logger; + +public class MapListener extends PluginListener { + private static final Logger log = Logger.getLogger("Minecraft"); + private MapManager mgr; + + public MapListener(MapManager mgr) + { + this.mgr = mgr; + } + + @Override + public boolean onBlockCreate(Player player, Block blockPlaced, Block blockClicked, int itemInHand) + { + if(mgr.touch(blockPlaced.getX(), blockPlaced.getY(), blockPlaced.getZ())) + mgr.debug(player.getName() + " touch " + blockPlaced.getX() + "," + blockPlaced.getY() + "," + blockPlaced.getZ() + " from onBlockCreate"); + return false; + } + + @Override + public boolean onBlockDestroy(Player player, Block block) + { + int x = block.getX(); + int y = block.getY(); + int z = block.getZ(); + if(x == 0 && y == 0 && z == 0) + return false; + + if(mgr.touch(x, y, z)) + mgr.debug(player.getName() + " touch " + x + "," + y + "," + z + " from onBlockBreak"); + + return false; + } + + @Override + public boolean onCommand(Player player, String[] split) + { + if(!player.canUseCommand(split[0])) + return false; + + if(split[0].equals("/map_wait")) { + if(split.length < 2) { + mgr.renderWait = 1000; + } else { + try { + mgr.renderWait = Integer.parseInt(split[1]); + } catch(NumberFormatException e) { + player.sendMessage(Colors.Rose + "Invalid number"); + } + } + return true; + } + + if(split[0].equals("/map_regen")) { + mgr.regenerate((int) player.getX(), (int) player.getY(), (int) player.getZ()); + player.sendMessage(Colors.Rose + "Map regeneration in progress"); + return true; + } + + if(split[0].equals("/map_stat")) { + player.sendMessage(Colors.Rose + "Stale tiles: " + mgr.getStaleCount() + " Recent updates: " + mgr.getRecentUpdateCount()); + return true; + } + + if(split[0].equals("/map_debug")) { + mgr.debugPlayer = player.getName(); + return true; + } + + if(split[0].equals("/map_nodebug")) { + mgr.debugPlayer = null; + return true; + } + + return false; + } +} diff --git a/MapManager.java b/MapManager.java new file mode 100644 index 00000000..f3b49aca --- /dev/null +++ b/MapManager.java @@ -0,0 +1,392 @@ +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.Vector; + +import java.io.File; +import java.io.IOException; + +import java.awt.Color; + +public class MapManager extends Thread { + protected static final Logger log = Logger.getLogger("Minecraft"); + + /* dimensions of a map tile */ + public static final int tileWidth = 128; + public static final int tileHeight = 128; + + /* lock for our data structures */ + public static final Object lock = new Object(); + + /* a hash table of known MapTiles, by their key (projection coords) */ + private HashMap tileStore; + + /* a list of MapTiles to be updated */ + private LinkedList staleTiles; + + /* whether the worker thread should be running now */ + private boolean running = false; + + /* map x, y, z for projection origin */ + public static final int anchorx = 0; + public static final int anchory = 127; + public static final int anchorz = 0; + + /* color database: id -> Color */ + public HashMap colors = null; + + /* path to colors.txt */ + private String colorsetpath = "colors.txt"; + + /* path to image tile directory */ + public String tilepath = "tiles/"; + + /* time to pause between rendering tiles (ms) */ + public int renderWait = 500; + + /* remember up to this old tile updates (ms) */ + private static final int maxTileAge = 60000; + + /* this list stores the tile updates */ + public LinkedList tileUpdates = null; + + /* map debugging mode (send debugging messages to this player) */ + public String debugPlayer = null; + + public void debug(String msg) + { + if(debugPlayer == null) return; + Server s = etc.getServer(); + Player p = s.getPlayer(debugPlayer); + if(p == null) return; + p.sendMessage("Map> " + Colors.Red + msg); + } + + public MapManager() + { + /* load configuration */ + PropertiesFile properties; + + properties = new PropertiesFile("server.properties"); + try { + tilepath = properties.getString("map-tilepath", "tiles/"); + colorsetpath = properties.getString("map-colorsetpath", "colors.txt"); + } catch(Exception ex) { + log.log(Level.SEVERE, "Exception while reading properties for dynamic map", ex); + } + + tileStore = new HashMap(); + staleTiles = new LinkedList(); + tileUpdates = new LinkedList(); + + + + } + + /* 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); + } + + /* initialize and start map manager */ + public void startManager() + { + colors = new HashMap(); + + /* load colorset */ + File cfile = new File(colorsetpath); + + try { + Scanner scanner = new Scanner(cfile); + int nc = 0; + while(scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (line.startsWith("#") || line.equals("")) { + continue; + } + + 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; + } + scanner.close(); + + log.info(nc + " colors loaded from " + colorsetpath); + } catch(Exception e) { + log.log(Level.SEVERE, "Failed to load colorset: " + colorsetpath, e); + return; + } + + running = true; + this.start(); + try { + this.setPriority(MIN_PRIORITY); + log.info("Set minimum priority for worker thread"); + } catch(SecurityException e) { + log.info("Failed to set minimum priority for worker thread!"); + } + } + + /* stop map manager */ + public void stopManager() + { + if(!running) + return; + + log.info("Stopping map renderer..."); + running = false; + + try { + this.join(); + } catch(InterruptedException e) { + log.info("Waiting for map renderer to stop is interrupted"); + } + } + + /* the worker/renderer thread */ + public void run() + { + log.info("Map renderer has started."); + + while(running) { + + /* + if(debugPlayer != null) { + Player p = etc.getServer().getPlayer(debugPlayer); + if(p != null) { + int x = (int) p.getX(); + int y = (int) p.getY(); + int z = (int) p.getZ(); + 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); + + p.sendMessage(Colors.Red + "pos " + x + "," + y + "," + z + " -> px=" + px + " py=" + py + " -> tx=" + tx + " ty=" + ty); + } + } + */ + + MapTile t = this.popStaleTile(); + if(t != null) { + t.render(this); + + long now = System.currentTimeMillis(); + long deadline = now - maxTileAge; + + /* update the tileupdate list */ + synchronized(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)); + } + + try { + this.sleep(renderWait); + } catch(InterruptedException e) { + } + } else { + try { + this.sleep(1000); + } catch(InterruptedException e) { + } + } + } + + log.info("Map renderer has stopped."); + } + + /* "touch" a block - its map tile will be regenerated */ + public boolean touch(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); + + boolean r; + + r = pushStaleTile(tx, ty); + + /* + if(r) { + debug("touch stale " + x + "," + y + "," + z + " -> px=" + px + " py=" + py + " -> tx=" + tx + " ty=" + 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) + r = pushStaleTile(tx - tileWidth, ty) || r; + if(redge) + r = pushStaleTile(tx + tileWidth, ty) || r; + if(tedge) + r = pushStaleTile(tx, ty - tileHeight) || r; + if(bedge) + r = pushStaleTile(tx, ty + tileHeight) || r; + + if(ledge && tedge) + r = pushStaleTile(tx - tileWidth, ty - tileHeight) || r; + if(ledge && bedge) + r = pushStaleTile(tx - tileWidth, ty + tileHeight) || r; + if(redge && tedge) + r = pushStaleTile(tx + tileWidth, ty - tileHeight) || r; + if(redge && bedge) + r = pushStaleTile(tx + tileWidth, ty + tileHeight) || r; + + return r; + } + + /* get next MapTile that needs to be regenerated, or null + * the mapTile is removed from the list of stale tiles! */ + public MapTile popStaleTile() + { + synchronized(lock) { + try { + MapTile t = staleTiles.removeFirst(); + t.stale = false; + return t; + } catch(NoSuchElementException e) { + return null; + } + } + } + + /* put a MapTile that needs to be regenerated on the list of stale tiles */ + public boolean pushStaleTile(MapTile m) + { + synchronized(lock) { + if(m.stale) return false; + + m.stale = true; + staleTiles.addLast(m); + + debug(m.toString() + " is now stale"); + + return true; + } + } + + /* make a MapTile stale by projection position */ + public boolean pushStaleTile(int tx, int ty) + { + return pushStaleTile(getTileByPosition(tx, ty)); + } + + /* get (or create) MapTile by projection position */ + private MapTile getTileByPosition(int px, int py) + { + Long key = MapTile.key(px, py); + synchronized(lock) { + MapTile t = tileStore.get(key); + if(t == null) { + /* no maptile exists, need to create one */ + t = new MapTile(px, py); + tileStore.put(key, t); + return t; + } else { + return t; + } + } + } + + /* return number of stale tiles */ + public int getStaleCount() + { + synchronized(lock) { + return staleTiles.size(); + } + } + + /* return number of recently updated tiles */ + public int getRecentUpdateCount() + { + synchronized(lock) { + return tileUpdates.size(); + } + } + + /* 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); + + Server s = etc.getServer(); + + while(open.size() > 0) { + MapTile t = open.remove(open.size() - 1); + if(t.stale) continue; + int h = s.getHighestBlockY(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)); + } + } +} diff --git a/MapTile.java b/MapTile.java new file mode 100644 index 00000000..91980ae0 --- /dev/null +++ b/MapTile.java @@ -0,0 +1,191 @@ +import java.util.logging.Logger; +import java.util.logging.Level; + +import java.awt.*; +import java.awt.image.*; + +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; + +public class MapTile { + protected static final Logger log = Logger.getLogger("Minecraft"); + + /* projection position */ + public int px, py; + + /* minecraft space origin */ + public int mx, my, mz; + + /* whether this tile needs to be updated */ + boolean stale = false; + + /* create new MapTile */ + public MapTile(int px, int py) + { + this.px = px; + this.py = py; + + mx = MapManager.anchorx + px / 2 + py / 2; + my = MapManager.anchory; + mz = MapManager.anchorz + px / 2 - py / 2; + } + + /* get key by projection position */ + public static long key(int px, int py) + { + long lpx = (long) px; + long lpy = (long) py; + + return ((lpx & (long) 0xffffffffL) << 32) | (lpy & (long) 0xffffffffL); + } + + /* hash value, based on projection position */ + public int hashCode() + { + return (px << 16) ^ py; + } + + /* equality comparison - based on projection position */ + public boolean equals(MapTile o) + { + return o.px == px && o.py == py; + } + + /* return a simple string representation... */ + public String toString() + { + return px + "_" + py; + } + + /* render this tile */ + public void render(MapManager mgr) + { + mgr.debug("Rendering tile: " + this.toString()); + + BufferedImage im = new BufferedImage(MapManager.tileWidth, MapManager.tileHeight, BufferedImage.TYPE_INT_RGB); + + WritableRaster r = im.getRaster(); + + int ix = mx; + int iy = my; + int iz = mz; + int jx, jz; + + int x, y; + + /* draw the map */ + for(y=0; y=0; x-=2) { + Color c1 = scan(mgr, jx, iy, jz, 0); + Color c2 = scan(mgr, jx, iy, jz, 2); + + r.setPixel(x, y, new int[] { c1.getRed(), c1.getGreen(), c1.getBlue() }); + r.setPixel(x-1, y, new int[] { c2.getRed(), c2.getGreen(), c2.getBlue() }); + + jx++; + jz++; + + } + + y ++; + + jx = ix; + jz = iz - 1; + + for(x=MapManager.tileWidth-1; x>=0; x-=2) { + Color c1 = scan(mgr, jx, iy, jz, 2); + jx++; + jz++; + Color c2 = scan(mgr, jx, iy, jz, 0); + + r.setPixel(x, y, new int[] { c1.getRed(), c1.getGreen(), c1.getBlue() }); + r.setPixel(x-1, y, new int[] { c2.getRed(), c2.getGreen(), c2.getBlue() }); + } + + y ++; + + ix ++; + iz --; + } + + /* save image */ + try { + String path = getPath(mgr); + File file = new File(path); + ImageIO.write(im, "png", file); + + //log.info("Saved tile: " + path); + } catch(IOException e) { + log.log(Level.SEVERE, "Failed to save tile: " + getPath(mgr), e); + } + } + + /* generate a path name for this map tile */ + String getPath(MapManager mgr) + { + return mgr.tilepath + "t_" + px + "_" + py + ".png"; + } + + /* cast a ray into the map */ + private Color scan(MapManager mgr, int x, int y, int z, int seq) + { + Server s = etc.getServer(); + + for(;;) { + if(y < 0) + return Color.BLUE; + + int id = s.getBlockIdAt(x, y, z); + + switch(seq) { + case 0: + x--; + break; + case 1: + y--; + break; + case 2: + z++; + break; + case 3: + y--; + break; + } + + seq = (seq + 1) & 3; + + if(id != 0) { + Color[] colors = mgr.colors.get(id); + if(colors != null) { + Color c = colors[seq]; + if(c.getAlpha() > 0) { + /* we found something that isn't transparent! */ + if(c.getAlpha() == 255) { + /* it's opaque - the ray ends here */ + return c; + } + + /* this block is transparent, so recurse */ + Color bg = scan(mgr, x, y, z, seq); + + int cr = c.getRed(); + int cg = c.getGreen(); + int cb = c.getBlue(); + int ca = c.getAlpha(); + cr *= ca; + cg *= ca; + cb *= ca; + int na = 255 - ca; + + return new Color((bg.getRed() * na + cr) >> 8, (bg.getGreen() * na + cg) >> 8, (bg.getBlue() * na + cb) >> 8); + } + } + } + } + } +} diff --git a/TileUpdate.java b/TileUpdate.java new file mode 100644 index 00000000..0d5ad1f5 --- /dev/null +++ b/TileUpdate.java @@ -0,0 +1,12 @@ +/* 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/WebServer.java b/WebServer.java new file mode 100644 index 00000000..e84bdff9 --- /dev/null +++ b/WebServer.java @@ -0,0 +1,53 @@ +import java.io.*; +import java.net.*; +import java.util.*; + +import java.util.logging.Logger; + +public class WebServer extends Thread { + + public static final String VERSION = "Huncraft"; + protected static final Logger log = Logger.getLogger("Minecraft"); + + private ServerSocket sock = null; + private boolean running = false; + + private MapManager mgr; + + public WebServer(int port, MapManager mgr) throws IOException + { + this.mgr = mgr; + sock = new ServerSocket(port, 5, InetAddress.getByName("127.0.0.1")); + running = true; + start(); + log.info("map WebServer started"); + } + + public void run() + { + while (running) { + try { + Socket socket = sock.accept(); + WebServerRequest requestThread = new WebServerRequest(socket, mgr); + requestThread.start(); + } + catch (IOException e) { + log.info("map WebServer.run() stops with IOException"); + break; + } + } + log.info("map WebServer run() exiting"); + } + + public void shutdown() + { + try { + if(sock != null) { + sock.close(); + } + } catch(IOException e) { + log.info("map stop() got IOException while closing socket"); + } + running = false; + } +} diff --git a/WebServerRequest.java b/WebServerRequest.java new file mode 100644 index 00000000..685bd42c --- /dev/null +++ b/WebServerRequest.java @@ -0,0 +1,97 @@ +import java.io.*; +import java.net.*; +import java.util.*; + +import java.util.logging.Logger; + +public class WebServerRequest extends Thread { + private Socket sock; + private MapManager mgr; + + public WebServerRequest(Socket socket, MapManager mgr) + { + sock = socket; + this.mgr = mgr; + } + + private static void sendHeader(BufferedOutputStream out, int code, String contentType, long contentLength, long lastModified) throws IOException + { + out.write(("HTTP/1.0 " + code + " OK\r\n" + + "Date: " + new Date().toString() + "\r\n" + + "Server: JibbleWebServer/1.0\r\n" + + "Content-Type: " + contentType + "\r\n" + + "Expires: Thu, 01 Dec 1994 16:00:00 GMT\r\n" + + ((contentLength != -1) ? "Content-Length: " + contentLength + "\r\n" : "") + + "Last-modified: " + new Date(lastModified).toString() + "\r\n" + + "\r\n").getBytes()); + } + + private static void sendError(BufferedOutputStream out, int code, String message) throws IOException + { + message = message + "
" + WebServer.VERSION; + sendHeader(out, code, "text/html", message.length(), System.currentTimeMillis()); + out.write(message.getBytes()); + out.flush(); + out.close(); + } + + public void run() + { + InputStream reader = null; + try { + sock.setSoTimeout(30000); + BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream())); + BufferedOutputStream out = new BufferedOutputStream(sock.getOutputStream()); + + String request = in.readLine(); + if (request == null || !request.startsWith("GET ") || !(request.endsWith(" HTTP/1.0") || request.endsWith("HTTP/1.1"))) { + // Invalid request type (no "GET") + sendError(out, 500, "Invalid Method."); + return; + } + + String path = request.substring(4, request.length() - 9); + + int current = (int) (System.currentTimeMillis() / 1000); + long cutoff = 0; + if(path.charAt(0) == '/') { + try { + cutoff = ((long) Integer.parseInt(path.substring(1))) * 1000; + } catch(NumberFormatException e) { + } + } + + sendHeader(out, 200, "text/plain", -1, System.currentTimeMillis()); + + StringBuilder sb = new StringBuilder(); + sb.append(current + "\n"); + + for(Player player : etc.getServer().getPlayerList()) { + sb.append(player.getName() + " " + player.getX() + " " + player.getY() + " " + player.getZ() + "\n"); + } + + synchronized(mgr.lock) { + for(TileUpdate tu : mgr.tileUpdates) { + if(tu.at >= cutoff) { + sb.append(tu.tile.px + "_" + tu.tile.py + "\n"); + } + } + } + + out.write(sb.toString().getBytes()); + + out.flush(); + out.close(); + } + catch (IOException e) { + if (reader != null) { + try { + reader.close(); + } + catch (Exception anye) { + // Do nothing. + } + } + } + } +} diff --git a/build.sh b/build.sh new file mode 100755 index 00000000..4212c080 --- /dev/null +++ b/build.sh @@ -0,0 +1,3 @@ +#!/bin/bash +rm -f *.class *.jar +javac *.java -cp ../hey0/bin/Minecraft_Mod.jar:../minecraft_server.jar && jar cvf map.jar *.class diff --git a/clean.sh b/clean.sh new file mode 100755 index 00000000..db437f5e --- /dev/null +++ b/clean.sh @@ -0,0 +1,2 @@ +#!/bin/bash +rm -f *.class *.jar diff --git a/colors.txt b/colors.txt new file mode 100644 index 00000000..b2ad8cc6 --- /dev/null +++ b/colors.txt @@ -0,0 +1,72 @@ +1 120 120 120 255 96 96 96 255 60 60 60 255 48 48 48 255 +2 117 176 73 255 93 140 58 255 58 88 36 255 46 70 29 255 +3 134 96 67 255 107 76 53 255 67 48 33 255 53 38 26 255 +4 115 115 115 255 92 92 92 255 57 57 57 255 46 46 46 255 +48 115 115 115 255 92 92 92 255 57 57 57 255 46 46 46 255 +5 157 128 79 255 125 102 63 255 78 64 39 255 62 51 31 255 +6 120 120 120 0 96 96 96 0 60 60 60 0 48 48 48 0 +7 84 84 84 255 67 67 67 255 42 42 42 255 33 33 33 255 +8 38 92 255 51 30 73 204 51 19 46 127 51 15 36 102 51 +9 38 92 255 51 30 73 204 51 19 46 127 51 15 36 102 51 +10 255 90 0 255 204 72 0 255 127 45 0 255 102 36 0 255 +11 255 90 0 255 204 72 0 255 127 45 0 255 102 36 0 255 +12 218 210 158 255 174 168 126 255 109 105 79 255 87 84 63 255 +13 136 126 126 255 108 100 100 255 68 63 63 255 54 50 50 255 +14 143 140 125 255 114 112 100 255 71 70 62 255 57 56 50 255 +15 136 130 127 255 108 104 101 255 68 65 63 255 54 52 50 255 +16 115 115 115 255 92 92 92 255 57 57 57 255 46 46 46 255 +17 102 81 51 255 81 64 40 255 51 40 25 255 40 32 20 255 +18 60 192 41 100 48 153 32 100 30 96 20 100 24 76 16 100 +20 255 255 255 64 204 204 204 64 127 127 127 64 102 102 102 64 +35 222 222 222 255 177 177 177 255 111 111 111 255 88 88 88 255 +37 255 0 0 255 204 0 0 255 127 0 0 255 102 0 0 255 +38 255 255 0 0 204 204 0 0 127 127 0 0 102 102 0 0 +41 232 245 46 255 185 196 36 255 116 122 23 255 92 98 18 255 +42 191 191 191 255 152 152 152 255 95 95 95 255 76 76 76 255 +43 200 200 200 255 160 160 160 255 100 100 100 255 80 80 80 255 +44 200 200 200 255 160 160 160 255 100 100 100 255 80 80 80 255 +45 170 86 62 255 136 68 49 255 85 43 31 255 68 34 24 255 +46 160 83 65 255 128 66 52 255 80 41 32 255 64 33 26 255 +49 26 11 43 255 20 8 34 255 13 5 21 255 10 4 17 255 +51 255 170 30 200 204 136 24 200 127 85 15 200 102 68 12 200 +53 157 128 79 255 125 102 63 255 78 64 39 255 62 51 31 255 +54 125 91 38 255 100 72 30 255 62 45 19 255 50 36 15 255 +56 129 140 143 255 103 112 114 255 64 70 71 255 51 56 57 255 +57 45 166 152 255 36 132 121 255 22 83 76 255 18 66 60 255 +58 114 88 56 255 91 70 44 255 57 44 28 255 45 35 22 255 +59 146 192 0 255 116 153 0 255 73 96 0 255 58 76 0 255 +60 95 58 30 255 76 46 24 255 47 29 15 255 38 23 12 255 +61 96 96 96 255 76 76 76 255 48 48 48 255 38 38 38 255 +62 96 96 96 255 76 76 76 255 48 48 48 255 38 38 38 255 +63 111 91 54 255 88 72 43 255 55 45 27 255 44 36 21 255 +64 136 109 67 255 108 87 53 255 68 54 33 255 54 43 26 255 +65 181 140 64 32 144 112 51 32 90 70 32 32 72 56 25 32 +66 150 134 102 180 120 107 81 180 75 67 51 180 60 53 40 180 +67 115 115 115 255 92 92 92 255 57 57 57 255 46 46 46 255 +71 191 191 191 255 152 152 152 255 95 95 95 255 76 76 76 255 +73 131 107 107 255 104 85 85 255 65 53 53 255 52 42 42 255 +74 131 107 107 255 104 85 85 255 65 53 53 255 52 42 42 255 +78 255 255 255 255 204 204 204 255 127 127 127 255 102 102 102 255 +79 83 113 163 51 66 90 130 51 41 56 81 51 33 45 65 51 +80 250 250 250 255 200 200 200 255 125 125 125 255 100 100 100 255 +81 25 120 25 255 20 96 20 255 12 60 12 255 10 48 10 255 +82 151 157 169 255 120 125 135 255 75 78 84 255 60 62 67 255 +83 193 234 150 255 154 187 120 255 96 117 75 255 77 93 60 255 + +redstone torch off +75 159 127 80 255 72 56 25 0 181 140 64 255 144 112 51 0 +redstone torch on +76 159 127 80 255 102 0 0 0 255 0 0 255 204 0 0 0 +torch +50 159 127 80 255 98 88 20 0 245 220 50 255 196 176 40 0 + +fence +85 172 136 82 255 172 136 82 0 91 70 37 255 91 70 37 0 +netherstone +87 166 89 89 255 141 80 62 255 135 15 15 255 96 6 6 255 +slowsand +88 133 109 94 255 121 97 82 255 90 70 57 255 79 59 46 255 +lightstone +89 249 212 156 255 255 188 94 255 192 143 70 255 122 91 44 255 +portal +90 99 66 222 128 99 66 222 128 99 66 222 128 99 66 222 128 diff --git a/map.java b/map.java new file mode 100644 index 00000000..0eeddfa9 --- /dev/null +++ b/map.java @@ -0,0 +1,52 @@ +import java.util.logging.Logger; +import java.io.IOException; + +public class map extends Plugin { + + protected static final Logger log = Logger.getLogger("Minecraft"); + + private WebServer server = null; + private MapManager mgr = null; + private MapListener listener = null; + + @Override + public void enable() { + log.info("Map INIT"); + + mgr = new MapManager(); + mgr.startManager(); + + try { + server = new WebServer(8123, mgr); + } catch(IOException e) { + log.info("position failed to start WebServer (IOException)"); + } + + listener = new MapListener(mgr); + } + + @Override + public void disable() { + log.info("Map UNINIT"); + + mgr.stopManager(); + + if(server != null) { + server.shutdown(); + server = null; + } + } + + @Override + public void initialize() { + etc.getLoader().addListener(PluginLoader.Hook.COMMAND, listener, this, PluginListener.Priority.MEDIUM); + etc.getLoader().addListener(PluginLoader.Hook.BLOCK_CREATED, listener, this, PluginListener.Priority.MEDIUM); + etc.getLoader().addListener(PluginLoader.Hook.BLOCK_DESTROYED, listener, this, PluginListener.Priority.MEDIUM); + + etc.getInstance().addCommand("/map_wait", " [wait] - set wait between tile renders (ms)"); + etc.getInstance().addCommand("/map_stat", " - query number of tiles in render queue"); + 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"); + } +} diff --git a/web/follow_off.png b/web/follow_off.png new file mode 100644 index 0000000000000000000000000000000000000000..ba506d09782dd239fa74a567d97b1beb6421c2fc GIT binary patch literal 377 zcmeAS@N?(olHy`uVBq!ia0vp^{2A!YBLG}_)Usv{9+`^o)CbL=ouLlZAmbgZgIOpf) zrskC}I2WZRmZYXAlxLP?D7bt2281{Ai31f~@pN$v(Kw%+uz%*A7I^Fo2cjokT?AZ<79+~MJ8-!m)G?9pdsVM8;e)1BSjkFWWha4Rh>z2o=R zC;b0<#p8eFr8X*72+h=syNeS3j3^ HP6OGP- literal 0 HcmV?d00001 diff --git a/web/follow_on.png b/web/follow_on.png new file mode 100644 index 0000000000000000000000000000000000000000..2fb7be7a830cb0db93246700a0f6551e47417d70 GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^{2A!YBLG}_)Usv{9+`^o4a@mtocL9YYOI#yLobz*Y zQ}ap~oQqNuOHxx5$}>wc6x=<11Hv2m#DR(~d%8G=Xq-<@SioSAB>ZHv`R{(cZ!aHc zx;4%E`R44u)$3-S`tbOibDRD8`-TQUu%Uy?&}Q9>qz(V-<~znUO-?+v%)Q6uwoc@n zLkECh!j`Sm6aRfv=P{S7O;x#-KQC?3|E>M&f3Dw`d96OBd1l8&4gG^BKbYIhYXkzv zZHsx{8L2r*$?~*H@WoV|E_i$~{Mhro#Sb1fgd3l6tEo#-I_8#QDHf=BXBOwp6AP94 zZ?ByD^3{LSCp-Q>{=`?5VZ7*e$Jv!Ei<>9(S~#}Nm#LJ}WMsG?o9OXzkEb8ddkmhg KelF{r5}E)!GMOO& literal 0 HcmV?d00001 diff --git a/web/index.html b/web/index.html new file mode 100644 index 00000000..4d967389 --- /dev/null +++ b/web/index.html @@ -0,0 +1,733 @@ + + + + + + + + + +
+
???
+ + diff --git a/web/list_off.png b/web/list_off.png new file mode 100644 index 0000000000000000000000000000000000000000..b7be11be0027d8df76b2599a1aa6923903c50d1b GIT binary patch literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`jKx9jP7LeL$-D$|*pj^6T^Rm@ z;DWu&Cj&(|3p^r=85sCBfH0%q>A!YBLG}_)Usv{9+`^oavU_hWIt~<)EOCt}an8@p zP0cG|a4t$sEJ;mKD9}%WVsZNFMr%S-rs_>evELm1!MM0+hlUzO8Uj<2qD$JbVAUe}6aU2sCaD>o^*v?OAj# zbK6AyX(s0%@E=ie4Z1knbJeO<+dV^aCS}cXQ)xcho!^`&G2yvo*CL18+thZL*dN_7 zS<`OL>DWa&u@7q^7q83yQro}o^@BOT=PtOTYW1eLaQDRjT6G(Pb>a`})IO-#bujil oTf_d)gXYI;^d56J=gEF#X?wMYPtmzs5aA!YBLG}_)Usv{9+`^oaCjT?6Rs)43OI#yLobz*Y zQ}ap~oQqNuOHxx5$}>wc6x=<11Hv2m#DR(qd%8G=Se$-4*^ujygMjOQHf`P9tCxIU z3byz>HeAD8JtyX<63d#kIZTs-v>kP>e!9J5k$~{zTj9DopP!%AdF|3tk(9yH0v;xlI&8<$($-5BSquvyS~VTXa|B)4Wqi=47__J-3( zB_#Y_?sboxrqQ+NcAiU1-_ww{@ yt0p};ueM_L&SDSQ{}Y}Ggf4yknfbo8J%b$kPNOr%tFHmQ!{F)a=d#Wzp$PyCu8B7Q literal 0 HcmV?d00001 diff --git a/web/map.js b/web/map.js new file mode 100644 index 00000000..42a5b462 --- /dev/null +++ b/web/map.js @@ -0,0 +1,484 @@ +/** + * This constructor creates a label and associates it with a marker. + * It is for the private use of the MarkerWithLabel class. + * @constructor + * @param {Marker} marker The marker with which the label is to be associated. + * @private + */ +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. + // Code is included here to ensure the veil is always exactly the same size as the label. + 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. + * @private + */ +MarkerLabel_.prototype.onAdd = function () { + var me = this; + var cMouseIsDown = false; + var cDraggingInProgress = false; + var cSavedPosition; + var cSavedZIndex; + var cLatOffset, cLngOffset; + var cIgnoreClick; + + // Stops all processing of an event. + // + var cAbortEvent = function (e) { + if (e.preventDefault) { + e.preventDefault(); + } + e.cancelBubble = true; + if (e.stopPropagation) { + 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) { + mEvent.latLng = cSavedPosition; + cIgnoreClick = true; // Set flag to ignore the click event reported after a label drag + google.maps.event.trigger(me.marker_, "dragend", mEvent); + } + cMouseIsDown = false; + google.maps.event.trigger(me.marker_, "mouseup", mEvent); + }), + google.maps.event.addListener(me.marker_.getMap(), "mousemove", function (mEvent) { + if (cMouseIsDown && me.marker_.getDraggable()) { + // Change the reported location from the mouse position to the marker position: + mEvent.latLng = new google.maps.LatLng(mEvent.latLng.lat() - cLatOffset, mEvent.latLng.lng() - cLngOffset); + cSavedPosition = mEvent.latLng; + if (cDraggingInProgress) { + google.maps.event.trigger(me.marker_, "drag", mEvent); + } else { + // Calculate offsets from the click point to the marker position: + cLatOffset = mEvent.latLng.lat() - me.marker_.getPosition().lat(); + cLngOffset = mEvent.latLng.lng() - me.marker_.getPosition().lng(); + google.maps.event.trigger(me.marker_, "dragstart", mEvent); + } + } + }), + google.maps.event.addDomListener(this.eventDiv_, "mouseover", function (e) { + //me.eventDiv_.style.cursor = "pointer"; + google.maps.event.trigger(me.marker_, "mouseover", e); + }), + google.maps.event.addDomListener(this.eventDiv_, "mouseout", function (e) { + //me.eventDiv_.style.cursor = me.marker_.getCursor(); + google.maps.event.trigger(me.marker_, "mouseout", e); + }), + google.maps.event.addDomListener(this.eventDiv_, "click", function (e) { + if (cIgnoreClick) { // Ignore the click reported when a label drag ends + cIgnoreClick = false; + } else { + cAbortEvent(e); // Prevent click from being passed on to map + google.maps.event.trigger(me.marker_, "click", e); + } + }), + google.maps.event.addDomListener(this.eventDiv_, "dblclick", function (e) { + cAbortEvent(e); // Prevent map zoom when double-clicking on a label + google.maps.event.trigger(me.marker_, "dblclick", e); + }), + google.maps.event.addDomListener(this.eventDiv_, "mousedown", function (e) { + cMouseIsDown = true; + cDraggingInProgress = false; + cLatOffset = 0; + cLngOffset = 0; + cAbortEvent(e); // Prevent map pan when starting a drag on a label + google.maps.event.trigger(me.marker_, "mousedown", e); + }), + google.maps.event.addListener(this.marker_, "dragstart", function (mEvent) { + cDraggingInProgress = true; + cSavedZIndex = me.marker_.getZIndex(); + }), + google.maps.event.addListener(this.marker_, "drag", function (mEvent) { + me.marker_.setPosition(mEvent.latLng); + me.marker_.setZIndex(1000000); // Moves the marker to the foreground during a drag + }), + google.maps.event.addListener(this.marker_, "dragend", function (mEvent) { + cDraggingInProgress = false; + me.marker_.setZIndex(cSavedZIndex); + }), + google.maps.event.addListener(this.marker_, "position_changed", function () { + me.setPosition(); + }), + google.maps.event.addListener(this.marker_, "zindex_changed", function () { + me.setZIndex(); + }), + google.maps.event.addListener(this.marker_, "visible_changed", function () { + me.setVisible(); + }), + google.maps.event.addListener(this.marker_, "labelvisible_changed", function () { + me.setVisible(); + }), + google.maps.event.addListener(this.marker_, "title_changed", function () { + me.setTitle(); + }), + google.maps.event.addListener(this.marker_, "labelcontent_changed", function () { + me.setContent(); + }), + google.maps.event.addListener(this.marker_, "labelanchor_changed", function () { + me.setAnchor(); + }), + google.maps.event.addListener(this.marker_, "labelclass_changed", function () { + me.setStyles(); + }), + google.maps.event.addListener(this.marker_, "labelstyle_changed", function () { + me.setStyles(); + }) + ]; +}; + +/** + * 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) + * method is called. + * @private + */ +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 + */ +MarkerLabel_.prototype.draw = function () { + this.setContent(); + this.setTitle(); + this.setStyles(); +}; + +/** + * Sets the content of the label. + * The content can be plain text or an HTML DOM node. + * @private + */ +MarkerLabel_.prototype.setContent = function () { + var content = this.marker_.get("labelContent"); + if (typeof content.nodeType === "undefined") { + this.labelDiv_.innerHTML = content; + this.eventDiv_.innerHTML = this.labelDiv_.innerHTML; + } else { + this.labelDiv_.appendChild(content); + content = content.cloneNode(true); + 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. + * @private + */ +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. + * @private + */ +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 = ""; + // Apply style values defined in the labelStyle parameter: + labelStyle = this.marker_.get("labelStyle"); + for (i in labelStyle) { + if (labelStyle.hasOwnProperty(i)) { + this.labelDiv_.style[i] = labelStyle[i]; + this.eventDiv_.style[i] = labelStyle[i]; + } + } + 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. + * @private + */ +MarkerLabel_.prototype.setMandatoryStyles = function () { + this.labelDiv_.style.position = "absolute"; + this.labelDiv_.style.overflow = "hidden"; + // Make sure the opacity setting causes the desired effect on MSIE: + 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 + this.eventDiv_.style.filter = "alpha(opacity=1)"; // For MSIE + + this.setAnchor(); + this.setPosition(); // This also updates zIndex, if necessary. + this.setVisible(); +}; + +/** + * Sets the anchor point of the label. + * @private + */ +MarkerLabel_.prototype.setAnchor = function () { + var anchor = this.marker_.get("labelAnchor"); + this.labelDiv_.style.marginLeft = -anchor.x + "px"; + this.labelDiv_.style.marginTop = -anchor.y + "px"; + 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 + */ +MarkerLabel_.prototype.setPosition = function () { + var position = this.getProjection().fromLatLngToDivPixel(this.marker_.getPosition()); + + this.labelDiv_.style.left = position.x + "px"; + 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 + * stacking order for Google Maps: markers to the south are in front of markers to the north. + * @private + */ +MarkerLabel_.prototype.setZIndex = function () { + var zAdjust = (this.marker_.get("labelInBackground") ? -1 : +1); + if (typeof this.marker_.getZIndex() === "undefined") { + this.labelDiv_.style.zIndex = parseInt(this.labelDiv_.style.top, 10) + zAdjust; + this.eventDiv_.style.zIndex = this.labelDiv_.style.zIndex; + } else { + this.labelDiv_.style.zIndex = this.marker_.getZIndex() + zAdjust; + 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. + * @private + */ +MarkerLabel_.prototype.setVisible = function () { + if (this.marker_.get("labelVisible")) { + this.labelDiv_.style.display = this.marker_.getVisible() ? "block" : "none"; + } else { + this.labelDiv_.style.display = "none"; + } + this.eventDiv_.style.display = this.labelDiv_.style.display; +}; + +/** + * @name MarkerWithLabelOptions + * @class This class represents the optional parameter passed to the {@link MarkerWithLabel} constructor. + * The properties available are the same as for google.maps.Marker with the addition + * of the properties listed below. To change any of these additional properties after the labeled + * marker has been created, call google.maps.Marker.set(propertyName, propertyValue). + *

+ * When any of these properties changes, a property changed event is fired. The names of these + * events are derived from the name of the property and are of the form propertyname_changed. + * For example, if the content of the label changes, a labelcontent_changed event + * is fired. + *

+ * @property {string|Node} [labelContent] The content of the label (plain text or an HTML DOM node). + * @property {Point} [labelAnchor] By default, a label is drawn with its anchor point at (0,0) so + * that its top left corner is positioned at the anchor point of the associated marker. Use this + * property to change the anchor point of the label. For example, to center a 50px-wide label + * beneath a marker, specify a labelAnchor of google.maps.Point(25, 0). + * (Note: x-values increase to the right and y-values increase to the bottom.) + * @property {string} [labelClass] The name of the CSS class defining the styles for the label. + * Note that style values for position, overflow, top, + * left, zIndex, display, marginLeft, and + * marginTop are ignored; these styles are for internal use only. + * @property {Object} [labelStyle] An object literal whose properties define specific CSS + * style values to be applied to the label. Style values defined here override those that may + * be defined in the labelClass style sheet. If this property is changed after the + * label has been created, all previously set styles (except those defined in the style sheet) + * are removed from the label before the new style values are applied. + * Note that style values for position, overflow, top, + * left, zIndex, display, marginLeft, and + * marginTop are ignored; these styles are for internal use only. + * @property {boolean} [labelInBackground] A flag indicating whether a label that overlaps its + * associated marker should appear in the background (i.e., in a plane below the marker). + * The default is false, which causes the label to appear in the foreground. + * @property {boolean} [labelVisible] A flag indicating whether the label is to be visible. + * The default is true. Note that even if labelVisible is + * true, the label will not be visible unless the associated marker is also + * visible (i.e., unless the marker's visible property is true). + */ +/** + * Creates a MarkerWithLabel with the options specified in {@link MarkerWithLabelOptions}. + * @constructor + * @param {MarkerWithLabelOptions} [opt_options] The optional parameters. + */ +function MarkerWithLabel(opt_options) { + opt_options = opt_options || {}; + opt_options.labelContent = opt_options.labelContent || ""; + opt_options.labelAnchor = opt_options.labelAnchor || new google.maps.Point(0, 0); + opt_options.labelClass = opt_options.labelClass || "markerLabels"; + opt_options.labelStyle = opt_options.labelStyle || {}; + opt_options.labelInBackground = opt_options.labelInBackground || false; + 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 + * type: 'text' by default (callback is called with response text) + * otherwise, callback is called with a parsed XML dom + * fail: callback function for failure + * post: if given, make a POST request instead of GET; post data given + * + * contenttype: if given for a POST, set request content-type header + */ +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) { + if(http_request.status == 200) { + func(http_request.responseText); + } else { + fail(http_request); + } + } + } + } else { + http_request.onreadystatechange = function() { + if(http_request.readyState == 4) { + 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') + http_request.setRequestHeader("Content-Type", contenttype); + http_request.send(post); + } else { + http_request.open('GET', url, true); + 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; + } + } + + for(var m in markers) { + if(!(m in loggedin)) { + markers[m].setMap(null); + delete markers[m]; + } + } + + setTimeout(playerUpdate, 2000); + }, 'text', function() { alert('failed to get position data'); } ); +} diff --git a/web/marker.png b/web/marker.png new file mode 100644 index 0000000000000000000000000000000000000000..1fd60c712766431227366cbb3bc35d39fd22a054 GIT binary patch literal 620 zcmV-y0+aoTP)Px#24YJ`L;(K)0000pCw%h&000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2igk~ z3JWIA-{K4a000?uMObu0Z*6U5Zgc=ca%Ew3Wn>_CX>@2HM@dakSAh-}0005UNklEa8>rr2iL^kGP` z5Q`9t5J3pW-{M@|3r=Fx*#!2$g#-8Uf1EpW&iw;PtTRfiIlioPDhR5;A2`-)Tx0~n51_LYEGd6M zENCfBdz#JWdMjqLxze;JXbIv)x{F}PilVl%Y~)FHB%MjByK`GEKXZE)SvFD>wFSqk z0U=ftwKX2!If>trnhS5@?`pcU(s+F5pz(4bR0S=svW}$f6=2V6`=#0}8@cPPE(4w` zO?zHt?}7PHtJJ>fc|H)T3d2k92Wfr)=3lS~S zTrQC$b=SkMCQXAluDQHjz)&#_(`@7ct*I@3763F2)2!tIciG$9arvJDfTm%ZyBizc zK5)upa_{nQ1!$XwX-@NiLk5Femwzt+Xd0$D%meo6cAqy+3jmsiY4%^g7Hew!4UV>r z4DYo+PaAoF|FOjgmip57ny8zbH%(Nqk`XS@CI