From 8860eb8c04df162f4c3012be27576ab582ebc981 Mon Sep 17 00:00:00 2001 From: FrozenCow Date: Sat, 8 Jan 2011 03:20:51 +0100 Subject: [PATCH] Splitted up MapManager. --- .../java/org/dynmap/DynmapBlockListener.java | 3 +- .../java/org/dynmap/DynmapPlayerListener.java | 25 ++ src/main/java/org/dynmap/Map.java | 26 ++ src/main/java/org/dynmap/MapLocation.java | 6 + src/main/java/org/dynmap/MapManager.java | 405 +----------------- src/main/java/org/dynmap/MapTile.java | 145 +------ src/main/java/org/dynmap/StaleQueue.java | 96 +++++ .../java/org/dynmap/WebServerRequest.java | 16 +- .../{render => kzedmap}/CaveTileRenderer.java | 2 +- .../{render => kzedmap}/DayTileRenderer.java | 20 +- src/main/java/org/dynmap/kzedmap/KzedMap.java | 295 +++++++++++++ .../java/org/dynmap/kzedmap/KzedMapTile.java | 151 +++++++ .../{render => kzedmap}/MapTileRenderer.java | 4 +- .../dynmap/render/CombinedTileRenderer.java | 18 - web/map.js | 38 +- 15 files changed, 655 insertions(+), 595 deletions(-) create mode 100644 src/main/java/org/dynmap/DynmapPlayerListener.java create mode 100644 src/main/java/org/dynmap/Map.java create mode 100644 src/main/java/org/dynmap/MapLocation.java create mode 100644 src/main/java/org/dynmap/StaleQueue.java rename src/main/java/org/dynmap/{render => kzedmap}/CaveTileRenderer.java (98%) rename src/main/java/org/dynmap/{render => kzedmap}/DayTileRenderer.java (93%) create mode 100644 src/main/java/org/dynmap/kzedmap/KzedMap.java create mode 100644 src/main/java/org/dynmap/kzedmap/KzedMapTile.java rename src/main/java/org/dynmap/{render => kzedmap}/MapTileRenderer.java (52%) delete mode 100644 src/main/java/org/dynmap/render/CombinedTileRenderer.java diff --git a/src/main/java/org/dynmap/DynmapBlockListener.java b/src/main/java/org/dynmap/DynmapBlockListener.java index 829ebf8f..1168ab6f 100644 --- a/src/main/java/org/dynmap/DynmapBlockListener.java +++ b/src/main/java/org/dynmap/DynmapBlockListener.java @@ -18,7 +18,6 @@ public class DynmapBlockListener extends BlockListener { @Override public void onBlockPlaced(BlockPlacedEvent event) { Block blockPlaced = event.getBlock(); - if(mgr.touch(blockPlaced.getX(), blockPlaced.getY(), blockPlaced.getZ())) - mgr.debug(/*player.getName() + */" touch " + blockPlaced.getX() + "," + blockPlaced.getY() + "," + blockPlaced.getZ() + " from onBlockCreate"); + mgr.touch(blockPlaced.getX(), blockPlaced.getY(), blockPlaced.getZ()); } } diff --git a/src/main/java/org/dynmap/DynmapPlayerListener.java b/src/main/java/org/dynmap/DynmapPlayerListener.java new file mode 100644 index 00000000..592e93e8 --- /dev/null +++ b/src/main/java/org/dynmap/DynmapPlayerListener.java @@ -0,0 +1,25 @@ +package org.dynmap; + +import java.util.logging.Logger; +import org.bukkit.*; +import org.bukkit.event.player.PlayerChatEvent; +import org.bukkit.event.player.PlayerListener; + +public class DynmapPlayerListener extends PlayerListener { + private static final Logger log = Logger.getLogger("Minecraft"); + private MapManager mgr; + + public DynmapPlayerListener(MapManager mgr) { + this.mgr = mgr; + } + + @Override + public void onPlayerCommand(PlayerChatEvent event) { + String[] split = event.getMessage().split(" "); + Player player = event.getPlayer(); + if (split[0].equalsIgnoreCase("/map_render")) { + mgr.touch(player.getLocation().getBlockX(), player.getLocation().getBlockY(), player.getLocation().getBlockZ()); + event.setCancelled(true); + } + } +} \ No newline at end of file diff --git a/src/main/java/org/dynmap/Map.java b/src/main/java/org/dynmap/Map.java new file mode 100644 index 00000000..e5dbbb97 --- /dev/null +++ b/src/main/java/org/dynmap/Map.java @@ -0,0 +1,26 @@ +package org.dynmap; + +import org.bukkit.Block; +import org.bukkit.Location; +import org.bukkit.World; + +public abstract class Map { + private World world; + public World getWorld() { + return world; + } + + private StaleQueue staleQueue; + + public Map(World world, StaleQueue queue) { + this.world = world; + this.staleQueue = queue; + } + + public void invalidateTile(MapTile tile) { + staleQueue.pushStaleTile(tile); + } + + public abstract void touch(Location l); + public abstract void render(MapTile tile); +} diff --git a/src/main/java/org/dynmap/MapLocation.java b/src/main/java/org/dynmap/MapLocation.java new file mode 100644 index 00000000..983c9df3 --- /dev/null +++ b/src/main/java/org/dynmap/MapLocation.java @@ -0,0 +1,6 @@ +package org.dynmap; + +public class MapLocation { + 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 3fdc7bc7..6ea1b778 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -35,10 +35,10 @@ import java.util.logging.Logger; import org.bukkit.*; import org.dynmap.debug.Debugger; -import org.dynmap.render.CaveTileRenderer; -import org.dynmap.render.CombinedTileRenderer; -import org.dynmap.render.DayTileRenderer; -import org.dynmap.render.MapTileRenderer; +import org.dynmap.kzedmap.CaveTileRenderer; +import org.dynmap.kzedmap.DayTileRenderer; +import org.dynmap.kzedmap.KzedMap; +import org.dynmap.kzedmap.MapTileRenderer; import javax.imageio.ImageIO; @@ -47,37 +47,15 @@ public class MapManager extends Thread { private World world; private Debugger debugger; - private MapTileRenderer renderer; - - /* 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; + private org.dynmap.Map map; + public StaleQueue staleQueue; /* lock for our data structures */ public static final Object lock = new Object(); - /* a hash table of known MapTiles, by their key (projection coords) */ - private HashMap tileStore; - - /* a list of MapTiles to be updated */ - private LinkedList staleTiles; - - /* this list stores the tile updates */ - public LinkedList tileUpdates = null; - /* whether the worker thread should be running now */ private boolean running = false; - /* map x, y, z for projection origin */ - public static final int anchorx = 0; - public static final int anchory = 127; - public static final int anchorz = 0; - /* path to colors.txt */ private String colorsetpath = "colors.txt"; @@ -95,9 +73,6 @@ public class MapManager extends Thread { /* time to pause between rendering tiles (ms) */ public int renderWait = 500; - - /* remember up to this old tile updates (ms) */ - private static final int maxTileAge = 60000; public void debug(String msg) { @@ -108,6 +83,7 @@ public class MapManager extends Thread { { this.world = world; this.debugger = debugger; + this.staleQueue = new StaleQueue(); tilepath = "/srv/http/dynmap/tiles/"; colorsetpath = "colors.txt"; @@ -115,55 +91,14 @@ public class MapManager extends Thread { bindaddress = "0.0.0.0"; //webPath = "/srv/http/dynmap/"; webPath = "[JAR]"; - - tileStore = new HashMap(); - staleTiles = new LinkedList(); - tileUpdates = new LinkedList(); Map colors = loadColorSet(colorsetpath); - renderer = new CombinedTileRenderer(new MapTileRenderer[] { + map = new KzedMap(world, staleQueue, new MapTileRenderer[] { new DayTileRenderer(debugger, colors, tilepath + "t_{X}_{Y}.png"), new CaveTileRenderer(debugger, colors, tilepath + "ct_{X}_{Y}.png") }); } - /* tile X for position x */ - static int tilex(int x) - { - 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); - } - /* initialize and start map manager */ public void startManager() { @@ -193,23 +128,6 @@ public class MapManager extends Thread { } } - /* update tile update list */ - private void updateUpdates(MapTile t, LinkedList lst) - { - long now = System.currentTimeMillis(); - long deadline = now - maxTileAge; - - synchronized(lock) { - ListIterator it = lst.listIterator(0); - while(it.hasNext()) { - TileUpdate tu = it.next(); - if(tu.at < deadline || tu.tile == t) - it.remove(); - } - lst.addLast(new TileUpdate(now, t)); - } - } - /* the worker/renderer thread */ public void run() { @@ -218,12 +136,12 @@ public class MapManager extends Thread { while(running) { boolean found = false; - MapTile t = this.popStaleTile(); + MapTile t = staleQueue.popStaleTile(); if(t != null) { debugger.debug("rendering tile " + t + "..."); - renderer.render(t); + t.getMap().render(t); - updateUpdates(t, tileUpdates); + staleQueue.freshenTile(t); try { this.sleep(renderWait); @@ -244,306 +162,9 @@ public class MapManager extends Thread { log.info("Map renderer has stopped."); } - /* "touch" a block - its map tile will be regenerated */ - public boolean touch(int x, int y, int z) - { - debugger.debug("touched: " + x + "," + y + "," + z); - - int dx = x - anchorx; - int dy = y - anchory; - int dz = z - anchorz; - int px = dx + dz; - int py = dx - dz - dy; - - int tx = tilex(px); - int ty = tiley(py); - - boolean r; - - r = pushStaleTile(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) - r = pushStaleTile(tx - tileWidth, ty) || r; - if(redge) - r = pushStaleTile(tx + tileWidth, ty) || r; - if(tedge) - r = pushStaleTile(tx, ty - tileHeight) || r; - if(bedge) - r = pushStaleTile(tx, ty + tileHeight) || r; - - if(ledge && tedge) - r = pushStaleTile(tx - tileWidth, ty - tileHeight) || r; - if(ledge && bedge) - r = pushStaleTile(tx - tileWidth, ty + tileHeight) || r; - if(redge && tedge) - r = pushStaleTile(tx + tileWidth, ty - tileHeight) || r; - if(redge && bedge) - r = pushStaleTile(tx + tileWidth, ty + tileHeight) || r; - - return r; + public void touch(int x, int y, int z) { + map.touch(new Location(world, x, y, z)); } - - /* get next MapTile that needs to be regenerated, or null - * the mapTile is removed from the list of stale tiles! */ - public MapTile popStaleTile() - { - synchronized(lock) { - try { - MapTile t = staleTiles.removeFirst(); - t.stale = false; - return t; - } catch(NoSuchElementException e) { - return null; - } - } - } - - /* put a MapTile that needs to be regenerated on the list of stale tiles */ - public boolean pushStaleTile(MapTile m) - { - synchronized(lock) { - boolean ret = false; - - if(!m.stale) { - m.stale = true; - staleTiles.addLast(m); - - debug(m.toString() + " is now stale"); - - ret = true; - } - - return ret; - } - } - - /* make a MapTile stale by projection position */ - public boolean pushStaleTile(int tx, int ty) - { - return pushStaleTile(getTileByPosition(tx, ty)); - } - - /* get (or create) MapTile by projection position */ - private MapTile getTileByPosition(int px, int py) - { - Long key = MapTile.key(px, py); - synchronized(lock) { - MapTile t = tileStore.get(key); - if(t == null) { - /* no maptile exists, need to create one */ - - t = new MapTile(world, px, py, ztilex(px), ztiley(py)); - tileStore.put(key, t); - return t; - } else { - return t; - } - } - } - - /* return number of stale tiles */ - public int getStaleCount() - { - synchronized(lock) { - return staleTiles.size(); - } - } - - /* return number of recently updated tiles */ - public int getRecentUpdateCount() - { - synchronized(lock) { - return tileUpdates.size(); - } - } - - /* - // regenerate the entire map, starting at position - public void regenerate(int x, int y, int z) - { - int dx = x - anchorx; - int dy = y - anchory; - int dz = z - anchorz; - int px = dx + dz; - int py = dx - dz - dy; - - int tx = tilex(px); - int ty = tiley(py); - - MapTile first = getTileByPosition(tx, ty); - - Vector open = new Vector(); - open.add(first); - - while(open.size() > 0) { - MapTile t = open.remove(open.size() - 1); - if(t.stale) continue; - int h = world.getHighestBlockYAt(t.mx, t.mz); - - log.info("walking: " + t.mx + ", " + t.mz + ", h = " + h); - if(h < 1) - continue; - - pushStaleTile(t); - - open.add(getTileByPosition(t.px + tileWidth, t.py)); - open.add(getTileByPosition(t.px - tileWidth, t.py)); - open.add(getTileByPosition(t.px, t.py + tileHeight)); - open.add(getTileByPosition(t.px, t.py - tileHeight)); - } - } - - // regenerate all zoom tiles, starting at position - public void regenerateZoom(int x, int y, int z) - { - int dx = x - anchorx; - int dy = y - anchory; - int dz = z - anchorz; - int px = dx + dz; - int py = dx - dz - dy; - - int fzpx = ztilex(tilex(px)); - int fzpy = ztiley(tiley(py)); - - class Pair implements Comparator { - public int x; - public int y; - public Pair(int x, int y) - { - this.x = x; - this.y = y; - } - - public int hashCode() - { - return (x << 16) ^ y; - } - - public boolean equals(Object o) - { - Pair p = (Pair) o; - return x == p.x && y == p.y; - } - - public int compare(Object o1, Object o2) - { - Pair p1 = (Pair) o1; - Pair p2 = (Pair) o2; - if(p1.x < p1.x) return -1; - if(p1.x > p1.x) return 1; - if(p1.y < p1.y) return -1; - if(p1.y > p1.y) return 1; - return 0; - } - } - - HashSet visited = new HashSet(); - Vector open = new Vector(); - - Pair fp = new Pair(fzpx, fzpy); - open.add(fp); - visited.add(fp); - - while(open.size() > 0) { - Pair p = open.remove(open.size() - 1); - - int zpx = p.x; - int zpy = p.y; - - log.info("Regenerating zoom tile " + zpx + "," + zpy); - - int g = regenZoomTile(zpx, zpy); - - if(g > 0) { - Pair[] np = new Pair[4]; - np[0] = new Pair(zpx-zTileWidth, zpy); - np[1] = new Pair(zpx+zTileWidth, zpy); - np[2] = new Pair(zpx, zpy-zTileHeight); - np[3] = new Pair(zpx, zpy+zTileHeight); - - for(int i=0; i<4; i++) { - if(!visited.contains(np[i])) { - visited.add(np[i]); - open.add(np[i]); - } - } - } - } - } - - // regenerate zoom-out tile - // returns number of valid subtiles - public int regenZoomTile(int zpx, int zpy) - { - int px1 = zpx + tileWidth; - int py1 = zpy; - int px2 = zpx; - int py2 = py1 + tileHeight; - - MapTile t1 = getTileByPosition(px1, py1); - MapTile t2 = getTileByPosition(px2, py1); - MapTile t3 = getTileByPosition(px1, py2); - MapTile t4 = getTileByPosition(px2, py2); - - BufferedImage im1 = t1.loadTile(this); - BufferedImage im2 = t2.loadTile(this); - BufferedImage im3 = t3.loadTile(this); - BufferedImage im4 = t4.loadTile(this); - - BufferedImage zIm = new BufferedImage(MapManager.tileWidth, MapManager.tileHeight, BufferedImage.TYPE_INT_RGB); - WritableRaster zr = zIm.getRaster(); - Graphics2D g2 = zIm.createGraphics(); - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - - int scw = tileWidth / 2; - int sch = tileHeight / 2; - - int good = 0; - - if(im1 != null) { - g2.drawImage(im1, 0, 0, scw, sch, null); - good ++; - } - - if(im2 != null) { - g2.drawImage(im2, scw, 0, scw, sch, null); - good ++; - } - - if(im3 != null) { - g2.drawImage(im3, 0, sch, scw, sch, null); - good ++; - } - - if(im4 != null) { - g2.drawImage(im4, scw, sch, scw, sch, null); - good ++; - } - - if(good == 0) { - return 0; - } - - String zPath = t1.getZoomPath(this); - // save zoom-out tile - try { - File file = new File(zPath); - ImageIO.write(zIm, "png", file); - log.info("regenZoomTile saved zoom-out tile at " + zPath); - } catch(IOException e) { - log.log(Level.SEVERE, "Failed to save zoom-out tile: " + zPath, e); - } catch(java.lang.NullPointerException e) { - log.log(Level.SEVERE, "Failed to save zoom-out tile (NullPointerException): " + zPath, e); - } - - return good; - } - */ public Map loadColorSet(String colorsetpath) { Map colors = new HashMap(); diff --git a/src/main/java/org/dynmap/MapTile.java b/src/main/java/org/dynmap/MapTile.java index fdcf13e0..1d9dcb2a 100644 --- a/src/main/java/org/dynmap/MapTile.java +++ b/src/main/java/org/dynmap/MapTile.java @@ -1,143 +1,14 @@ package org.dynmap; -import java.util.logging.Logger; -import java.util.logging.Level; - -import java.awt.*; -import java.awt.image.*; - -import java.io.File; -import java.io.IOException; - -import javax.imageio.ImageIO; -import org.bukkit.World; -import org.bukkit.Server; - -public class MapTile { - protected static final Logger log = Logger.getLogger("Minecraft"); - - private World world; - - /* projection position */ - public int px, py; - - /* projection position of zoom-out tile */ - public int zpx, zpy; - - /* minecraft space origin */ - public int mx, my, mz; - - /* whether this tile needs to be updated */ - boolean stale = false; - - /* whether the cave map of this tile needs to be updated */ - boolean staleCave = false; - - /* create new MapTile */ - public MapTile(World world, int px, int py, int zpx, int zpy) - { - this.world = world; - this.px = px; - this.py = py; - this.zpx = zpx; - this.zpy = zpy; - - mx = MapManager.anchorx + px / 2 + py / 2; - my = MapManager.anchory; - mz = MapManager.anchorz + px / 2 - py / 2; - } - - public World getWorld() { - return world; +public abstract class MapTile { + private Map map; + public Map getMap() { + return map; } - /* try to get the server to load the relevant chunks */ - public void loadChunks() - { - int x1 = mx - 64; - int x2 = mx + MapManager.tileWidth / 2 + MapManager.tileHeight / 2; - - int z1 = mz - MapManager.tileHeight / 2; - int z2 = mz + MapManager.tileWidth / 2 + 64; - - int x, z; - - for(x=x1; x 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(); + } + + /* 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 freshenTile(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/WebServerRequest.java b/src/main/java/org/dynmap/WebServerRequest.java index 764f9ffa..b0884084 100644 --- a/src/main/java/org/dynmap/WebServerRequest.java +++ b/src/main/java/org/dynmap/WebServerRequest.java @@ -112,18 +112,18 @@ public class WebServerRequest extends Thread { long relativeTime = server.getTime() % 24000; sb.append(current + " " + relativeTime + "\n"); - for(Player player : server.getOnlinePlayers()) { - sb.append(player.getName() + " player " + player.getLocation().getX() + " " + player.getLocation().getY() + " " + player.getLocation().getZ() + "\n"); + Player[] players = server.getOnlinePlayers(); + for(Player player : players) { + sb.append("player " + player.getName() + " " + player.getLocation().getX() + " " + player.getLocation().getY() + " " + player.getLocation().getZ() + "\n"); } - synchronized(mgr.lock) { - for(TileUpdate tu : mgr.tileUpdates) { - if(tu.at >= cutoff) { - sb.append(tu.tile.px + "_" + tu.tile.py + " " + tu.tile.zpx + "_" + tu.tile.zpy + " t\n"); - } - } + TileUpdate[] tileUpdates = mgr.staleQueue.getTileUpdates(cutoff); + for(TileUpdate tu : tileUpdates) { + sb.append("tile " + tu.tile.getName() + "\n"); } + //debugger.debug("Sending " + players.length + " players and " + tileUpdates.length + " tile-updates. " + path + ";" + cutoff); + byte[] bytes = sb.toString().getBytes(); String dateStr = new Date().toString(); diff --git a/src/main/java/org/dynmap/render/CaveTileRenderer.java b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java similarity index 98% rename from src/main/java/org/dynmap/render/CaveTileRenderer.java rename to src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java index 593e9c3e..5b98cedc 100644 --- a/src/main/java/org/dynmap/render/CaveTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java @@ -1,4 +1,4 @@ -package org.dynmap.render; +package org.dynmap.kzedmap; import java.awt.Color; import java.util.Map; diff --git a/src/main/java/org/dynmap/render/DayTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DayTileRenderer.java similarity index 93% rename from src/main/java/org/dynmap/render/DayTileRenderer.java rename to src/main/java/org/dynmap/kzedmap/DayTileRenderer.java index 0dbf1dd2..3b7e54d5 100644 --- a/src/main/java/org/dynmap/render/DayTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DayTileRenderer.java @@ -1,4 +1,4 @@ -package org.dynmap.render; +package org.dynmap.kzedmap; import java.awt.Color; import java.awt.Graphics2D; @@ -41,9 +41,9 @@ public class DayTileRenderer implements MapTileRenderer { return new File(outputFile.getParentFile(), zoomFilename).getPath(); } - public void render(MapTile tile) { - World world = tile.getWorld(); - BufferedImage im = new BufferedImage(MapManager.tileWidth, MapManager.tileHeight, BufferedImage.TYPE_INT_RGB); + public void render(KzedMapTile tile) { + World world = tile.getMap().getWorld(); + BufferedImage im = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); WritableRaster r = im.getRaster(); @@ -55,11 +55,11 @@ public class DayTileRenderer implements MapTileRenderer { int x, y; /* draw the map */ - for(y=0; y=0; x-=2) { + 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); @@ -76,7 +76,7 @@ public class DayTileRenderer implements MapTileRenderer { jx = ix; jz = iz - 1; - for(x=MapManager.tileWidth-1; x>=0; x-=2) { + for(x=KzedMap.tileWidth-1; x>=0; x-=2) { Color c1 = scan(world, jx, iy, jz, 2); jx++; jz++; @@ -152,7 +152,7 @@ public class DayTileRenderer implements MapTileRenderer { } /* save rendered tile, update zoom-out tile */ - public void saveTile(MapTile tile, BufferedImage im) + public void saveTile(KzedMapTile tile, BufferedImage im) { String tilePath = outputPath .replace("{X}", Integer.toString(tile.px)) @@ -238,7 +238,7 @@ public class DayTileRenderer implements MapTileRenderer { } /* try to load already generated image */ - public BufferedImage loadTile(MapTile tile) + public BufferedImage loadTile(KzedMapTile tile) { try { String path = getPath(tile); @@ -254,7 +254,7 @@ public class DayTileRenderer implements MapTileRenderer { return null; } - public String getPath(MapTile tile) + public String getPath(KzedMapTile tile) { return outputPath .replace("{X}", Integer.toString(tile.px)) diff --git a/src/main/java/org/dynmap/kzedmap/KzedMap.java b/src/main/java/org/dynmap/kzedmap/KzedMap.java new file mode 100644 index 00000000..73c4179f --- /dev/null +++ b/src/main/java/org/dynmap/kzedmap/KzedMap.java @@ -0,0 +1,295 @@ +package org.dynmap.kzedmap; + +import org.bukkit.Location; +import org.bukkit.World; +import org.dynmap.Map; +import org.dynmap.MapTile; +import org.dynmap.StaleQueue; + +public class KzedMap extends Map { + MapTileRenderer[] renderers; + + /* 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 KzedMap(World world, StaleQueue queue, MapTileRenderer[] renderers) { + super(world, queue); + this.renderers = renderers; + } + + @Override + public void touch(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); + + invalidateTile(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) invalidateTile(tx - tileWidth, ty); + if(redge) invalidateTile(tx + tileWidth, ty); + if(tedge) invalidateTile(tx, ty - tileHeight); + if(bedge) invalidateTile(tx, ty + tileHeight); + + if(ledge && tedge) invalidateTile(tx - tileWidth, ty - tileHeight); + if(ledge && bedge) invalidateTile(tx - tileWidth, ty + tileHeight); + if(redge && tedge) invalidateTile(tx + tileWidth, ty - tileHeight); + if(redge && bedge) invalidateTile(tx + tileWidth, ty + tileHeight); + } + + public void invalidateTile(int px, int py) { + invalidateTile(new KzedMapTile(this, "t", px, py, ztilex(px), ztiley(py))); + } + + @Override + public void render(MapTile tile) { + KzedMapTile t = (KzedMapTile)tile; + for(MapTileRenderer renderer : renderers) { + renderer.render(t); + } + } + + /* 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); + } + + /* + // regenerate the entire map, starting at position + public void regenerate(int x, int y, int z) + { + int dx = x - anchorx; + int dy = y - anchory; + int dz = z - anchorz; + int px = dx + dz; + int py = dx - dz - dy; + + int tx = tilex(px); + int ty = tiley(py); + + MapTile first = getTileByPosition(tx, ty); + + Vector open = new Vector(); + open.add(first); + + while(open.size() > 0) { + MapTile t = open.remove(open.size() - 1); + if(t.stale) continue; + int h = world.getHighestBlockYAt(t.mx, t.mz); + + log.info("walking: " + t.mx + ", " + t.mz + ", h = " + h); + if(h < 1) + continue; + + pushStaleTile(t); + + open.add(getTileByPosition(t.px + tileWidth, t.py)); + open.add(getTileByPosition(t.px - tileWidth, t.py)); + open.add(getTileByPosition(t.px, t.py + tileHeight)); + open.add(getTileByPosition(t.px, t.py - tileHeight)); + } + } + + // regenerate all zoom tiles, starting at position + public void regenerateZoom(int x, int y, int z) + { + int dx = x - anchorx; + int dy = y - anchory; + int dz = z - anchorz; + int px = dx + dz; + int py = dx - dz - dy; + + int fzpx = ztilex(tilex(px)); + int fzpy = ztiley(tiley(py)); + + class Pair implements Comparator { + public int x; + public int y; + public Pair(int x, int y) + { + this.x = x; + this.y = y; + } + + public int hashCode() + { + return (x << 16) ^ y; + } + + public boolean equals(Object o) + { + Pair p = (Pair) o; + return x == p.x && y == p.y; + } + + public int compare(Object o1, Object o2) + { + Pair p1 = (Pair) o1; + Pair p2 = (Pair) o2; + if(p1.x < p1.x) return -1; + if(p1.x > p1.x) return 1; + if(p1.y < p1.y) return -1; + if(p1.y > p1.y) return 1; + return 0; + } + } + + HashSet visited = new HashSet(); + Vector open = new Vector(); + + Pair fp = new Pair(fzpx, fzpy); + open.add(fp); + visited.add(fp); + + while(open.size() > 0) { + Pair p = open.remove(open.size() - 1); + + int zpx = p.x; + int zpy = p.y; + + log.info("Regenerating zoom tile " + zpx + "," + zpy); + + int g = regenZoomTile(zpx, zpy); + + if(g > 0) { + Pair[] np = new Pair[4]; + np[0] = new Pair(zpx-zTileWidth, zpy); + np[1] = new Pair(zpx+zTileWidth, zpy); + np[2] = new Pair(zpx, zpy-zTileHeight); + np[3] = new Pair(zpx, zpy+zTileHeight); + + for(int i=0; i<4; i++) { + if(!visited.contains(np[i])) { + visited.add(np[i]); + open.add(np[i]); + } + } + } + } + } + + // regenerate zoom-out tile + // returns number of valid subtiles + public int regenZoomTile(int zpx, int zpy) + { + int px1 = zpx + tileWidth; + int py1 = zpy; + int px2 = zpx; + int py2 = py1 + tileHeight; + + MapTile t1 = getTileByPosition(px1, py1); + MapTile t2 = getTileByPosition(px2, py1); + MapTile t3 = getTileByPosition(px1, py2); + MapTile t4 = getTileByPosition(px2, py2); + + BufferedImage im1 = t1.loadTile(this); + BufferedImage im2 = t2.loadTile(this); + BufferedImage im3 = t3.loadTile(this); + BufferedImage im4 = t4.loadTile(this); + + BufferedImage zIm = new BufferedImage(MapManager.tileWidth, MapManager.tileHeight, BufferedImage.TYPE_INT_RGB); + WritableRaster zr = zIm.getRaster(); + Graphics2D g2 = zIm.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + + int scw = tileWidth / 2; + int sch = tileHeight / 2; + + int good = 0; + + if(im1 != null) { + g2.drawImage(im1, 0, 0, scw, sch, null); + good ++; + } + + if(im2 != null) { + g2.drawImage(im2, scw, 0, scw, sch, null); + good ++; + } + + if(im3 != null) { + g2.drawImage(im3, 0, sch, scw, sch, null); + good ++; + } + + if(im4 != null) { + g2.drawImage(im4, scw, sch, scw, sch, null); + good ++; + } + + if(good == 0) { + return 0; + } + + String zPath = t1.getZoomPath(this); + // save zoom-out tile + try { + File file = new File(zPath); + ImageIO.write(zIm, "png", file); + log.info("regenZoomTile saved zoom-out tile at " + zPath); + } catch(IOException e) { + log.log(Level.SEVERE, "Failed to save zoom-out tile: " + zPath, e); + } catch(java.lang.NullPointerException e) { + log.log(Level.SEVERE, "Failed to save zoom-out tile (NullPointerException): " + zPath, e); + } + + return good; + } + */ +} diff --git a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java new file mode 100644 index 00000000..092d24a0 --- /dev/null +++ b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java @@ -0,0 +1,151 @@ +package org.dynmap.kzedmap; + +import java.util.logging.Logger; +import java.util.logging.Level; + +import java.awt.*; +import java.awt.image.*; + +import java.io.File; +import java.io.IOException; + +import javax.imageio.ImageIO; + +import org.bukkit.Player; +import org.bukkit.World; +import org.bukkit.Server; +import org.dynmap.MapTile; + +public class KzedMapTile extends MapTile { + protected static final Logger log = Logger.getLogger("Minecraft"); + + public KzedMap map; + + public String prefix; + + /* projection position */ + public int px, py; + + /* projection position of zoom-out tile */ + public int zpx, zpy; + + /* minecraft space origin */ + public int mx, my, mz; + + /* create new MapTile */ + public KzedMapTile(KzedMap map, String prefix, int px, int py, int zpx, int zpy) { + super(map); + this.prefix = prefix; + this.px = px; + this.py = py; + this.zpx = zpx; + this.zpy = zpy; + + mx = KzedMap.anchorx + px / 2 + py / 2; + my = KzedMap.anchory; + mz = KzedMap.anchorz + px / 2 - py / 2; + } + + @Override + public String getName() { + return prefix + "_" + this.px + "_" + this.py; + } + + /* try to get the server to load the relevant chunks */ + public void loadChunks() + { + int x1 = mx - 64; + int x2 = mx + KzedMap.tileWidth / 2 + KzedMap.tileHeight / 2; + + int z1 = mz - KzedMap.tileHeight / 2; + int z2 = mz + KzedMap.tileWidth / 2 + 64; + + int x, z; + + for(x=x1; x