diff --git a/src/main/java/org/dynmap/Cache.java b/src/main/java/org/dynmap/Cache.java index 0093759a..056681e9 100644 --- a/src/main/java/org/dynmap/Cache.java +++ b/src/main/java/org/dynmap/Cache.java @@ -2,115 +2,110 @@ package org.dynmap; import java.util.HashMap; -public class Cache -{ - private final int size; - private int len; +public class Cache { + private final int size; + private int len; - private CacheNode head; - private CacheNode tail; + private CacheNode head; + private CacheNode tail; - private class CacheNode - { - public CacheNode prev; - public CacheNode next; - public K key; - public V value; + private class CacheNode { + public CacheNode prev; + public CacheNode next; + public K key; + public V value; - public CacheNode(K key, V value) - { - this.key = key; - this.value = value; - prev = null; - next = null; - } + public CacheNode(K key, V value) { + this.key = key; + this.value = value; + prev = null; + next = null; + } - public void unlink() - { - if(prev == null) { - head = next; - } else { - prev.next = next; - } + public void unlink() { + if (prev == null) { + head = next; + } else { + prev.next = next; + } - if(next == null) { - tail = prev; - } else { - next.prev = prev; - } + if (next == null) { + tail = prev; + } else { + next.prev = prev; + } - prev = null; - next = null; + prev = null; + next = null; - len --; - } + len--; + } - public void append() - { - if(tail == null) { - head = this; - tail = this; - } else { - tail.next = this; - prev = tail; - tail = this; - } + public void append() { + if (tail == null) { + head = this; + tail = this; + } else { + tail.next = this; + prev = tail; + tail = this; + } - len ++; - } - } + len++; + } + } - private HashMap map; + private HashMap map; - public Cache(int size) - { - this.size = size; - len = 0; + public Cache(int size) { + this.size = size; + len = 0; - head = null; - tail = null; + head = null; + tail = null; - map = new HashMap(); - } + map = new HashMap(); + } - /* returns value for key, if key exists in the cache - * otherwise null */ - public V get(K key) - { - CacheNode n = map.get(key); - if(n == null) - return null; - return n.value; - } + /* + * returns value for key, if key exists in the cache otherwise null + */ + public V get(K key) { + CacheNode n = map.get(key); + if (n == null) + return null; + return n.value; + } - /* puts a new key-value pair in the cache - * if the key existed already, the value is updated, and the old value is returned - * if the key didn't exist, it is added; the oldest value (now pushed out of the - * cache) may be returned, or null if the cache isn't yet full */ - public V put(K key, V value) - { - CacheNode n = map.get(key); - if(n == null) { - V ret = null; + /* + * puts a new key-value pair in the cache if the key existed already, the + * value is updated, and the old value is returned if the key didn't exist, + * it is added; the oldest value (now pushed out of the cache) may be + * returned, or null if the cache isn't yet full + */ + public V put(K key, V value) { + CacheNode n = map.get(key); + if (n == null) { + V ret = null; - if(len >= size) { - CacheNode first = head; - first.unlink(); - map.remove(first.key); - ret = first.value; - } + if (len >= size) { + CacheNode first = head; + first.unlink(); + map.remove(first.key); + ret = first.value; + } - CacheNode add = new CacheNode(key, value); - add.append(); - map.put(key, add); + CacheNode add = new CacheNode(key, value); + add.append(); + map.put(key, add); - return ret; - } else { - n.unlink(); - V old = n.value; - n.value = value; - n.append(); - return old; - } - } + return ret; + } else { + n.unlink(); + V old = n.value; + n.value = value; + n.append(); + return old; + } + } } diff --git a/src/main/java/org/dynmap/ChatQueue.java b/src/main/java/org/dynmap/ChatQueue.java index 1cee4fda..d0fb1df4 100644 --- a/src/main/java/org/dynmap/ChatQueue.java +++ b/src/main/java/org/dynmap/ChatQueue.java @@ -6,65 +6,58 @@ import java.util.LinkedList; import org.bukkit.event.player.PlayerChatEvent; public class ChatQueue { - - public class ChatMessage - { - public long time; - public String playerName; - public String message; - - public ChatMessage(PlayerChatEvent event) - { - time = System.currentTimeMillis(); - playerName = event.getPlayer().getName(); - message = event.getMessage(); - } - } - - /* a list of recent chat message */ - private LinkedList messageQueue; - - /* remember up to this old chat messages (ms) */ - private static final int maxChatAge = 120000; - - public ChatQueue() { - messageQueue = new LinkedList(); - } - - /* put a chat message in the queue */ - public void pushChatMessage(PlayerChatEvent event) - { - synchronized(MapManager.lock) { - messageQueue.add(new ChatMessage(event)); - } - } - - public ChatMessage[] getChatMessages(long cutoff) { - ArrayList queue = new ArrayList(); - ArrayList updateList = new ArrayList(); - queue.addAll(messageQueue); - - long now = System.currentTimeMillis(); - long deadline = now - maxChatAge; - - synchronized(MapManager.lock) { - - for (ChatMessage message : queue) - { - if (message.time < deadline) - { - messageQueue.remove(message); - } - else if (message.time >= cutoff) - { - updateList.add(message); - } - } - } - ChatMessage[] messages = new ChatMessage[updateList.size()]; - updateList.toArray(messages); - return messages; - } + public class ChatMessage { + public long time; + public String playerName; + public String message; + + public ChatMessage(PlayerChatEvent event) { + time = System.currentTimeMillis(); + playerName = event.getPlayer().getName(); + message = event.getMessage(); + } + } + + /* a list of recent chat message */ + private LinkedList messageQueue; + + /* remember up to this old chat messages (ms) */ + private static final int maxChatAge = 120000; + + public ChatQueue() { + messageQueue = new LinkedList(); + } + + /* put a chat message in the queue */ + public void pushChatMessage(PlayerChatEvent event) { + synchronized (MapManager.lock) { + messageQueue.add(new ChatMessage(event)); + } + } + + public ChatMessage[] getChatMessages(long cutoff) { + + ArrayList queue = new ArrayList(); + ArrayList updateList = new ArrayList(); + queue.addAll(messageQueue); + + long now = System.currentTimeMillis(); + long deadline = now - maxChatAge; + + synchronized (MapManager.lock) { + + for (ChatMessage message : queue) { + if (message.time < deadline) { + messageQueue.remove(message); + } else if (message.time >= cutoff) { + updateList.add(message); + } + } + } + ChatMessage[] messages = new ChatMessage[updateList.size()]; + updateList.toArray(messages); + return messages; + } } diff --git a/src/main/java/org/dynmap/DynmapBlockListener.java b/src/main/java/org/dynmap/DynmapBlockListener.java index 9af19904..f1cf4367 100644 --- a/src/main/java/org/dynmap/DynmapBlockListener.java +++ b/src/main/java/org/dynmap/DynmapBlockListener.java @@ -7,22 +7,22 @@ import org.bukkit.event.block.BlockListener; import org.bukkit.event.block.BlockPlaceEvent; public class DynmapBlockListener extends BlockListener { - private MapManager mgr; - - public DynmapBlockListener(MapManager mgr) { - this.mgr = mgr; - } + private MapManager mgr; - @Override - public void onBlockPlace(BlockPlaceEvent event) { - Block blockPlaced = event.getBlockPlaced(); - mgr.touch(blockPlaced.getX(), blockPlaced.getY(), blockPlaced.getZ()); - } + public DynmapBlockListener(MapManager mgr) { + this.mgr = mgr; + } - public void onBlockDamage(BlockDamageEvent event) { - if (event.getDamageLevel() == BlockDamageLevel.BROKEN) { - Block blockBroken = event.getBlock(); - mgr.touch(blockBroken.getX(), blockBroken.getY(), blockBroken.getZ()); - } - } + @Override + public void onBlockPlace(BlockPlaceEvent event) { + Block blockPlaced = event.getBlockPlaced(); + mgr.touch(blockPlaced.getX(), blockPlaced.getY(), blockPlaced.getZ()); + } + + public void onBlockDamage(BlockDamageEvent event) { + if (event.getDamageLevel() == BlockDamageLevel.BROKEN) { + Block blockBroken = event.getBlock(); + mgr.touch(blockBroken.getX(), blockBroken.getY(), blockBroken.getZ()); + } + } } diff --git a/src/main/java/org/dynmap/DynmapChunk.java b/src/main/java/org/dynmap/DynmapChunk.java index 2b0c49e2..82afb6ee 100644 --- a/src/main/java/org/dynmap/DynmapChunk.java +++ b/src/main/java/org/dynmap/DynmapChunk.java @@ -1,9 +1,10 @@ package org.dynmap; public class DynmapChunk { - public int x,y; - public DynmapChunk(int x, int y) { - this.x = x; - this.y = y; - } + public int x, y; + + public DynmapChunk(int x, int y) { + this.x = x; + this.y = y; + } } diff --git a/src/main/java/org/dynmap/DynmapPlayerListener.java b/src/main/java/org/dynmap/DynmapPlayerListener.java index ab12ed05..b11bc4a3 100644 --- a/src/main/java/org/dynmap/DynmapPlayerListener.java +++ b/src/main/java/org/dynmap/DynmapPlayerListener.java @@ -5,53 +5,59 @@ import org.bukkit.event.player.PlayerChatEvent; import org.bukkit.event.player.PlayerListener; public class DynmapPlayerListener extends PlayerListener { - private MapManager mgr; - private PlayerList playerList; - - public DynmapPlayerListener(MapManager mgr, PlayerList playerList) { - this.mgr = mgr; - this.playerList = playerList; - } - - @Override - public void onPlayerCommand(PlayerChatEvent event) { - String[] split = event.getMessage().split(" "); + private MapManager mgr; + private PlayerList playerList; + + public DynmapPlayerListener(MapManager mgr, PlayerList playerList) { + this.mgr = mgr; + this.playerList = playerList; + } + + @Override + public void onPlayerCommand(PlayerChatEvent event) { + String[] split = event.getMessage().split(" "); if (split[0].equalsIgnoreCase("/dynmap")) { - if (split.length > 1) { - if (split[1].equals("render")) { - Player player = event.getPlayer(); - mgr.touch(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ()); - event.setCancelled(true); - } else if (split[1].equals("hide")) { - if (split.length == 2) { - playerList.hide(event.getPlayer().getName()); - } else for (int i=2;i 1) { + if (split[1].equals("render")) { + Player player = event.getPlayer(); + mgr.touch(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ()); + event.setCancelled(true); + } else if (split[1].equals("hide")) { + if (split.length == 2) { + playerList.hide(event.getPlayer().getName()); + } else { + for (int i = 2; i < split.length; i++) { + playerList.hide(split[i]); + } + } + event.setCancelled(true); + } else if (split[1].equals("show")) { + if (split.length == 2) { + playerList.show(event.getPlayer().getName()); + } else { + for (int i = 2; i < split.length; i++) { + playerList.show(split[i]); + } + } + event.setCancelled(true); + } else if (split[1].equals("fullrender")) { + Player player = event.getPlayer(); + mgr.renderFullWorld(player.getLocation()); + } else if (split[1].equals("fullrenderasync")) { + Player player = event.getPlayer(); + mgr.renderFullWorldAsync(player.getLocation()); + } + } } - } - + } + /** * Called when a player sends a chat message - * - * @param event Relevant event details + * + * @param event + * Relevant event details */ - public void onPlayerChat(PlayerChatEvent event) - { - mgr.addChatEvent(event); + public void onPlayerChat(PlayerChatEvent event) { + mgr.addChatEvent(event); } } \ No newline at end of file diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index e8fd3955..89c02a93 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -16,69 +16,69 @@ import org.dynmap.web.WebServer; public class DynmapPlugin extends JavaPlugin { - protected static final Logger log = Logger.getLogger("Minecraft"); + protected static final Logger log = Logger.getLogger("Minecraft"); - private WebServer webServer = null; - private MapManager mapManager = null; - private PlayerList playerList; - - private BukkitPlayerDebugger debugger = new BukkitPlayerDebugger(this); - - public static File dataRoot; - - public DynmapPlugin(PluginLoader pluginLoader, Server instance, PluginDescriptionFile desc, File folder, File plugin, ClassLoader cLoader) { - super(pluginLoader, instance, desc, folder, plugin, cLoader); - dataRoot = folder; - } + private WebServer webServer = null; + private MapManager mapManager = null; + private PlayerList playerList; - public World getWorld() { - return getServer().getWorlds()[0]; - } - - public MapManager getMapManager() { - return mapManager; - } - - public WebServer getWebServer() { - return webServer; - } + private BukkitPlayerDebugger debugger = new BukkitPlayerDebugger(this); - public void onEnable() { - Configuration configuration = new Configuration(new File(this.getDataFolder(), "configuration.txt")); - configuration.load(); - - debugger.enable(); - playerList = new PlayerList(getServer()); - playerList.load(); - - mapManager = new MapManager(getWorld(), debugger, configuration); - mapManager.startManager(); + public static File dataRoot; - try { - webServer = new WebServer(mapManager, getWorld(), playerList, debugger, configuration); - } catch(IOException e) { - log.info("position failed to start WebServer (IOException)"); - } - - registerEvents(); - } + public DynmapPlugin(PluginLoader pluginLoader, Server instance, PluginDescriptionFile desc, File folder, File plugin, ClassLoader cLoader) { + super(pluginLoader, instance, desc, folder, plugin, cLoader); + dataRoot = folder; + } - public void onDisable() { - mapManager.stopManager(); + public World getWorld() { + return getServer().getWorlds()[0]; + } - if(webServer != null) { - webServer.shutdown(); - webServer = null; - } - debugger.disable(); - } + public MapManager getMapManager() { + return mapManager; + } - public void registerEvents() { - BlockListener blockListener = new DynmapBlockListener(mapManager); - getServer().getPluginManager().registerEvent(Event.Type.BLOCK_PLACED, blockListener, Priority.Normal, this); - getServer().getPluginManager().registerEvent(Event.Type.BLOCK_DAMAGED, blockListener, Priority.Normal, this); - - getServer().getPluginManager().registerEvent(Event.Type.PLAYER_COMMAND, new DynmapPlayerListener(mapManager, playerList), Priority.Normal, this); - getServer().getPluginManager().registerEvent(Event.Type.PLAYER_CHAT, new DynmapPlayerListener(mapManager, playerList), Priority.Normal, this); - } + public WebServer getWebServer() { + return webServer; + } + + public void onEnable() { + Configuration configuration = new Configuration(new File(this.getDataFolder(), "configuration.txt")); + configuration.load(); + + debugger.enable(); + playerList = new PlayerList(getServer()); + playerList.load(); + + mapManager = new MapManager(getWorld(), debugger, configuration); + mapManager.startManager(); + + try { + webServer = new WebServer(mapManager, getWorld(), playerList, debugger, configuration); + } catch (IOException e) { + log.info("position failed to start WebServer (IOException)"); + } + + registerEvents(); + } + + public void onDisable() { + mapManager.stopManager(); + + if (webServer != null) { + webServer.shutdown(); + webServer = null; + } + debugger.disable(); + } + + public void registerEvents() { + BlockListener blockListener = new DynmapBlockListener(mapManager); + getServer().getPluginManager().registerEvent(Event.Type.BLOCK_PLACED, blockListener, Priority.Normal, this); + getServer().getPluginManager().registerEvent(Event.Type.BLOCK_DAMAGED, blockListener, Priority.Normal, this); + + getServer().getPluginManager().registerEvent(Event.Type.PLAYER_COMMAND, new DynmapPlayerListener(mapManager, playerList), Priority.Normal, this); + getServer().getPluginManager().registerEvent(Event.Type.PLAYER_CHAT, new DynmapPlayerListener(mapManager, playerList), Priority.Normal, this); + } } diff --git a/src/main/java/org/dynmap/MapLocation.java b/src/main/java/org/dynmap/MapLocation.java index 983c9df3..854ae5c0 100644 --- a/src/main/java/org/dynmap/MapLocation.java +++ b/src/main/java/org/dynmap/MapLocation.java @@ -1,6 +1,6 @@ package org.dynmap; public class MapLocation { - public float x; - public float y; + public float x; + public float y; } diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index e8802245..fc2fed32 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -15,274 +15,274 @@ import org.bukkit.util.config.ConfigurationNode; import org.dynmap.debug.Debugger; public class MapManager extends Thread { - protected static final Logger log = Logger.getLogger("Minecraft"); - - private World world; - private Debugger debugger; - private MapType[] maps; - public StaleQueue staleQueue; - public ChatQueue chatQueue; - public PlayerList playerList; - - /* lock for our data structures */ - public static final Object lock = new Object(); - - /* whether the worker thread should be running now */ - private boolean running = false; - - /* path to image tile directory */ - public File tileDirectory; - - /* web files location */ - public File webDirectory; - - /* bind web server to ip-address */ - public String bindaddress = "0.0.0.0"; - - /* port to run web server on */ - public int serverport = 8123; - - /* time to pause between rendering tiles (ms) */ - public int renderWait = 500; - - 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.chatQueue = new ChatQueue(); - - 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 renderFullWorldAsync(Location l) { - fullmapTiles.clear(); - fullmapTilesRendered.clear(); - debugger.debug("Full render starting..."); - for(MapType map : maps) { - for(MapTile tile : map.getTiles(l)) { - fullmapTiles.add(tile); - invalidateTile(tile); - } - } - debugger.debug("Full render finished."); - } - - void renderFullWorld(Location l) { - debugger.debug("Full render starting..."); - for(MapType map : maps) { - HashSet found = new HashSet(); - LinkedList renderQueue = new LinkedList(); - - for(MapTile tile : map.getTiles(l)) { - if(!(found.contains(tile) || map.isRendered(tile))) { - found.add(tile); - renderQueue.add(tile); - } - } - while(!renderQueue.isEmpty()) { - MapTile tile = renderQueue.pollFirst(); - - loadRequiredChunks(tile); - debugger.debug("renderQueue: " + renderQueue.size() + "/" + found.size()); - if(map.render(tile)) { - found.remove(tile); - staleQueue.onTileUpdated(tile); - for(MapTile adjTile : map.getAdjecentTiles(tile)) { - if(!(found.contains(adjTile) || map.isRendered(adjTile))) { - found.add(adjTile); - renderQueue.add(adjTile); - } - } - } - found.remove(tile); - System.gc(); - } - } - debugger.debug("Full render finished."); - } - - public HashSet fullmapTiles = new HashSet(); - public boolean fullmapRenderStarting = false; - public HashSet fullmapTilesRendered = new HashSet(); - - void handleFullMapRender(MapTile tile) { - if(!fullmapTiles.contains(tile)) { - debugger.debug("Non fullmap-render tile: " + tile); - return; - } - fullmapTilesRendered.add(tile); - MapType map = tile.getMap(); - MapTile[] adjecenttiles = map.getAdjecentTiles(tile); - for(int i = 0; i < adjecenttiles.length; i++) { - MapTile adjecentTile = adjecenttiles[i]; - if(!fullmapTiles.contains(adjecentTile)) { - fullmapTiles.add(adjecentTile); - staleQueue.pushStaleTile(adjecentTile); - } - } - debugger.debug("Queue size: " + staleQueue.size() + "+" + fullmapTilesRendered.size() + "/" + fullmapTiles.size()); - } - - private boolean hasEnoughMemory() { - return Runtime.getRuntime().freeMemory() >= 100 * 1024 * 1024; - } - - private void waitForMemory() { - if(!hasEnoughMemory()) { - debugger.debug("Waiting for memory..."); - // Wait until there is at least 50mb of free memory. - do { - System.gc(); - try { - Thread.sleep(500); - } catch(InterruptedException e) { - e.printStackTrace(); - } - } while(!hasEnoughMemory()); - debugger.debug(Runtime.getRuntime().freeMemory() / (1024 * 1024) + "MB of memory free, will continue..."); - } - } - - private void loadRequiredChunks(MapTile tile) { - if(!loadChunks) - return; - waitForMemory(); - - // Actually load the chunks. - for(DynmapChunk chunk : tile.getMap().getRequiredChunks(tile)) { - if(!world.isChunkLoaded(chunk.x, chunk.y)) - world.loadChunk(chunk.x, chunk.y); - } - } - - private MapType[] loadMapTypes(ConfigurationNode configuration) { - List configuredMaps = (List) configuration.getProperty("maps"); - ArrayList mapTypes = new ArrayList(); - for(Object configuredMapObj : configuredMaps) { - try { - @SuppressWarnings("unchecked") - Map configuredMap = (Map) configuredMapObj; - String typeName = (String) configuredMap.get("class"); - log.info("Loading map '" + typeName.toString() + "'..."); - Class mapTypeClass = Class.forName(typeName); - Constructor constructor = mapTypeClass.getConstructor(MapManager.class, World.class, Debugger.class, Map.class); - MapType mapType = (MapType) constructor.newInstance(this, world, debugger, configuredMap); - mapTypes.add(mapType); - } catch(Exception e) { - debugger.error("Error loading map", e); - } - } - MapType[] result = new MapType[mapTypes.size()]; - mapTypes.toArray(result); - return result; - } - - /* initialize and start map manager */ - public void startManager() { - synchronized(lock) { - running = true; - this.start(); - try { - this.setPriority(MIN_PRIORITY); - log.info("Set minimum priority for worker thread"); - } catch(SecurityException e) { - log.info("Failed to set minimum priority for worker thread!"); - } - } - } - - /* stop map manager */ - public void stopManager() { - 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) { - loadRequiredChunks(t); - - debugger.debug("Rendering tile " + t + "..."); - boolean isNonEmptyTile = t.getMap().render(t); - staleQueue.onTileUpdated(t); - - if(isNonEmptyTile) - handleFullMapRender(t); - - 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++) { - invalidateTile(tiles[j]); - } - } - } - - public void invalidateTile(MapTile tile) { - debugger.debug("Invalidating tile " + tile.getName()); - staleQueue.pushStaleTile(tile); - } - - public void addChatEvent(PlayerChatEvent event) { - chatQueue.pushChatMessage(event); - } + protected static final Logger log = Logger.getLogger("Minecraft"); + + private World world; + private Debugger debugger; + private MapType[] maps; + public StaleQueue staleQueue; + public ChatQueue chatQueue; + public PlayerList playerList; + + /* lock for our data structures */ + public static final Object lock = new Object(); + + /* whether the worker thread should be running now */ + private boolean running = false; + + /* path to image tile directory */ + public File tileDirectory; + + /* web files location */ + public File webDirectory; + + /* bind web server to ip-address */ + public String bindaddress = "0.0.0.0"; + + /* port to run web server on */ + public int serverport = 8123; + + /* time to pause between rendering tiles (ms) */ + public int renderWait = 500; + + 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.chatQueue = new ChatQueue(); + + 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 renderFullWorldAsync(Location l) { + fullmapTiles.clear(); + fullmapTilesRendered.clear(); + debugger.debug("Full render starting..."); + for (MapType map : maps) { + for (MapTile tile : map.getTiles(l)) { + fullmapTiles.add(tile); + invalidateTile(tile); + } + } + debugger.debug("Full render finished."); + } + + void renderFullWorld(Location l) { + debugger.debug("Full render starting..."); + for (MapType map : maps) { + HashSet found = new HashSet(); + LinkedList renderQueue = new LinkedList(); + + for (MapTile tile : map.getTiles(l)) { + if (!(found.contains(tile) || map.isRendered(tile))) { + found.add(tile); + renderQueue.add(tile); + } + } + while (!renderQueue.isEmpty()) { + MapTile tile = renderQueue.pollFirst(); + + loadRequiredChunks(tile); + debugger.debug("renderQueue: " + renderQueue.size() + "/" + found.size()); + if (map.render(tile)) { + found.remove(tile); + staleQueue.onTileUpdated(tile); + for (MapTile adjTile : map.getAdjecentTiles(tile)) { + if (!(found.contains(adjTile) || map.isRendered(adjTile))) { + found.add(adjTile); + renderQueue.add(adjTile); + } + } + } + found.remove(tile); + System.gc(); + } + } + debugger.debug("Full render finished."); + } + + public HashSet fullmapTiles = new HashSet(); + public boolean fullmapRenderStarting = false; + public HashSet fullmapTilesRendered = new HashSet(); + + void handleFullMapRender(MapTile tile) { + if (!fullmapTiles.contains(tile)) { + debugger.debug("Non fullmap-render tile: " + tile); + return; + } + fullmapTilesRendered.add(tile); + MapType map = tile.getMap(); + MapTile[] adjecenttiles = map.getAdjecentTiles(tile); + for (int i = 0; i < adjecenttiles.length; i++) { + MapTile adjecentTile = adjecenttiles[i]; + if (!fullmapTiles.contains(adjecentTile)) { + fullmapTiles.add(adjecentTile); + staleQueue.pushStaleTile(adjecentTile); + } + } + debugger.debug("Queue size: " + staleQueue.size() + "+" + fullmapTilesRendered.size() + "/" + fullmapTiles.size()); + } + + private boolean hasEnoughMemory() { + return Runtime.getRuntime().freeMemory() >= 100 * 1024 * 1024; + } + + private void waitForMemory() { + if (!hasEnoughMemory()) { + debugger.debug("Waiting for memory..."); + // Wait until there is at least 50mb of free memory. + do { + System.gc(); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } while (!hasEnoughMemory()); + debugger.debug(Runtime.getRuntime().freeMemory() / (1024 * 1024) + "MB of memory free, will continue..."); + } + } + + private void loadRequiredChunks(MapTile tile) { + if (!loadChunks) + return; + waitForMemory(); + + // Actually load the chunks. + for (DynmapChunk chunk : tile.getMap().getRequiredChunks(tile)) { + if (!world.isChunkLoaded(chunk.x, chunk.y)) + world.loadChunk(chunk.x, chunk.y); + } + } + + private MapType[] loadMapTypes(ConfigurationNode configuration) { + List configuredMaps = (List) configuration.getProperty("maps"); + ArrayList mapTypes = new ArrayList(); + for (Object configuredMapObj : configuredMaps) { + try { + @SuppressWarnings("unchecked") + Map configuredMap = (Map) configuredMapObj; + String typeName = (String) configuredMap.get("class"); + log.info("Loading map '" + typeName.toString() + "'..."); + Class mapTypeClass = Class.forName(typeName); + Constructor constructor = mapTypeClass.getConstructor(MapManager.class, World.class, Debugger.class, Map.class); + MapType mapType = (MapType) constructor.newInstance(this, world, debugger, configuredMap); + mapTypes.add(mapType); + } catch (Exception e) { + debugger.error("Error loading map", e); + } + } + MapType[] result = new MapType[mapTypes.size()]; + mapTypes.toArray(result); + return result; + } + + /* initialize and start map manager */ + public void startManager() { + synchronized (lock) { + running = true; + this.start(); + try { + this.setPriority(MIN_PRIORITY); + log.info("Set minimum priority for worker thread"); + } catch (SecurityException e) { + log.info("Failed to set minimum priority for worker thread!"); + } + } + } + + /* stop map manager */ + public void stopManager() { + 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) { + loadRequiredChunks(t); + + debugger.debug("Rendering tile " + t + "..."); + boolean isNonEmptyTile = t.getMap().render(t); + staleQueue.onTileUpdated(t); + + if (isNonEmptyTile) + handleFullMapRender(t); + + 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++) { + invalidateTile(tiles[j]); + } + } + } + + public void invalidateTile(MapTile tile) { + debugger.debug("Invalidating tile " + tile.getName()); + staleQueue.pushStaleTile(tile); + } + + public void addChatEvent(PlayerChatEvent event) { + chatQueue.pushChatMessage(event); + } } diff --git a/src/main/java/org/dynmap/MapTile.java b/src/main/java/org/dynmap/MapTile.java index 529e821d..16c894fa 100644 --- a/src/main/java/org/dynmap/MapTile.java +++ b/src/main/java/org/dynmap/MapTile.java @@ -1,14 +1,15 @@ package org.dynmap; public abstract class MapTile { - private MapType map; - public MapType getMap() { - return map; - } - - public abstract String getName(); - - public MapTile(MapType map) { - this.map = map; - } + private MapType map; + + public MapType getMap() { + return map; + } + + public abstract String getName(); + + public MapTile(MapType map) { + this.map = map; + } } diff --git a/src/main/java/org/dynmap/MapType.java b/src/main/java/org/dynmap/MapType.java index 2c6f0861..e80034b2 100644 --- a/src/main/java/org/dynmap/MapType.java +++ b/src/main/java/org/dynmap/MapType.java @@ -5,30 +5,37 @@ import org.bukkit.World; import org.dynmap.debug.Debugger; public abstract class MapType { - private MapManager manager; - 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[] getAdjecentTiles(MapTile tile); - public abstract DynmapChunk[] getRequiredChunks(MapTile tile); - public abstract boolean render(MapTile tile); - public abstract boolean isRendered(MapTile tile); + private MapManager manager; + + 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[] getAdjecentTiles(MapTile tile); + + public abstract DynmapChunk[] getRequiredChunks(MapTile tile); + + public abstract boolean render(MapTile tile); + + public abstract boolean isRendered(MapTile tile); } diff --git a/src/main/java/org/dynmap/PlayerList.java b/src/main/java/org/dynmap/PlayerList.java index a9ba72f2..6037fb23 100644 --- a/src/main/java/org/dynmap/PlayerList.java +++ b/src/main/java/org/dynmap/PlayerList.java @@ -14,68 +14,71 @@ import org.bukkit.Server; import org.bukkit.entity.Player; public class PlayerList { - private Server server; - private HashSet hiddenPlayerNames = new HashSet(); - private File hiddenPlayersFile = new File(DynmapPlugin.dataRoot, "hiddenplayers.txt"); - - public PlayerList(Server server) { - this.server = server; - } - - public void save() { - OutputStream stream; - try { - stream = new FileOutputStream(hiddenPlayersFile); - OutputStreamWriter writer = new OutputStreamWriter(stream); - for(String player : hiddenPlayerNames) { - writer.write(player); - writer.write("\n"); - } - writer.close(); - stream.close(); - } catch(IOException e) { - e.printStackTrace(); - } - } - - public void load() { - try { - Scanner scanner = new Scanner(hiddenPlayersFile); - while(scanner.hasNextLine()) { - String line = scanner.nextLine(); - hiddenPlayerNames.add(line); - } - scanner.close(); - } catch (FileNotFoundException e) { - return; - } - } - - public void hide(String playerName) { - hiddenPlayerNames.add(playerName); - save(); - } - - public void show(String playerName) { - hiddenPlayerNames.remove(playerName); - save(); - } - - public void setVisible(String playerName, boolean visible) { - if (visible) show(playerName); else hide(playerName); - } - - public Player[] getVisiblePlayers() { - ArrayList visiblePlayers = new ArrayList(); - Player[] onlinePlayers = server.getOnlinePlayers(); - for(int i=0;i hiddenPlayerNames = new HashSet(); + private File hiddenPlayersFile = new File(DynmapPlugin.dataRoot, "hiddenplayers.txt"); + + public PlayerList(Server server) { + this.server = server; + } + + public void save() { + OutputStream stream; + try { + stream = new FileOutputStream(hiddenPlayersFile); + OutputStreamWriter writer = new OutputStreamWriter(stream); + for (String player : hiddenPlayerNames) { + writer.write(player); + writer.write("\n"); + } + writer.close(); + stream.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void load() { + try { + Scanner scanner = new Scanner(hiddenPlayersFile); + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + hiddenPlayerNames.add(line); + } + scanner.close(); + } catch (FileNotFoundException e) { + return; + } + } + + public void hide(String playerName) { + hiddenPlayerNames.add(playerName); + save(); + } + + public void show(String playerName) { + hiddenPlayerNames.remove(playerName); + save(); + } + + public void setVisible(String playerName, boolean visible) { + if (visible) + show(playerName); + else + hide(playerName); + } + + public Player[] getVisiblePlayers() { + ArrayList visiblePlayers = new ArrayList(); + Player[] onlinePlayers = server.getOnlinePlayers(); + for (int i = 0; i < onlinePlayers.length; i++) { + Player p = onlinePlayers[i]; + if (!hiddenPlayerNames.contains(p.getName())) { + visiblePlayers.add(p); + } + } + Player[] result = new Player[visiblePlayers.size()]; + visiblePlayers.toArray(result); + return result; + } } diff --git a/src/main/java/org/dynmap/StaleQueue.java b/src/main/java/org/dynmap/StaleQueue.java index eecb081c..2d7eddc0 100644 --- a/src/main/java/org/dynmap/StaleQueue.java +++ b/src/main/java/org/dynmap/StaleQueue.java @@ -9,92 +9,95 @@ import java.util.NoSuchElementException; import java.util.Set; public class StaleQueue { - /* a list of MapTiles to be updated */ - private LinkedList staleTilesQueue; - private Set staleTiles; + /* a list of MapTiles to be updated */ + private LinkedList staleTilesQueue; + private Set staleTiles; - /* this list stores the tile updates */ - public LinkedList tileUpdates = null; - - /* remember up to this old tile updates (ms) */ - private static final int maxTileAge = 60000; - - public StaleQueue() { - staleTilesQueue = new LinkedList(); - staleTiles = new HashSet(); - tileUpdates = new LinkedList(); - } - - public int size() { - return staleTilesQueue.size(); - } - - /* put a MapTile that needs to be regenerated on the list of stale tiles */ - public boolean pushStaleTile(MapTile m) - { - synchronized(MapManager.lock) { - if(staleTiles.add(m)) { - staleTilesQueue.addLast(m); - return true; - } - return false; - } - } - - /* get next MapTile that needs to be regenerated, or null - * the mapTile is removed from the list of stale tiles! */ - public MapTile popStaleTile() - { - synchronized(MapManager.lock) { - try { - MapTile t = staleTilesQueue.removeFirst(); - if(!staleTiles.remove(t)) { - // This should never happen. - } - return t; - } catch(NoSuchElementException e) { - return null; - } - } - } - - public void onTileUpdated(MapTile t) { - long now = System.currentTimeMillis(); - long deadline = now - maxTileAge; - synchronized(MapManager.lock) { - ListIterator it = tileUpdates.listIterator(0); - while(it.hasNext()) { - TileUpdate tu = it.next(); - if(tu.at < deadline || tu.tile == t) - it.remove(); - } - tileUpdates.addLast(new TileUpdate(now, t)); - } - } - - private ArrayList tmpupdates = new ArrayList(); - public TileUpdate[] getTileUpdates(long cutoff) { - long now = System.currentTimeMillis(); - long deadline = now - maxTileAge; - TileUpdate[] updates; - synchronized(MapManager.lock) { - tmpupdates.clear(); - Iterator it = tileUpdates.descendingIterator(); - while(it.hasNext()) { - TileUpdate tu = it.next(); - if(tu.at >= cutoff) { // Tile is new. - tmpupdates.add(tu); - } else if(tu.at < deadline) { // Tile is too old, removing this one (will eventually decrease). - it.remove(); - break; - } else { // Tile is old, but not old enough for removal. - break; - } - } - updates = new TileUpdate[tmpupdates.size()]; - tmpupdates.toArray(updates); - } - return updates; - } + /* this list stores the tile updates */ + public LinkedList tileUpdates = null; + + /* remember up to this old tile updates (ms) */ + private static final int maxTileAge = 60000; + + public StaleQueue() { + staleTilesQueue = new LinkedList(); + staleTiles = new HashSet(); + tileUpdates = new LinkedList(); + } + + public int size() { + return staleTilesQueue.size(); + } + + /* put a MapTile that needs to be regenerated on the list of stale tiles */ + public boolean pushStaleTile(MapTile m) { + synchronized (MapManager.lock) { + if (staleTiles.add(m)) { + staleTilesQueue.addLast(m); + return true; + } + return false; + } + } + + /* + * get next MapTile that needs to be regenerated, or null the mapTile is + * removed from the list of stale tiles! + */ + public MapTile popStaleTile() { + synchronized (MapManager.lock) { + try { + MapTile t = staleTilesQueue.removeFirst(); + if (!staleTiles.remove(t)) { + // This should never happen. + } + return t; + } catch (NoSuchElementException e) { + return null; + } + } + } + + public void onTileUpdated(MapTile t) { + long now = System.currentTimeMillis(); + long deadline = now - maxTileAge; + synchronized (MapManager.lock) { + ListIterator it = tileUpdates.listIterator(0); + while (it.hasNext()) { + TileUpdate tu = it.next(); + if (tu.at < deadline || tu.tile == t) + it.remove(); + } + tileUpdates.addLast(new TileUpdate(now, t)); + } + } + + private ArrayList tmpupdates = new ArrayList(); + + public TileUpdate[] getTileUpdates(long cutoff) { + long now = System.currentTimeMillis(); + long deadline = now - maxTileAge; + TileUpdate[] updates; + synchronized (MapManager.lock) { + tmpupdates.clear(); + Iterator it = tileUpdates.descendingIterator(); + while (it.hasNext()) { + TileUpdate tu = it.next(); + if (tu.at >= cutoff) { // Tile is new. + tmpupdates.add(tu); + } else if (tu.at < deadline) { // Tile is too old, removing this + // one (will eventually + // decrease). + it.remove(); + break; + } else { // Tile is old, but not old enough for removal. + break; + } + } + updates = new TileUpdate[tmpupdates.size()]; + tmpupdates.toArray(updates); + } + return updates; + } } diff --git a/src/main/java/org/dynmap/TileUpdate.java b/src/main/java/org/dynmap/TileUpdate.java index f09a58c6..689cab9d 100644 --- a/src/main/java/org/dynmap/TileUpdate.java +++ b/src/main/java/org/dynmap/TileUpdate.java @@ -3,12 +3,11 @@ package org.dynmap; /* this class stores a tile update */ public class TileUpdate { - public long at; - public MapTile tile; + public long at; + public MapTile tile; - public TileUpdate(long at, MapTile tile) - { - this.at = at; - this.tile = tile; - } + public TileUpdate(long at, MapTile tile) { + this.at = at; + this.tile = tile; + } } diff --git a/src/main/java/org/dynmap/debug/BukkitPlayerDebugger.java b/src/main/java/org/dynmap/debug/BukkitPlayerDebugger.java index b1b3fadb..56b2d587 100644 --- a/src/main/java/org/dynmap/debug/BukkitPlayerDebugger.java +++ b/src/main/java/org/dynmap/debug/BukkitPlayerDebugger.java @@ -15,85 +15,86 @@ import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.java.JavaPlugin; public class BukkitPlayerDebugger implements Debugger { - protected static final Logger log = Logger.getLogger("Minecraft"); - - private boolean isLogging = false; - - private JavaPlugin plugin; - private HashSet debugees = new HashSet(); - private String debugCommand; - private String undebugCommand; - private String prepend; - - public BukkitPlayerDebugger(JavaPlugin plugin) { - this.plugin = plugin; - - PluginDescriptionFile pdfFile = plugin.getDescription(); - debugCommand = "/debug_" + pdfFile.getName(); - undebugCommand = "/undebug_" + pdfFile.getName(); - prepend = pdfFile.getName() + ": "; - } - - public synchronized void enable() { - plugin.getServer().getPluginManager().registerEvent(Event.Type.PLAYER_COMMAND, new CommandListener(), Priority.Normal, plugin); - plugin.getServer().getPluginManager().registerEvent(Event.Type.PLAYER_QUIT, new CommandListener(), Priority.Normal, plugin); - } - - public synchronized void disable() { - clearDebugees(); - } - - public synchronized void addDebugee(Player p) { - debugees.add(p); - } - - public synchronized void removeDebugee(Player p) { - debugees.remove(p); - } - - public synchronized void clearDebugees() { - debugees.clear(); - } - - public synchronized void sendToDebuggees(String message) { - for (Player p : debugees) { - p.sendMessage(prepend + message); - } - } - - public synchronized void debug(String message) { - sendToDebuggees(message); - if (isLogging) log.info(prepend + message); - } - - public synchronized void error(String message) { - sendToDebuggees(prepend + ChatColor.RED + message); - log.log(Level.SEVERE, prepend + message); - } - - public synchronized void error(String message, Throwable thrown) { - sendToDebuggees(prepend + ChatColor.RED + message); - sendToDebuggees(thrown.toString()); - log.log(Level.SEVERE, prepend + message); - } - - protected class CommandListener extends PlayerListener { - @Override - public void onPlayerCommand(PlayerChatEvent event) { - String[] split = event.getMessage().split(" "); - Player player = event.getPlayer(); - if (split[0].equalsIgnoreCase(debugCommand)) { - addDebugee(player); - event.setCancelled(true); - } else if (split[0].equalsIgnoreCase(undebugCommand)) { - removeDebugee(player); - event.setCancelled(true); - } - } - - @Override - public void onPlayerQuit(PlayerEvent event) { - removeDebugee(event.getPlayer()); - } - } + protected static final Logger log = Logger.getLogger("Minecraft"); + + private boolean isLogging = false; + + private JavaPlugin plugin; + private HashSet debugees = new HashSet(); + private String debugCommand; + private String undebugCommand; + private String prepend; + + public BukkitPlayerDebugger(JavaPlugin plugin) { + this.plugin = plugin; + + PluginDescriptionFile pdfFile = plugin.getDescription(); + debugCommand = "/debug_" + pdfFile.getName(); + undebugCommand = "/undebug_" + pdfFile.getName(); + prepend = pdfFile.getName() + ": "; + } + + public synchronized void enable() { + plugin.getServer().getPluginManager().registerEvent(Event.Type.PLAYER_COMMAND, new CommandListener(), Priority.Normal, plugin); + plugin.getServer().getPluginManager().registerEvent(Event.Type.PLAYER_QUIT, new CommandListener(), Priority.Normal, plugin); + } + + public synchronized void disable() { + clearDebugees(); + } + + public synchronized void addDebugee(Player p) { + debugees.add(p); + } + + public synchronized void removeDebugee(Player p) { + debugees.remove(p); + } + + public synchronized void clearDebugees() { + debugees.clear(); + } + + public synchronized void sendToDebuggees(String message) { + for (Player p : debugees) { + p.sendMessage(prepend + message); + } + } + + public synchronized void debug(String message) { + sendToDebuggees(message); + if (isLogging) + log.info(prepend + message); + } + + public synchronized void error(String message) { + sendToDebuggees(prepend + ChatColor.RED + message); + log.log(Level.SEVERE, prepend + message); + } + + public synchronized void error(String message, Throwable thrown) { + sendToDebuggees(prepend + ChatColor.RED + message); + sendToDebuggees(thrown.toString()); + log.log(Level.SEVERE, prepend + message); + } + + protected class CommandListener extends PlayerListener { + @Override + public void onPlayerCommand(PlayerChatEvent event) { + String[] split = event.getMessage().split(" "); + Player player = event.getPlayer(); + if (split[0].equalsIgnoreCase(debugCommand)) { + addDebugee(player); + event.setCancelled(true); + } else if (split[0].equalsIgnoreCase(undebugCommand)) { + removeDebugee(player); + event.setCancelled(true); + } + } + + @Override + public void onPlayerQuit(PlayerEvent event) { + removeDebugee(event.getPlayer()); + } + } } diff --git a/src/main/java/org/dynmap/debug/Debugger.java b/src/main/java/org/dynmap/debug/Debugger.java index 45583367..2919e140 100644 --- a/src/main/java/org/dynmap/debug/Debugger.java +++ b/src/main/java/org/dynmap/debug/Debugger.java @@ -1,7 +1,9 @@ package org.dynmap.debug; public interface Debugger { - void debug(String message); - void error(String message); - void error(String message, Throwable thrown); + void debug(String message); + + void error(String message); + + void error(String message, Throwable thrown); } diff --git a/src/main/java/org/dynmap/debug/NullDebugger.java b/src/main/java/org/dynmap/debug/NullDebugger.java index 061e51f8..2bae1663 100644 --- a/src/main/java/org/dynmap/debug/NullDebugger.java +++ b/src/main/java/org/dynmap/debug/NullDebugger.java @@ -1,14 +1,15 @@ package org.dynmap.debug; public class NullDebugger implements Debugger { - public static final NullDebugger instance = new NullDebugger(); - public void debug(String message) { - } + public static final NullDebugger instance = new NullDebugger(); - public void error(String message) { - } + public void debug(String message) { + } - public void error(String message, Throwable thrown) { - } + public void error(String message) { + } + + public void error(String message, Throwable thrown) { + } } diff --git a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java index a83f4c31..b56ccd66 100644 --- a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java @@ -7,89 +7,88 @@ import org.dynmap.debug.Debugger; public class CaveTileRenderer extends DefaultTileRenderer { - public CaveTileRenderer(Debugger debugger, Map configuration) { - super(debugger, configuration); - } + public CaveTileRenderer(Debugger debugger, Map configuration) { + super(debugger, configuration); + } - @Override - protected Color scan(World world, int x, int y, int z, int seq) - { - boolean air = true; + @Override + protected Color scan(World world, int x, int y, int z, int seq) { + boolean air = true; - for(;;) { - if(y < 0) - return translucent; + for (;;) { + if (y < 0) + return translucent; - int id = world.getBlockTypeIdAt(x, y, z); + int id = world.getBlockTypeIdAt(x, y, z); - switch(seq) { - case 0: - x--; - break; - case 1: - y--; - break; - case 2: - z++; - break; - case 3: - y--; - break; - } + switch (seq) { + case 0: + x--; + break; + case 1: + y--; + break; + case 2: + z++; + break; + case 3: + y--; + break; + } - seq = (seq + 1) & 3; + seq = (seq + 1) & 3; - switch(id) { - case 20: - case 18: - case 17: - case 78: - case 79: - id = 0; - break; - default: - } + switch (id) { + case 20: + case 18: + case 17: + case 78: + case 79: + id = 0; + break; + default: + } - if(id != 0) { - air = false; - continue; - } + if (id != 0) { + air = false; + continue; + } - if(id == 0 && !air) { - int cr, cg, cb; - int mult = 256; + if (id == 0 && !air) { + int cr, cg, cb; + int mult = 256; - if(y < 64) { - cr = 0; - cg = 64 + y * 3; - cb = 255 - y * 4; - } else { - cr = (y-64) * 4; - cg = 255; - cb = 0; - } + if (y < 64) { + cr = 0; + cg = 64 + y * 3; + cb = 255 - y * 4; + } else { + cr = (y - 64) * 4; + cg = 255; + cb = 0; + } - switch(seq) { - case 0: - mult = 224; - break; - case 1: - mult = 256; - break; - case 2: - mult = 192; - break; - case 3: - mult = 160; - break; - } + switch (seq) { + case 0: + mult = 224; + break; + case 1: + mult = 256; + break; + case 2: + mult = 192; + break; + case 3: + mult = 160; + break; + } - cr = cr * mult / 256; - cg = cg * mult / 256; - cb = cb * mult / 256; + cr = cr * mult / 256; + cg = cg * mult / 256; + cb = cb * mult / 256; - return new Color(cr, cg, cb); - } - } - } + return new Color(cr, cg, cb); + } + } + } } diff --git a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java index 6493c744..953f82fb 100644 --- a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java @@ -13,152 +13,164 @@ import org.bukkit.World; import org.dynmap.debug.Debugger; public class DefaultTileRenderer implements MapTileRenderer { - protected static Color translucent = new Color(0, 0, 0, 0); - private String name; - protected Debugger debugger; + protected static Color translucent = new Color(0, 0, 0, 0); + private String name; + protected Debugger debugger; - public String getName() { - return name; - } + public String getName() { + return name; + } - public DefaultTileRenderer(Debugger debugger, Map configuration) { - this.debugger = debugger; - name = (String) configuration.get("prefix"); - } + public DefaultTileRenderer(Debugger debugger, Map configuration) { + this.debugger = debugger; + name = (String) configuration.get("prefix"); + } - public boolean render(KzedMapTile tile, String path) { - World world = tile.getMap().getWorld(); - BufferedImage im = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); + public boolean render(KzedMapTile tile, String path) { + World world = tile.getMap().getWorld(); + BufferedImage im = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); - WritableRaster r = im.getRaster(); - boolean isempty = true; + WritableRaster r = im.getRaster(); + boolean isempty = true; - int ix = tile.mx; - int iy = tile.my; - int iz = tile.mz; + int ix = tile.mx; + int iy = tile.my; + int iz = tile.mz; - int jx, jz; + int jx, jz; - int x, y; + int x, y; - /* draw the map */ - for (y = 0; y < KzedMap.tileHeight;) { - jx = ix; - jz = iz; + /* draw the map */ + for (y = 0; y < KzedMap.tileHeight;) { + jx = ix; + jz = iz; - for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { - Color c1 = scan(world, jx, iy, jz, 0); - Color c2 = scan(world, jx, iy, jz, 2); - isempty = isempty && c1 == translucent && c2 == translucent; - r.setPixel(x, y, new int[] { c1.getRed(), c1.getGreen(), c1.getBlue() }); - r.setPixel(x - 1, y, new int[] { c2.getRed(), c2.getGreen(), c2.getBlue() }); + for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { + Color c1 = scan(world, jx, iy, jz, 0); + Color c2 = scan(world, jx, iy, jz, 2); + isempty = isempty && c1 == translucent && c2 == translucent; + r.setPixel(x, y, new int[] { + c1.getRed(), + c1.getGreen(), + c1.getBlue() }); + r.setPixel(x - 1, y, new int[] { + c2.getRed(), + c2.getGreen(), + c2.getBlue() }); - jx++; - jz++; + jx++; + jz++; - } + } - y++; + y++; - jx = ix; - jz = iz - 1; + jx = ix; + jz = iz - 1; - for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { - Color c1 = scan(world, jx, iy, jz, 2); - jx++; - jz++; - Color c2 = scan(world, jx, iy, jz, 0); - isempty = isempty && c1 == translucent && c2 == translucent; - r.setPixel(x, y, new int[] { c1.getRed(), c1.getGreen(), c1.getBlue() }); - r.setPixel(x - 1, y, new int[] { c2.getRed(), c2.getGreen(), c2.getBlue() }); - } + for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { + Color c1 = scan(world, jx, iy, jz, 2); + jx++; + jz++; + Color c2 = scan(world, jx, iy, jz, 0); + isempty = isempty && c1 == translucent && c2 == translucent; + r.setPixel(x, y, new int[] { + c1.getRed(), + c1.getGreen(), + c1.getBlue() }); + r.setPixel(x - 1, y, new int[] { + c2.getRed(), + c2.getGreen(), + c2.getBlue() }); + } - y++; + y++; - ix++; - iz--; - } + ix++; + iz--; + } - /* save the generated tile */ - saveTile(tile, im, path); + /* save the generated tile */ + saveTile(tile, im, path); - ((KzedMap) tile.getMap()).invalidateTile(new KzedZoomedMapTile((KzedMap)tile.getMap(), im, tile)); + ((KzedMap) tile.getMap()).invalidateTile(new KzedZoomedMapTile((KzedMap) tile.getMap(), im, tile)); - return !isempty; - } + return !isempty; + } - protected Color scan(World world, int x, int y, int z, int seq) { - for (;;) { - if (y < 0) - return translucent; + protected Color scan(World world, int x, int y, int z, int seq) { + for (;;) { + if (y < 0) + return translucent; - int id = world.getBlockTypeIdAt(x, y, z); + int id = world.getBlockTypeIdAt(x, y, z); - switch (seq) { - case 0: - x--; - break; - case 1: - y--; - break; - case 2: - z++; - break; - case 3: - y--; - break; - } + switch (seq) { + case 0: + x--; + break; + case 1: + y--; + break; + case 2: + z++; + break; + case 3: + y--; + break; + } - seq = (seq + 1) & 3; + seq = (seq + 1) & 3; - if (id != 0) { - Color[] colors = KzedMap.colors.get(id); - if (colors != null) { - Color c = colors[seq]; - if (c.getAlpha() > 0) { - /* we found something that isn't transparent! */ - if (c.getAlpha() == 255) { - /* it's opaque - the ray ends here */ - return c; - } + if (id != 0) { + Color[] colors = KzedMap.colors.get(id); + if (colors != null) { + Color c = colors[seq]; + if (c.getAlpha() > 0) { + /* we found something that isn't transparent! */ + if (c.getAlpha() == 255) { + /* it's opaque - the ray ends here */ + return c; + } - /* this block is transparent, so recurse */ - Color bg = scan(world, x, y, z, seq); + /* this block is transparent, so recurse */ + Color bg = scan(world, x, y, z, seq); - int cr = c.getRed(); - int cg = c.getGreen(); - int cb = c.getBlue(); - int ca = c.getAlpha(); - cr *= ca; - cg *= ca; - cb *= ca; - int na = 255 - ca; + int cr = c.getRed(); + int cg = c.getGreen(); + int cb = c.getBlue(); + int ca = c.getAlpha(); + cr *= ca; + cg *= ca; + cb *= ca; + int na = 255 - ca; - return new Color((bg.getRed() * na + cr) >> 8, (bg.getGreen() * na + cg) >> 8, (bg.getBlue() * na + cb) >> 8); - } - } - } - } - } + return new Color((bg.getRed() * na + cr) >> 8, (bg.getGreen() * na + cg) >> 8, (bg.getBlue() * na + cb) >> 8); + } + } + } + } + } - /* save rendered tile, update zoom-out tile */ - public void saveTile(KzedMapTile tile, BufferedImage im, String path) { - String tilePath = getPath(tile, path); + /* save rendered tile, update zoom-out tile */ + public void saveTile(KzedMapTile tile, BufferedImage im, String path) { + String tilePath = getPath(tile, path); - debugger.debug("saving tile " + tilePath); + debugger.debug("saving tile " + tilePath); - /* save image */ - try { - File file = new File(tilePath); - ImageIO.write(im, "png", file); - } catch (IOException e) { - debugger.error("Failed to save tile: " + tilePath, e); - } catch (java.lang.NullPointerException e) { - debugger.error("Failed to save tile (NullPointerException): " + tilePath, e); - } - } + /* save image */ + try { + File file = new File(tilePath); + ImageIO.write(im, "png", file); + } catch (IOException e) { + debugger.error("Failed to save tile: " + tilePath, e); + } catch (java.lang.NullPointerException e) { + debugger.error("Failed to save tile (NullPointerException): " + tilePath, e); + } + } - public static String getPath(KzedMapTile tile, String outputPath) { - return new File(new File(outputPath), tile.getName() + ".png").getPath(); - } + public static String getPath(KzedMapTile tile, String outputPath) { + return new File(new File(outputPath), tile.getName() + ".png").getPath(); + } } diff --git a/src/main/java/org/dynmap/kzedmap/KzedMap.java b/src/main/java/org/dynmap/kzedmap/KzedMap.java index d337150b..eebf393a 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMap.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMap.java @@ -20,249 +20,258 @@ import org.dynmap.MapType; import org.dynmap.debug.Debugger; public class KzedMap extends MapType { - protected static final Logger log = Logger.getLogger("Minecraft"); - - /* dimensions of a map tile */ - public static final int tileWidth = 128; - public static final int tileHeight = 128; - - /* (logical!) dimensions of a zoomed out map tile - * must be twice the size of the normal tile */ - public static final int zTileWidth = 256; - public static final int zTileHeight = 256; - - /* map x, y, z for projection origin */ - public static final int anchorx = 0; - public static final int anchory = 127; - public static final int anchorz = 0; - - public static java.util.Map colors; - MapTileRenderer[] renderers; - ZoomedTileRenderer zoomrenderer; - - public KzedMap(MapManager manager, World world, Debugger debugger, Map configuration) { - super(manager, world, debugger); - if (colors == null) { - colors = loadColorSet("colors.txt"); - } - - renderers = loadRenderers(configuration); - zoomrenderer = new ZoomedTileRenderer(debugger, configuration); - } - - private MapTileRenderer[] loadRenderers(Map configuration) { - List configuredRenderers = (List) configuration.get("renderers"); - ArrayList renderers = new ArrayList(); - for (Object configuredRendererObj : configuredRenderers) { - try { - @SuppressWarnings("unchecked") - Map configuredRenderer = (Map) configuredRendererObj; - String typeName = (String) configuredRenderer.get("class"); - log.info("Loading renderer '" + typeName.toString() + "'..."); - Class mapTypeClass = Class.forName(typeName); - Constructor constructor = mapTypeClass.getConstructor(Debugger.class, Map.class); - MapTileRenderer mapTileRenderer = (MapTileRenderer) constructor.newInstance(getDebugger(), configuredRenderer); - renderers.add(mapTileRenderer); - } catch (Exception e) { - getDebugger().error("Error loading renderer", e); - } - } - MapTileRenderer[] result = new MapTileRenderer[renderers.size()]; - renderers.toArray(result); - return result; - } - - @Override - public MapTile[] getTiles(Location l) { - int x = l.getBlockX(); - int y = l.getBlockY(); - int z = l.getBlockZ(); - - int dx = x - anchorx; - int dy = y - anchory; - int dz = z - anchorz; - int px = dx + dz; - int py = dx - dz - dy; - - int tx = tilex(px); - int ty = tiley(py); - - ArrayList tiles = new ArrayList(); - - addTile(tiles, tx, ty); - - boolean ledge = tilex(px - 4) != tx; - boolean tedge = tiley(py - 4) != ty; - boolean redge = tilex(px + 4) != tx; - boolean bedge = tiley(py + 4) != ty; - - if (ledge) addTile(tiles, tx - tileWidth, ty); - if (redge) addTile(tiles, tx + tileWidth, ty); - if (tedge) addTile(tiles, tx, ty - tileHeight); - if (bedge) addTile(tiles, tx, ty + tileHeight); - - if (ledge && tedge) addTile(tiles, tx - tileWidth, ty - tileHeight); - if (ledge && bedge) addTile(tiles, tx - tileWidth, ty + tileHeight); - if (redge && tedge) addTile(tiles, tx + tileWidth, ty - tileHeight); - if (redge && bedge) addTile(tiles, tx + tileWidth, ty + tileHeight); - - MapTile[] result = new MapTile[tiles.size()]; - tiles.toArray(result); - return result; - } - - @Override - public MapTile[] getAdjecentTiles(MapTile tile) { - if (tile instanceof KzedMapTile) { - KzedMapTile t = (KzedMapTile) tile; - MapTileRenderer renderer = t.renderer; - return new MapTile[] { - new KzedMapTile(this, renderer, t.px - tileWidth, t.py), - new KzedMapTile(this, renderer, t.px + tileWidth, t.py), - new KzedMapTile(this, renderer, t.px, t.py - tileHeight), - new KzedMapTile(this, renderer, t.px, t.py + tileHeight) - }; - } - return new MapTile[0]; - } - - public void addTile(ArrayList tiles, int px, int py) { - for (int i = 0; i < renderers.length; i++) { - tiles.add(new KzedMapTile(this, renderers[i], px, py)); - } - } - - public void invalidateTile(MapTile tile) { - getMapManager().invalidateTile(tile); - } - - @Override - public DynmapChunk[] getRequiredChunks(MapTile tile) { - if (tile instanceof KzedMapTile) { - KzedMapTile t = (KzedMapTile) tile; - int x1 = t.mx - KzedMap.tileHeight / 2; - int x2 = t.mx + KzedMap.tileWidth / 2 + KzedMap.tileHeight / 2; - - int z1 = t.mz - KzedMap.tileHeight / 2; - int z2 = t.mz + KzedMap.tileWidth / 2 + KzedMap.tileHeight / 2; - - int x, z; - - ArrayList chunks = new ArrayList(); - for (x = x1; x < x2; x += 16) { - for (z = z1; z < z2; z += 16) { - DynmapChunk chunk = new DynmapChunk(x / 16, z / 16); - chunks.add(chunk); - } - } - DynmapChunk[] result = new DynmapChunk[chunks.size()]; - chunks.toArray(result); - return result; - } else { - return new DynmapChunk[0]; - } - } - - @Override - public boolean render(MapTile tile) { - if (tile instanceof KzedZoomedMapTile) { - zoomrenderer.render((KzedZoomedMapTile) tile, getMapManager().tileDirectory.getAbsolutePath()); - return true; - } else if (tile instanceof KzedMapTile) { - return ((KzedMapTile) tile).renderer.render((KzedMapTile) tile, getMapManager().tileDirectory.getAbsolutePath()); - } - 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; - } - - /* tile X for position x */ - static int tilex(int x) { - if (x < 0) - return x - (tileWidth + (x % tileWidth)); - else - return x - (x % tileWidth); - } - - /* tile Y for position y */ - static int tiley(int y) { - if (y < 0) - return y - (tileHeight + (y % tileHeight)); - else - return y - (y % tileHeight); - } - - /* zoomed-out tile X for tile position x */ - static int ztilex(int x) { - if (x < 0) - return x + x % zTileWidth; - else - return x - (x % zTileWidth); - } - - /* zoomed-out tile Y for tile position y */ - static int ztiley(int y) { - if (y < 0) - return y + y % zTileHeight; - //return y - (zTileHeight + (y % zTileHeight)); - else - return y - (y % zTileHeight); - } + protected static final Logger log = Logger.getLogger("Minecraft"); - public java.util.Map loadColorSet(String colorsetpath) { - java.util.Map colors = new HashMap(); - - InputStream stream; - - try { - /* load colorset */ - File cfile = new File(colorsetpath); - if (cfile.isFile()) { - getDebugger().debug("Loading colors from '" + colorsetpath + "'..."); - stream = new FileInputStream(cfile); - } else { - getDebugger().debug("Loading colors from jar..."); - stream = KzedMap.class.getResourceAsStream("/colors.txt"); - } - - Scanner scanner = new Scanner(stream); - int nc = 0; - while (scanner.hasNextLine()) { - String line = scanner.nextLine(); - if (line.startsWith("#") || line.equals("")) { - continue; - } - - String[] split = line.split("\t"); - if (split.length < 17) { - continue; - } - - Integer id = new Integer(split[0]); - - Color[] c = new Color[4]; - - /* store colors by raycast sequence number */ - c[0] = new Color(Integer.parseInt(split[1]), Integer.parseInt(split[2]), Integer.parseInt(split[3]), Integer.parseInt(split[4])); - c[3] = new Color(Integer.parseInt(split[5]), Integer.parseInt(split[6]), Integer.parseInt(split[7]), Integer.parseInt(split[8])); - c[1] = new Color(Integer.parseInt(split[9]), Integer.parseInt(split[10]), Integer.parseInt(split[11]), Integer.parseInt(split[12])); - c[2] = new Color(Integer.parseInt(split[13]), Integer.parseInt(split[14]), Integer.parseInt(split[15]), Integer.parseInt(split[16])); - - colors.put(id, c); - nc += 1; - } - scanner.close(); - } catch (Exception e) { - getDebugger().error("Could not load colors", e); - return null; - } - return colors; - } + /* dimensions of a map tile */ + public static final int tileWidth = 128; + public static final int tileHeight = 128; + + /* + * (logical!) dimensions of a zoomed out map tile must be twice the size of + * the normal tile + */ + public static final int zTileWidth = 256; + public static final int zTileHeight = 256; + + /* map x, y, z for projection origin */ + public static final int anchorx = 0; + public static final int anchory = 127; + public static final int anchorz = 0; + + public static java.util.Map colors; + MapTileRenderer[] renderers; + ZoomedTileRenderer zoomrenderer; + + public KzedMap(MapManager manager, World world, Debugger debugger, Map configuration) { + super(manager, world, debugger); + if (colors == null) { + colors = loadColorSet("colors.txt"); + } + + renderers = loadRenderers(configuration); + zoomrenderer = new ZoomedTileRenderer(debugger, configuration); + } + + private MapTileRenderer[] loadRenderers(Map configuration) { + List configuredRenderers = (List) configuration.get("renderers"); + ArrayList renderers = new ArrayList(); + for (Object configuredRendererObj : configuredRenderers) { + try { + @SuppressWarnings("unchecked") + Map configuredRenderer = (Map) configuredRendererObj; + String typeName = (String) configuredRenderer.get("class"); + log.info("Loading renderer '" + typeName.toString() + "'..."); + Class mapTypeClass = Class.forName(typeName); + Constructor constructor = mapTypeClass.getConstructor(Debugger.class, Map.class); + MapTileRenderer mapTileRenderer = (MapTileRenderer) constructor.newInstance(getDebugger(), configuredRenderer); + renderers.add(mapTileRenderer); + } catch (Exception e) { + getDebugger().error("Error loading renderer", e); + } + } + MapTileRenderer[] result = new MapTileRenderer[renderers.size()]; + renderers.toArray(result); + return result; + } + + @Override + public MapTile[] getTiles(Location l) { + int x = l.getBlockX(); + int y = l.getBlockY(); + int z = l.getBlockZ(); + + int dx = x - anchorx; + int dy = y - anchory; + int dz = z - anchorz; + int px = dx + dz; + int py = dx - dz - dy; + + int tx = tilex(px); + int ty = tiley(py); + + ArrayList tiles = new ArrayList(); + + addTile(tiles, tx, ty); + + boolean ledge = tilex(px - 4) != tx; + boolean tedge = tiley(py - 4) != ty; + boolean redge = tilex(px + 4) != tx; + boolean bedge = tiley(py + 4) != ty; + + if (ledge) + addTile(tiles, tx - tileWidth, ty); + if (redge) + addTile(tiles, tx + tileWidth, ty); + if (tedge) + addTile(tiles, tx, ty - tileHeight); + if (bedge) + addTile(tiles, tx, ty + tileHeight); + + if (ledge && tedge) + addTile(tiles, tx - tileWidth, ty - tileHeight); + if (ledge && bedge) + addTile(tiles, tx - tileWidth, ty + tileHeight); + if (redge && tedge) + addTile(tiles, tx + tileWidth, ty - tileHeight); + if (redge && bedge) + addTile(tiles, tx + tileWidth, ty + tileHeight); + + MapTile[] result = new MapTile[tiles.size()]; + tiles.toArray(result); + return result; + } + + @Override + public MapTile[] getAdjecentTiles(MapTile tile) { + if (tile instanceof KzedMapTile) { + KzedMapTile t = (KzedMapTile) tile; + MapTileRenderer renderer = t.renderer; + return new MapTile[] { + new KzedMapTile(this, renderer, t.px - tileWidth, t.py), + new KzedMapTile(this, renderer, t.px + tileWidth, t.py), + new KzedMapTile(this, renderer, t.px, t.py - tileHeight), + new KzedMapTile(this, renderer, t.px, t.py + tileHeight) }; + } + return new MapTile[0]; + } + + public void addTile(ArrayList tiles, int px, int py) { + for (int i = 0; i < renderers.length; i++) { + tiles.add(new KzedMapTile(this, renderers[i], px, py)); + } + } + + public void invalidateTile(MapTile tile) { + getMapManager().invalidateTile(tile); + } + + @Override + public DynmapChunk[] getRequiredChunks(MapTile tile) { + if (tile instanceof KzedMapTile) { + KzedMapTile t = (KzedMapTile) tile; + int x1 = t.mx - KzedMap.tileHeight / 2; + int x2 = t.mx + KzedMap.tileWidth / 2 + KzedMap.tileHeight / 2; + + int z1 = t.mz - KzedMap.tileHeight / 2; + int z2 = t.mz + KzedMap.tileWidth / 2 + KzedMap.tileHeight / 2; + + int x, z; + + ArrayList chunks = new ArrayList(); + for (x = x1; x < x2; x += 16) { + for (z = z1; z < z2; z += 16) { + DynmapChunk chunk = new DynmapChunk(x / 16, z / 16); + chunks.add(chunk); + } + } + DynmapChunk[] result = new DynmapChunk[chunks.size()]; + chunks.toArray(result); + return result; + } else { + return new DynmapChunk[0]; + } + } + + @Override + public boolean render(MapTile tile) { + if (tile instanceof KzedZoomedMapTile) { + zoomrenderer.render((KzedZoomedMapTile) tile, getMapManager().tileDirectory.getAbsolutePath()); + return true; + } else if (tile instanceof KzedMapTile) { + return ((KzedMapTile) tile).renderer.render((KzedMapTile) tile, getMapManager().tileDirectory.getAbsolutePath()); + } + 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; + } + + /* tile X for position x */ + static int tilex(int x) { + if (x < 0) + return x - (tileWidth + (x % tileWidth)); + else + return x - (x % tileWidth); + } + + /* tile Y for position y */ + static int tiley(int y) { + if (y < 0) + return y - (tileHeight + (y % tileHeight)); + else + return y - (y % tileHeight); + } + + /* zoomed-out tile X for tile position x */ + static int ztilex(int x) { + if (x < 0) + return x + x % zTileWidth; + else + return x - (x % zTileWidth); + } + + /* zoomed-out tile Y for tile position y */ + static int ztiley(int y) { + if (y < 0) + return y + y % zTileHeight; + // return y - (zTileHeight + (y % zTileHeight)); + else + return y - (y % zTileHeight); + } + + public java.util.Map loadColorSet(String colorsetpath) { + java.util.Map colors = new HashMap(); + + InputStream stream; + + try { + /* load colorset */ + File cfile = new File(colorsetpath); + if (cfile.isFile()) { + getDebugger().debug("Loading colors from '" + colorsetpath + "'..."); + stream = new FileInputStream(cfile); + } else { + getDebugger().debug("Loading colors from jar..."); + stream = KzedMap.class.getResourceAsStream("/colors.txt"); + } + + Scanner scanner = new Scanner(stream); + int nc = 0; + while (scanner.hasNextLine()) { + String line = scanner.nextLine(); + if (line.startsWith("#") || line.equals("")) { + continue; + } + + String[] split = line.split("\t"); + if (split.length < 17) { + continue; + } + + Integer id = new Integer(split[0]); + + Color[] c = new Color[4]; + + /* store colors by raycast sequence number */ + c[0] = new Color(Integer.parseInt(split[1]), Integer.parseInt(split[2]), Integer.parseInt(split[3]), Integer.parseInt(split[4])); + c[3] = new Color(Integer.parseInt(split[5]), Integer.parseInt(split[6]), Integer.parseInt(split[7]), Integer.parseInt(split[8])); + c[1] = new Color(Integer.parseInt(split[9]), Integer.parseInt(split[10]), Integer.parseInt(split[11]), Integer.parseInt(split[12])); + c[2] = new Color(Integer.parseInt(split[13]), Integer.parseInt(split[14]), Integer.parseInt(split[15]), Integer.parseInt(split[16])); + + colors.put(id, c); + nc += 1; + } + scanner.close(); + } catch (Exception e) { + getDebugger().error("Could not load colors", e); + return null; + } + return colors; + } } diff --git a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java index 78a15c09..2872169a 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java @@ -4,54 +4,54 @@ import java.util.logging.Logger; import org.dynmap.MapTile; public class KzedMapTile extends MapTile { - protected static final Logger log = Logger.getLogger("Minecraft"); + 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; + /* projection position */ + public int px, py; - /* minecraft space origin */ - public int mx, my, mz; + /* minecraft space origin */ + public int mx, my, mz; - /* create new MapTile */ - public KzedMapTile(KzedMap map, MapTileRenderer renderer, int px, int py) { - super(map); - this.map = map; - this.renderer = renderer; - this.px = px; - this.py = py; + /* create new MapTile */ + public KzedMapTile(KzedMap map, MapTileRenderer renderer, int px, int py) { + super(map); + this.map = map; + this.renderer = renderer; + this.px = px; + this.py = py; - mx = KzedMap.anchorx + px / 2 + py / 2; - my = KzedMap.anchory; - mz = KzedMap.anchorz + px / 2 - py / 2; - } + mx = KzedMap.anchorx + px / 2 + py / 2; + my = KzedMap.anchory; + mz = KzedMap.anchorz + px / 2 - py / 2; + } - @Override - public String getName() { - return renderer.getName() + "_" + px + "_" + py; - } + @Override + public String getName() { + return renderer.getName() + "_" + px + "_" + py; + } - public int hashCode() { - return getName().hashCode(); - } + public int hashCode() { + return getName().hashCode(); + } - @Override - public boolean equals(Object obj) { - if (obj instanceof KzedMapTile) { - return equals((KzedMapTile) obj); - } - return super.equals(obj); - } + @Override + public boolean equals(Object obj) { + if (obj instanceof KzedMapTile) { + return equals((KzedMapTile) obj); + } + return super.equals(obj); + } - public boolean equals(KzedMapTile o) { - return o.getName().equals(getName()); - } + public boolean equals(KzedMapTile o) { + return o.getName().equals(getName()); + } - /* return a simple string representation... */ - public String toString() { - return getName(); - } + /* return a simple string representation... */ + public String toString() { + return getName(); + } } diff --git a/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java index 3cde3a9d..749bb877 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java +++ b/src/main/java/org/dynmap/kzedmap/KzedZoomedMapTile.java @@ -4,53 +4,53 @@ import java.awt.image.BufferedImage; import org.dynmap.MapTile; public class KzedZoomedMapTile extends MapTile { - @Override - public String getName() { - return "z" + originalTile.renderer.getName() + "_" + getTileX() + "_" + getTileY(); - } + @Override + public String getName() { + return "z" + originalTile.renderer.getName() + "_" + getTileX() + "_" + getTileY(); + } - public BufferedImage unzoomedImage; - public KzedMapTile originalTile; + public BufferedImage unzoomedImage; + public KzedMapTile originalTile; - public KzedZoomedMapTile(KzedMap map, BufferedImage unzoomedImage, KzedMapTile original) { - super(map); - this.unzoomedImage = unzoomedImage; - this.originalTile = original; - } + public KzedZoomedMapTile(KzedMap map, BufferedImage unzoomedImage, KzedMapTile original) { + super(map); + this.unzoomedImage = unzoomedImage; + this.originalTile = original; + } - public int getTileX() { - return ztilex(originalTile.px + KzedMap.tileWidth); - } + public int getTileX() { + return ztilex(originalTile.px + KzedMap.tileWidth); + } - public int getTileY() { - return ztiley(originalTile.py); - } + public int getTileY() { + return ztiley(originalTile.py); + } - private static int ztilex(int x) { - if (x < 0) - return x + (x % (KzedMap.tileWidth * 2)); - else - return x - (x % (KzedMap.tileWidth * 2)); - } + private static int ztilex(int x) { + if (x < 0) + return x + (x % (KzedMap.tileWidth * 2)); + else + return x - (x % (KzedMap.tileWidth * 2)); + } - /* zoomed-out tile Y for tile position y */ - private static int ztiley(int y) { - if (y < 0) - return y + (y % (KzedMap.tileHeight * 2)); - else - return y - (y % (KzedMap.tileHeight * 2)); - } + /* zoomed-out tile Y for tile position y */ + private static int ztiley(int y) { + if (y < 0) + return y + (y % (KzedMap.tileHeight * 2)); + else + return y - (y % (KzedMap.tileHeight * 2)); + } - @Override - public int hashCode() { - return getName().hashCode(); - } + @Override + public int hashCode() { + return getName().hashCode(); + } - @Override - public boolean equals(Object obj) { - if (obj instanceof KzedZoomedMapTile) { - return ((KzedZoomedMapTile) obj).originalTile.equals(originalTile); - } - return super.equals(obj); - } + @Override + public boolean equals(Object obj) { + if (obj instanceof KzedZoomedMapTile) { + return ((KzedZoomedMapTile) obj).originalTile.equals(originalTile); + } + return super.equals(obj); + } } diff --git a/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java b/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java index af2fc549..eb87a5c1 100644 --- a/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/MapTileRenderer.java @@ -1,7 +1,7 @@ package org.dynmap.kzedmap; - public interface MapTileRenderer { - String getName(); - boolean render(KzedMapTile tile, String path); + String getName(); + + boolean render(KzedMapTile tile, String path); } diff --git a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java index d76c5661..d616042a 100644 --- a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java @@ -10,66 +10,68 @@ import javax.imageio.ImageIO; import org.dynmap.debug.Debugger; public class ZoomedTileRenderer { - protected Debugger debugger; - - public ZoomedTileRenderer(Debugger debugger, Map configuration) { - this.debugger = debugger; - } - - public void render(KzedZoomedMapTile zt, String outputPath) { - KzedMapTile t = zt.originalTile; - String zoomPath = new File(new File(outputPath), zt.getName() + ".png").getPath(); - render(t.px, t.py, zt.getTileX(), zt.getTileY(), zt.unzoomedImage, zoomPath); - } - - public void render(int px, int py, int zpx, int zpy, BufferedImage image, String zoomPath) { - BufferedImage zIm = null; - debugger.debug("Trying to load zoom-out tile: " + zoomPath); - try { - File file = new File(zoomPath); - zIm = ImageIO.read(file); - } catch(IOException e) { - } + protected Debugger debugger; - if(zIm == null) { - /* create new one */ - zIm = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); - debugger.debug("New zoom-out tile created " + zoomPath); - } else { - debugger.debug("Loaded zoom-out tile from " + zoomPath); - } + public ZoomedTileRenderer(Debugger debugger, Map configuration) { + this.debugger = debugger; + } - /* update zoom-out tile */ + public void render(KzedZoomedMapTile zt, String outputPath) { + KzedMapTile t = zt.originalTile; + String zoomPath = new File(new File(outputPath), zt.getName() + ".png").getPath(); + render(t.px, t.py, zt.getTileX(), zt.getTileY(), zt.unzoomedImage, zoomPath); + } - /* scaled size */ - int scw = KzedMap.tileWidth / 2; - int sch = KzedMap.tileHeight / 2; + public void render(int px, int py, int zpx, int zpy, BufferedImage image, String zoomPath) { + BufferedImage zIm = null; + debugger.debug("Trying to load zoom-out tile: " + zoomPath); + try { + File file = new File(zoomPath); + zIm = ImageIO.read(file); + } catch (IOException e) { + } - /* origin in zoomed-out tile */ - int ox = 0; - int oy = 0; + if (zIm == null) { + /* create new one */ + zIm = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); + debugger.debug("New zoom-out tile created " + zoomPath); + } else { + debugger.debug("Loaded zoom-out tile from " + zoomPath); + } - if(zpx != px) ox = scw; - if(zpy != py) oy = sch; + /* update zoom-out tile */ - /* blit scaled rendered tile onto zoom-out tile */ - //WritableRaster zr = zIm.getRaster(); - Graphics2D g2 = zIm.createGraphics(); - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.drawImage(image, ox, oy, scw, sch, null); + /* scaled size */ + int scw = KzedMap.tileWidth / 2; + int sch = KzedMap.tileHeight / 2; - image.flush(); - - /* save zoom-out tile */ - try { - File file = new File(zoomPath); - ImageIO.write(zIm, "png", file); - debugger.debug("Saved zoom-out tile at " + zoomPath); - } catch(IOException e) { - debugger.error("Failed to save zoom-out tile: " + zoomPath, e); - } catch(java.lang.NullPointerException e) { - debugger.error("Failed to save zoom-out tile (NullPointerException): " + zoomPath, e); - } - zIm.flush(); - } + /* origin in zoomed-out tile */ + int ox = 0; + int oy = 0; + + if (zpx != px) + ox = scw; + if (zpy != py) + oy = sch; + + /* blit scaled rendered tile onto zoom-out tile */ + // WritableRaster zr = zIm.getRaster(); + Graphics2D g2 = zIm.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(image, ox, oy, scw, sch, null); + + image.flush(); + + /* save zoom-out tile */ + try { + File file = new File(zoomPath); + ImageIO.write(zIm, "png", file); + debugger.debug("Saved zoom-out tile at " + zoomPath); + } catch (IOException e) { + debugger.error("Failed to save zoom-out tile: " + zoomPath, e); + } catch (java.lang.NullPointerException e) { + debugger.error("Failed to save zoom-out tile (NullPointerException): " + zoomPath, e); + } + zIm.flush(); + } } diff --git a/src/main/java/org/dynmap/web/WebServer.java b/src/main/java/org/dynmap/web/WebServer.java index 680f94d1..4c094998 100644 --- a/src/main/java/org/dynmap/web/WebServer.java +++ b/src/main/java/org/dynmap/web/WebServer.java @@ -14,65 +14,63 @@ import org.dynmap.debug.Debugger; public class WebServer extends Thread { - public static final String VERSION = "Huncraft"; - protected static final Logger log = Logger.getLogger("Minecraft"); + public static final String VERSION = "Huncraft"; + protected static final Logger log = Logger.getLogger("Minecraft"); - private Debugger debugger; - - private ServerSocket sock = null; - private boolean running = false; + private Debugger debugger; - private MapManager mgr; - private World world; - private PlayerList playerList; - private ConfigurationNode configuration; + private ServerSocket sock = null; + private boolean running = false; - public WebServer(MapManager mgr, World world, PlayerList playerList, Debugger debugger, ConfigurationNode configuration) throws IOException - { - this.mgr = mgr; - this.world = world; - this.playerList = playerList; - this.configuration = configuration; - this.debugger = debugger; - - String bindAddress = configuration.getString("webserver-bindaddress", "0.0.0.0"); - int port = configuration.getInt("webserver-port", 8123); - - sock = new ServerSocket(port, 5, bindAddress.equals("0.0.0.0") ? null : InetAddress.getByName(bindAddress)); - running = true; - start(); - log.info("Dynmap WebServer started on " + bindAddress + ":" + port); - } + private MapManager mgr; + private World world; + private PlayerList playerList; + private ConfigurationNode configuration; - public void run() - { - try { - while (running) { - try { - Socket socket = sock.accept(); - WebServerRequest requestThread = new WebServerRequest(socket, mgr, world, playerList, configuration, debugger); - requestThread.start(); - } - catch (IOException e) { - log.info("map WebServer.run() stops with IOException"); - break; - } - } - log.info("map WebServer run() exiting"); - } catch (Exception ex) { - debugger.error("Exception on WebServer-thread: " + ex.toString()); - } - } + public WebServer(MapManager mgr, World world, PlayerList playerList, Debugger debugger, ConfigurationNode configuration) throws IOException { + this.mgr = mgr; + this.world = world; + this.playerList = playerList; + this.configuration = configuration; + this.debugger = debugger; - public void shutdown() - { - try { - if(sock != null) { - sock.close(); - } - } catch(IOException e) { - log.info("map stop() got IOException while closing socket"); - } - running = false; - } + String bindAddress = configuration.getString("webserver-bindaddress", "0.0.0.0"); + int port = configuration.getInt("webserver-port", 8123); + + sock = new ServerSocket(port, 5, bindAddress.equals("0.0.0.0") + ? null + : InetAddress.getByName(bindAddress)); + running = true; + start(); + log.info("Dynmap WebServer started on " + bindAddress + ":" + port); + } + + public void run() { + try { + while (running) { + try { + Socket socket = sock.accept(); + WebServerRequest requestThread = new WebServerRequest(socket, mgr, world, playerList, configuration, debugger); + requestThread.start(); + } catch (IOException e) { + log.info("map WebServer.run() stops with IOException"); + break; + } + } + log.info("map WebServer run() exiting"); + } catch (Exception ex) { + debugger.error("Exception on WebServer-thread: " + ex.toString()); + } + } + + public void shutdown() { + try { + if (sock != null) { + sock.close(); + } + } catch (IOException e) { + log.info("map stop() got IOException while closing socket"); + } + running = false; + } } diff --git a/src/main/java/org/dynmap/web/WebServerRequest.java b/src/main/java/org/dynmap/web/WebServerRequest.java index 57bce14b..f3927a1b 100644 --- a/src/main/java/org/dynmap/web/WebServerRequest.java +++ b/src/main/java/org/dynmap/web/WebServerRequest.java @@ -25,263 +25,286 @@ import org.dynmap.TileUpdate; import org.dynmap.debug.Debugger; public class WebServerRequest extends Thread { - protected static final Logger log = Logger.getLogger("Minecraft"); + protected static final Logger log = Logger.getLogger("Minecraft"); - private Debugger debugger; - private Socket socket; - private MapManager mgr; - private World world; - private PlayerList playerList; - private ConfigurationNode configuration; + private Debugger debugger; + private Socket socket; + private MapManager mgr; + private World world; + private PlayerList playerList; + private ConfigurationNode configuration; - public WebServerRequest(Socket socket, MapManager mgr, World world, PlayerList playerList, ConfigurationNode configuration, Debugger debugger) - { - this.debugger = debugger; - this.socket = socket; - this.mgr = mgr; - this.world = world; - this.playerList = playerList; - this.configuration = configuration; - } - - private static void writeHttpHeader(BufferedOutputStream out, int statusCode, String statusText) throws IOException { - out.write("HTTP/1.0 ".getBytes()); - out.write(Integer.toString(statusCode).getBytes()); - out.write((" " + statusText + "\r\n").getBytes()); - } - - private static void writeHeaderField(BufferedOutputStream out, String name, String value) throws IOException { - out.write(name.getBytes()); - out.write((int)':'); - out.write((int)' '); - out.write(value.getBytes()); - out.write(13); - out.write(10); - } - - private static void writeEndOfHeaders(BufferedOutputStream out) throws IOException { - out.write(13); - out.write(10); - } + public WebServerRequest(Socket socket, MapManager mgr, World world, PlayerList playerList, ConfigurationNode configuration, Debugger debugger) { + this.debugger = debugger; + this.socket = socket; + this.mgr = mgr; + this.world = world; + this.playerList = playerList; + this.configuration = configuration; + } - public void run() - { - BufferedReader in = null; - BufferedOutputStream out = null; - try { - socket.setSoTimeout(30000); - in = new BufferedReader(new InputStreamReader(socket.getInputStream())); - out = new BufferedOutputStream(socket.getOutputStream()); + private static void writeHttpHeader(BufferedOutputStream out, int statusCode, String statusText) throws IOException { + out.write("HTTP/1.0 ".getBytes()); + out.write(Integer.toString(statusCode).getBytes()); + out.write((" " + statusText + "\r\n").getBytes()); + } - String request = in.readLine(); - if (request == null || !request.startsWith("GET ") || !(request.endsWith(" HTTP/1.0") || request.endsWith("HTTP/1.1"))) { - // Invalid request type (no "GET") - writeHttpHeader(out, 500, "Invalid Method."); - writeEndOfHeaders(out); - return; - } + private static void writeHeaderField(BufferedOutputStream out, String name, String value) throws IOException { + out.write(name.getBytes()); + out.write((int) ':'); + out.write((int) ' '); + out.write(value.getBytes()); + out.write(13); + out.write(10); + } - String path = request.substring(4, request.length() - 9); - debugger.debug("request: " + path); - if (path.equals("/up/configuration")) { - handleConfiguration(out); - } else if (path.startsWith("/up/")) { - handleUp(out, path.substring(3)); - } else if (path.startsWith("/tiles/")) { - handleMapToDirectory(out, path.substring(6), mgr.tileDirectory); - } else if (path.startsWith("/")) { - handleMapToDirectory(out, path, mgr.webDirectory); - } - out.flush(); - out.close(); - } - catch (IOException e) { - if (out != null) { try { out.close(); } catch (Exception anye) { } } - if (in != null) { try { in.close(); } catch (Exception anye) { } } - } - catch(Exception ex) { - if (out != null) { try { out.close(); } catch (Exception anye) { } } - if (in != null) { try { in.close(); } catch (Exception anye) { } } - debugger.error("Exception on WebRequest-thread: " + ex.toString()); - } - } - - public String stringifyJson(Object o) { - if (o == null) { - return "null"; - } else if (o instanceof Boolean) { - return ((Boolean)o) ? "true" : "false"; - } else if (o instanceof String) { - return "\"" + o + "\""; - } else if (o instanceof Integer || o instanceof Long || o instanceof Float || o instanceof Double) { - return o.toString(); - } else if (o instanceof LinkedHashMap) { - @SuppressWarnings("unchecked") - LinkedHashMap m = (LinkedHashMap)o; - StringBuilder sb = new StringBuilder(); - sb.append("{"); - boolean first = true; - for (String key : m.keySet()) { - if (first) first = false; - else sb.append(","); - - sb.append(stringifyJson(key)); - sb.append(": "); - sb.append(stringifyJson(m.get(key))); - } - sb.append("}"); - return sb.toString(); - } else if (o instanceof ArrayList) { - @SuppressWarnings("unchecked") - ArrayList l = (ArrayList)o; - StringBuilder sb = new StringBuilder(); - int count = 0; - for(int i=0;i 0) extension = path.substring(dotindex); - - writeHttpHeader(out, 200, "OK"); - writeHeaderField(out, "Content-Type", getMimeTypeFromExtension(extension)); - writeHeaderField(out, "Connection", "close"); - writeEndOfHeaders(out); - try { - int readBytes; - while((readBytes = fileInput.read(readBuffer)) > 0) { - out.write(readBuffer, 0, readBytes); - } - } catch(IOException e) { - fileInput.close(); - throw e; - } - fileInput.close(); - } - - public String getFilePath(String path) { - int qmark = path.indexOf('?'); - if (qmark >= 0) path = path.substring(0, qmark); - path = path.substring(1); - - if (path.startsWith("/") || path.startsWith(".")) - return null; - if (path.length() == 0) path = "index.html"; - return path; - } - - public void handleMapToJar(BufferedOutputStream out, String path) throws IOException { - path = getFilePath(path); - if (path != null) { - InputStream s = this.getClass().getResourceAsStream("/web/" + path); - if (s != null) { - writeFile(out, path, s); - return; - } - } - writeHttpHeader(out, 404, "Not found"); - writeEndOfHeaders(out); - } - - public void handleMapToDirectory(BufferedOutputStream out, String path, File directory) throws IOException { - path = getFilePath(path); - if (path != null) { - File tileFile = new File(directory, path); - - if (tileFile.getAbsolutePath().startsWith(directory.getAbsolutePath()) && tileFile.isFile()) { - FileInputStream s = new FileInputStream(tileFile); - writeFile(out, path, s); - return; - } - } - writeHttpHeader(out, 404, "Not found"); - writeEndOfHeaders(out); - } - - private static Map mimes = new HashMap(); - static { - mimes.put(".html", "text/html"); - mimes.put(".htm", "text/html"); - mimes.put(".js", "text/javascript"); - mimes.put(".png", "image/png"); - mimes.put(".css", "text/css"); - mimes.put(".txt", "text/plain"); - } - public static String getMimeTypeFromExtension(String extension) { - String m = mimes.get(extension); - if (m != null) return m; - return "application/octet-steam"; - } + public String stringifyJson(Object o) { + if (o == null) { + return "null"; + } else if (o instanceof Boolean) { + return ((Boolean) o) ? "true" : "false"; + } else if (o instanceof String) { + return "\"" + o + "\""; + } else if (o instanceof Integer || o instanceof Long || o instanceof Float || o instanceof Double) { + return o.toString(); + } else if (o instanceof LinkedHashMap) { + @SuppressWarnings("unchecked") + LinkedHashMap m = (LinkedHashMap) o; + StringBuilder sb = new StringBuilder(); + sb.append("{"); + boolean first = true; + for (String key : m.keySet()) { + if (first) + first = false; + else + sb.append(","); + + sb.append(stringifyJson(key)); + sb.append(": "); + sb.append(stringifyJson(m.get(key))); + } + sb.append("}"); + return sb.toString(); + } else if (o instanceof ArrayList) { + @SuppressWarnings("unchecked") + ArrayList l = (ArrayList) o; + StringBuilder sb = new StringBuilder(); + int count = 0; + for (int i = 0; i < l.size(); i++) { + sb.append(count++ == 0 ? "[" : ","); + sb.append(stringifyJson(l.get(i))); + } + sb.append("]"); + return sb.toString(); + } else { + return "undefined"; + } + } + + public void handleConfiguration(BufferedOutputStream out) throws IOException { + + String s = stringifyJson(configuration.getProperty("web")); + + byte[] bytes = s.getBytes(); + String dateStr = new Date().toString(); + writeHttpHeader(out, 200, "OK"); + writeHeaderField(out, "Date", dateStr); + writeHeaderField(out, "Content-Type", "text/plain"); + writeHeaderField(out, "Expires", "Thu, 01 Dec 1994 16:00:00 GMT"); + writeHeaderField(out, "Last-modified", dateStr); + writeHeaderField(out, "Content-Length", Integer.toString(bytes.length)); + writeEndOfHeaders(out); + out.write(bytes); + } + + public void handleUp(BufferedOutputStream out, String path) throws IOException { + int current = (int) (System.currentTimeMillis() / 1000); + long cutoff = 0; + + if (path.charAt(0) == '/') { + try { + cutoff = ((long) Integer.parseInt(path.substring(1))) * 1000; + } catch (NumberFormatException e) { + } + } + + StringBuilder sb = new StringBuilder(); + long relativeTime = world.getTime() % 24000; + sb.append(current + " " + relativeTime + "\n"); + + Player[] players = playerList.getVisiblePlayers(); + for (Player player : players) { + sb.append("player " + player.getName() + " " + player.getLocation().getX() + " " + player.getLocation().getY() + " " + player.getLocation().getZ() + "\n"); + } + + TileUpdate[] tileUpdates = mgr.staleQueue.getTileUpdates(cutoff); + for (TileUpdate tu : tileUpdates) { + sb.append("tile " + tu.tile.getName() + "\n"); + } + + ChatQueue.ChatMessage[] messages = mgr.chatQueue.getChatMessages(cutoff); + for (ChatQueue.ChatMessage cu : messages) { + sb.append("chat " + cu.playerName + " " + cu.message + "\n"); + } + + debugger.debug("Sending " + players.length + " players, " + tileUpdates.length + " tile-updates, and " + messages.length + " chats. " + path + ";" + cutoff); + + byte[] bytes = sb.toString().getBytes(); + + String dateStr = new Date().toString(); + writeHttpHeader(out, 200, "OK"); + writeHeaderField(out, "Date", dateStr); + writeHeaderField(out, "Content-Type", "text/plain"); + writeHeaderField(out, "Expires", "Thu, 01 Dec 1994 16:00:00 GMT"); + writeHeaderField(out, "Last-modified", dateStr); + writeHeaderField(out, "Content-Length", Integer.toString(bytes.length)); + writeEndOfHeaders(out); + out.write(bytes); + } + + private byte[] readBuffer = new byte[40960]; + + public void writeFile(BufferedOutputStream out, String path, InputStream fileInput) throws IOException { + int dotindex = path.lastIndexOf('.'); + String extension = null; + if (dotindex > 0) + extension = path.substring(dotindex); + + writeHttpHeader(out, 200, "OK"); + writeHeaderField(out, "Content-Type", getMimeTypeFromExtension(extension)); + writeHeaderField(out, "Connection", "close"); + writeEndOfHeaders(out); + try { + int readBytes; + while ((readBytes = fileInput.read(readBuffer)) > 0) { + out.write(readBuffer, 0, readBytes); + } + } catch (IOException e) { + fileInput.close(); + throw e; + } + fileInput.close(); + } + + public String getFilePath(String path) { + int qmark = path.indexOf('?'); + if (qmark >= 0) + path = path.substring(0, qmark); + path = path.substring(1); + + if (path.startsWith("/") || path.startsWith(".")) + return null; + if (path.length() == 0) + path = "index.html"; + return path; + } + + public void handleMapToJar(BufferedOutputStream out, String path) throws IOException { + path = getFilePath(path); + if (path != null) { + InputStream s = this.getClass().getResourceAsStream("/web/" + path); + if (s != null) { + writeFile(out, path, s); + return; + } + } + writeHttpHeader(out, 404, "Not found"); + writeEndOfHeaders(out); + } + + public void handleMapToDirectory(BufferedOutputStream out, String path, File directory) throws IOException { + path = getFilePath(path); + if (path != null) { + File tileFile = new File(directory, path); + + if (tileFile.getAbsolutePath().startsWith(directory.getAbsolutePath()) && tileFile.isFile()) { + FileInputStream s = new FileInputStream(tileFile); + writeFile(out, path, s); + return; + } + } + writeHttpHeader(out, 404, "Not found"); + writeEndOfHeaders(out); + } + + private static Map mimes = new HashMap(); + static { + mimes.put(".html", "text/html"); + mimes.put(".htm", "text/html"); + mimes.put(".js", "text/javascript"); + mimes.put(".png", "image/png"); + mimes.put(".css", "text/css"); + mimes.put(".txt", "text/plain"); + } + + public static String getMimeTypeFromExtension(String extension) { + String m = mimes.get(extension); + if (m != null) + return m; + return "application/octet-steam"; + } }