diff --git a/build.xml b/build.xml
index 83f7871f..f2efef73 100644
--- a/build.xml
+++ b/build.xml
@@ -11,7 +11,7 @@
-
+
@@ -19,6 +19,7 @@
+
diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java
index d126fb04..45c525d9 100644
--- a/src/main/java/org/dynmap/DynmapPlugin.java
+++ b/src/main/java/org/dynmap/DynmapPlugin.java
@@ -17,7 +17,6 @@ public class DynmapPlugin extends JavaPlugin {
private WebServer server = null;
private MapManager mgr = null;
- private DynmapBlockListener listener = null;
private BukkitPlayerDebugger debugger = new BukkitPlayerDebugger(this);
@@ -31,36 +30,33 @@ public class DynmapPlugin extends JavaPlugin {
@Override
public void onEnable() {
- log.info("Map INIT");
-
+ debugger.enable();
mgr = new MapManager(getWorld(), debugger);
mgr.startManager();
try {
- server = new WebServer(mgr.serverport, mgr);
+ server = new WebServer(mgr.serverport, mgr, getServer(), debugger);
} catch(IOException e) {
log.info("position failed to start WebServer (IOException)");
}
-
- listener = new DynmapBlockListener(mgr);
registerEvents();
}
@Override
public void onDisable() {
- log.info("Map UNINIT");
-
mgr.stopManager();
if(server != null) {
server.shutdown();
server = null;
}
+ debugger.disable();
}
public void registerEvents() {
- getServer().getPluginManager().registerEvent(Event.Type.BLOCK_PLACED, listener, Priority.Normal, this);
+ getServer().getPluginManager().registerEvent(Event.Type.BLOCK_PLACED, new DynmapBlockListener(mgr), Priority.Normal, this);
+ getServer().getPluginManager().registerEvent(Event.Type.PLAYER_COMMAND, new DynmapPlayerListener(mgr), Priority.Normal, this);
//getServer().getPluginManager().registerEvent(Event.Type.BLOCK_DESTROYED, listener, Priority.Normal, this);
/* etc.getLoader().addListener(PluginLoader.Hook.COMMAND, listener, this, PluginListener.Priority.MEDIUM);
etc.getLoader().addListener(PluginLoader.Hook.BLOCK_CREATED, listener, this, PluginListener.Priority.MEDIUM);
diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java
index d8826de9..e69e26ea 100644
--- a/src/main/java/org/dynmap/MapManager.java
+++ b/src/main/java/org/dynmap/MapManager.java
@@ -87,12 +87,12 @@ public class MapManager extends Thread {
/* path to signs file */
public String signspath = "signs.txt";
- /* port to run web server on */
- public int serverport = 8123;
-
/* bind web server to ip-address */
public String bindaddress = "0.0.0.0";
+ /* port to run web server on */
+ public int serverport = 8123;
+
/* time to pause between rendering tiles (ms) */
public int renderWait = 500;
@@ -114,6 +114,9 @@ public class MapManager extends Thread {
/* zoomed-out tile cache */
public Cache zoomCache;
+ /* web files location */
+ public String webPath;
+
public void debug(String msg)
{
debugger.debug(msg);
@@ -129,6 +132,8 @@ public class MapManager extends Thread {
signspath = "signs.txt";
serverport = 8123;
bindaddress = "0.0.0.0";
+ //webPath = "/srv/http/dynmap/";
+ webPath = "[JAR]";
tileStore = new HashMap();
staleTiles = new LinkedList();
@@ -138,11 +143,11 @@ public class MapManager extends Thread {
colors = loadColorSet(colorsetpath);
renderer = new CombinedTileRenderer(new MapTileRenderer[] {
- new DayTileRenderer(colors, tilepath + "t_{X}_{Y}.png"),
- new CaveTileRenderer(colors, tilepath + "ct_{X}_{Y}.png")
+ new DayTileRenderer(debugger, colors, tilepath + "t_{X}_{Y}.png"),
+ new CaveTileRenderer(debugger, colors, tilepath + "ct_{X}_{Y}.png")
});
}
-
+
/* tile X for position x */
static int tilex(int x)
{
@@ -236,6 +241,7 @@ public class MapManager extends Thread {
MapTile t = this.popStaleTile();
if(t != null) {
+ debugger.debug("rendering tile " + t + "...");
renderer.render(t);
updateUpdates(t, tileUpdates);
@@ -262,6 +268,8 @@ public class MapManager extends Thread {
/* "touch" a block - its map tile will be regenerated */
public boolean touch(int x, int y, int z)
{
+ debugger.debug("touched: " + x + "," + y + "," + z);
+
int dx = x - anchorx;
int dy = y - anchory;
int dz = z - anchorz;
diff --git a/src/main/java/org/dynmap/MapTile.java b/src/main/java/org/dynmap/MapTile.java
index 6e1084e1..fdcf13e0 100644
--- a/src/main/java/org/dynmap/MapTile.java
+++ b/src/main/java/org/dynmap/MapTile.java
@@ -81,7 +81,7 @@ public class MapTile {
/* check if all relevant chunks are loaded */
public boolean isMapLoaded()
{
- int x1 = mx - 64;
+ int x1 = mx - MapManager.tileHeight / 2;
int x2 = mx + MapManager.tileWidth / 2 + MapManager.tileHeight / 2;
int z1 = mz - MapManager.tileHeight / 2;
diff --git a/src/main/java/org/dynmap/WebServer.java b/src/main/java/org/dynmap/WebServer.java
index 32ad19e1..280148ff 100644
--- a/src/main/java/org/dynmap/WebServer.java
+++ b/src/main/java/org/dynmap/WebServer.java
@@ -4,6 +4,7 @@ import java.io.*;
import java.net.*;
import java.util.*;
import org.bukkit.*;
+import org.dynmap.debug.Debugger;
import java.util.logging.Logger;
@@ -12,14 +13,19 @@ public class WebServer extends Thread {
public static final String VERSION = "Huncraft";
protected static final Logger log = Logger.getLogger("Minecraft");
+ private Debugger debugger;
+
private ServerSocket sock = null;
private boolean running = false;
private MapManager mgr;
+ private Server server;
- public WebServer(int port, MapManager mgr) throws IOException
+ public WebServer(int port, MapManager mgr, Server server, Debugger debugger) throws IOException
{
this.mgr = mgr;
+ this.server = server;
+ this.debugger = debugger;
sock = new ServerSocket(port, 5, mgr.bindaddress.equals("0.0.0.0") ? null : InetAddress.getByName(mgr.bindaddress));
running = true;
start();
@@ -31,7 +37,7 @@ public class WebServer extends Thread {
while (running) {
try {
Socket socket = sock.accept();
- WebServerRequest requestThread = new WebServerRequest(socket, mgr);
+ WebServerRequest requestThread = new WebServerRequest(socket, mgr, server, debugger);
requestThread.start();
}
catch (IOException e) {
diff --git a/src/main/java/org/dynmap/WebServerRequest.java b/src/main/java/org/dynmap/WebServerRequest.java
index c926f592..764f9ffa 100644
--- a/src/main/java/org/dynmap/WebServerRequest.java
+++ b/src/main/java/org/dynmap/WebServerRequest.java
@@ -2,97 +2,85 @@ package org.dynmap;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.Socket;
import java.util.Date;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.logging.Logger;
import org.bukkit.*;
+import org.dynmap.debug.Debugger;
public class WebServerRequest extends Thread {
protected static final Logger log = Logger.getLogger("Minecraft");
- private Socket sock;
+ private Debugger debugger;
+ private Socket socket;
private MapManager mgr;
- private DynmapPlugin etc;
+ private Server server;
- public WebServerRequest(Socket socket, MapManager mgr)
+ public WebServerRequest(Socket socket, MapManager mgr, Server server, Debugger debugger)
{
- sock = socket;
+ this.debugger = debugger;
+ this.socket = socket;
this.mgr = mgr;
+ this.server = server;
}
-
- 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 writeHttpHeader(BufferedOutputStream out, int statusCode, String statusText) throws IOException {
+ out.write("HTTP/1.0 ".getBytes());
+ out.write(Integer.toString(statusCode).getBytes());
+ out.write((" " + statusText + "\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();
+
+ private static void writeHeaderField(BufferedOutputStream out, String name, String value) throws IOException {
+ out.write(name.getBytes());
+ out.write((int)':');
+ out.write((int)' ');
+ out.write(value.getBytes());
+ out.write(13);
+ out.write(10);
+ }
+
+ private static void writeEndOfHeaders(BufferedOutputStream out) throws IOException {
+ out.write(13);
+ out.write(10);
}
public void run()
{
InputStream reader = null;
try {
- sock.setSoTimeout(30000);
- BufferedReader in = new BufferedReader(new InputStreamReader(sock.getInputStream()));
- BufferedOutputStream out = new BufferedOutputStream(sock.getOutputStream());
+ socket.setSoTimeout(30000);
+ BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+ BufferedOutputStream out = new BufferedOutputStream(socket.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.");
+ writeHttpHeader(out, 500, "Invalid Method.");
+ writeEndOfHeaders(out);
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) {
+ debugger.debug("request: " + path);
+ if (path.startsWith("/up/")) {
+ handleUp(out, path.substring(3));
+ } else if (path.startsWith("/tiles/")) {
+ handleMapToDirectory(out, path.substring(6), "/srv/http/dynmap/tiles/");
+ } else if (path.startsWith("/")) {
+ if(mgr.webPath.equals("[JAR]")) {
+ handleMapToJar(out, path);
+ } else if(mgr.webPath.length() > 0) {
+ handleMapToDirectory(out, path, mgr.webPath);
}
}
-
- sendHeader(out, 200, "text/plain", -1, System.currentTimeMillis());
-
- StringBuilder sb = new StringBuilder();
-
- long relativeTime = etc.getServer().getTime() % 24000;
- sb.append(current + " " + relativeTime + "\n");
-
- for(Player player : etc.getServer().getOnlinePlayers()) {
- sb.append(player.getName() + " player " + player.getLocation().getX() + " " + player.getLocation().getY() + " " + player.getLocation().getZ() + "\n");
- }
-
- synchronized(mgr.lock) {
- for(TileUpdate tu : mgr.tileUpdates) {
- if(tu.at >= cutoff) {
- sb.append(tu.tile.px + "_" + tu.tile.py + " " + tu.tile.zpx + "_" + tu.tile.zpy + " t\n");
- }
- }
- }
-
- out.write(sb.toString().getBytes());
-
out.flush();
out.close();
}
@@ -107,4 +95,123 @@ public class WebServerRequest extends Thread {
}
}
}
+
+ public void handleUp(BufferedOutputStream out, String path) throws IOException {
+ 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) {
+ }
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ long relativeTime = server.getTime() % 24000;
+ sb.append(current + " " + relativeTime + "\n");
+
+ for(Player player : server.getOnlinePlayers()) {
+ sb.append(player.getName() + " player " + player.getLocation().getX() + " " + player.getLocation().getY() + " " + player.getLocation().getZ() + "\n");
+ }
+
+ synchronized(mgr.lock) {
+ for(TileUpdate tu : mgr.tileUpdates) {
+ if(tu.at >= cutoff) {
+ sb.append(tu.tile.px + "_" + tu.tile.py + " " + tu.tile.zpx + "_" + tu.tile.zpy + " t\n");
+ }
+ }
+ }
+
+ byte[] bytes = sb.toString().getBytes();
+
+ String dateStr = new Date().toString();
+ writeHttpHeader(out, 200, "OK");
+ writeHeaderField(out, "Date", dateStr);
+ writeHeaderField(out, "Content-Type", "text/plain");
+ writeHeaderField(out, "Expires", "Thu, 01 Dec 1994 16:00:00 GMT");
+ writeHeaderField(out, "Last-modified", dateStr);
+ writeHeaderField(out, "Content-Length", Integer.toString(bytes.length));
+ writeEndOfHeaders(out);
+ out.write(bytes);
+ }
+
+ private byte[] readBuffer = new byte[40960];
+
+ public void writeFile(BufferedOutputStream out, String path, InputStream fileInput) throws IOException {
+ int dotindex = path.lastIndexOf('.');
+ String extension = null;
+ if (dotindex > 0) extension = path.substring(dotindex);
+
+ writeHttpHeader(out, 200, "OK");
+ writeHeaderField(out, "Content-Type", getMimeTypeFromExtension(extension));
+ writeHeaderField(out, "Connection", "close");
+ writeEndOfHeaders(out);
+ try {
+ int readBytes;
+ while((readBytes = fileInput.read(readBuffer)) > 0) {
+ out.write(readBuffer, 0, readBytes);
+ }
+ } catch(IOException e) {
+ fileInput.close();
+ throw e;
+ }
+ fileInput.close();
+ }
+
+ public String getFilePath(String path) {
+ int qmark = path.indexOf('?');
+ if (qmark >= 0) path = path.substring(0, qmark);
+ path = path.substring(1);
+
+ if (path.startsWith("/") || path.startsWith("."))
+ return null;
+ if (path.length() == 0) path = "index.html";
+ return path;
+ }
+
+ public void handleMapToJar(BufferedOutputStream out, String path) throws IOException {
+ path = getFilePath(path);
+ if (path != null) {
+ InputStream s = this.getClass().getResourceAsStream("/web/" + path);
+ if (s != null) {
+ writeFile(out, path, s);
+ return;
+ }
+ }
+ writeHttpHeader(out, 404, "Not found");
+ writeEndOfHeaders(out);
+ }
+
+ public void handleMapToDirectory(BufferedOutputStream out, String path, String directoryPath) throws IOException {
+ path = getFilePath(path);
+ if (path != null) {
+ File tilesDirectory = new File(directoryPath);
+ File tileFile = new File(tilesDirectory, path);
+
+ if (tileFile.getAbsolutePath().startsWith(tilesDirectory.getAbsolutePath()) && tileFile.isFile()) {
+ FileInputStream s = new FileInputStream(tileFile);
+ writeFile(out, path, s);
+ return;
+ }
+ }
+ writeHttpHeader(out, 404, "Not found");
+ writeEndOfHeaders(out);
+ }
+
+ private static Map mimes = new HashMap();
+ static {
+ mimes.put(".html", "text/html");
+ mimes.put(".htm", "text/html");
+ mimes.put(".js", "text/javascript");
+ mimes.put(".png", "image/png");
+ mimes.put(".css", "text/css");
+ mimes.put(".txt", "text/plain");
+ }
+ public static String getMimeTypeFromExtension(String extension) {
+ String m = mimes.get(extension);
+ if (m != null) return m;
+ return "application/octet-steam";
+ }
}
diff --git a/src/main/java/org/dynmap/debug/BukkitPlayerDebugger.java b/src/main/java/org/dynmap/debug/BukkitPlayerDebugger.java
index 4d71a85d..3b772865 100644
--- a/src/main/java/org/dynmap/debug/BukkitPlayerDebugger.java
+++ b/src/main/java/org/dynmap/debug/BukkitPlayerDebugger.java
@@ -1,7 +1,10 @@
package org.dynmap.debug;
import java.util.HashSet;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.bukkit.Color;
import org.bukkit.Player;
import org.bukkit.event.Event;
import org.bukkit.event.Event.Priority;
@@ -12,6 +15,10 @@ import org.bukkit.plugin.PluginDescriptionFile;
import org.bukkit.plugin.java.JavaPlugin;
public class BukkitPlayerDebugger implements Debugger {
+ protected static final Logger log = Logger.getLogger("Minecraft");
+
+ private boolean isLogging = true;
+
private JavaPlugin plugin;
private HashSet debugees = new HashSet();
private String debugCommand;
@@ -30,6 +37,7 @@ public class BukkitPlayerDebugger implements Debugger {
public void enable() {
plugin.getServer().getPluginManager().registerEvent(Event.Type.PLAYER_COMMAND, new CommandListener(), Priority.Normal, plugin);
plugin.getServer().getPluginManager().registerEvent(Event.Type.PLAYER_QUIT, new CommandListener(), Priority.Normal, plugin);
+ log.info("Debugger enabled, use: " + debugCommand);
}
public void disable() {
@@ -48,12 +56,28 @@ public class BukkitPlayerDebugger implements Debugger {
debugees.clear();
}
- public void debug(String message) {
+ public void sendToDebuggees(String message) {
for (Player p : debugees) {
p.sendMessage(prepend + message);
}
}
+ public void debug(String message) {
+ sendToDebuggees(message);
+ if (isLogging) log.info(prepend + message);
+ }
+
+ public void error(String message) {
+ sendToDebuggees(prepend + Color.RED + message);
+ if (isLogging) log.log(Level.SEVERE, prepend + message);
+ }
+
+ public void error(String message, Throwable thrown) {
+ sendToDebuggees(prepend + Color.RED + message);
+ sendToDebuggees(thrown.toString());
+ if (isLogging) log.log(Level.SEVERE, prepend + message);
+ }
+
protected class CommandListener extends PlayerListener {
@Override
public void onPlayerCommand(PlayerChatEvent event) {
diff --git a/src/main/java/org/dynmap/debug/Debugger.java b/src/main/java/org/dynmap/debug/Debugger.java
index 43f5cbe3..45583367 100644
--- a/src/main/java/org/dynmap/debug/Debugger.java
+++ b/src/main/java/org/dynmap/debug/Debugger.java
@@ -2,4 +2,6 @@ package org.dynmap.debug;
public interface Debugger {
void debug(String message);
+ void error(String message);
+ void error(String message, Throwable thrown);
}
diff --git a/src/main/java/org/dynmap/render/CaveTileRenderer.java b/src/main/java/org/dynmap/render/CaveTileRenderer.java
index 9f233307..593e9c3e 100644
--- a/src/main/java/org/dynmap/render/CaveTileRenderer.java
+++ b/src/main/java/org/dynmap/render/CaveTileRenderer.java
@@ -5,11 +5,12 @@ import java.util.Map;
import org.bukkit.World;
import org.dynmap.MapManager;
+import org.dynmap.debug.Debugger;
public class CaveTileRenderer extends DayTileRenderer {
- public CaveTileRenderer(Map colors, String outputPath) {
- super(colors, outputPath);
+ public CaveTileRenderer(Debugger debugger, Map colors, String outputPath) {
+ super(debugger, colors, outputPath);
}
@Override
diff --git a/src/main/java/org/dynmap/render/DayTileRenderer.java b/src/main/java/org/dynmap/render/DayTileRenderer.java
index 26218acf..0dbf1dd2 100644
--- a/src/main/java/org/dynmap/render/DayTileRenderer.java
+++ b/src/main/java/org/dynmap/render/DayTileRenderer.java
@@ -16,16 +16,31 @@ import javax.imageio.ImageIO;
import org.bukkit.World;
import org.dynmap.MapManager;
import org.dynmap.MapTile;
+import org.dynmap.debug.Debugger;
public class DayTileRenderer implements MapTileRenderer {
- protected static final Logger log = Logger.getLogger("Minecraft");
+ protected Debugger debugger;
protected String outputPath;
+ protected String outputZoomPath;
private Map colors;
- public DayTileRenderer(Map colors, String outputPath) {
+ public DayTileRenderer(Debugger debugger, Map colors, String outputPath) {
+ this(debugger, colors, outputPath, convertToZoomPath(outputPath));
+ }
+
+ public DayTileRenderer(Debugger debugger, Map colors, String outputPath, String outputZoomPath) {
+ this.debugger = debugger;
this.colors = colors;
this.outputPath = outputPath;
+ this.outputZoomPath = outputZoomPath;
}
+
+ private static String convertToZoomPath(String outputPath) {
+ File outputFile = new File(outputPath);
+ String zoomFilename = "z" + outputFile.getName();
+ return new File(outputFile.getParentFile(), zoomFilename).getPath();
+ }
+
public void render(MapTile tile) {
World world = tile.getWorld();
BufferedImage im = new BufferedImage(MapManager.tileWidth, MapManager.tileHeight, BufferedImage.TYPE_INT_RGB);
@@ -142,14 +157,17 @@ public class DayTileRenderer implements MapTileRenderer {
String tilePath = outputPath
.replace("{X}", Integer.toString(tile.px))
.replace("{Y}", Integer.toString(tile.py));
+
+ debugger.debug("saving tile " + tilePath);
+
/* save image */
try {
File file = new File(tilePath);
ImageIO.write(im, "png", file);
} catch(IOException e) {
- log.log(Level.SEVERE, "Failed to save tile: " + tilePath, e);
+ debugger.error("Failed to save tile: " + tilePath, e);
} catch(java.lang.NullPointerException e) {
- log.log(Level.SEVERE, "Failed to save tile (NullPointerException): " + tilePath, e);
+ debugger.error("Failed to save tile (NullPointerException): " + tilePath, e);
}
/* now update zoom-out tile */
diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml
index 245488a0..228eb52a 100644
--- a/src/main/resources/plugin.yml
+++ b/src/main/resources/plugin.yml
@@ -1,3 +1,3 @@
-name: Dynamic Map
+name: dynmap
main: org.dynmap.DynmapPlugin
version: 0.1