Merge branch 'master' of git://github.com/FrozenCow/dynmap
Conflicts: configuration.txt src/main/java/org/dynmap/DynmapPlugin.java
This commit is contained in:
commit
92b45b8d31
45 changed files with 1095 additions and 517 deletions
|
|
@ -1,58 +1,73 @@
|
||||||
# All paths in this configuration file are relative to Dynmap's data-folder: minecraft_server/plugins/dynmap/
|
# All paths in this configuration file are relative to Dynmap's data-folder: minecraft_server/plugins/dynmap/
|
||||||
|
|
||||||
# How often a tile gets rendered (in seconds).
|
# How often a tile gets rendered (in seconds).
|
||||||
renderinterval: 1
|
renderinterval: 1
|
||||||
|
|
||||||
# The path where the tile-files are placed.
|
# The path where the tile-files are placed.
|
||||||
tilepath: web/tiles
|
tilespath: web/tiles
|
||||||
|
|
||||||
# The path where the web-files are located.
|
# The path where the web-files are located.
|
||||||
webpath: web
|
webpath: web
|
||||||
|
|
||||||
# The network-interface the webserver will bind to (0.0.0.0 for all interfaces, 127.0.0.1 for only local access).
|
# The network-interface the webserver will bind to (0.0.0.0 for all interfaces, 127.0.0.1 for only local access).
|
||||||
webserver-bindaddress: 0.0.0.0
|
webserver-bindaddress: 0.0.0.0
|
||||||
|
|
||||||
# The TCP-port the webserver will listen on.
|
# The TCP-port the webserver will listen on.
|
||||||
webserver-port: 8123
|
webserver-port: 8123
|
||||||
|
|
||||||
# Disables Webserver portion of Dynmap (Advanced users only)
|
# Disables Webserver portion of Dynmap (Advanced users only)
|
||||||
disable-webserver: true
|
disable-webserver: true
|
||||||
|
|
||||||
# Writes JSON to file in the webpath
|
# Writes JSON to file in the webpath
|
||||||
jsonfile: true
|
jsonfile: true
|
||||||
|
|
||||||
# How often the json file gets written to(in seconds)
|
# How often the json file gets written to(in seconds)
|
||||||
jsonfile-interval: 1000
|
jsonfile-interval: 1000
|
||||||
|
|
||||||
disabledcommands:
|
disabledcommands:
|
||||||
- fullrender
|
- fullrender
|
||||||
|
|
||||||
# The maptypes Dynmap will use to render.
|
# The maptypes Dynmap will use to render.
|
||||||
maps:
|
maps:
|
||||||
- class: org.dynmap.kzedmap.KzedMap
|
- class: org.dynmap.kzedmap.KzedMap
|
||||||
renderers:
|
renderers:
|
||||||
- class: org.dynmap.kzedmap.DefaultTileRenderer
|
- class: org.dynmap.kzedmap.DefaultTileRenderer
|
||||||
prefix: t
|
prefix: t
|
||||||
- class: org.dynmap.kzedmap.CaveTileRenderer
|
maximumheight: 127
|
||||||
prefix: ct
|
- class: org.dynmap.kzedmap.CaveTileRenderer
|
||||||
|
prefix: ct
|
||||||
web:
|
maximumheight: 127
|
||||||
# Interval the browser should poll for updates.
|
|
||||||
updaterate: 2000
|
web:
|
||||||
|
# Interval the browser should poll for updates.
|
||||||
showchatballoons: true
|
updaterate: 2000
|
||||||
showplayerfacesonmap: true
|
|
||||||
showplayerfacesinmenu: true
|
showchatballoons: true
|
||||||
focuschatballoons: false
|
showplayerfacesonmap: true
|
||||||
|
showplayerfacesinmenu: true
|
||||||
# The name of the map shown when opening Dynmap's page (must be in menu).
|
focuschatballoons: false
|
||||||
defaultmap: defaultmap
|
|
||||||
|
# The clock that is shown alongside the map.
|
||||||
# The maps shown in the menu.
|
clock: timeofday
|
||||||
shownmaps:
|
#clock: digital
|
||||||
- type: KzedMapType
|
|
||||||
name: defaultmap
|
# The name of the map shown when opening Dynmap's page (must be in menu).
|
||||||
prefix: t
|
defaultmap: defaultmap
|
||||||
- type: KzedMapType
|
|
||||||
name: cavemap
|
# The maps shown in the menu.
|
||||||
prefix: ct
|
shownmaps:
|
||||||
|
- type: KzedMapType
|
||||||
|
name: defaultmap
|
||||||
|
prefix: t
|
||||||
|
- type: KzedMapType
|
||||||
|
name: cavemap
|
||||||
|
prefix: ct
|
||||||
|
|
||||||
|
# The name of the world shown when opening Dynmap's page.
|
||||||
|
defaultworld: world
|
||||||
|
|
||||||
|
# The worlds shown in the menu.
|
||||||
|
shownworlds:
|
||||||
|
- world
|
||||||
|
- nether
|
||||||
|
- world_bad
|
||||||
|
|
|
||||||
108
src/main/java/org/dynmap/AsynchronousQueue.java
Normal file
108
src/main/java/org/dynmap/AsynchronousQueue.java
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
package org.dynmap;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class AsynchronousQueue<T> {
|
||||||
|
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||||
|
|
||||||
|
private Object lock = new Object();
|
||||||
|
private Thread thread;
|
||||||
|
private LinkedList<T> queue = new LinkedList<T>();
|
||||||
|
private Set<T> set = new HashSet<T>();
|
||||||
|
private Handler<T> handler;
|
||||||
|
private int dequeueTime;
|
||||||
|
|
||||||
|
public AsynchronousQueue(Handler<T> handler, int dequeueTime) {
|
||||||
|
this.handler = handler;
|
||||||
|
this.dequeueTime = dequeueTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean push(T t) {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (set.add(t)) {
|
||||||
|
queue.addLast(t);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private T pop() {
|
||||||
|
synchronized (lock) {
|
||||||
|
try {
|
||||||
|
T t = queue.removeFirst();
|
||||||
|
if (!set.remove(t)) {
|
||||||
|
// This should never happen.
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
} catch (NoSuchElementException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return set.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void start() {
|
||||||
|
synchronized (lock) {
|
||||||
|
thread = new Thread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
running();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
try {
|
||||||
|
thread.setPriority(Thread.MIN_PRIORITY);
|
||||||
|
} catch (SecurityException e) {
|
||||||
|
log.info("Failed to set minimum priority for worker thread!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
synchronized (lock) {
|
||||||
|
if (thread == null)
|
||||||
|
return;
|
||||||
|
Thread oldThread = thread;
|
||||||
|
thread = null;
|
||||||
|
|
||||||
|
log.info("Stopping map renderer...");
|
||||||
|
|
||||||
|
try {
|
||||||
|
oldThread.join();
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
log.info("Waiting for map renderer to stop is interrupted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void running() {
|
||||||
|
try {
|
||||||
|
while (Thread.currentThread() == thread) {
|
||||||
|
T t = pop();
|
||||||
|
if (t != null) {
|
||||||
|
handler.handle(t);
|
||||||
|
}
|
||||||
|
sleep(dequeueTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.log(Level.SEVERE, "Exception on rendering-thread", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void sleep(int time) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(time);
|
||||||
|
} catch (InterruptedException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -11,10 +11,12 @@ public class Client {
|
||||||
public static class Player {
|
public static class Player {
|
||||||
public String type = "player";
|
public String type = "player";
|
||||||
public String name;
|
public String name;
|
||||||
|
public String world;
|
||||||
public double x, y, z;
|
public double x, y, z;
|
||||||
|
|
||||||
public Player(String name, double x, double y, double z) {
|
public Player(String name, String world, double x, double y, double z) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.world = world;
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
this.z = z;
|
this.z = z;
|
||||||
|
|
|
||||||
|
|
@ -16,13 +16,13 @@ public class DynmapBlockListener extends BlockListener {
|
||||||
@Override
|
@Override
|
||||||
public void onBlockPlace(BlockPlaceEvent event) {
|
public void onBlockPlace(BlockPlaceEvent event) {
|
||||||
Block blockPlaced = event.getBlockPlaced();
|
Block blockPlaced = event.getBlockPlaced();
|
||||||
mgr.touch(blockPlaced.getX(), blockPlaced.getY(), blockPlaced.getZ());
|
mgr.touch(blockPlaced.getLocation());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onBlockDamage(BlockDamageEvent event) {
|
public void onBlockDamage(BlockDamageEvent event) {
|
||||||
if (event.getDamageLevel() == BlockDamageLevel.BROKEN) {
|
if (event.getDamageLevel() == BlockDamageLevel.BROKEN) {
|
||||||
Block blockBroken = event.getBlock();
|
Block blockBroken = event.getBlock();
|
||||||
mgr.touch(blockBroken.getX(), blockBroken.getY(), blockBroken.getZ());
|
mgr.touch(blockBroken.getLocation());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ public class DynmapPlayerListener extends PlayerListener {
|
||||||
|
|
||||||
if (split[1].equals("render")) {
|
if (split[1].equals("render")) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
mgr.touch(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ());
|
mgr.touch(player.getLocation());
|
||||||
event.setCancelled(true);
|
event.setCancelled(true);
|
||||||
} else if (split[1].equals("hide")) {
|
} else if (split[1].equals("hide")) {
|
||||||
if (split.length == 2) {
|
if (split.length == 2) {
|
||||||
|
|
@ -54,6 +54,7 @@ public class DynmapPlayerListener extends PlayerListener {
|
||||||
} else if (split[1].equals("fullrender")) {
|
} else if (split[1].equals("fullrender")) {
|
||||||
Player player = event.getPlayer();
|
Player player = event.getPlayer();
|
||||||
mgr.renderFullWorld(player.getLocation());
|
mgr.renderFullWorld(player.getLocation());
|
||||||
|
event.setCancelled(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,6 +67,6 @@ public class DynmapPlayerListener extends PlayerListener {
|
||||||
* Relevant event details
|
* Relevant event details
|
||||||
*/
|
*/
|
||||||
public void onPlayerChat(PlayerChatEvent event) {
|
public void onPlayerChat(PlayerChatEvent event) {
|
||||||
mgr.updateQueue.pushUpdate(new Client.ChatMessage(event.getPlayer().getName(), event.getMessage()));
|
mgr.pushUpdate(new Client.ChatMessage(event.getPlayer().getName(), event.getMessage()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -20,11 +20,15 @@ import org.bukkit.plugin.PluginDescriptionFile;
|
||||||
import org.bukkit.plugin.PluginLoader;
|
import org.bukkit.plugin.PluginLoader;
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
import org.bukkit.util.config.Configuration;
|
import org.bukkit.util.config.Configuration;
|
||||||
import org.dynmap.debug.BukkitPlayerDebugger;
|
import org.dynmap.Event.Listener;
|
||||||
|
import org.dynmap.debug.Debug;
|
||||||
|
import org.dynmap.debug.LogDebugger;
|
||||||
import org.dynmap.web.HttpServer;
|
import org.dynmap.web.HttpServer;
|
||||||
import org.dynmap.web.handlers.ClientConfigurationHandler;
|
import org.dynmap.web.handlers.ClientConfigurationHandler;
|
||||||
import org.dynmap.web.handlers.ClientUpdateHandler;
|
import org.dynmap.web.handlers.ClientUpdateHandler;
|
||||||
import org.dynmap.web.handlers.FilesystemHandler;
|
import org.dynmap.web.handlers.FilesystemHandler;
|
||||||
|
import org.dynmap.web.handlers.SendMessageHandler;
|
||||||
|
import org.dynmap.web.handlers.SendMessageHandler.Message;
|
||||||
import org.dynmap.web.Json;
|
import org.dynmap.web.Json;
|
||||||
|
|
||||||
public class DynmapPlugin extends JavaPlugin {
|
public class DynmapPlugin extends JavaPlugin {
|
||||||
|
|
@ -36,10 +40,8 @@ public class DynmapPlugin extends JavaPlugin {
|
||||||
private PlayerList playerList;
|
private PlayerList playerList;
|
||||||
private Configuration configuration;
|
private Configuration configuration;
|
||||||
|
|
||||||
|
public static File tilesDirectory;
|
||||||
private Timer timer;
|
private Timer timer;
|
||||||
|
|
||||||
private BukkitPlayerDebugger debugger = new BukkitPlayerDebugger(this);
|
|
||||||
|
|
||||||
public static File dataRoot;
|
public static File dataRoot;
|
||||||
|
|
||||||
public DynmapPlugin(PluginLoader pluginLoader, Server instance, PluginDescriptionFile desc, File folder, File plugin, ClassLoader cLoader) {
|
public DynmapPlugin(PluginLoader pluginLoader, Server instance, PluginDescriptionFile desc, File folder, File plugin, ClassLoader cLoader) {
|
||||||
|
|
@ -60,15 +62,19 @@ public class DynmapPlugin extends JavaPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onEnable() {
|
public void onEnable() {
|
||||||
|
Debug.addDebugger(new LogDebugger());
|
||||||
|
|
||||||
configuration = new Configuration(new File(this.getDataFolder(), "configuration.txt"));
|
configuration = new Configuration(new File(this.getDataFolder(), "configuration.txt"));
|
||||||
configuration.load();
|
configuration.load();
|
||||||
|
|
||||||
debugger.enable();
|
tilesDirectory = getFile(configuration.getString("tilespath", "web/tiles"));
|
||||||
|
tilesDirectory.mkdirs();
|
||||||
|
|
||||||
playerList = new PlayerList(getServer());
|
playerList = new PlayerList(getServer());
|
||||||
playerList.load();
|
playerList.load();
|
||||||
|
|
||||||
mapManager = new MapManager(getWorld(), debugger, configuration);
|
mapManager = new MapManager(configuration);
|
||||||
mapManager.startManager();
|
mapManager.startRendering();
|
||||||
|
|
||||||
if(!configuration.getBoolean("disable-webserver", true)) {
|
if(!configuration.getBoolean("disable-webserver", true)) {
|
||||||
InetAddress bindAddress;
|
InetAddress bindAddress;
|
||||||
|
|
@ -85,11 +91,21 @@ public class DynmapPlugin extends JavaPlugin {
|
||||||
int port = configuration.getInt("webserver-port", 8123);
|
int port = configuration.getInt("webserver-port", 8123);
|
||||||
|
|
||||||
webServer = new HttpServer(bindAddress, port);
|
webServer = new HttpServer(bindAddress, port);
|
||||||
webServer.handlers.put("/", new FilesystemHandler(mapManager.webDirectory));
|
webServer.handlers.put("/", new FilesystemHandler(getFile(configuration.getString("webpath", "web"))));
|
||||||
webServer.handlers.put("/tiles/", new FilesystemHandler(mapManager.tileDirectory));
|
webServer.handlers.put("/tiles/", new FilesystemHandler(tilesDirectory));
|
||||||
webServer.handlers.put("/up/", new ClientUpdateHandler(mapManager, playerList, getWorld()));
|
webServer.handlers.put("/up/", new ClientUpdateHandler(mapManager, playerList, getServer()));
|
||||||
webServer.handlers.put("/up/configuration", new ClientConfigurationHandler((Map<?, ?>) configuration.getProperty("web")));
|
webServer.handlers.put("/up/configuration", new ClientConfigurationHandler((Map<?, ?>) configuration.getProperty("web")));
|
||||||
|
|
||||||
|
SendMessageHandler messageHandler = new SendMessageHandler();
|
||||||
|
messageHandler.onMessageReceived.addListener(new Listener<SendMessageHandler.Message>() {
|
||||||
|
@Override
|
||||||
|
public void triggered(Message t) {
|
||||||
|
log.info("[WEB] " + t.name + ": " + t.message);
|
||||||
|
getServer().broadcastMessage("[WEB] " + t.name + ": " + t.message);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
webServer.handlers.put("/up/sendmessage", messageHandler);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
webServer.startServer();
|
webServer.startServer();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -108,25 +124,39 @@ public class DynmapPlugin extends JavaPlugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onDisable() {
|
public void onDisable() {
|
||||||
mapManager.stopManager();
|
mapManager.stopRendering();
|
||||||
|
|
||||||
if (webServer != null) {
|
if (webServer != null) {
|
||||||
webServer.shutdown();
|
webServer.shutdown();
|
||||||
webServer = null;
|
webServer = null;
|
||||||
}
|
}
|
||||||
debugger.disable();
|
Debug.clearDebuggers();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void registerEvents() {
|
public void registerEvents() {
|
||||||
BlockListener blockListener = new DynmapBlockListener(mapManager);
|
BlockListener blockListener = new DynmapBlockListener(mapManager);
|
||||||
getServer().getPluginManager().registerEvent(Event.Type.BLOCK_PLACED, blockListener, Priority.Normal, this);
|
getServer().getPluginManager().registerEvent(Event.Type.BLOCK_PLACED, blockListener, Priority.Monitor, this);
|
||||||
getServer().getPluginManager().registerEvent(Event.Type.BLOCK_DAMAGED, blockListener, Priority.Normal, this);
|
getServer().getPluginManager().registerEvent(Event.Type.BLOCK_DAMAGED, blockListener, Priority.Monitor, this);
|
||||||
|
|
||||||
PlayerListener playerListener = new DynmapPlayerListener(mapManager, playerList, configuration);
|
PlayerListener playerListener = new DynmapPlayerListener(mapManager, playerList, configuration);
|
||||||
getServer().getPluginManager().registerEvent(Event.Type.PLAYER_COMMAND, playerListener, Priority.Normal, this);
|
getServer().getPluginManager().registerEvent(Event.Type.PLAYER_COMMAND, playerListener, Priority.Normal, this);
|
||||||
getServer().getPluginManager().registerEvent(Event.Type.PLAYER_CHAT, playerListener, Priority.Normal, this);
|
getServer().getPluginManager().registerEvent(Event.Type.PLAYER_CHAT, playerListener, Priority.Normal, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static File combinePaths(File parent, String path) {
|
||||||
|
return combinePaths(parent, new File(path));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static File combinePaths(File parent, File path) {
|
||||||
|
if (path.isAbsolute())
|
||||||
|
return path;
|
||||||
|
return new File(parent, path.getPath());
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile(String path) {
|
||||||
|
return combinePaths(DynmapPlugin.dataRoot, path);
|
||||||
|
}
|
||||||
|
|
||||||
private void jsonConfig()
|
private void jsonConfig()
|
||||||
{
|
{
|
||||||
File outputFile;
|
File outputFile;
|
||||||
|
|
|
||||||
26
src/main/java/org/dynmap/Event.java
Normal file
26
src/main/java/org/dynmap/Event.java
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
package org.dynmap;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Event<T> {
|
||||||
|
private List<Listener<T>> listeners = new LinkedList<Listener<T>>();
|
||||||
|
|
||||||
|
public synchronized void addListener(Listener<T> l) {
|
||||||
|
listeners.add(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void removeListener(Listener<T> l) {
|
||||||
|
listeners.remove(l);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void trigger(T t) {
|
||||||
|
for (Listener<T> l : listeners) {
|
||||||
|
l.triggered(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Listener<T> {
|
||||||
|
void triggered(T t);
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/main/java/org/dynmap/Handler.java
Normal file
5
src/main/java/org/dynmap/Handler.java
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
package org.dynmap;
|
||||||
|
|
||||||
|
public interface Handler<T> {
|
||||||
|
void handle(T t);
|
||||||
|
}
|
||||||
|
|
@ -1,6 +0,0 @@
|
||||||
package org.dynmap;
|
|
||||||
|
|
||||||
public class MapLocation {
|
|
||||||
public float x;
|
|
||||||
public float y;
|
|
||||||
}
|
|
||||||
|
|
@ -3,84 +3,50 @@ package org.dynmap;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.util.config.ConfigurationNode;
|
import org.bukkit.util.config.ConfigurationNode;
|
||||||
import org.dynmap.debug.Debugger;
|
import org.dynmap.debug.Debug;
|
||||||
|
|
||||||
public class MapManager extends Thread {
|
public class MapManager {
|
||||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||||
|
|
||||||
private World world;
|
private MapType[] mapTypes;
|
||||||
private Debugger debugger;
|
public AsynchronousQueue<MapTile> tileQueue;
|
||||||
private MapType[] maps;
|
|
||||||
public StaleQueue staleQueue;
|
public Map<String, UpdateQueue> worldUpdateQueues = new HashMap<String, UpdateQueue>();
|
||||||
public UpdateQueue updateQueue;
|
public ArrayList<String> worlds = new ArrayList<String>();
|
||||||
|
|
||||||
public PlayerList playerList;
|
public PlayerList playerList;
|
||||||
|
|
||||||
/* lock for our data structures */
|
/* lock for our data structures */
|
||||||
public static final Object lock = new Object();
|
public static final Object lock = new Object();
|
||||||
|
|
||||||
/* whether the worker thread should be running now */
|
public MapManager(ConfigurationNode configuration) {
|
||||||
private boolean running = false;
|
this.tileQueue = new AsynchronousQueue<MapTile>(new Handler<MapTile>() {
|
||||||
|
@Override
|
||||||
/* path to image tile directory */
|
public void handle(MapTile t) {
|
||||||
public File tileDirectory;
|
render(t);
|
||||||
|
}
|
||||||
/* web files location */
|
}, (int) (configuration.getDouble("renderinterval", 0.5) * 1000));
|
||||||
public File webDirectory;
|
|
||||||
|
mapTypes = loadMapTypes(configuration);
|
||||||
/* bind web server to ip-address */
|
|
||||||
public String bindaddress = "0.0.0.0";
|
tileQueue.start();
|
||||||
|
|
||||||
/* port to run web server on */
|
|
||||||
public int serverport = 8123;
|
|
||||||
|
|
||||||
/* time to pause between rendering tiles (ms) */
|
|
||||||
public int renderWait = 500;
|
|
||||||
|
|
||||||
public boolean loadChunks = true;
|
|
||||||
|
|
||||||
public void debug(String msg) {
|
|
||||||
debugger.debug(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File combinePaths(File parent, String path) {
|
|
||||||
return combinePaths(parent, new File(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static File combinePaths(File parent, File path) {
|
|
||||||
if (path.isAbsolute())
|
|
||||||
return path;
|
|
||||||
return new File(parent, path.getPath());
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapManager(World world, Debugger debugger, ConfigurationNode configuration) {
|
|
||||||
this.world = world;
|
|
||||||
this.debugger = debugger;
|
|
||||||
this.staleQueue = new StaleQueue();
|
|
||||||
this.updateQueue = new UpdateQueue();
|
|
||||||
|
|
||||||
tileDirectory = combinePaths(DynmapPlugin.dataRoot, configuration.getString("tilespath", "web/tiles"));
|
|
||||||
webDirectory = combinePaths(DynmapPlugin.dataRoot, configuration.getString("webpath", "web"));
|
|
||||||
renderWait = (int) (configuration.getDouble("renderinterval", 0.5) * 1000);
|
|
||||||
loadChunks = configuration.getBoolean("loadchunks", true);
|
|
||||||
|
|
||||||
if (!tileDirectory.isDirectory())
|
|
||||||
tileDirectory.mkdirs();
|
|
||||||
|
|
||||||
maps = loadMapTypes(configuration);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void renderFullWorld(Location l) {
|
void renderFullWorld(Location l) {
|
||||||
debugger.debug("Full render starting...");
|
World world = l.getWorld();
|
||||||
for (MapType map : maps) {
|
log.info("Full render starting...");
|
||||||
|
for (MapType map : mapTypes) {
|
||||||
int requiredChunkCount = 200;
|
int requiredChunkCount = 200;
|
||||||
HashSet<MapTile> found = new HashSet<MapTile>();
|
HashSet<MapTile> found = new HashSet<MapTile>();
|
||||||
HashSet<MapTile> rendered = new HashSet<MapTile>();
|
HashSet<MapTile> rendered = new HashSet<MapTile>();
|
||||||
|
|
@ -114,10 +80,9 @@ public class MapManager extends Thread {
|
||||||
loadedChunks.add(chunk);
|
loadedChunks.add(chunk);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (map.render(tile)) {
|
if (render(tile)) {
|
||||||
found.remove(tile);
|
found.remove(tile);
|
||||||
rendered.add(tile);
|
rendered.add(tile);
|
||||||
updateQueue.pushUpdate(new Client.Tile(tile.getName()));
|
|
||||||
for (MapTile adjTile : map.getAdjecentTiles(tile)) {
|
for (MapTile adjTile : map.getAdjecentTiles(tile)) {
|
||||||
if (!found.contains(adjTile) && !rendered.contains(adjTile)) {
|
if (!found.contains(adjTile) && !rendered.contains(adjTile)) {
|
||||||
found.add(adjTile);
|
found.add(adjTile);
|
||||||
|
|
@ -135,10 +100,17 @@ public class MapManager extends Thread {
|
||||||
world.unloadChunk(c.x, c.z, false, true);
|
world.unloadChunk(c.x, c.z, false, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
debugger.debug("Full render finished.");
|
log.info("Full render finished.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private MapType[] loadMapTypes(ConfigurationNode configuration) {
|
private MapType[] loadMapTypes(ConfigurationNode configuration) {
|
||||||
|
Event.Listener<MapTile> invalitateListener = new Event.Listener<MapTile>() {
|
||||||
|
@Override
|
||||||
|
public void triggered(MapTile t) {
|
||||||
|
invalidateTile(t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
List<?> configuredMaps = (List<?>) configuration.getProperty("maps");
|
List<?> configuredMaps = (List<?>) configuration.getProperty("maps");
|
||||||
ArrayList<MapType> mapTypes = new ArrayList<MapType>();
|
ArrayList<MapType> mapTypes = new ArrayList<MapType>();
|
||||||
for (Object configuredMapObj : configuredMaps) {
|
for (Object configuredMapObj : configuredMaps) {
|
||||||
|
|
@ -148,83 +120,24 @@ public class MapManager extends Thread {
|
||||||
String typeName = (String) configuredMap.get("class");
|
String typeName = (String) configuredMap.get("class");
|
||||||
log.info("Loading map '" + typeName.toString() + "'...");
|
log.info("Loading map '" + typeName.toString() + "'...");
|
||||||
Class<?> mapTypeClass = Class.forName(typeName);
|
Class<?> mapTypeClass = Class.forName(typeName);
|
||||||
Constructor<?> constructor = mapTypeClass.getConstructor(MapManager.class, World.class, Debugger.class, Map.class);
|
Constructor<?> constructor = mapTypeClass.getConstructor(Map.class);
|
||||||
MapType mapType = (MapType) constructor.newInstance(this, world, debugger, configuredMap);
|
MapType mapType = (MapType) constructor.newInstance(configuredMap);
|
||||||
|
mapType.onTileInvalidated.addListener(invalitateListener);
|
||||||
mapTypes.add(mapType);
|
mapTypes.add(mapType);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
debugger.error("Error loading map", e);
|
log.log(Level.SEVERE, "Error loading maptype", e);
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MapType[] result = new MapType[mapTypes.size()];
|
MapType[] result = new MapType[mapTypes.size()];
|
||||||
mapTypes.toArray(result);
|
mapTypes.toArray(result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* initialize and start map manager */
|
public void touch(Location l) {
|
||||||
public void startManager() {
|
Debug.debug("Touched " + l.toString());
|
||||||
synchronized (lock) {
|
for (int i = 0; i < mapTypes.length; i++) {
|
||||||
running = true;
|
MapTile[] tiles = mapTypes[i].getTiles(l);
|
||||||
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() {
|
|
||||||
synchronized (lock) {
|
|
||||||
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() {
|
|
||||||
try {
|
|
||||||
log.info("Map renderer has started.");
|
|
||||||
|
|
||||||
while (running) {
|
|
||||||
MapTile t = staleQueue.popStaleTile();
|
|
||||||
if (t != null) {
|
|
||||||
debugger.debug("Rendering tile " + t + "...");
|
|
||||||
boolean isNonEmptyTile = t.getMap().render(t);
|
|
||||||
updateQueue.pushUpdate(new Client.Tile(t.getName()));
|
|
||||||
|
|
||||||
try {
|
|
||||||
Thread.sleep(renderWait);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
Thread.sleep(500);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("Map renderer has stopped.");
|
|
||||||
} catch (Exception ex) {
|
|
||||||
debugger.error("Exception on rendering-thread: " + ex.toString());
|
|
||||||
ex.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void touch(int x, int y, int z) {
|
|
||||||
for (int i = 0; i < maps.length; i++) {
|
|
||||||
MapTile[] tiles = maps[i].getTiles(new Location(world, x, y, z));
|
|
||||||
for (int j = 0; j < tiles.length; j++) {
|
for (int j = 0; j < tiles.length; j++) {
|
||||||
invalidateTile(tiles[j]);
|
invalidateTile(tiles[j]);
|
||||||
}
|
}
|
||||||
|
|
@ -232,7 +145,61 @@ public class MapManager extends Thread {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidateTile(MapTile tile) {
|
public void invalidateTile(MapTile tile) {
|
||||||
debugger.debug("Invalidating tile " + tile.getName());
|
Debug.debug("Invalidating tile " + tile.getFilename());
|
||||||
staleQueue.pushStaleTile(tile);
|
tileQueue.push(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void startRendering() {
|
||||||
|
tileQueue.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stopRendering() {
|
||||||
|
tileQueue.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean render(MapTile tile) {
|
||||||
|
boolean result = tile.getMap().render(tile, getTileFile(tile));
|
||||||
|
pushUpdate(tile.getWorld(), new Client.Tile(tile.getFilename()));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private HashMap<World, File> worldTileDirectories = new HashMap<World, File>();
|
||||||
|
private File getTileFile(MapTile tile) {
|
||||||
|
World world = tile.getWorld();
|
||||||
|
File worldTileDirectory = worldTileDirectories.get(world);
|
||||||
|
if (worldTileDirectory == null) {
|
||||||
|
worldTileDirectory = new File(DynmapPlugin.tilesDirectory, tile.getWorld().getName());
|
||||||
|
worldTileDirectories.put(world, worldTileDirectory);
|
||||||
|
}
|
||||||
|
worldTileDirectory.mkdirs();
|
||||||
|
return new File(worldTileDirectory, tile.getFilename());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushUpdate(Object update) {
|
||||||
|
for(int i=0;i<worlds.size();i++) {
|
||||||
|
UpdateQueue queue = worldUpdateQueues.get(worlds.get(i));
|
||||||
|
queue.pushUpdate(update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushUpdate(World world, Object update) {
|
||||||
|
pushUpdate(world.getName(), update);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pushUpdate(String world, Object update) {
|
||||||
|
UpdateQueue updateQueue = worldUpdateQueues.get(world);
|
||||||
|
if (updateQueue == null) {
|
||||||
|
worldUpdateQueues.put(world, updateQueue = new UpdateQueue());
|
||||||
|
worlds.add(world);
|
||||||
|
}
|
||||||
|
updateQueue.pushUpdate(update);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getWorldUpdates(String worldName, long since) {
|
||||||
|
UpdateQueue queue = worldUpdateQueues.get(worldName);
|
||||||
|
if (queue == null)
|
||||||
|
return new Object[0];
|
||||||
|
return queue.getUpdatedObjects(since);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,28 @@
|
||||||
package org.dynmap;
|
package org.dynmap;
|
||||||
|
|
||||||
|
import org.bukkit.World;
|
||||||
|
|
||||||
public abstract class MapTile {
|
public abstract class MapTile {
|
||||||
|
private World world;
|
||||||
private MapType map;
|
private MapType map;
|
||||||
|
|
||||||
|
public World getWorld() {
|
||||||
|
return world;
|
||||||
|
}
|
||||||
|
|
||||||
public MapType getMap() {
|
public MapType getMap() {
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract String getName();
|
public abstract String getFilename();
|
||||||
|
|
||||||
public MapTile(MapType map) {
|
public MapTile(World world, MapType map) {
|
||||||
|
this.world = world;
|
||||||
this.map = map;
|
this.map = map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return getFilename().hashCode() ^ getWorld().hashCode();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,17 @@
|
||||||
package org.dynmap;
|
package org.dynmap;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
|
||||||
import org.dynmap.debug.Debugger;
|
|
||||||
|
|
||||||
public abstract class MapType {
|
public abstract class MapType {
|
||||||
private MapManager manager;
|
public Event<MapTile> onTileInvalidated = new Event<MapTile>();
|
||||||
|
|
||||||
public MapManager getMapManager() {
|
|
||||||
return manager;
|
|
||||||
}
|
|
||||||
|
|
||||||
private World world;
|
|
||||||
|
|
||||||
public World getWorld() {
|
|
||||||
return world;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Debugger debugger;
|
|
||||||
|
|
||||||
public Debugger getDebugger() {
|
|
||||||
return debugger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MapType(MapManager manager, World world, Debugger debugger) {
|
|
||||||
this.manager = manager;
|
|
||||||
this.world = world;
|
|
||||||
this.debugger = debugger;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract MapTile[] getTiles(Location l);
|
public abstract MapTile[] getTiles(Location l);
|
||||||
|
|
||||||
public abstract MapTile[] getAdjecentTiles(MapTile tile);
|
public abstract MapTile[] getAdjecentTiles(MapTile tile);
|
||||||
|
|
||||||
public abstract DynmapChunk[] getRequiredChunks(MapTile tile);
|
public abstract DynmapChunk[] getRequiredChunks(MapTile tile);
|
||||||
|
|
||||||
public abstract boolean render(MapTile tile);
|
public abstract boolean render(MapTile tile, File outputFile);
|
||||||
|
|
||||||
public abstract boolean isRendered(MapTile tile);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,21 @@ public class PlayerList {
|
||||||
hide(playerName);
|
hide(playerName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Clean this up... one day
|
||||||
|
public Player[] getVisiblePlayers(String worldName) {
|
||||||
|
ArrayList<Player> visiblePlayers = new ArrayList<Player>();
|
||||||
|
Player[] onlinePlayers = server.getOnlinePlayers();
|
||||||
|
for (int i = 0; i < onlinePlayers.length; i++) {
|
||||||
|
Player p = onlinePlayers[i];
|
||||||
|
if (p.getWorld().getName().equals(worldName) && !hiddenPlayerNames.contains(p.getName())) {
|
||||||
|
visiblePlayers.add(p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Player[] result = new Player[visiblePlayers.size()];
|
||||||
|
visiblePlayers.toArray(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public Player[] getVisiblePlayers() {
|
public Player[] getVisiblePlayers() {
|
||||||
ArrayList<Player> visiblePlayers = new ArrayList<Player>();
|
ArrayList<Player> visiblePlayers = new ArrayList<Player>();
|
||||||
Player[] onlinePlayers = server.getOnlinePlayers();
|
Player[] onlinePlayers = server.getOnlinePlayers();
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ public class UpdateQueue {
|
||||||
|
|
||||||
private static final int maxUpdateAge = 120000;
|
private static final int maxUpdateAge = 120000;
|
||||||
|
|
||||||
public void pushUpdate(Object obj) {
|
public synchronized void pushUpdate(Object obj) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long deadline = now - maxUpdateAge;
|
long deadline = now - maxUpdateAge;
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
|
|
@ -27,7 +27,7 @@ public class UpdateQueue {
|
||||||
|
|
||||||
private ArrayList<Object> tmpupdates = new ArrayList<Object>();
|
private ArrayList<Object> tmpupdates = new ArrayList<Object>();
|
||||||
|
|
||||||
public Object[] getUpdatedObjects(long since) {
|
public synchronized Object[] getUpdatedObjects(long since) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
long deadline = now - maxUpdateAge;
|
long deadline = now - maxUpdateAge;
|
||||||
Object[] updates;
|
Object[] updates;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
package org.dynmap.debug;
|
package org.dynmap.debug;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
|
|
@ -11,14 +9,9 @@ import org.bukkit.event.Event.Priority;
|
||||||
import org.bukkit.event.player.PlayerChatEvent;
|
import org.bukkit.event.player.PlayerChatEvent;
|
||||||
import org.bukkit.event.player.PlayerEvent;
|
import org.bukkit.event.player.PlayerEvent;
|
||||||
import org.bukkit.event.player.PlayerListener;
|
import org.bukkit.event.player.PlayerListener;
|
||||||
import org.bukkit.plugin.PluginDescriptionFile;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
import org.bukkit.plugin.java.JavaPlugin;
|
||||||
|
|
||||||
public class BukkitPlayerDebugger implements Debugger {
|
public class BukkitPlayerDebugger implements Debugger {
|
||||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
|
||||||
|
|
||||||
private boolean isLogging = false;
|
|
||||||
|
|
||||||
private JavaPlugin plugin;
|
private JavaPlugin plugin;
|
||||||
private HashSet<Player> debugees = new HashSet<Player>();
|
private HashSet<Player> debugees = new HashSet<Player>();
|
||||||
private String debugCommand;
|
private String debugCommand;
|
||||||
|
|
@ -28,10 +21,10 @@ public class BukkitPlayerDebugger implements Debugger {
|
||||||
public BukkitPlayerDebugger(JavaPlugin plugin) {
|
public BukkitPlayerDebugger(JavaPlugin plugin) {
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
||||||
PluginDescriptionFile pdfFile = plugin.getDescription();
|
String name = "dynmap";
|
||||||
debugCommand = "/debug_" + pdfFile.getName();
|
debugCommand = "/debug_" + name;
|
||||||
undebugCommand = "/undebug_" + pdfFile.getName();
|
undebugCommand = "/undebug_" + name;
|
||||||
prepend = pdfFile.getName() + ": ";
|
prepend = name + ": ";
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void enable() {
|
public synchronized void enable() {
|
||||||
|
|
@ -63,19 +56,15 @@ public class BukkitPlayerDebugger implements Debugger {
|
||||||
|
|
||||||
public synchronized void debug(String message) {
|
public synchronized void debug(String message) {
|
||||||
sendToDebuggees(message);
|
sendToDebuggees(message);
|
||||||
if (isLogging)
|
|
||||||
log.info(prepend + message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void error(String message) {
|
public synchronized void error(String message) {
|
||||||
sendToDebuggees(prepend + ChatColor.RED + message);
|
sendToDebuggees(prepend + ChatColor.RED + message);
|
||||||
log.log(Level.SEVERE, prepend + message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void error(String message, Throwable thrown) {
|
public synchronized void error(String message, Throwable thrown) {
|
||||||
sendToDebuggees(prepend + ChatColor.RED + message);
|
sendToDebuggees(prepend + ChatColor.RED + message);
|
||||||
sendToDebuggees(thrown.toString());
|
sendToDebuggees(thrown.toString());
|
||||||
log.log(Level.SEVERE, prepend + message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class CommandListener extends PlayerListener {
|
protected class CommandListener extends PlayerListener {
|
||||||
|
|
|
||||||
32
src/main/java/org/dynmap/debug/Debug.java
Normal file
32
src/main/java/org/dynmap/debug/Debug.java
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
package org.dynmap.debug;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Debug {
|
||||||
|
private static List<Debugger> debuggers = new LinkedList<Debugger>();
|
||||||
|
|
||||||
|
public synchronized static void addDebugger(Debugger d) {
|
||||||
|
debuggers.add(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void removeDebugger(Debugger d) {
|
||||||
|
debuggers.remove(d);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void clearDebuggers() {
|
||||||
|
debuggers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void debug(String message) {
|
||||||
|
for(Debugger d : debuggers) d.debug(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void error(String message) {
|
||||||
|
for(Debugger d : debuggers) d.error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized static void error(String message, Throwable thrown) {
|
||||||
|
for(Debugger d : debuggers) d.error(message, thrown);
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/main/java/org/dynmap/debug/LogDebugger.java
Normal file
25
src/main/java/org/dynmap/debug/LogDebugger.java
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
package org.dynmap.debug;
|
||||||
|
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class LogDebugger implements Debugger {
|
||||||
|
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||||
|
private static String prepend = "dynmap: ";
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void debug(String message) {
|
||||||
|
log.info(prepend + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String message) {
|
||||||
|
log.log(Level.SEVERE, prepend + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void error(String message, Throwable thrown) {
|
||||||
|
log.log(Level.SEVERE, prepend + message);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -4,12 +4,11 @@ import java.awt.Color;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.dynmap.debug.Debugger;
|
|
||||||
|
|
||||||
public class CaveTileRenderer extends DefaultTileRenderer {
|
public class CaveTileRenderer extends DefaultTileRenderer {
|
||||||
|
|
||||||
public CaveTileRenderer(Debugger debugger, Map<String, Object> configuration) {
|
public CaveTileRenderer(Map<String, Object> configuration) {
|
||||||
super(debugger, configuration);
|
super(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -10,32 +10,38 @@ import java.util.Map;
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.dynmap.debug.Debugger;
|
import org.dynmap.debug.Debug;
|
||||||
|
|
||||||
public class DefaultTileRenderer implements MapTileRenderer {
|
public class DefaultTileRenderer implements MapTileRenderer {
|
||||||
protected static Color translucent = new Color(0, 0, 0, 0);
|
protected static Color translucent = new Color(0, 0, 0, 0);
|
||||||
private String name;
|
private String name;
|
||||||
protected Debugger debugger;
|
protected int maximumHeight = 127;
|
||||||
|
|
||||||
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DefaultTileRenderer(Debugger debugger, Map<String, Object> configuration) {
|
public DefaultTileRenderer(Map<String, Object> configuration) {
|
||||||
this.debugger = debugger;
|
|
||||||
name = (String) configuration.get("prefix");
|
name = (String) configuration.get("prefix");
|
||||||
|
Object o = configuration.get("maximumheight");
|
||||||
|
if (o != null) {
|
||||||
|
maximumHeight = Integer.parseInt(String.valueOf(o));
|
||||||
|
if (maximumHeight > 127)
|
||||||
|
maximumHeight = 127;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean render(KzedMapTile tile, String path) {
|
public boolean render(KzedMapTile tile, File outputFile) {
|
||||||
World world = tile.getMap().getWorld();
|
World world = tile.getWorld();
|
||||||
BufferedImage im = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB);
|
BufferedImage im = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB);
|
||||||
|
|
||||||
WritableRaster r = im.getRaster();
|
WritableRaster r = im.getRaster();
|
||||||
boolean isempty = true;
|
boolean isempty = true;
|
||||||
|
|
||||||
int ix = tile.mx;
|
int ix = KzedMap.anchorx + tile.px / 2 + tile.py / 2;
|
||||||
int iy = tile.my;
|
int iy = maximumHeight;
|
||||||
int iz = tile.mz;
|
int iz = KzedMap.anchorz + tile.px / 2 - tile.py / 2;
|
||||||
|
|
||||||
int jx, jz;
|
int jx, jz;
|
||||||
|
|
||||||
|
|
@ -92,9 +98,11 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save the generated tile */
|
/* save the generated tile */
|
||||||
saveTile(tile, im, path);
|
saveImage(im, outputFile);
|
||||||
im.flush();
|
im.flush();
|
||||||
((KzedMap) tile.getMap()).invalidateTile(new KzedZoomedMapTile((KzedMap) tile.getMap(), tile));
|
|
||||||
|
tile.file = outputFile;
|
||||||
|
((KzedMap) tile.getMap()).invalidateTile(new KzedZoomedMapTile(world, (KzedMap) tile.getMap(), tile));
|
||||||
|
|
||||||
return !isempty;
|
return !isempty;
|
||||||
}
|
}
|
||||||
|
|
@ -154,23 +162,15 @@ public class DefaultTileRenderer implements MapTileRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* save rendered tile, update zoom-out tile */
|
/* save rendered tile, update zoom-out tile */
|
||||||
public void saveTile(KzedMapTile tile, BufferedImage im, String path) {
|
public void saveImage(BufferedImage im, File outputFile) {
|
||||||
String tilePath = getPath(tile, path);
|
Debug.debug("saving image " + outputFile.getPath());
|
||||||
|
|
||||||
debugger.debug("saving tile " + tilePath);
|
|
||||||
|
|
||||||
/* save image */
|
/* save image */
|
||||||
try {
|
try {
|
||||||
File file = new File(tilePath);
|
ImageIO.write(im, "png", outputFile);
|
||||||
ImageIO.write(im, "png", file);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
debugger.error("Failed to save tile: " + tilePath, e);
|
Debug.error("Failed to save image: " + outputFile.getPath(), e);
|
||||||
} catch (java.lang.NullPointerException e) {
|
} catch (java.lang.NullPointerException e) {
|
||||||
debugger.error("Failed to save tile (NullPointerException): " + tilePath, e);
|
Debug.error("Failed to save image (NullPointerException): " + outputFile.getPath(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getPath(KzedMapTile tile, String outputPath) {
|
|
||||||
return new File(new File(outputPath), tile.getName() + ".png").getPath();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,9 @@ import java.util.logging.Logger;
|
||||||
import org.bukkit.Location;
|
import org.bukkit.Location;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.dynmap.DynmapChunk;
|
import org.dynmap.DynmapChunk;
|
||||||
import org.dynmap.MapManager;
|
|
||||||
import org.dynmap.MapTile;
|
import org.dynmap.MapTile;
|
||||||
import org.dynmap.MapType;
|
import org.dynmap.MapType;
|
||||||
import org.dynmap.debug.Debugger;
|
import org.dynmap.debug.Debug;
|
||||||
|
|
||||||
public class KzedMap extends MapType {
|
public class KzedMap extends MapType {
|
||||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||||
|
|
@ -43,14 +42,13 @@ public class KzedMap extends MapType {
|
||||||
MapTileRenderer[] renderers;
|
MapTileRenderer[] renderers;
|
||||||
ZoomedTileRenderer zoomrenderer;
|
ZoomedTileRenderer zoomrenderer;
|
||||||
|
|
||||||
public KzedMap(MapManager manager, World world, Debugger debugger, Map<String, Object> configuration) {
|
public KzedMap(Map<String, Object> configuration) {
|
||||||
super(manager, world, debugger);
|
|
||||||
if (colors == null) {
|
if (colors == null) {
|
||||||
colors = loadColorSet("colors.txt");
|
colors = loadColorSet("colors.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
renderers = loadRenderers(configuration);
|
renderers = loadRenderers(configuration);
|
||||||
zoomrenderer = new ZoomedTileRenderer(debugger, configuration);
|
zoomrenderer = new ZoomedTileRenderer(configuration);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MapTileRenderer[] loadRenderers(Map<String, Object> configuration) {
|
private MapTileRenderer[] loadRenderers(Map<String, Object> configuration) {
|
||||||
|
|
@ -63,11 +61,12 @@ public class KzedMap extends MapType {
|
||||||
String typeName = (String) configuredRenderer.get("class");
|
String typeName = (String) configuredRenderer.get("class");
|
||||||
log.info("Loading renderer '" + typeName.toString() + "'...");
|
log.info("Loading renderer '" + typeName.toString() + "'...");
|
||||||
Class<?> mapTypeClass = Class.forName(typeName);
|
Class<?> mapTypeClass = Class.forName(typeName);
|
||||||
Constructor<?> constructor = mapTypeClass.getConstructor(Debugger.class, Map.class);
|
Constructor<?> constructor = mapTypeClass.getConstructor(Map.class);
|
||||||
MapTileRenderer mapTileRenderer = (MapTileRenderer) constructor.newInstance(getDebugger(), configuredRenderer);
|
MapTileRenderer mapTileRenderer = (MapTileRenderer) constructor.newInstance(configuredRenderer);
|
||||||
renderers.add(mapTileRenderer);
|
renderers.add(mapTileRenderer);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
getDebugger().error("Error loading renderer", e);
|
Debug.error("Error loading renderer", e);
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MapTileRenderer[] result = new MapTileRenderer[renderers.size()];
|
MapTileRenderer[] result = new MapTileRenderer[renderers.size()];
|
||||||
|
|
@ -77,6 +76,8 @@ public class KzedMap extends MapType {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MapTile[] getTiles(Location l) {
|
public MapTile[] getTiles(Location l) {
|
||||||
|
World world = l.getWorld();
|
||||||
|
|
||||||
int x = l.getBlockX();
|
int x = l.getBlockX();
|
||||||
int y = l.getBlockY();
|
int y = l.getBlockY();
|
||||||
int z = l.getBlockZ();
|
int z = l.getBlockZ();
|
||||||
|
|
@ -92,7 +93,7 @@ public class KzedMap extends MapType {
|
||||||
|
|
||||||
ArrayList<MapTile> tiles = new ArrayList<MapTile>();
|
ArrayList<MapTile> tiles = new ArrayList<MapTile>();
|
||||||
|
|
||||||
addTile(tiles, tx, ty);
|
addTile(tiles, world, tx, ty);
|
||||||
|
|
||||||
boolean ledge = tilex(px - 4) != tx;
|
boolean ledge = tilex(px - 4) != tx;
|
||||||
boolean tedge = tiley(py - 4) != ty;
|
boolean tedge = tiley(py - 4) != ty;
|
||||||
|
|
@ -100,22 +101,22 @@ public class KzedMap extends MapType {
|
||||||
boolean bedge = tiley(py + 4) != ty;
|
boolean bedge = tiley(py + 4) != ty;
|
||||||
|
|
||||||
if (ledge)
|
if (ledge)
|
||||||
addTile(tiles, tx - tileWidth, ty);
|
addTile(tiles, world, tx - tileWidth, ty);
|
||||||
if (redge)
|
if (redge)
|
||||||
addTile(tiles, tx + tileWidth, ty);
|
addTile(tiles, world, tx + tileWidth, ty);
|
||||||
if (tedge)
|
if (tedge)
|
||||||
addTile(tiles, tx, ty - tileHeight);
|
addTile(tiles, world, tx, ty - tileHeight);
|
||||||
if (bedge)
|
if (bedge)
|
||||||
addTile(tiles, tx, ty + tileHeight);
|
addTile(tiles, world, tx, ty + tileHeight);
|
||||||
|
|
||||||
if (ledge && tedge)
|
if (ledge && tedge)
|
||||||
addTile(tiles, tx - tileWidth, ty - tileHeight);
|
addTile(tiles, world, tx - tileWidth, ty - tileHeight);
|
||||||
if (ledge && bedge)
|
if (ledge && bedge)
|
||||||
addTile(tiles, tx - tileWidth, ty + tileHeight);
|
addTile(tiles, world, tx - tileWidth, ty + tileHeight);
|
||||||
if (redge && tedge)
|
if (redge && tedge)
|
||||||
addTile(tiles, tx + tileWidth, ty - tileHeight);
|
addTile(tiles, world, tx + tileWidth, ty - tileHeight);
|
||||||
if (redge && bedge)
|
if (redge && bedge)
|
||||||
addTile(tiles, tx + tileWidth, ty + tileHeight);
|
addTile(tiles, world, tx + tileWidth, ty + tileHeight);
|
||||||
|
|
||||||
MapTile[] result = new MapTile[tiles.size()];
|
MapTile[] result = new MapTile[tiles.size()];
|
||||||
tiles.toArray(result);
|
tiles.toArray(result);
|
||||||
|
|
@ -126,35 +127,41 @@ public class KzedMap extends MapType {
|
||||||
public MapTile[] getAdjecentTiles(MapTile tile) {
|
public MapTile[] getAdjecentTiles(MapTile tile) {
|
||||||
if (tile instanceof KzedMapTile) {
|
if (tile instanceof KzedMapTile) {
|
||||||
KzedMapTile t = (KzedMapTile) tile;
|
KzedMapTile t = (KzedMapTile) tile;
|
||||||
|
World world = tile.getWorld();
|
||||||
MapTileRenderer renderer = t.renderer;
|
MapTileRenderer renderer = t.renderer;
|
||||||
return new MapTile[] {
|
return new MapTile[] {
|
||||||
new KzedMapTile(this, renderer, t.px - tileWidth, t.py),
|
new KzedMapTile(world, this, renderer, t.px - tileWidth, t.py),
|
||||||
new KzedMapTile(this, renderer, t.px + tileWidth, t.py),
|
new KzedMapTile(world, this, renderer, t.px + tileWidth, t.py),
|
||||||
new KzedMapTile(this, renderer, t.px, t.py - tileHeight),
|
new KzedMapTile(world, this, renderer, t.px, t.py - tileHeight),
|
||||||
new KzedMapTile(this, renderer, t.px, t.py + tileHeight) };
|
new KzedMapTile(world, this, renderer, t.px, t.py + tileHeight) };
|
||||||
}
|
}
|
||||||
return new MapTile[0];
|
return new MapTile[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTile(ArrayList<MapTile> tiles, int px, int py) {
|
public void addTile(ArrayList<MapTile> tiles, World world, int px, int py) {
|
||||||
for (int i = 0; i < renderers.length; i++) {
|
for (int i = 0; i < renderers.length; i++) {
|
||||||
tiles.add(new KzedMapTile(this, renderers[i], px, py));
|
tiles.add(new KzedMapTile(world, this, renderers[i], px, py));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void invalidateTile(MapTile tile) {
|
public void invalidateTile(MapTile tile) {
|
||||||
getMapManager().invalidateTile(tile);
|
onTileInvalidated.trigger(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DynmapChunk[] getRequiredChunks(MapTile tile) {
|
public DynmapChunk[] getRequiredChunks(MapTile tile) {
|
||||||
if (tile instanceof KzedMapTile) {
|
if (tile instanceof KzedMapTile) {
|
||||||
KzedMapTile t = (KzedMapTile) tile;
|
KzedMapTile t = (KzedMapTile) tile;
|
||||||
int x1 = t.mx - KzedMap.tileHeight / 2;
|
|
||||||
int x2 = t.mx + KzedMap.tileWidth / 2 + KzedMap.tileHeight / 2;
|
int ix = KzedMap.anchorx + t.px / 2 + t.py / 2;
|
||||||
|
int iy = 127;
|
||||||
|
int iz = KzedMap.anchorz + t.px / 2 - t.py / 2;
|
||||||
|
|
||||||
|
int x1 = ix - KzedMap.tileHeight / 2;
|
||||||
|
int x2 = ix + KzedMap.tileWidth / 2 + KzedMap.tileHeight / 2;
|
||||||
|
|
||||||
int z1 = t.mz - KzedMap.tileHeight / 2;
|
int z1 = iz - KzedMap.tileHeight / 2;
|
||||||
int z2 = t.mz + KzedMap.tileWidth / 2 + KzedMap.tileHeight / 2;
|
int z2 = iz + KzedMap.tileWidth / 2 + KzedMap.tileHeight / 2;
|
||||||
|
|
||||||
int x, z;
|
int x, z;
|
||||||
|
|
||||||
|
|
@ -174,21 +181,12 @@ public class KzedMap extends MapType {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean render(MapTile tile) {
|
public boolean render(MapTile tile, File outputFile) {
|
||||||
if (tile instanceof KzedZoomedMapTile) {
|
if (tile instanceof KzedZoomedMapTile) {
|
||||||
zoomrenderer.render((KzedZoomedMapTile) tile, getMapManager().tileDirectory.getAbsolutePath());
|
zoomrenderer.render((KzedZoomedMapTile) tile, outputFile);
|
||||||
return true;
|
return true;
|
||||||
} else if (tile instanceof KzedMapTile) {
|
} else if (tile instanceof KzedMapTile) {
|
||||||
return ((KzedMapTile) tile).renderer.render((KzedMapTile) tile, getMapManager().tileDirectory.getAbsolutePath());
|
return ((KzedMapTile) tile).renderer.render((KzedMapTile) tile, outputFile);
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isRendered(MapTile tile) {
|
|
||||||
if (tile instanceof KzedMapTile) {
|
|
||||||
File tileFile = new File(DefaultTileRenderer.getPath((KzedMapTile) tile, getMapManager().tileDirectory.getAbsolutePath()));
|
|
||||||
return tileFile.exists();
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -235,10 +233,10 @@ public class KzedMap extends MapType {
|
||||||
/* load colorset */
|
/* load colorset */
|
||||||
File cfile = new File(colorsetpath);
|
File cfile = new File(colorsetpath);
|
||||||
if (cfile.isFile()) {
|
if (cfile.isFile()) {
|
||||||
getDebugger().debug("Loading colors from '" + colorsetpath + "'...");
|
Debug.debug("Loading colors from '" + colorsetpath + "'...");
|
||||||
stream = new FileInputStream(cfile);
|
stream = new FileInputStream(cfile);
|
||||||
} else {
|
} else {
|
||||||
getDebugger().debug("Loading colors from jar...");
|
Debug.debug("Loading colors from jar...");
|
||||||
stream = KzedMap.class.getResourceAsStream("/colors.txt");
|
stream = KzedMap.class.getResourceAsStream("/colors.txt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,7 +268,7 @@ public class KzedMap extends MapType {
|
||||||
}
|
}
|
||||||
scanner.close();
|
scanner.close();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
getDebugger().error("Could not load colors", e);
|
Debug.error("Could not load colors", e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return colors;
|
return colors;
|
||||||
|
|
|
||||||
|
|
@ -1,42 +1,33 @@
|
||||||
package org.dynmap.kzedmap;
|
package org.dynmap.kzedmap;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.io.File;
|
||||||
|
import org.bukkit.World;
|
||||||
import org.dynmap.MapTile;
|
import org.dynmap.MapTile;
|
||||||
|
|
||||||
public class KzedMapTile extends MapTile {
|
public class KzedMapTile extends MapTile {
|
||||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
|
||||||
|
|
||||||
public KzedMap map;
|
public KzedMap map;
|
||||||
|
|
||||||
public MapTileRenderer renderer;
|
public MapTileRenderer renderer;
|
||||||
|
|
||||||
/* projection position */
|
|
||||||
public int px, py;
|
public int px, py;
|
||||||
|
|
||||||
|
// Hack.
|
||||||
|
public File file = null;
|
||||||
|
|
||||||
/* minecraft space origin */
|
public KzedMapTile(World world, KzedMap map, MapTileRenderer renderer, int px, int py) {
|
||||||
public int mx, my, mz;
|
super(world, map);
|
||||||
|
|
||||||
/* create new MapTile */
|
|
||||||
public KzedMapTile(KzedMap map, MapTileRenderer renderer, int px, int py) {
|
|
||||||
super(map);
|
|
||||||
this.map = map;
|
this.map = map;
|
||||||
this.renderer = renderer;
|
this.renderer = renderer;
|
||||||
this.px = px;
|
this.px = px;
|
||||||
this.py = py;
|
this.py = py;
|
||||||
|
|
||||||
mx = KzedMap.anchorx + px / 2 + py / 2;
|
|
||||||
my = KzedMap.anchory;
|
|
||||||
mz = KzedMap.anchorz + px / 2 - py / 2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getFilename() {
|
||||||
return renderer.getName() + "_" + px + "_" + py;
|
return renderer.getName() + "_" + px + "_" + py + ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return getName().hashCode();
|
return getFilename().hashCode() ^ getWorld().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -48,11 +39,10 @@ public class KzedMapTile extends MapTile {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean equals(KzedMapTile o) {
|
public boolean equals(KzedMapTile o) {
|
||||||
return o.getName().equals(getName());
|
return o.px == px && o.py == py && o.getWorld().equals(getWorld());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return a simple string representation... */
|
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return getName();
|
return getWorld().getName() + ":" + getFilename();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,18 @@
|
||||||
package org.dynmap.kzedmap;
|
package org.dynmap.kzedmap;
|
||||||
|
|
||||||
import java.awt.image.BufferedImage;
|
import org.bukkit.World;
|
||||||
|
|
||||||
import org.dynmap.MapTile;
|
import org.dynmap.MapTile;
|
||||||
|
|
||||||
public class KzedZoomedMapTile extends MapTile {
|
public class KzedZoomedMapTile extends MapTile {
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getFilename() {
|
||||||
return "z" + originalTile.renderer.getName() + "_" + getTileX() + "_" + getTileY();
|
return "z" + originalTile.renderer.getName() + "_" + getTileX() + "_" + getTileY() + ".png";
|
||||||
}
|
}
|
||||||
|
|
||||||
public KzedMapTile originalTile;
|
public KzedMapTile originalTile;
|
||||||
|
|
||||||
public KzedZoomedMapTile(KzedMap map, KzedMapTile original) {
|
public KzedZoomedMapTile(World world, KzedMap map, KzedMapTile original) {
|
||||||
super(map);
|
super(world, map);
|
||||||
this.originalTile = original;
|
this.originalTile = original;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,7 +41,7 @@ public class KzedZoomedMapTile extends MapTile {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return getName().hashCode();
|
return getFilename().hashCode() ^ getWorld().hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
package org.dynmap.kzedmap;
|
package org.dynmap.kzedmap;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
public interface MapTileRenderer {
|
public interface MapTileRenderer {
|
||||||
String getName();
|
String getName();
|
||||||
|
|
||||||
boolean render(KzedMapTile tile, String path);
|
boolean render(KzedMapTile tile, File outputFile);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,19 +6,14 @@ import java.awt.image.BufferedImage;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.imageio.ImageIO;
|
import javax.imageio.ImageIO;
|
||||||
|
import org.dynmap.debug.Debug;
|
||||||
import org.dynmap.debug.Debugger;
|
|
||||||
|
|
||||||
public class ZoomedTileRenderer {
|
public class ZoomedTileRenderer {
|
||||||
protected Debugger debugger;
|
public ZoomedTileRenderer(Map<String, Object> configuration) {
|
||||||
|
|
||||||
public ZoomedTileRenderer(Debugger debugger, Map<String, Object> configuration) {
|
|
||||||
this.debugger = debugger;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void render(KzedZoomedMapTile zt, String outputPath) {
|
public void render(KzedZoomedMapTile zt, File outputPath) {
|
||||||
KzedMapTile originalTile = zt.originalTile;
|
KzedMapTile originalTile = zt.originalTile;
|
||||||
int px = originalTile.px;
|
int px = originalTile.px;
|
||||||
int py = originalTile.py;
|
int py = originalTile.py;
|
||||||
|
|
@ -27,17 +22,17 @@ public class ZoomedTileRenderer {
|
||||||
|
|
||||||
BufferedImage image = null;
|
BufferedImage image = null;
|
||||||
try {
|
try {
|
||||||
image = ImageIO.read(new File(new File(outputPath), originalTile.getName() + ".png"));
|
image = ImageIO.read(originalTile.file);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (image == null) {
|
if (image == null) {
|
||||||
debugger.debug("Could not load original tile, won't render zoom-out tile.");
|
Debug.debug("Could not load original tile, won't render zoom-out tile.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
BufferedImage zIm = null;
|
BufferedImage zIm = null;
|
||||||
File zoomFile = new File(new File(outputPath), zt.getName() + ".png");
|
File zoomFile = outputPath;
|
||||||
try {
|
try {
|
||||||
zIm = ImageIO.read(zoomFile);
|
zIm = ImageIO.read(zoomFile);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
|
@ -46,9 +41,9 @@ public class ZoomedTileRenderer {
|
||||||
if (zIm == null) {
|
if (zIm == null) {
|
||||||
/* create new one */
|
/* create new one */
|
||||||
zIm = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB);
|
zIm = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB);
|
||||||
debugger.debug("New zoom-out tile created " + zt.getName());
|
Debug.debug("New zoom-out tile created " + zt.getFilename());
|
||||||
} else {
|
} else {
|
||||||
debugger.debug("Loaded zoom-out tile from " + zt.getName());
|
Debug.debug("Loaded zoom-out tile from " + zt.getFilename());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update zoom-out tile */
|
/* update zoom-out tile */
|
||||||
|
|
@ -77,11 +72,11 @@ public class ZoomedTileRenderer {
|
||||||
/* save zoom-out tile */
|
/* save zoom-out tile */
|
||||||
try {
|
try {
|
||||||
ImageIO.write(zIm, "png", zoomFile);
|
ImageIO.write(zIm, "png", zoomFile);
|
||||||
debugger.debug("Saved zoom-out tile at " + zoomFile.getName());
|
Debug.debug("Saved zoom-out tile at " + zoomFile.getName());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
debugger.error("Failed to save zoom-out tile: " + zoomFile.getName(), e);
|
Debug.error("Failed to save zoom-out tile: " + zoomFile.getName(), e);
|
||||||
} catch (java.lang.NullPointerException e) {
|
} catch (java.lang.NullPointerException e) {
|
||||||
debugger.error("Failed to save zoom-out tile (NullPointerException): " + zoomFile.getName(), e);
|
Debug.error("Failed to save zoom-out tile (NullPointerException): " + zoomFile.getName(), e);
|
||||||
}
|
}
|
||||||
zIm.flush();
|
zIm.flush();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
61
src/main/java/org/dynmap/web/BoundInputStream.java
Normal file
61
src/main/java/org/dynmap/web/BoundInputStream.java
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
package org.dynmap.web;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
public class BoundInputStream extends InputStream {
|
||||||
|
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||||
|
private InputStream base;
|
||||||
|
private long bound;
|
||||||
|
|
||||||
|
public BoundInputStream(InputStream base, long bound) {
|
||||||
|
this.base = base;
|
||||||
|
this.bound = bound;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read() throws IOException {
|
||||||
|
if (bound <= 0) return -1;
|
||||||
|
int r = base.read();
|
||||||
|
if (r >= 0)
|
||||||
|
bound--;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int available() throws IOException {
|
||||||
|
return (int)Math.min(base.available(), bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean markSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b, int off, int len) throws IOException {
|
||||||
|
if (bound <= 0) return -1;
|
||||||
|
len = (int)Math.min(bound, len);
|
||||||
|
int r = base.read(b, off, len);
|
||||||
|
bound -= r;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int read(byte[] b) throws IOException {
|
||||||
|
return read(b, 0, b.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long skip(long n) throws IOException {
|
||||||
|
long r = base.skip(Math.min(bound, n));
|
||||||
|
bound -= r;
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
base.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
20
src/main/java/org/dynmap/web/HttpErrorHandler.java
Normal file
20
src/main/java/org/dynmap/web/HttpErrorHandler.java
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package org.dynmap.web;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public class HttpErrorHandler {
|
||||||
|
public static void handle(HttpResponse response, int statusCode, String statusMessage) throws IOException {
|
||||||
|
response.statusCode = statusCode;
|
||||||
|
response.statusMessage = statusMessage;
|
||||||
|
response.fields.put("Content-Length", "0");
|
||||||
|
response.getBody();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleNotFound(HttpResponse response) throws IOException {
|
||||||
|
handle(response, 404, "Not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void handleMethodNotAllowed(HttpResponse response) throws IOException {
|
||||||
|
handle(response, 405, "Method not allowed");
|
||||||
|
}
|
||||||
|
}
|
||||||
6
src/main/java/org/dynmap/web/HttpField.java
Normal file
6
src/main/java/org/dynmap/web/HttpField.java
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
package org.dynmap.web;
|
||||||
|
|
||||||
|
public class HttpField {
|
||||||
|
public static final String contentLength = "Content-Length";
|
||||||
|
public static final String contentType = "Content-Type";
|
||||||
|
}
|
||||||
8
src/main/java/org/dynmap/web/HttpMethods.java
Normal file
8
src/main/java/org/dynmap/web/HttpMethods.java
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
package org.dynmap.web;
|
||||||
|
|
||||||
|
public class HttpMethods {
|
||||||
|
public static final String Get = "GET";
|
||||||
|
public static final String Post = "POST";
|
||||||
|
public static final String Put = "PUT";
|
||||||
|
public static final String Delete = "DELETE";
|
||||||
|
}
|
||||||
|
|
@ -6,6 +6,7 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class HttpResponse {
|
public class HttpResponse {
|
||||||
|
private HttpServerConnection connection;
|
||||||
public String version = "1.0";
|
public String version = "1.0";
|
||||||
public int statusCode = 200;
|
public int statusCode = 200;
|
||||||
public String statusMessage = "OK";
|
public String statusMessage = "OK";
|
||||||
|
|
@ -14,7 +15,7 @@ public class HttpResponse {
|
||||||
private OutputStream body;
|
private OutputStream body;
|
||||||
public OutputStream getBody() throws IOException {
|
public OutputStream getBody() throws IOException {
|
||||||
if (body != null) {
|
if (body != null) {
|
||||||
HttpServerConnection.writeResponseHeader(body, this);
|
connection.writeResponseHeader(this);
|
||||||
OutputStream b = body;
|
OutputStream b = body;
|
||||||
body = null;
|
body = null;
|
||||||
return b;
|
return b;
|
||||||
|
|
@ -22,7 +23,8 @@ public class HttpResponse {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HttpResponse(OutputStream body) {
|
public HttpResponse(HttpServerConnection connection, OutputStream body) {
|
||||||
|
this.connection = connection;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
package org.dynmap.web;
|
package org.dynmap.web;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.io.StringWriter;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
@ -13,26 +12,58 @@ import java.util.logging.Logger;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.dynmap.debug.Debug;
|
||||||
|
|
||||||
public class HttpServerConnection extends Thread {
|
public class HttpServerConnection extends Thread {
|
||||||
protected static final Logger log = Logger.getLogger("Minecraft");
|
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||||
|
|
||||||
|
private static Pattern requestHeaderLine = Pattern.compile("^(\\S+)\\s+(\\S+)\\s+HTTP/(.+)$");
|
||||||
|
private static Pattern requestHeaderField = Pattern.compile("^([^:]+):\\s*(.+)$");
|
||||||
|
|
||||||
private Socket socket;
|
private Socket socket;
|
||||||
private HttpServer server;
|
private HttpServer server;
|
||||||
|
|
||||||
|
private PrintStream printOut;
|
||||||
|
private StringWriter sw = new StringWriter();
|
||||||
|
private Matcher requestHeaderLineMatcher;
|
||||||
|
private Matcher requestHeaderFieldMatcher;
|
||||||
|
|
||||||
public HttpServerConnection(Socket socket, HttpServer server) {
|
public HttpServerConnection(Socket socket, HttpServer server) {
|
||||||
this.socket = socket;
|
this.socket = socket;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Pattern requestHeaderLine = Pattern.compile("^(\\S+)\\s+(\\S+)\\s+HTTP/(.+)$");
|
private final static void readLine(InputStream in, StringWriter sw) throws IOException {
|
||||||
private static Pattern requestHeaderField = Pattern.compile("^([^:]+):\\s*(.+)$");
|
int readc;
|
||||||
|
while((readc = in.read()) > 0) {
|
||||||
|
char c = (char)readc;
|
||||||
|
if (c == '\n')
|
||||||
|
break;
|
||||||
|
else if (c != '\r')
|
||||||
|
sw.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String readLine(InputStream in) throws IOException {
|
||||||
|
readLine(in, sw);
|
||||||
|
String r = sw.toString();
|
||||||
|
sw.getBuffer().setLength(0);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean readRequestHeader(InputStream in, HttpRequest request) throws IOException {
|
private final boolean readRequestHeader(InputStream in, HttpRequest request) throws IOException {
|
||||||
BufferedReader r = new BufferedReader(new InputStreamReader(in));
|
String statusLine = readLine(in);
|
||||||
String statusLine = r.readLine();
|
|
||||||
if (statusLine == null)
|
if (statusLine == null)
|
||||||
return false;
|
return false;
|
||||||
Matcher m = requestHeaderLine.matcher(statusLine);
|
|
||||||
|
if (requestHeaderLineMatcher == null) {
|
||||||
|
requestHeaderLineMatcher = requestHeaderLine.matcher(statusLine);
|
||||||
|
} else {
|
||||||
|
requestHeaderLineMatcher.reset(statusLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
Matcher m = requestHeaderLineMatcher;
|
||||||
if (!m.matches())
|
if (!m.matches())
|
||||||
return false;
|
return false;
|
||||||
request.method = m.group(1);
|
request.method = m.group(1);
|
||||||
|
|
@ -40,10 +71,14 @@ public class HttpServerConnection extends Thread {
|
||||||
request.version = m.group(3);
|
request.version = m.group(3);
|
||||||
|
|
||||||
String line;
|
String line;
|
||||||
while ((line = r.readLine()) != null) {
|
while (!(line = readLine(in)).equals("")) {
|
||||||
if (line.equals(""))
|
if (requestHeaderFieldMatcher == null) {
|
||||||
break;
|
requestHeaderFieldMatcher = requestHeaderField.matcher(line);
|
||||||
m = requestHeaderField.matcher(line);
|
} else {
|
||||||
|
requestHeaderFieldMatcher.reset(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
m = requestHeaderFieldMatcher;
|
||||||
// Warning: unknown lines are ignored.
|
// Warning: unknown lines are ignored.
|
||||||
if (m.matches()) {
|
if (m.matches()) {
|
||||||
String fieldName = m.group(1);
|
String fieldName = m.group(1);
|
||||||
|
|
@ -55,79 +90,119 @@ public class HttpServerConnection extends Thread {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeResponseHeader(OutputStream out, HttpResponse response) throws IOException {
|
public static final void writeResponseHeader(PrintStream out, HttpResponse response) throws IOException {
|
||||||
BufferedOutputStream o = new BufferedOutputStream(out);
|
out.append("HTTP/");
|
||||||
StringBuilder sb = new StringBuilder();
|
out.append(response.version);
|
||||||
sb.append("HTTP/");
|
out.append(" ");
|
||||||
sb.append(response.version);
|
out.append(String.valueOf(response.statusCode));
|
||||||
sb.append(" ");
|
out.append(" ");
|
||||||
sb.append(response.statusCode);
|
out.append(response.statusMessage);
|
||||||
sb.append(" ");
|
out.append("\r\n");
|
||||||
sb.append(response.statusMessage);
|
|
||||||
sb.append("\r\n");
|
|
||||||
for (Entry<String, String> field : response.fields.entrySet()) {
|
for (Entry<String, String> field : response.fields.entrySet()) {
|
||||||
sb.append(field.getKey());
|
out.append(field.getKey());
|
||||||
sb.append(": ");
|
out.append(": ");
|
||||||
sb.append(field.getValue());
|
out.append(field.getValue());
|
||||||
sb.append("\r\n");
|
out.append("\r\n");
|
||||||
}
|
}
|
||||||
sb.append("\r\n");
|
out.append("\r\n");
|
||||||
o.write(sb.toString().getBytes());
|
out.flush();
|
||||||
o.flush();
|
}
|
||||||
|
|
||||||
|
public final void writeResponseHeader(HttpResponse response) throws IOException {
|
||||||
|
writeResponseHeader(printOut, response);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
socket.setSoTimeout(5000);
|
socket.setSoTimeout(5000);
|
||||||
|
InputStream in = socket.getInputStream();
|
||||||
HttpRequest request = new HttpRequest();
|
OutputStream out = socket.getOutputStream();
|
||||||
if (!readRequestHeader(socket.getInputStream(), request)) {
|
|
||||||
socket.close();
|
printOut = new PrintStream(out);
|
||||||
return;
|
while (true) {
|
||||||
}
|
HttpRequest request = new HttpRequest();
|
||||||
|
|
||||||
// TODO: Optimize HttpHandler-finding by using a real path-aware
|
if (!readRequestHeader(in, request)) {
|
||||||
// tree.
|
|
||||||
HttpHandler handler = null;
|
|
||||||
String relativePath = null;
|
|
||||||
for (Entry<String, HttpHandler> entry : server.handlers.entrySet()) {
|
|
||||||
String key = entry.getKey();
|
|
||||||
boolean directoryHandler = key.endsWith("/");
|
|
||||||
if (directoryHandler && request.path.startsWith(entry.getKey()) || !directoryHandler && request.path.equals(entry.getKey())) {
|
|
||||||
relativePath = request.path.substring(entry.getKey().length());
|
|
||||||
handler = entry.getValue();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handler == null) {
|
|
||||||
socket.close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
HttpResponse response = new HttpResponse(socket.getOutputStream());
|
|
||||||
|
|
||||||
try {
|
|
||||||
handler.handle(relativePath, request, response);
|
|
||||||
} catch (IOException e) {
|
|
||||||
throw e;
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.log(Level.SEVERE, "HttpHandler '" + handler + "' has thown an exception", e);
|
|
||||||
if (socket != null) {
|
|
||||||
socket.close();
|
socket.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long bound = -1;
|
||||||
|
BoundInputStream boundBody = null;
|
||||||
|
{
|
||||||
|
String contentLengthStr = request.fields.get(HttpField.contentLength);
|
||||||
|
if (contentLengthStr != null) {
|
||||||
|
try {
|
||||||
|
bound = Long.parseLong(contentLengthStr);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
}
|
||||||
|
if (bound >= 0) {
|
||||||
|
request.body = boundBody = new BoundInputStream(in, bound);
|
||||||
|
} else {
|
||||||
|
request.body = in;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.fields.get("Content-Length") == null) {
|
// TODO: Optimize HttpHandler-finding by using a real path-aware tree.
|
||||||
response.fields.put("Content-Length", "0");
|
HttpHandler handler = null;
|
||||||
/* OutputStream out = */response.getBody();
|
String relativePath = null;
|
||||||
}
|
for (Entry<String, HttpHandler> entry : server.handlers.entrySet()) {
|
||||||
|
String key = entry.getKey();
|
||||||
|
boolean directoryHandler = key.endsWith("/");
|
||||||
|
if (directoryHandler && request.path.startsWith(entry.getKey()) || !directoryHandler && request.path.equals(entry.getKey())) {
|
||||||
|
relativePath = request.path.substring(entry.getKey().length());
|
||||||
|
handler = entry.getValue();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String connection = response.fields.get("Connection");
|
if (handler == null) {
|
||||||
if (connection != null && connection.equals("close")) {
|
socket.close();
|
||||||
socket.close();
|
return;
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
if (bound > 0) {
|
||||||
|
boundBody.skip(bound);
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse response = new HttpResponse(this, out);
|
||||||
|
|
||||||
|
try {
|
||||||
|
handler.handle(relativePath, request, response);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.log(Level.SEVERE, "HttpHandler '" + handler + "' has thown an exception", e);
|
||||||
|
if (socket != null) {
|
||||||
|
out.flush();
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String connection = response.fields.get("Connection");
|
||||||
|
String contentLength = response.fields.get("Content-Length");
|
||||||
|
if (contentLength == null && connection == null) {
|
||||||
|
response.fields.put("Content-Length", "0");
|
||||||
|
OutputStream responseBody = response.getBody();
|
||||||
|
|
||||||
|
// The HttpHandler has already send the headers and written to the body without setting the Content-Length.
|
||||||
|
if (responseBody == null) {
|
||||||
|
Debug.debug("Response was given without Content-Length by '" + handler + "' for path '" + request.path + "'.");
|
||||||
|
out.flush();
|
||||||
|
socket.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connection != null && connection.equals("close")) {
|
||||||
|
out.flush();
|
||||||
|
socket.close();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
out.flush();
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (socket != null) {
|
if (socket != null) {
|
||||||
|
|
@ -136,6 +211,7 @@ public class HttpServerConnection extends Thread {
|
||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (socket != null) {
|
if (socket != null) {
|
||||||
try {
|
try {
|
||||||
|
|
@ -145,6 +221,7 @@ public class HttpServerConnection extends Thread {
|
||||||
}
|
}
|
||||||
log.log(Level.SEVERE, "Exception while handling request: ", e);
|
log.log(Level.SEVERE, "Exception while handling request: ", e);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,11 @@ package org.dynmap.web.handlers;
|
||||||
|
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
import org.bukkit.Location;
|
||||||
|
import org.bukkit.Server;
|
||||||
import org.bukkit.World;
|
import org.bukkit.World;
|
||||||
import org.bukkit.entity.Player;
|
import org.bukkit.entity.Player;
|
||||||
import org.dynmap.Client;
|
import org.dynmap.Client;
|
||||||
|
|
@ -16,22 +20,36 @@ import org.dynmap.web.Json;
|
||||||
public class ClientUpdateHandler implements HttpHandler {
|
public class ClientUpdateHandler implements HttpHandler {
|
||||||
private MapManager mapManager;
|
private MapManager mapManager;
|
||||||
private PlayerList playerList;
|
private PlayerList playerList;
|
||||||
private World world;
|
private Server server;
|
||||||
|
|
||||||
public ClientUpdateHandler(MapManager mapManager, PlayerList playerList, World world) {
|
public ClientUpdateHandler(MapManager mapManager, PlayerList playerList, Server server) {
|
||||||
this.mapManager = mapManager;
|
this.mapManager = mapManager;
|
||||||
this.playerList = playerList;
|
this.playerList = playerList;
|
||||||
this.world = world;
|
this.server = server;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Pattern updatePathPattern = Pattern.compile("world/([a-zA-Z0-9_]+)/([0-9]*)");
|
||||||
@Override
|
@Override
|
||||||
public void handle(String path, HttpRequest request, HttpResponse response) throws Exception {
|
public void handle(String path, HttpRequest request, HttpResponse response) throws Exception {
|
||||||
|
|
||||||
|
Matcher match = updatePathPattern.matcher(path);
|
||||||
|
|
||||||
|
if (!match.matches())
|
||||||
|
return;
|
||||||
|
|
||||||
|
String worldName = match.group(1);
|
||||||
|
String timeKey = match.group(2);
|
||||||
|
|
||||||
|
World world = server.getWorld(worldName);
|
||||||
|
if (world == null)
|
||||||
|
return;
|
||||||
|
|
||||||
long current = System.currentTimeMillis();
|
long current = System.currentTimeMillis();
|
||||||
long cutoff = 0;
|
long since = 0;
|
||||||
|
|
||||||
if (path.length() > 0) {
|
if (path.length() > 0) {
|
||||||
try {
|
try {
|
||||||
cutoff = Long.parseLong(path);
|
since = Long.parseLong(timeKey);
|
||||||
} catch (NumberFormatException e) {
|
} catch (NumberFormatException e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -45,10 +63,12 @@ public class ClientUpdateHandler implements HttpHandler {
|
||||||
update.players = new Client.Player[players.length];
|
update.players = new Client.Player[players.length];
|
||||||
for(int i=0;i<players.length;i++) {
|
for(int i=0;i<players.length;i++) {
|
||||||
Player p = players[i];
|
Player p = players[i];
|
||||||
update.players[i] = new Client.Player(p.getName(), p.getLocation().getX(), p.getLocation().getY(), p.getLocation().getZ());
|
Location pl = p.getLocation();
|
||||||
|
update.players[i] = new Client.Player(p.getName(), pl.getWorld().getName(), pl.getX(), pl.getY(), pl.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
update.updates = mapManager.updateQueue.getUpdatedObjects(cutoff);
|
update.updates = mapManager.getWorldUpdates(worldName, since);
|
||||||
|
|
||||||
|
|
||||||
byte[] bytes = Json.stringifyJson(update).getBytes();
|
byte[] bytes = Json.stringifyJson(update).getBytes();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@ public abstract class FileHandler implements HttpHandler {
|
||||||
return "application/octet-steam";
|
return "application/octet-steam";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract InputStream getFileInput(String path);
|
protected abstract InputStream getFileInput(String path, HttpRequest request, HttpResponse response);
|
||||||
|
|
||||||
protected String getExtension(String path) {
|
protected String getExtension(String path) {
|
||||||
int dotindex = path.lastIndexOf('.');
|
int dotindex = path.lastIndexOf('.');
|
||||||
|
|
@ -60,10 +60,12 @@ public abstract class FileHandler implements HttpHandler {
|
||||||
InputStream fileInput = null;
|
InputStream fileInput = null;
|
||||||
try {
|
try {
|
||||||
path = formatPath(path);
|
path = formatPath(path);
|
||||||
fileInput = getFileInput(path);
|
fileInput = getFileInput(path, request, response);
|
||||||
if (fileInput == null) {
|
if (fileInput == null) {
|
||||||
response.statusCode = 404;
|
response.statusCode = 404;
|
||||||
response.statusMessage = "Not found";
|
response.statusMessage = "Not found";
|
||||||
|
response.fields.put("Content-Length", "0");
|
||||||
|
response.getBody();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,9 @@ import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.dynmap.web.HttpRequest;
|
||||||
|
import org.dynmap.web.HttpResponse;
|
||||||
|
|
||||||
|
|
||||||
public class FilesystemHandler extends FileHandler {
|
public class FilesystemHandler extends FileHandler {
|
||||||
private File root;
|
private File root;
|
||||||
|
|
@ -14,14 +17,17 @@ public class FilesystemHandler extends FileHandler {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected InputStream getFileInput(String path) {
|
protected InputStream getFileInput(String path, HttpRequest request, HttpResponse response) {
|
||||||
File file = new File(root, path);
|
File file = new File(root, path);
|
||||||
if (file.getAbsolutePath().startsWith(root.getAbsolutePath()) && file.isFile()) {
|
if (file.getAbsolutePath().startsWith(root.getAbsolutePath()) && file.isFile()) {
|
||||||
|
FileInputStream result;
|
||||||
try {
|
try {
|
||||||
return new FileInputStream(file);
|
result = new FileInputStream(file);
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
response.fields.put("Content-Length", Long.toString(file.length()));
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,9 @@ package org.dynmap.web.handlers;
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
import org.dynmap.web.HttpRequest;
|
||||||
|
import org.dynmap.web.HttpResponse;
|
||||||
|
|
||||||
|
|
||||||
public class JarFileHandler extends FileHandler {
|
public class JarFileHandler extends FileHandler {
|
||||||
private String root;
|
private String root;
|
||||||
|
|
@ -10,7 +13,7 @@ public class JarFileHandler extends FileHandler {
|
||||||
this.root = root;
|
this.root = root;
|
||||||
}
|
}
|
||||||
@Override
|
@Override
|
||||||
protected InputStream getFileInput(String path) {
|
protected InputStream getFileInput(String path, HttpRequest request, HttpResponse response) {
|
||||||
return this.getClass().getResourceAsStream(root + "/" + path);
|
return this.getClass().getResourceAsStream(root + "/" + path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
package org.dynmap.web.handlers;
|
||||||
|
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.dynmap.Event;
|
||||||
|
import org.dynmap.web.HttpErrorHandler;
|
||||||
|
import org.dynmap.web.HttpField;
|
||||||
|
import org.dynmap.web.HttpHandler;
|
||||||
|
import org.dynmap.web.HttpMethods;
|
||||||
|
import org.dynmap.web.HttpRequest;
|
||||||
|
import org.dynmap.web.HttpResponse;
|
||||||
|
import org.json.simple.JSONObject;
|
||||||
|
import org.json.simple.parser.JSONParser;
|
||||||
|
|
||||||
|
public class SendMessageHandler implements HttpHandler {
|
||||||
|
protected static final Logger log = Logger.getLogger("Minecraft");
|
||||||
|
|
||||||
|
private static final JSONParser parser = new JSONParser();
|
||||||
|
public Event<Message> onMessageReceived = new Event<SendMessageHandler.Message>();
|
||||||
|
@Override
|
||||||
|
public void handle(String path, HttpRequest request, HttpResponse response) throws Exception {
|
||||||
|
if (!request.method.equals(HttpMethods.Post)) {
|
||||||
|
HttpErrorHandler.handleMethodNotAllowed(response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InputStreamReader reader = new InputStreamReader(request.body);
|
||||||
|
|
||||||
|
JSONObject o = (JSONObject)parser.parse(reader);
|
||||||
|
Message message = new Message();
|
||||||
|
message.name = String.valueOf(o.get("name"));
|
||||||
|
message.message = String.valueOf(o.get("message"));
|
||||||
|
|
||||||
|
onMessageReceived.trigger(message);
|
||||||
|
|
||||||
|
response.fields.put(HttpField.contentLength, "0");
|
||||||
|
response.getBody();
|
||||||
|
}
|
||||||
|
public class Message {
|
||||||
|
public String name;
|
||||||
|
public String message;
|
||||||
|
}
|
||||||
|
}
|
||||||
42
web/clock.digital.js
Normal file
42
web/clock.digital.js
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
function MinecraftDigitalClock(element) {
|
||||||
|
this.create(element);
|
||||||
|
}
|
||||||
|
MinecraftDigitalClock.prototype = {
|
||||||
|
element: null,
|
||||||
|
timeout: null,
|
||||||
|
time: null,
|
||||||
|
create: function(element) {
|
||||||
|
this.element = element;
|
||||||
|
$(element).addClass('clock');
|
||||||
|
},
|
||||||
|
setTime: function(time) {
|
||||||
|
if (this.timeout != null) {
|
||||||
|
window.clearTimeout(this.timeout);
|
||||||
|
this.timeout = null;
|
||||||
|
}
|
||||||
|
this.time = getMinecraftTime(time);
|
||||||
|
this.element
|
||||||
|
.addClass(this.time.day ? 'day' : 'night')
|
||||||
|
.removeClass(this.time.night ? 'day' : 'night')
|
||||||
|
.text(this.formatTime(this.time));
|
||||||
|
|
||||||
|
if (this.timeout == null) {
|
||||||
|
var me = this;
|
||||||
|
this.timeout = window.setTimeout(function() {
|
||||||
|
me.timeout = null;
|
||||||
|
me.setTime(me.time.servertime+(1000/60));
|
||||||
|
}, 700);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatTime: function(time) {
|
||||||
|
var formatDigits = function(n, digits) {
|
||||||
|
var s = n.toString();
|
||||||
|
while (s.length < digits) {
|
||||||
|
s = '0' + s;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
return formatDigits(time.hours, 2) + ':' + formatDigits(time.minutes, 2);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
clocks.digital = function(element) { return new MinecraftDigitalClock(element); };
|
||||||
59
web/clock.timeofday.js
Normal file
59
web/clock.timeofday.js
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
function MinecraftTimeOfDay(element,elementsun,elementmoon) {
|
||||||
|
this.create(element, elementsun, elementmoon);
|
||||||
|
}
|
||||||
|
MinecraftTimeOfDay.prototype = {
|
||||||
|
element: null,
|
||||||
|
elementsun: null,
|
||||||
|
elementmoon: null,
|
||||||
|
create: function(element,elementsun,elementmoon) {
|
||||||
|
if (!element) element = $('<div/>');
|
||||||
|
this.element = element;
|
||||||
|
|
||||||
|
if (!elementsun) elementsun = $('<div/>');
|
||||||
|
this.elementsun = elementsun;
|
||||||
|
this.elementsun.appendTo(this.element);
|
||||||
|
if (!elementmoon) elementmoon = $('<div/>');
|
||||||
|
this.elementmoon = elementmoon;
|
||||||
|
this.elementmoon.appendTo(this.elementsun);
|
||||||
|
this.element.height(60);
|
||||||
|
this.element.addClass('timeofday');
|
||||||
|
this.elementsun.height(60);
|
||||||
|
this.elementsun.addClass('timeofday');
|
||||||
|
this.elementsun.addClass('sun');
|
||||||
|
this.elementmoon.height(60);
|
||||||
|
this.elementmoon.addClass('timeofday');
|
||||||
|
this.elementmoon.addClass('moon');
|
||||||
|
this.elementmoon.html(" ‏ ");
|
||||||
|
this.elementsun.css("background-position", (-120) + "px " + (-120) + "px");
|
||||||
|
this.elementmoon.css("background-position", (-120) + "px " + (-120) + "px");
|
||||||
|
|
||||||
|
return element;
|
||||||
|
},
|
||||||
|
setTime: function(time) {
|
||||||
|
var sunangle;
|
||||||
|
|
||||||
|
if(time > 23100 || time < 12900)
|
||||||
|
{
|
||||||
|
//day mode
|
||||||
|
var movedtime = time + 900;
|
||||||
|
movedtime = (movedtime >= 24000) ? movedtime - 24000 : movedtime;
|
||||||
|
//Now we have 0 -> 13800 for the day period
|
||||||
|
//Devide by 13800*2=27600 instead of 24000 to compress day
|
||||||
|
sunangle = ((movedtime)/27600 * 2 * Math.PI);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//night mode
|
||||||
|
var movedtime = time - 12900;
|
||||||
|
//Now we have 0 -> 10200 for the night period
|
||||||
|
//Devide by 10200*2=20400 instead of 24000 to expand night
|
||||||
|
sunangle = Math.PI + ((movedtime)/20400 * 2 * Math.PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
var moonangle = sunangle + Math.PI;
|
||||||
|
|
||||||
|
this.elementsun.css("background-position", (-50 * Math.cos(sunangle)) + "px " + (-50 * Math.sin(sunangle)) + "px");
|
||||||
|
this.elementmoon.css("background-position", (-50 * Math.cos(moonangle)) + "px " + (-50 * Math.sin(moonangle)) + "px");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
clocks.timeofday = function(element) { return new MinecraftTimeOfDay(element); };
|
||||||
BIN
web/compass.png
Normal file
BIN
web/compass.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.7 KiB |
|
|
@ -17,7 +17,9 @@
|
||||||
<script type="text/javascript" src="custommarker.js"></script>
|
<script type="text/javascript" src="custommarker.js"></script>
|
||||||
<script type="text/javascript" src="minecraft.js"></script>
|
<script type="text/javascript" src="minecraft.js"></script>
|
||||||
<script type="text/javascript" src="map.js"></script>
|
<script type="text/javascript" src="map.js"></script>
|
||||||
<script type="text/javascript" src="kzedmaps.js"></script>
|
<script type="text/javascript" src="kzedmaps.js"></script>
|
||||||
|
<script type="text/javascript" src="clock.timeofday.js"></script>
|
||||||
|
<script type="text/javascript" src="clock.digital.js"></script>
|
||||||
<script type="text/javascript" src="config.js"></script>
|
<script type="text/javascript" src="config.js"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ KzedMapType.prototype = $.extend(new DynMapType(), {
|
||||||
|
|
||||||
tileSize = 128;
|
tileSize = 128;
|
||||||
imgSize = tileSize;
|
imgSize = tileSize;
|
||||||
tileName = 'z' + this.prefix + '_' + (-coord.x * tileSize*2) + '_' + (coord.y * tileSize*2);
|
tileName = 'z' + this.prefix + '_' + (-coord.x * tileSize*2) + '_' + (coord.y * tileSize*2) + '.png';
|
||||||
} else {
|
} else {
|
||||||
// Other zoom levels.
|
// Other zoom levels.
|
||||||
tileSize = 128;
|
tileSize = 128;
|
||||||
|
|
@ -72,7 +72,7 @@ KzedMapType.prototype = $.extend(new DynMapType(), {
|
||||||
tileSize = imgSize;
|
tileSize = imgSize;
|
||||||
|
|
||||||
if (offset.x == 0 && offset.y == 0) {
|
if (offset.x == 0 && offset.y == 0) {
|
||||||
tileName = this.prefix + '_' + (-mapcoord.x) + '_' + mapcoord.y;
|
tileName = this.prefix + '_' + (-mapcoord.x) + '_' + mapcoord.y + '.png';
|
||||||
}
|
}
|
||||||
offset = {x: 0, y: 0};
|
offset = {x: 0, y: 0};
|
||||||
// The next line is not:
|
// The next line is not:
|
||||||
|
|
|
||||||
105
web/map.js
105
web/map.js
|
|
@ -1,6 +1,7 @@
|
||||||
//if (!console) console = { log: function() {} };
|
//if (!console) console = { log: function() {} };
|
||||||
|
|
||||||
var maptypes = {};
|
var maptypes = {};
|
||||||
|
var clocks = {};
|
||||||
|
|
||||||
function splitArgs(s) {
|
function splitArgs(s) {
|
||||||
var r = s.split(' ');
|
var r = s.split(' ');
|
||||||
|
|
@ -28,44 +29,17 @@ DynMapType.prototype = {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
function MinecraftClock(element) { this.element = element; }
|
function MinecraftCompass(element) { this.element = element; }
|
||||||
MinecraftClock.prototype = {
|
MinecraftCompass.prototype = {
|
||||||
element: null,
|
element: null,
|
||||||
timeout: null,
|
|
||||||
time: null,
|
|
||||||
create: function(element) {
|
create: function(element) {
|
||||||
if (!element) element = $('<div/>');
|
if (!element) element = $('<div/>');
|
||||||
this.element = element;
|
this.element = element;
|
||||||
return element;
|
return element;
|
||||||
},
|
},
|
||||||
setTime: function(time) {
|
initialize: function() {
|
||||||
if (this.timeout != null) {
|
this.element.html(" ‏ ");
|
||||||
window.clearTimeout(this.timeout);
|
this.element.height(120);
|
||||||
this.timeout = null;
|
|
||||||
}
|
|
||||||
this.time = time;
|
|
||||||
this.element
|
|
||||||
.addClass(time.day ? 'day' : 'night')
|
|
||||||
.removeClass(time.night ? 'day' : 'night')
|
|
||||||
.text(this.formatTime(time));
|
|
||||||
|
|
||||||
if (this.timeout == null) {
|
|
||||||
var me = this;
|
|
||||||
this.timeout = window.setTimeout(function() {
|
|
||||||
me.timeout = null;
|
|
||||||
me.setTime(getMinecraftTime(me.time.servertime+(1000/60)));
|
|
||||||
}, 700);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
formatTime: function(time) {
|
|
||||||
var formatDigits = function(n, digits) {
|
|
||||||
var s = n.toString();
|
|
||||||
while (s.length < digits) {
|
|
||||||
s = '0' + s;
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
return formatDigits(time.hours, 2) + ':' + formatDigits(time.minutes, 2);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -79,7 +53,6 @@ function DynMap(options) {
|
||||||
}
|
}
|
||||||
DynMap.prototype = {
|
DynMap.prototype = {
|
||||||
registeredTiles: new Array(),
|
registeredTiles: new Array(),
|
||||||
clock: null,
|
|
||||||
markers: new Array(),
|
markers: new Array(),
|
||||||
chatPopups: new Array(),
|
chatPopups: new Array(),
|
||||||
lasttimestamp: '0',
|
lasttimestamp: '0',
|
||||||
|
|
@ -91,6 +64,7 @@ DynMap.prototype = {
|
||||||
$.each(me.options.shownmaps, function(index, mapentry) {
|
$.each(me.options.shownmaps, function(index, mapentry) {
|
||||||
me.options.maps[mapentry.name] = maptypes[mapentry.type](mapentry);
|
me.options.maps[mapentry.name] = maptypes[mapentry.type](mapentry);
|
||||||
});
|
});
|
||||||
|
me.world = me.options.defaultworld;
|
||||||
},
|
},
|
||||||
initialize: function() {
|
initialize: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
@ -131,11 +105,48 @@ DynMap.prototype = {
|
||||||
.addClass('sidebar')
|
.addClass('sidebar')
|
||||||
.appendTo(container);
|
.appendTo(container);
|
||||||
|
|
||||||
|
// The world list.
|
||||||
|
var worldlist = me.worldlist = $('<div/>')
|
||||||
|
.addClass('worldlist')
|
||||||
|
.appendTo(sidebar);
|
||||||
|
|
||||||
|
$.each(me.options.shownworlds, function(index, name) {
|
||||||
|
var worldButton;
|
||||||
|
$('<div/>')
|
||||||
|
.addClass('worldrow')
|
||||||
|
.append(worldButton = $('<input/>')
|
||||||
|
.addClass('worldbutton')
|
||||||
|
.addClass('world_' + name)
|
||||||
|
.attr({
|
||||||
|
type: 'radio',
|
||||||
|
name: 'world',
|
||||||
|
value: name
|
||||||
|
})
|
||||||
|
.attr('checked', me.options.defaultworld == name ? 'checked' : null)
|
||||||
|
)
|
||||||
|
.append($('<label/>')
|
||||||
|
.attr('for', 'worldbutton_' + name)
|
||||||
|
.text(name)
|
||||||
|
)
|
||||||
|
.click(function() {
|
||||||
|
$('.worldbutton', worldlist).removeAttr('checked');
|
||||||
|
map.setMapTypeId('none');
|
||||||
|
me.world = name;
|
||||||
|
// Another workaround for GMaps not being able to reload tiles.
|
||||||
|
window.setTimeout(function() {
|
||||||
|
map.setMapTypeId(me.options.defaultmap);
|
||||||
|
}, 1);
|
||||||
|
worldButton.attr('checked', 'checked');
|
||||||
|
})
|
||||||
|
.data('world', name)
|
||||||
|
.appendTo(worldlist);
|
||||||
|
});
|
||||||
|
|
||||||
// The map list.
|
// The map list.
|
||||||
var maplist = me.maplist = $('<div/>')
|
var maplist = me.maplist = $('<div/>')
|
||||||
.addClass('maplist')
|
.addClass('maplist')
|
||||||
.appendTo(sidebar);
|
.appendTo(sidebar);
|
||||||
|
|
||||||
$.each(me.options.maps, function(name, mapType){
|
$.each(me.options.maps, function(name, mapType){
|
||||||
mapType.dynmap = me;
|
mapType.dynmap = me;
|
||||||
map.mapTypes.set(name, mapType);
|
map.mapTypes.set(name, mapType);
|
||||||
|
|
@ -144,11 +155,12 @@ DynMap.prototype = {
|
||||||
$('<div/>')
|
$('<div/>')
|
||||||
.addClass('maprow')
|
.addClass('maprow')
|
||||||
.append(mapButton = $('<input/>')
|
.append(mapButton = $('<input/>')
|
||||||
|
.addClass('mapbutton')
|
||||||
.addClass('maptype_' + name)
|
.addClass('maptype_' + name)
|
||||||
.attr({
|
.attr({
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
name: 'map',
|
name: 'map',
|
||||||
id: 'maptypebutton_' + name
|
value: name
|
||||||
})
|
})
|
||||||
.attr('checked', me.options.defaultmap == name ? 'checked' : null)
|
.attr('checked', me.options.defaultmap == name ? 'checked' : null)
|
||||||
)
|
)
|
||||||
|
|
@ -171,13 +183,20 @@ DynMap.prototype = {
|
||||||
.addClass('playerlist')
|
.addClass('playerlist')
|
||||||
.appendTo(sidebar);
|
.appendTo(sidebar);
|
||||||
|
|
||||||
// The Clock
|
// The clock
|
||||||
var clock = me.clock = new MinecraftClock(
|
var clock = me.clock = clocks[me.options.clock](
|
||||||
$('<div/>')
|
$('<div/>')
|
||||||
.addClass('clock')
|
|
||||||
.appendTo(sidebar)
|
.appendTo(sidebar)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// The Compass
|
||||||
|
var compass = me.compass = new MinecraftCompass(
|
||||||
|
$('<div/>')
|
||||||
|
.addClass('compass')
|
||||||
|
.appendTo(sidebar)
|
||||||
|
);
|
||||||
|
compass.initialize();
|
||||||
|
|
||||||
// TODO: Enable hash-links.
|
// TODO: Enable hash-links.
|
||||||
/*
|
/*
|
||||||
var link;
|
var link;
|
||||||
|
|
@ -199,11 +218,11 @@ DynMap.prototype = {
|
||||||
// TODO: is there a better place for this?
|
// TODO: is there a better place for this?
|
||||||
this.cleanPopups();
|
this.cleanPopups();
|
||||||
|
|
||||||
$.getJSON(me.options.updateUrl + me.lasttimestamp, function(update) {
|
$.getJSON(me.options.updateUrl + "world/" + me.world + "/" + me.lasttimestamp, function(update) {
|
||||||
me.alertbox.hide();
|
me.alertbox.hide();
|
||||||
|
|
||||||
me.lasttimestamp = update.timestamp;
|
me.lasttimestamp = update.timestamp;
|
||||||
me.clock.setTime(getMinecraftTime(update.servertime));
|
me.clock.setTime(update.servertime);
|
||||||
|
|
||||||
var typeVisibleMap = {};
|
var typeVisibleMap = {};
|
||||||
var newmarkers = {};
|
var newmarkers = {};
|
||||||
|
|
@ -300,7 +319,7 @@ DynMap.prototype = {
|
||||||
popup.popupTime = now.getTime();
|
popup.popupTime = now.getTime();
|
||||||
if (!popup.infoWindow) {
|
if (!popup.infoWindow) {
|
||||||
popup.infoWindow = new google.maps.InfoWindow({
|
popup.infoWindow = new google.maps.InfoWindow({
|
||||||
disableAutoPan: me.options.focuschatballoons || false,
|
disableAutoPan: !(me.options.focuschatballoons || false),
|
||||||
content: htmlMessage
|
content: htmlMessage
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -415,9 +434,9 @@ DynMap.prototype = {
|
||||||
var tile = me.registeredTiles[tileName];
|
var tile = me.registeredTiles[tileName];
|
||||||
|
|
||||||
if(tile) {
|
if(tile) {
|
||||||
return me.options.tileUrl + tileName + '.png?' + tile.lastseen;
|
return me.options.tileUrl + me.world + '/' + tileName + '?' + tile.lastseen;
|
||||||
} else {
|
} else {
|
||||||
return me.options.tileUrl + tileName + '.png?0';
|
return me.options.tileUrl + me.world + '/' + tileName + '?0';
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
registerTile: function(mapType, tileName, tile) {
|
registerTile: function(mapType, tileName, tile) {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ function blitImage(ctx, image, sx ,sy, sw, sh, dx, dy, dw, dh) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createMinecraftHead(player,completed) {
|
function createMinecraftHead(player,completed,failed) {
|
||||||
var skinImage = new Image();
|
var skinImage = new Image();
|
||||||
skinImage.onload = function() {
|
skinImage.onload = function() {
|
||||||
var headCanvas = document.createElement('canvas');
|
var headCanvas = document.createElement('canvas');
|
||||||
|
|
@ -27,6 +27,13 @@ function createMinecraftHead(player,completed) {
|
||||||
blitImage(headContext, skinImage, 40,8,8,8, 0,0,8,8);
|
blitImage(headContext, skinImage, 40,8,8,8, 0,0,8,8);
|
||||||
completed(headCanvas);
|
completed(headCanvas);
|
||||||
};
|
};
|
||||||
|
skinImage.onerror = function() {
|
||||||
|
if (skinImage.src == 'http://www.minecraft.net/img/char.png') {
|
||||||
|
failed();
|
||||||
|
} else {
|
||||||
|
skinImage.src = 'http://www.minecraft.net/img/char.png';
|
||||||
|
}
|
||||||
|
};
|
||||||
skinImage.src = 'http://www.minecraft.net/skin/' + player + '.png';
|
skinImage.src = 'http://www.minecraft.net/skin/' + player + '.png';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -60,6 +67,8 @@ function getMinecraftHead(player,size,completed) {
|
||||||
for(i=0;i<hooks.length;i++) {
|
for(i=0;i<hooks.length;i++) {
|
||||||
hooks[i].f(resizeImage(head,hooks[i].s));
|
hooks[i].f(resizeImage(head,hooks[i].s));
|
||||||
}
|
}
|
||||||
|
}, function() {
|
||||||
|
|
||||||
});
|
});
|
||||||
} else if (head.working) {
|
} else if (head.working) {
|
||||||
//console.log('Other process working on head of ',player,', will add myself to hooks...');
|
//console.log('Other process working on head of ',player,', will add myself to hooks...');
|
||||||
|
|
@ -84,4 +93,4 @@ function getMinecraftTime(servertime) {
|
||||||
day: day,
|
day: day,
|
||||||
night: !day
|
night: !day
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
BIN
web/moon.png
Normal file
BIN
web/moon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
|
|
@ -2,6 +2,11 @@ html { height: 100% }
|
||||||
body { height: 100%; margin: 0px; padding: 0px ; background-color: #000; }
|
body { height: 100%; margin: 0px; padding: 0px ; background-color: #000; }
|
||||||
#mcmap { width:100%; height: 100% }
|
#mcmap { width:100%; height: 100% }
|
||||||
|
|
||||||
|
/* hide gmaps copyrights */
|
||||||
|
div.map > DIV > DIV:first-child + DIV { visibility: hidden !important; }
|
||||||
|
div.map > DIV > DIV:first-child + DIV + DIV { visibility: hidden !important; }
|
||||||
|
|
||||||
|
|
||||||
.map {
|
.map {
|
||||||
width: 100%; height: 100%;
|
width: 100%; height: 100%;
|
||||||
background-color: black;
|
background-color: black;
|
||||||
|
|
@ -77,6 +82,18 @@ a, a:visited, label {
|
||||||
.clock.night { background-image: url(clock_night.png); }
|
.clock.night { background-image: url(clock_night.png); }
|
||||||
.clock.day { background-image: url(clock_day.png); }
|
.clock.day { background-image: url(clock_day.png); }
|
||||||
|
|
||||||
|
.compass {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-image: url(compass.png);
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeofday {
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
|
.timeofday.sun { background-image: url(sun.png); }
|
||||||
|
.timeofday.moon { background-image: url(moon.png); }
|
||||||
|
|
||||||
.alertbox {
|
.alertbox {
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
|
|
||||||
BIN
web/sun.png
Normal file
BIN
web/sun.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.1 KiB |
Loading…
Add table
Add a link
Reference in a new issue