From 491e88f74576950b679024ef46f83da92752cfbd Mon Sep 17 00:00:00 2001 From: zeeZ Date: Sat, 14 May 2011 20:32:18 +0800 Subject: [PATCH 1/9] Fixed kzed map scrolling on higher zoom levels --- web/js/kzedmaps.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/js/kzedmaps.js b/web/js/kzedmaps.js index 262e7ba9..825023e2 100644 --- a/web/js/kzedmaps.js +++ b/web/js/kzedmaps.js @@ -1,8 +1,8 @@ function KzedProjection() {} KzedProjection.prototype = { fromLatLngToPoint: function(latLng) { - var x = (latLng.lng() * config.tileWidth)|0; - var y = (latLng.lat() * config.tileHeight)|0; + var x = latLng.lng() * config.tileWidth; + var y = latLng.lat() * config.tileHeight; return new google.maps.Point(x, y); }, From fe93f64273820eab7f2bf022f705dbed86244d39 Mon Sep 17 00:00:00 2001 From: zeeZ Date: Sat, 14 May 2011 21:20:10 +0800 Subject: [PATCH 2/9] Added data value colored blocks to flat map --- src/main/java/org/dynmap/flat/FlatMap.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index 2bed72bb..acdc49c0 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -81,7 +81,15 @@ public class FlatMap extends MapType { int mz = y + t.y * t.size; int my = w.getHighestBlockYAt(mx, mz) - 1; int blockType = w.getBlockTypeIdAt(mx, my, mz); - Color[] colors = colorScheme.colors[blockType]; + byte data = 0; + if(colorScheme.datacolors[blockType] != null) { /* If data colored */ + data = w.getBlockAt(mx, my, mz).getData(); + } + Color[] colors; + if(data != 0) + colors = colorScheme.datacolors[blockType][data]; + else + colors = colorScheme.colors[blockType]; if (colors == null) continue; Color c = colors[0]; From 600dd00bcd5dffc745ab0de28bbcea1ac73f6486 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sat, 14 May 2011 09:23:08 -0500 Subject: [PATCH 3/9] HeroChat support - reflection based binding to it --- configuration.txt | 6 + src/main/java/org/dynmap/DynmapPlugin.java | 3 + src/main/java/org/dynmap/HeroChatHandler.java | 203 ++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 src/main/java/org/dynmap/HeroChatHandler.java diff --git a/configuration.txt b/configuration.txt index 31b7be9b..f12252b5 100644 --- a/configuration.txt +++ b/configuration.txt @@ -88,6 +88,12 @@ web: allowchat: true allowwebchat: true webchat-interval: 5 + # Set to true to enable HeroChat support + enableherochat: false + # Control which HeroChat channel messages from web are directed to + herochatwebchannel: Global + # Control which channels are monitored and reported to the web + herochatchannels: [ Global ] showplayerfacesinmenu: true diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 26decd3a..0a950e1e 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -58,6 +58,7 @@ public class DynmapPlugin extends JavaPlugin { public Configuration configuration; public HashSet enabledTriggers = new HashSet(); public PermissionProvider permissions; + public HeroChatHandler hchand; public Timer timer; @@ -106,6 +107,8 @@ public class DynmapPlugin extends JavaPlugin { timer.scheduleAtFixedRate(new JsonTimerTask(this, configuration), jsonInterval, jsonInterval); } + hchand = new HeroChatHandler(configuration, this, getServer()); + enabledTriggers.clear(); for (Object trigger : configuration.getList("render-triggers")) { enabledTriggers.add((String) trigger); diff --git a/src/main/java/org/dynmap/HeroChatHandler.java b/src/main/java/org/dynmap/HeroChatHandler.java new file mode 100644 index 00000000..5afb1d72 --- /dev/null +++ b/src/main/java/org/dynmap/HeroChatHandler.java @@ -0,0 +1,203 @@ +package org.dynmap; + +import java.util.logging.Logger; + +import org.bukkit.Server; +import org.bukkit.event.CustomEventListener; +import org.bukkit.plugin.Plugin; +import org.bukkit.util.config.Configuration; +import org.bukkit.event.Event; +import org.bukkit.event.server.PluginEnableEvent; +import org.bukkit.event.server.ServerListener; +import java.util.List; +import java.util.Collections; +import java.lang.reflect.Method; + +public class HeroChatHandler { + protected static final Logger log = Logger.getLogger("Minecraft"); + + private static final String DEF_CHANNEL = "Global"; + private static final List DEF_CHANNELS = Collections + .singletonList(DEF_CHANNEL); + + private List hcchannels; + private String hcwebinputchannel; + private DynmapPlugin plugin; + + private class OurPluginListener extends ServerListener { + @Override + public void onPluginEnable(PluginEnableEvent event) { + Plugin plugin = event.getPlugin(); + String name = plugin.getDescription().getName(); + + if (name.equals("HeroChat")) { + activateHeroChat(plugin); + } + } + } + + /* Reflection-based access wrapper for ChannelChatEvent from HeroChat */ + private static class HeroChatChannelChatEvent { + private static Class channelchatevent; + private static Method getchannel; + private static Method getsource; + private static Method getmessage; + private static Method iscancelled; + private static boolean isgood = false; + private Event evt; + + @SuppressWarnings("unchecked") + public static boolean initialize() { + try { + channelchatevent = Class + .forName("com.herocraftonline.dthielke.herochat.event.ChannelChatEvent"); + getchannel = channelchatevent.getMethod("getChannel", + new Class[0]); + getsource = channelchatevent.getMethod("getSource", + new Class[0]); + getmessage = channelchatevent.getMethod("getMessage", + new Class[0]); + iscancelled = channelchatevent.getMethod("isCancelled", + new Class[0]); + isgood = true; + } catch (ClassNotFoundException cnfx) { + } catch (NoSuchMethodException nsmx) { + } + return isgood; + } + + public HeroChatChannelChatEvent(Event evt) { + this.evt = evt; + } + + public static boolean isInstance(Event evt) { + return channelchatevent.isInstance(evt); + } + + public HeroChatChannel getChannel() { + try { + Object o; + o = getchannel.invoke(evt); + if (o != null) { + return new HeroChatChannel(o); + } + } catch (Exception x) { + } + return null; + } + + public String getSource() { + try { + return (String) getsource.invoke(evt); + } catch (Exception x) { + return null; + } + } + + public String getMessage() { + try { + return (String) getmessage.invoke(evt); + } catch (Exception x) { + return null; + } + } + + public boolean isCancelled() { + try { + return (Boolean) iscancelled.invoke(evt); + } catch (Exception x) { + return true; + } + } + } + + /* Reflection-based access wrapper for Channel from HeroChat */ + private static class HeroChatChannel { + private static Class channel; + private static Method getname; + private static boolean isgood = false; + private Object chan; + + @SuppressWarnings("unchecked") + public static boolean initialize() { + try { + channel = Class + .forName("com.herocraftonline.dthielke.herochat.channels.Channel"); + getname = channel.getMethod("getName", new Class[0]); + isgood = true; + } catch (ClassNotFoundException cnfx) { + } catch (NoSuchMethodException nsmx) { + } + return isgood; + } + + public HeroChatChannel(Object chan) { + this.chan = chan; + } + + public static boolean isInstance(Object obj) { + return channel.isInstance(obj); + } + + public String getName() { + try { + return (String) getname.invoke(chan); + } catch (Exception x) { + return null; + } + } + } + + private class OurEventListener extends CustomEventListener { + /** + * Handle custom events + */ + @Override + public void onCustomEvent(Event event) { + if (HeroChatChannelChatEvent.isInstance(event)) { + HeroChatChannelChatEvent cce = new HeroChatChannelChatEvent( + event); + if (cce.isCancelled()) + return; + HeroChatChannel c = cce.getChannel(); + if (hcchannels.contains(c.getName())) { + plugin.mapManager.pushUpdate(new Client.ChatMessage( + "player", "[" + c.getName() + "] " + + cce.getSource(), cce.getMessage())); + } + } + } + } + + public HeroChatHandler(Configuration cfg, DynmapPlugin plugin, Server server) { + /* If we're enabling hero chat support */ + if (cfg.getNode("web").getBoolean("enableherochat", false)) { + log.info("[dynmap] HeroChat support configured"); + this.plugin = plugin; + /* Now, get the monitored channel list */ + hcchannels = cfg.getNode("web").getStringList("herochatchannels", + DEF_CHANNELS); + /* And get channel to send web messages */ + hcwebinputchannel = cfg.getNode("web").getString( + "herochatwebchannel", DEF_CHANNEL); + /* Set up to hear when HeroChat is enabled */ + server.getPluginManager().registerEvent(Event.Type.PLUGIN_ENABLE, + new OurPluginListener(), Event.Priority.Normal, plugin); + } + } + + private void activateHeroChat(Plugin herochat) { + if (HeroChatChannelChatEvent.initialize() == false) { + log.severe("[dynmap] Cannot load HeroChat event class!"); + return; + } + if (HeroChatChannel.initialize() == false) { + log.severe("[dynmap] Cannot load HeroChat channel class!"); + return; + } + /* Register event handler */ + plugin.getServer().getPluginManager().registerEvent(Event.Type.CUSTOM_EVENT, + new OurEventListener(), Event.Priority.Monitor, plugin); + log.info("[dynmap] HeroChat integration active"); + } +} From 0ffc825b0591130bd1d2dd287e971916a4a5e18b Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sat, 14 May 2011 23:25:59 -0500 Subject: [PATCH 4/9] Re-merge the nether render support, and update race condition fixes --- configuration.txt | 2 +- src/main/java/org/dynmap/DynmapPlugin.java | 8 +- src/main/java/org/dynmap/UpdateQueue.java | 13 +- src/main/java/org/dynmap/flat/FlatMap.java | 80 ++++--- .../org/dynmap/kzedmap/CaveTileRenderer.java | 8 +- .../dynmap/kzedmap/DefaultTileRenderer.java | 202 +++++++++--------- .../dynmap/kzedmap/HighlightTileRenderer.java | 22 +- .../dynmap/kzedmap/ZoomedTileRenderer.java | 10 - web/js/map.js | 6 +- 9 files changed, 197 insertions(+), 154 deletions(-) diff --git a/configuration.txt b/configuration.txt index f12252b5..20e94a60 100644 --- a/configuration.txt +++ b/configuration.txt @@ -75,7 +75,7 @@ worlds: renderers: - class: org.dynmap.kzedmap.DefaultTileRenderer prefix: nt - maximumheight: 64 + maximumheight: 127 colorscheme: default web: diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 0a950e1e..02383afb 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -115,6 +115,10 @@ public class DynmapPlugin extends JavaPlugin { } registerEvents(); + + /* Print version info */ + PluginDescriptionFile pdfFile = this.getDescription(); + log.info("[dynmap] version " + pdfFile.getVersion() + " is enabled" ); } public void loadWebserver() { @@ -157,9 +161,7 @@ public class DynmapPlugin extends JavaPlugin { } catch (IOException e) { log.severe("Failed to start WebServer on " + bindAddress + ":" + port + "!"); } - /* Print version info */ - PluginDescriptionFile pdfFile = this.getDescription(); - log.info("[dynmap] version " + pdfFile.getVersion() + " is enabled" ); + } public void onDisable() { diff --git a/src/main/java/org/dynmap/UpdateQueue.java b/src/main/java/org/dynmap/UpdateQueue.java index 368c987d..995a57c5 100644 --- a/src/main/java/org/dynmap/UpdateQueue.java +++ b/src/main/java/org/dynmap/UpdateQueue.java @@ -11,10 +11,11 @@ public class UpdateQueue { private static final int maxUpdateAge = 120000; - public synchronized void pushUpdate(Object obj) { - long now = System.currentTimeMillis(); - long deadline = now - maxUpdateAge; + public void pushUpdate(Object obj) { synchronized (lock) { + /* Do inside lock - prevent delay between time and actual work */ + long now = System.currentTimeMillis(); + long deadline = now - maxUpdateAge; ListIterator i = updateQueue.listIterator(0); while (i.hasNext()) { Update u = i.next(); @@ -27,11 +28,11 @@ public class UpdateQueue { private ArrayList tmpupdates = new ArrayList(); - public synchronized Object[] getUpdatedObjects(long since) { - long now = System.currentTimeMillis(); - long deadline = now - maxUpdateAge; + public Object[] getUpdatedObjects(long since) { Object[] updates; synchronized (lock) { + long now = System.currentTimeMillis(); + long deadline = now - maxUpdateAge; tmpupdates.clear(); Iterator it = updateQueue.descendingIterator(); while (it.hasNext()) { diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index acdc49c0..987c95f5 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -11,6 +11,7 @@ import javax.imageio.ImageIO; import org.bukkit.Location; import org.bukkit.World; +import org.bukkit.World.Environment; import org.dynmap.Client; import org.dynmap.ColorScheme; import org.dynmap.DynmapChunk; @@ -22,10 +23,17 @@ import org.dynmap.debug.Debug; public class FlatMap extends MapType { private String prefix; private ColorScheme colorScheme; - + private int maximumHeight = 127; + public FlatMap(Map configuration) { prefix = (String) configuration.get("prefix"); colorScheme = ColorScheme.getScheme((String) configuration.get("colorscheme")); + Object o = configuration.get("maximumheight"); + if (o != null) { + maximumHeight = Integer.parseInt(String.valueOf(o)); + if (maximumHeight > 127) + maximumHeight = 127; + } } @Override @@ -68,6 +76,7 @@ public class FlatMap extends MapType { public boolean render(MapTile tile, File outputFile) { FlatMapTile t = (FlatMapTile) tile; World w = t.getWorld(); + boolean isnether = (w.getEnvironment() == Environment.NETHER) && (maximumHeight == 127); boolean rendered = false; BufferedImage im = new BufferedImage(t.size, t.size, BufferedImage.TYPE_INT_RGB); @@ -79,17 +88,40 @@ public class FlatMap extends MapType { for (int y = 0; y < t.size; y++) { int mx = x + t.x * t.size; int mz = y + t.y * t.size; - int my = w.getHighestBlockYAt(mx, mz) - 1; - int blockType = w.getBlockTypeIdAt(mx, my, mz); - byte data = 0; - if(colorScheme.datacolors[blockType] != null) { /* If data colored */ - data = w.getBlockAt(mx, my, mz).getData(); + int my; + int blockType; + if(isnether) { + /* Scan until we hit air */ + my = 127; + while((blockType = w.getBlockTypeIdAt(mx, my, mz)) != 0) { + my--; + if(my < 0) { /* Solid - use top */ + my = 127; + blockType = w.getBlockTypeIdAt(mx, my, mz); + break; + } + } + if(blockType == 0) { /* Hit air - now find non-air */ + while((blockType = w.getBlockTypeIdAt(mx, my, mz)) == 0) { + my--; + if(my < 0) { + my = 0; + break; + } + } + } + } + else { + my = w.getHighestBlockYAt(mx, mz) - 1; + if(my > maximumHeight) my = maximumHeight; + blockType = w.getBlockTypeIdAt(mx, my, mz); + } + byte data = 0; + Color[] colors = colorScheme.colors[blockType]; + if(colorScheme.datacolors[blockType] != null) { + data = w.getBlockAt(mx, my, mz).getData(); + colors = colorScheme.datacolors[blockType][data]; } - Color[] colors; - if(data != 0) - colors = colorScheme.datacolors[blockType][data]; - else - colors = colorScheme.colors[blockType]; if (colors == null) continue; Color c = colors[0]; @@ -135,19 +167,19 @@ public class FlatMap extends MapType { final MapTile mtile = tile; final BufferedImage img = im; MapManager.mapman.enqueueImageWrite(new Runnable() { - public void run() { - Debug.debug("saving image " + fname.getPath()); - try { - ImageIO.write(img, "png", fname); - } catch (IOException e) { - Debug.error("Failed to save image: " + fname.getPath(), e); - } catch (java.lang.NullPointerException e) { - Debug.error("Failed to save image (NullPointerException): " + fname.getPath(), e); - } - img.flush(); - MapManager.mapman.pushUpdate(mtile.getWorld(), - new Client.Tile(mtile.getFilename())); - } + public void run() { + Debug.debug("saving image " + fname.getPath()); + try { + ImageIO.write(img, "png", fname); + } catch (IOException e) { + Debug.error("Failed to save image: " + fname.getPath(), e); + } catch (java.lang.NullPointerException e) { + Debug.error("Failed to save image (NullPointerException): " + fname.getPath(), e); + } + img.flush(); + MapManager.mapman.pushUpdate(mtile.getWorld(), + new Client.Tile(mtile.getFilename())); + } }); return rendered; diff --git a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java index afdb1db7..eab3d04d 100644 --- a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java @@ -12,7 +12,7 @@ public class CaveTileRenderer extends DefaultTileRenderer { } @Override - protected Color scan(World world, int x, int y, int z, int seq) { + protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { boolean air = true; for (;;) { @@ -20,6 +20,12 @@ public class CaveTileRenderer extends DefaultTileRenderer { return translucent; int id = world.getBlockTypeIdAt(x, y, z); + if(isnether) { /* Make ceiling into air in nether */ + if(id != 0) + id = 0; + else + isnether = false; + } switch (seq) { case 0: diff --git a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java index 3b486df2..2f332dc1 100644 --- a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java @@ -12,7 +12,9 @@ import java.util.Map; import javax.imageio.ImageIO; +import org.bukkit.Material; import org.bukkit.World; +import org.bukkit.World.Environment; import org.dynmap.Client; import org.dynmap.ColorScheme; import org.dynmap.MapManager; @@ -28,25 +30,6 @@ public class DefaultTileRenderer implements MapTileRenderer { protected HashSet highlightBlocks = new HashSet(); protected Color highlightColor = new Color(255, 0, 0); - private static final Color[] woolshades = { - Color.WHITE, - Color.ORANGE, - Color.MAGENTA, - new Color(51,204,255), - Color.YELLOW, - new Color(102,255,102), - Color.PINK, - Color.GRAY, - Color.LIGHT_GRAY, - Color.CYAN, - new Color(255,0,255), - Color.BLUE, - new Color(102,51,51), - Color.GREEN, - Color.RED, - Color.BLACK - }; - @Override public String getName() { return name; @@ -65,6 +48,7 @@ public class DefaultTileRenderer implements MapTileRenderer { public boolean render(KzedMapTile tile, File outputFile) { World world = tile.getWorld(); + boolean isnether = (world.getEnvironment() == Environment.NETHER); BufferedImage im = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); WritableRaster r = im.getRaster(); @@ -74,6 +58,10 @@ public class DefaultTileRenderer implements MapTileRenderer { int iy = maximumHeight; int iz = KzedMap.anchorz + tile.px / 2 - tile.py / 2 + ((127-maximumHeight)/2); + /* Don't mess with existing height-clipped renders */ + if(maximumHeight < 127) + isnether = false; + int jx, jz; int x, y; @@ -84,8 +72,8 @@ public class DefaultTileRenderer implements MapTileRenderer { 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); + Color c1 = scan(world, jx, iy, jz, 0, isnether); + Color c2 = scan(world, jx, iy, jz, 2, isnether); isempty = isempty && c1 == translucent && c2 == translucent; r.setPixel(x, y, new int[] { c1.getRed(), @@ -107,10 +95,10 @@ public class DefaultTileRenderer implements MapTileRenderer { jz = iz - 1; for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { - Color c1 = scan(world, jx, iy, jz, 2); + Color c1 = scan(world, jx, iy, jz, 2, isnether); jx++; jz++; - Color c2 = scan(world, jx, iy, jz, 0); + Color c2 = scan(world, jx, iy, jz, 0, isnether); isempty = isempty && c1 == translucent && c2 == translucent; r.setPixel(x, y, new int[] { c1.getRed(), @@ -132,100 +120,112 @@ public class DefaultTileRenderer implements MapTileRenderer { final File fname = outputFile; final KzedMapTile mtile = tile; final BufferedImage img = im; - final KzedZoomedMapTile zmtile = new KzedZoomedMapTile(mtile.getWorld(), - (KzedMap) mtile.getMap(), mtile); - final File zoomFile = MapManager.mapman.getTileFile(zmtile); - - MapManager.mapman.enqueueImageWrite(new Runnable() { - public void run() { - doFileWrites(fname, mtile, img, zmtile, zoomFile); - } + final KzedZoomedMapTile zmtile = new KzedZoomedMapTile(mtile.getWorld(), + (KzedMap) mtile.getMap(), mtile); + final File zoomFile = MapManager.mapman.getTileFile(zmtile); + + MapManager.mapman.enqueueImageWrite(new Runnable() { + public void run() { + doFileWrites(fname, mtile, img, zmtile, zoomFile); + } }); return !isempty; } private void doFileWrites(final File fname, final KzedMapTile mtile, - final BufferedImage img, final KzedZoomedMapTile zmtile, final File zoomFile) { - Debug.debug("saving image " + fname.getPath()); - try { - ImageIO.write(img, "png", fname); - } catch (IOException e) { - Debug.error("Failed to save image: " + fname.getPath(), e); - } catch (java.lang.NullPointerException e) { - Debug.error("Failed to save image (NullPointerException): " + fname.getPath(), e); - } - mtile.file = fname; - // Since we've already got the new tile, and we're on an async thread, just - // make the zoomed tile here - int px = mtile.px; - int py = mtile.py; - int zpx = zmtile.getTileX(); - int zpy = zmtile.getTileY(); + final BufferedImage img, final KzedZoomedMapTile zmtile, final File zoomFile) { + Debug.debug("saving image " + fname.getPath()); + try { + ImageIO.write(img, "png", fname); + } catch (IOException e) { + Debug.error("Failed to save image: " + fname.getPath(), e); + } catch (java.lang.NullPointerException e) { + Debug.error("Failed to save image (NullPointerException): " + fname.getPath(), e); + } + mtile.file = fname; + // Since we've already got the new tile, and we're on an async thread, just + // make the zoomed tile here + int px = mtile.px; + int py = mtile.py; + int zpx = zmtile.getTileX(); + int zpy = zmtile.getTileY(); - /* scaled size */ - int scw = KzedMap.tileWidth / 2; - int sch = KzedMap.tileHeight / 2; + /* scaled size */ + int scw = KzedMap.tileWidth / 2; + int sch = KzedMap.tileHeight / 2; - /* origin in zoomed-out tile */ - int ox = 0; - int oy = 0; + /* origin in zoomed-out tile */ + int ox = 0; + int oy = 0; - if (zpx != px) - ox = scw; - if (zpy != py) - oy = sch; + if (zpx != px) + ox = scw; + if (zpy != py) + oy = sch; - BufferedImage zIm = null; - try { - zIm = ImageIO.read(zoomFile); - } catch (IOException e) { - } catch (IndexOutOfBoundsException e) { - } + BufferedImage zIm = null; + try { + zIm = ImageIO.read(zoomFile); + } catch (IOException e) { + } catch (IndexOutOfBoundsException e) { + } - if (zIm == null) { - /* create new one */ - zIm = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); - Debug.debug("New zoom-out tile created " + zmtile.getFilename()); - } else { - Debug.debug("Loaded zoom-out tile from " + zmtile.getFilename()); - } - - /* blit scaled rendered tile onto zoom-out tile */ - Graphics2D g2 = zIm.createGraphics(); - g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); - g2.drawImage(img, ox, oy, scw, sch, null); + if (zIm == null) { + /* create new one */ + zIm = new BufferedImage(KzedMap.tileWidth, KzedMap.tileHeight, BufferedImage.TYPE_INT_RGB); + Debug.debug("New zoom-out tile created " + zmtile.getFilename()); + } else { + Debug.debug("Loaded zoom-out tile from " + zmtile.getFilename()); + } + + /* blit scaled rendered tile onto zoom-out tile */ + Graphics2D g2 = zIm.createGraphics(); + g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); + g2.drawImage(img, ox, oy, scw, sch, null); - img.flush(); + img.flush(); - /* save zoom-out tile */ - - try { - ImageIO.write(zIm, "png", zoomFile); - Debug.debug("Saved zoom-out tile at " + zoomFile.getName()); - } catch (IOException e) { - Debug.error("Failed to save zoom-out tile: " + zoomFile.getName(), e); - } catch (java.lang.NullPointerException e) { - Debug.error("Failed to save zoom-out tile (NullPointerException): " + zoomFile.getName(), e); - } - zIm.flush(); - /* Push updates for both files.*/ - MapManager.mapman.pushUpdate(mtile.getWorld(), - new Client.Tile(mtile.getFilename())); - MapManager.mapman.pushUpdate(zmtile.getWorld(), - new Client.Tile(zmtile.getFilename())); + /* save zoom-out tile */ + + try { + ImageIO.write(zIm, "png", zoomFile); + Debug.debug("Saved zoom-out tile at " + zoomFile.getName()); + } catch (IOException e) { + Debug.error("Failed to save zoom-out tile: " + zoomFile.getName(), e); + } catch (java.lang.NullPointerException e) { + Debug.error("Failed to save zoom-out tile (NullPointerException): " + zoomFile.getName(), e); + } + zIm.flush(); + /* Push updates for both files.*/ + MapManager.mapman.pushUpdate(mtile.getWorld(), + new Client.Tile(mtile.getFilename())); + MapManager.mapman.pushUpdate(zmtile.getWorld(), + new Client.Tile(zmtile.getFilename())); } - protected Color scan(World world, int x, int y, int z, int seq) { + protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { + Color result = translucent; for (;;) { - if (y < 0) - return translucent; - + if (y < 0) { + return result; + } int id = world.getBlockTypeIdAt(x, y, z); byte data = 0; - if(colorScheme.datacolors[id] != null) { /* If data colored */ - data = world.getBlockAt(x, y, z).getData(); + if(isnether) { /* Make bedrock ceiling into air in nether */ + if(id != 0) { + /* Remember first color we see, in case we wind up solid */ + if(result == translucent) + if(colorScheme.colors[id] != null) + result = colorScheme.colors[id][seq]; + id = 0; + } + else + isnether = false; + } + if(colorScheme.datacolors[id] != null) { /* If data colored */ + data = world.getBlockAt(x, y, z).getData(); } switch (seq) { case 0: @@ -250,9 +250,9 @@ public class DefaultTileRenderer implements MapTileRenderer { } Color[] colors; if(data != 0) - colors = colorScheme.datacolors[id][data]; + colors = colorScheme.datacolors[id][data]; else - colors = colorScheme.colors[id]; + colors = colorScheme.colors[id]; if (colors != null) { Color c = colors[seq]; if (c.getAlpha() > 0) { @@ -263,7 +263,7 @@ public class DefaultTileRenderer implements MapTileRenderer { } /* this block is transparent, so recurse */ - Color bg = scan(world, x, y, z, seq); + Color bg = scan(world, x, y, z, seq, isnether); int cr = c.getRed(); int cg = c.getGreen(); diff --git a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java index dd639b70..247515af 100644 --- a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java @@ -23,17 +23,29 @@ public class HighlightTileRenderer extends DefaultTileRenderer { } @Override - protected Color scan(World world, int x, int y, int z, int seq) { + protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { Color result = translucent; + int top_nether_id = 0; for (;;) { if (y < 0) { break; } int id = world.getBlockTypeIdAt(x, y, z); + if(isnether) { /* Make bedrock ceiling into air in nether */ + if(id != 0) { + /* Remember first color we see, in case we wind up solid */ + if(result == translucent) + if(colorScheme.colors[id] != null) + result = colorScheme.colors[id][seq]; + id = 0; + } + else + isnether = false; + } byte data = 0; - if(colorScheme.datacolors[id] != null) { /* If data colored */ - data = world.getBlockAt(x, y, z).getData(); + if(colorScheme.datacolors[id] != null) { /* If data colored */ + data = world.getBlockAt(x, y, z).getData(); } switch (seq) { @@ -56,9 +68,9 @@ public class HighlightTileRenderer extends DefaultTileRenderer { if (id != 0) { Color[] colors; if(data != 0) - colors = colorScheme.datacolors[id][data]; + colors = colorScheme.datacolors[id][data]; else - colors = colorScheme.colors[id]; + colors = colorScheme.colors[id]; if (colors != null) { Color c = colors[seq]; diff --git a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java index 4d5dee8d..a0de1520 100644 --- a/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/ZoomedTileRenderer.java @@ -1,18 +1,8 @@ package org.dynmap.kzedmap; -import java.awt.Graphics2D; -import java.awt.RenderingHints; -import java.awt.image.BufferedImage; import java.io.File; -import java.io.IOException; import java.util.Map; -import javax.imageio.ImageIO; - -import org.dynmap.Client; -import org.dynmap.MapManager; -import org.dynmap.debug.Debug; - public class ZoomedTileRenderer { public ZoomedTileRenderer(Map configuration) { } diff --git a/web/js/map.js b/web/js/map.js index 0ae78f17..fdd69d2b 100644 --- a/web/js/map.js +++ b/web/js/map.js @@ -386,7 +386,7 @@ DynMap.prototype = { swtch(update.type, { tile: function() { - me.onTileUpdated(update.name); + me.onTileUpdated(update.name,update.timestamp); }, playerjoin: function() { $(me).trigger('playerjoin', [ update.playerName ]); @@ -436,12 +436,12 @@ DynMap.prototype = { unregisterTile: function(mapType, tileName) { delete this.registeredTiles[tileName]; }, - onTileUpdated: function(tileName) { + onTileUpdated: function(tileName,timestamp) { var me = this; var tile = this.registeredTiles[tileName]; if (tile) { - tile.lastseen = this.lasttimestamp; + tile.lastseen = timestamp; tile.mapType.onTileUpdated(tile.tileElement, tileName); } }, From 03376dab52748b8d13d519d6316f16371d2098ca Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 15 May 2011 07:56:45 -0500 Subject: [PATCH 5/9] Finish HeroChat support - handle directing web messages to selected channel (versus spamming everyone) --- src/main/java/org/dynmap/DynmapPlugin.java | 4 +- src/main/java/org/dynmap/HeroChatHandler.java | 151 +++++++++++++----- 2 files changed, 118 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 02383afb..c9f731b1 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -407,6 +407,8 @@ public class DynmapPlugin extends JavaPlugin { public void webChat(String name, String message) { mapManager.pushUpdate(new Client.ChatMessage("web", name, message)); log.info("[WEB]" + name + ": " + message); - getServer().broadcastMessage("[WEB]" + name + ": " + message); + /* Let HeroChat take a look - only broadcast to players if it doesn't handle it */ + if(hchand.sendWebMessageToHeroChat(name, message) == false) + getServer().broadcastMessage("[WEB]" + name + ": " + message); } } diff --git a/src/main/java/org/dynmap/HeroChatHandler.java b/src/main/java/org/dynmap/HeroChatHandler.java index 5afb1d72..3f84a7d9 100644 --- a/src/main/java/org/dynmap/HeroChatHandler.java +++ b/src/main/java/org/dynmap/HeroChatHandler.java @@ -22,6 +22,7 @@ public class HeroChatHandler { private List hcchannels; private String hcwebinputchannel; + private HeroChatChannel hcwebinputchan; private DynmapPlugin plugin; private class OurPluginListener extends ServerListener { @@ -39,10 +40,8 @@ public class HeroChatHandler { /* Reflection-based access wrapper for ChannelChatEvent from HeroChat */ private static class HeroChatChannelChatEvent { private static Class channelchatevent; - private static Method getchannel; private static Method getsource; private static Method getmessage; - private static Method iscancelled; private static boolean isgood = false; private Event evt; @@ -51,14 +50,8 @@ public class HeroChatHandler { try { channelchatevent = Class .forName("com.herocraftonline.dthielke.herochat.event.ChannelChatEvent"); - getchannel = channelchatevent.getMethod("getChannel", - new Class[0]); - getsource = channelchatevent.getMethod("getSource", - new Class[0]); - getmessage = channelchatevent.getMethod("getMessage", - new Class[0]); - iscancelled = channelchatevent.getMethod("isCancelled", - new Class[0]); + getsource = channelchatevent.getMethod("getSource", new Class[0]); + getmessage = channelchatevent.getMethod("getMessage", new Class[0]); isgood = true; } catch (ClassNotFoundException cnfx) { } catch (NoSuchMethodException nsmx) { @@ -74,18 +67,6 @@ public class HeroChatHandler { return channelchatevent.isInstance(evt); } - public HeroChatChannel getChannel() { - try { - Object o; - o = getchannel.invoke(evt); - if (o != null) { - return new HeroChatChannel(o); - } - } catch (Exception x) { - } - return null; - } - public String getSource() { try { return (String) getsource.invoke(evt); @@ -101,6 +82,49 @@ public class HeroChatHandler { return null; } } + } + + /* Reflection-based access wrapper for ChannelEvent from HeroChat */ + private static class HeroChatChannelEvent { + private static Class channelevent; + private static Method getchannel; + private static Method iscancelled; + private static boolean isgood = false; + private Event evt; + + @SuppressWarnings("unchecked") + public static boolean initialize() { + try { + channelevent = Class + .forName("com.herocraftonline.dthielke.herochat.event.ChannelEvent"); + getchannel = channelevent.getMethod("getChannel", new Class[0]); + iscancelled = channelevent.getMethod("isCancelled", new Class[0]); + isgood = true; + } catch (ClassNotFoundException cnfx) { + } catch (NoSuchMethodException nsmx) { + } + return isgood; + } + + public HeroChatChannelEvent(Event evt) { + this.evt = evt; + } + + public static boolean isInstance(Event evt) { + return channelevent.isInstance(evt); + } + + public HeroChatChannel getChannel() { + try { + Object o; + o = getchannel.invoke(evt); + if (o != null) { + return new HeroChatChannel(o); + } + } catch (Exception x) { + } + return null; + } public boolean isCancelled() { try { @@ -115,6 +139,8 @@ public class HeroChatHandler { private static class HeroChatChannel { private static Class channel; private static Method getname; + private static Method getnick; + private static Method sendmessage; private static boolean isgood = false; private Object chan; @@ -123,10 +149,14 @@ public class HeroChatHandler { try { channel = Class .forName("com.herocraftonline.dthielke.herochat.channels.Channel"); - getname = channel.getMethod("getName", new Class[0]); + getname = channel.getMethod("getName"); + getnick = channel.getMethod("getNick", new Class[0]); + sendmessage = channel.getMethod("sendMessage", new Class[] { + String.class, String.class, String.class, boolean.class } ); isgood = true; } catch (ClassNotFoundException cnfx) { } catch (NoSuchMethodException nsmx) { + System.out.println(nsmx); } return isgood; } @@ -135,10 +165,6 @@ public class HeroChatHandler { this.chan = chan; } - public static boolean isInstance(Object obj) { - return channel.isInstance(obj); - } - public String getName() { try { return (String) getname.invoke(chan); @@ -146,6 +172,21 @@ public class HeroChatHandler { return null; } } + + public String getNick() { + try { + return (String) getnick.invoke(chan); + } catch (Exception x) { + return null; + } + } + + public void sendMessage(String source, String msg, String format, boolean sentByPlayer) { + try { + sendmessage.invoke(chan, source, msg, format, sentByPlayer); + } catch (Exception x) { + } + } } private class OurEventListener extends CustomEventListener { @@ -154,16 +195,32 @@ public class HeroChatHandler { */ @Override public void onCustomEvent(Event event) { - if (HeroChatChannelChatEvent.isInstance(event)) { - HeroChatChannelChatEvent cce = new HeroChatChannelChatEvent( - event); - if (cce.isCancelled()) + if (HeroChatChannelEvent.isInstance(event)) { + HeroChatChannelEvent ce = new HeroChatChannelEvent(event); + /* Snoop for our web channel - we'll need it, and we'll see it before it matters, + * since anyone that joins the channel will give us an event (and reflection on + * the plugin class to get the manager didn't work, due to a dependency on the IRC + * plugin that may not be present....) + */ + HeroChatChannel c = ce.getChannel(); + /* If channel name or nickname matches out web channel, remember it */ + if((c != null) && (hcwebinputchannel != null) && + ((c.getName().equals(hcwebinputchannel)) || + c.getNick().equals(hcwebinputchannel))) { + hcwebinputchan = c; + } + if (ce.isCancelled()) return; - HeroChatChannel c = cce.getChannel(); - if (hcchannels.contains(c.getName())) { - plugin.mapManager.pushUpdate(new Client.ChatMessage( - "player", "[" + c.getName() + "] " + if (HeroChatChannelChatEvent.isInstance(event)) { + HeroChatChannelChatEvent cce = new HeroChatChannelChatEvent( + event); + /* Match on name or nickname of channel */ + if (hcchannels.contains(c.getName()) || + hcchannels.contains(c.getNick())) { + plugin.mapManager.pushUpdate(new Client.ChatMessage( + "player", "[" + c.getNick() + "] " + cce.getSource(), cce.getMessage())); + } } } } @@ -188,16 +245,38 @@ public class HeroChatHandler { private void activateHeroChat(Plugin herochat) { if (HeroChatChannelChatEvent.initialize() == false) { - log.severe("[dynmap] Cannot load HeroChat event class!"); + log.severe("[dynmap] Cannot load HeroChat chat event class!"); return; } if (HeroChatChannel.initialize() == false) { log.severe("[dynmap] Cannot load HeroChat channel class!"); return; } + if (HeroChatChannelEvent.initialize() == false) { + log.severe("[dynmap] Cannot load HeroChat channel event class!"); + return; + } + /* Register event handler */ plugin.getServer().getPluginManager().registerEvent(Event.Type.CUSTOM_EVENT, new OurEventListener(), Event.Priority.Monitor, plugin); log.info("[dynmap] HeroChat integration active"); } + /** + * Send message from web to appropriate HeroChat channel + * @param sender - sender ID + * @param message - message + * @return true if herochat is handling this, false if not + */ + public boolean sendWebMessageToHeroChat(String sender, String message) { + if(hcwebinputchannel != null) { /* Are we handling them? */ + if(hcwebinputchan != null) { /* Have we seen it yet? Maybe no if nobody has logged on or + * joined it, but then who would see it anyway? + */ + hcwebinputchan.sendMessage(sender, message, "{default}", false); + } + return true; + } + return false; + } } From e26ac97787a88138a9b3083fee73171c69fb9829 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 15 May 2011 08:02:45 -0500 Subject: [PATCH 6/9] Clean up tabs --- src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java | 2 +- src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java | 2 +- src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java index eab3d04d..7a5ee9d4 100644 --- a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java @@ -14,7 +14,7 @@ public class CaveTileRenderer extends DefaultTileRenderer { @Override protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { boolean air = true; - + for (;;) { if (y < 0) return translucent; diff --git a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java index 2f332dc1..6fa68dbc 100644 --- a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java @@ -63,7 +63,7 @@ public class DefaultTileRenderer implements MapTileRenderer { isnether = false; int jx, jz; - + int x, y; /* draw the map */ diff --git a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java index 247515af..c0541289 100644 --- a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java @@ -21,7 +21,7 @@ public class HighlightTileRenderer extends DefaultTileRenderer { highlightBlocks.add((Integer)highlightObj); } } - + @Override protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { Color result = translucent; From 355d54842f3cae12a04a5d39a7c8b31239672bde Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 15 May 2011 08:05:36 -0500 Subject: [PATCH 7/9] One more with the tabs --- src/main/java/org/dynmap/flat/FlatMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index 987c95f5..32d6d90c 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -135,7 +135,7 @@ public class FlatMap extends MapType { // Defines the 'step' in coloring. float step = 10 / 128.0f; - + // The step applied to height. float scale = ((int)(height/step))*step; From 271990b87c299710a90c82635ee2df1887faf8c5 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 15 May 2011 22:25:45 -0500 Subject: [PATCH 8/9] Reduce heap memory allocation on render path (less heap junk) by replacing java.awt.Color with work-alike, lightweight updatable alternative. --- src/main/java/org/dynmap/Color.java | 46 +++++++++++ src/main/java/org/dynmap/ColorScheme.java | 2 +- src/main/java/org/dynmap/flat/FlatMap.java | 2 +- .../org/dynmap/kzedmap/CaveTileRenderer.java | 11 +-- .../dynmap/kzedmap/DefaultTileRenderer.java | 76 ++++++++++--------- .../dynmap/kzedmap/HighlightTileRenderer.java | 29 ++++--- 6 files changed, 109 insertions(+), 57 deletions(-) create mode 100644 src/main/java/org/dynmap/Color.java diff --git a/src/main/java/org/dynmap/Color.java b/src/main/java/org/dynmap/Color.java new file mode 100644 index 00000000..4c7fe0ae --- /dev/null +++ b/src/main/java/org/dynmap/Color.java @@ -0,0 +1,46 @@ +package org.dynmap; + +/** + * Simple replacement for java.awt.Color for dynmap - it's not an invariant, so we don't make millions + * of them during rendering + */ +public class Color { + /* RGBA value */ + private int val; + + public static final int TRANSPARENT = 0; + + public Color(int red, int green, int blue, int alpha) { + setRGBA(red, green, blue, alpha); + } + public Color(int red, int green, int blue) { + setRGBA(red, green, blue, 0xFF); + } + public Color() { + setTransparent(); + } + public final int getRed() { + return (val >> 24) & 0xFF; + } + public final int getGreen() { + return (val >> 16) & 0xFF; + } + public final int getBlue() { + return (val >> 8) & 0xFF; + } + public final int getAlpha() { + return (val & 0xFF); + } + public final boolean isTransparent() { + return (val == TRANSPARENT); + } + public final void setTransparent() { + val = TRANSPARENT; + } + public final void setColor(Color c) { + val = c.val; + } + public final void setRGBA(int red, int green, int blue, int alpha) { + val = ((red & 0xFF) << 24) | ((green & 0xFF) << 16) | ((blue & 0xFF) << 8) | (alpha & 0xFF); + } +} diff --git a/src/main/java/org/dynmap/ColorScheme.java b/src/main/java/org/dynmap/ColorScheme.java index 99894b82..bd59b34d 100644 --- a/src/main/java/org/dynmap/ColorScheme.java +++ b/src/main/java/org/dynmap/ColorScheme.java @@ -1,6 +1,6 @@ package org.dynmap; -import java.awt.Color; +import org.dynmap.Color; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; diff --git a/src/main/java/org/dynmap/flat/FlatMap.java b/src/main/java/org/dynmap/flat/FlatMap.java index 32d6d90c..b9e2abd3 100644 --- a/src/main/java/org/dynmap/flat/FlatMap.java +++ b/src/main/java/org/dynmap/flat/FlatMap.java @@ -1,6 +1,6 @@ package org.dynmap.flat; -import java.awt.Color; +import org.dynmap.Color; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; import java.io.File; diff --git a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java index 7a5ee9d4..3c1f557f 100644 --- a/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/CaveTileRenderer.java @@ -1,6 +1,6 @@ package org.dynmap.kzedmap; -import java.awt.Color; +import org.dynmap.Color; import java.util.Map; import org.bukkit.World; @@ -12,12 +12,12 @@ public class CaveTileRenderer extends DefaultTileRenderer { } @Override - protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { + protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result) { boolean air = true; - + result.setTransparent(); for (;;) { if (y < 0) - return translucent; + return; int id = world.getBlockTypeIdAt(x, y, z); if(isnether) { /* Make ceiling into air in nether */ @@ -93,7 +93,8 @@ public class CaveTileRenderer extends DefaultTileRenderer { cg = cg * mult / 256; cb = cb * mult / 256; - return new Color(cr, cg, cb); + result.setRGBA(cr, cg, cb, 255); + return; } } } diff --git a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java index 6fa68dbc..8e034835 100644 --- a/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/DefaultTileRenderer.java @@ -1,6 +1,6 @@ package org.dynmap.kzedmap; -import java.awt.Color; +import org.dynmap.Color; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.image.BufferedImage; @@ -66,24 +66,28 @@ public class DefaultTileRenderer implements MapTileRenderer { int x, y; + Color c1 = new Color(); + Color c2 = new Color(); + int[] rgb = new int[3]; /* 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, isnether); - Color c2 = scan(world, jx, iy, jz, 2, isnether); - 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() }); - + scan(world, jx, iy, jz, 0, isnether, c1); + scan(world, jx, iy, jz, 2, isnether, c2); + if(c1.isTransparent() == false) { + rgb[0] = c1.getRed(); rgb[1] = c1.getGreen(); rgb[2] = c1.getBlue(); + r.setPixel(x, y, rgb); + isempty = false; + } + if(c2.isTransparent() == false) { + rgb[0] = c2.getRed(); rgb[1] = c2.getGreen(); rgb[2] = c2.getBlue(); + r.setPixel(x - 1, y, rgb); + isempty = false; + } + jx++; jz++; @@ -95,19 +99,21 @@ public class DefaultTileRenderer implements MapTileRenderer { jz = iz - 1; for (x = KzedMap.tileWidth - 1; x >= 0; x -= 2) { - Color c1 = scan(world, jx, iy, jz, 2, isnether); + scan(world, jx, iy, jz, 2, isnether, c1); jx++; jz++; - Color c2 = scan(world, jx, iy, jz, 0, isnether); - 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() }); + scan(world, jx, iy, jz, 0, isnether, c2); + if(c1.isTransparent() == false) { + rgb[0] = c1.getRed(); rgb[1] = c1.getGreen(); rgb[2] = c1.getBlue(); + r.setPixel(x, y, rgb); + isempty = false; + } + if(c2.isTransparent() == false) { + rgb[0] = c2.getRed(); rgb[1] = c2.getGreen(); rgb[2] = c2.getBlue(); + + r.setPixel(x - 1, y, rgb); + isempty = false; + } } y++; @@ -205,20 +211,20 @@ public class DefaultTileRenderer implements MapTileRenderer { } - protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { - Color result = translucent; + protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result) { + result.setTransparent(); for (;;) { if (y < 0) { - return result; + return; } int id = world.getBlockTypeIdAt(x, y, z); byte data = 0; if(isnether) { /* Make bedrock ceiling into air in nether */ if(id != 0) { /* Remember first color we see, in case we wind up solid */ - if(result == translucent) + if(result.isTransparent()) if(colorScheme.colors[id] != null) - result = colorScheme.colors[id][seq]; + result.setColor(colorScheme.colors[id][seq]); id = 0; } else @@ -246,7 +252,8 @@ public class DefaultTileRenderer implements MapTileRenderer { if (id != 0) { if (highlightBlocks.contains(id)) { - return highlightColor; + result.setColor(highlightColor); + return; } Color[] colors; if(data != 0) @@ -259,11 +266,12 @@ public class DefaultTileRenderer implements MapTileRenderer { /* we found something that isn't transparent! */ if (c.getAlpha() == 255) { /* it's opaque - the ray ends here */ - return c; + result.setColor(c); + return; } /* this block is transparent, so recurse */ - Color bg = scan(world, x, y, z, seq, isnether); + scan(world, x, y, z, seq, isnether, result); int cr = c.getRed(); int cg = c.getGreen(); @@ -273,8 +281,8 @@ public class DefaultTileRenderer implements MapTileRenderer { 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); + result.setRGBA((result.getRed() * na + cr) >> 8, (result.getGreen() * na + cg) >> 8, (result.getBlue() * na + cb) >> 8, 255); + return; } } } diff --git a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java index c0541289..f30c73f6 100644 --- a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java @@ -1,6 +1,6 @@ package org.dynmap.kzedmap; -import java.awt.Color; +import org.dynmap.Color; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -23,8 +23,8 @@ public class HighlightTileRenderer extends DefaultTileRenderer { } @Override - protected Color scan(World world, int x, int y, int z, int seq, boolean isnether) { - Color result = translucent; + protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result) { + result.setTransparent(); int top_nether_id = 0; for (;;) { if (y < 0) { @@ -35,9 +35,9 @@ public class HighlightTileRenderer extends DefaultTileRenderer { if(isnether) { /* Make bedrock ceiling into air in nether */ if(id != 0) { /* Remember first color we see, in case we wind up solid */ - if(result == translucent) + if(result.isTransparent()) if(colorScheme.colors[id] != null) - result = colorScheme.colors[id][seq]; + result.setColor(colorScheme.colors[id][seq]); id = 0; } else @@ -75,7 +75,8 @@ public class HighlightTileRenderer extends DefaultTileRenderer { Color c = colors[seq]; if (highlightBlocks.contains(id)) { - return c; + result.setColor(c); + return; } if (c.getAlpha() > 0) { @@ -88,26 +89,22 @@ public class HighlightTileRenderer extends DefaultTileRenderer { // No need to blend if result is opaque. if (result.getAlpha() < 255) { - Color bg = c; - c = result; - - int cr = c.getRed(); - int cg = c.getGreen(); - int cb = c.getBlue(); - int ca = c.getAlpha(); + int cr = result.getRed(); + int cg = result.getGreen(); + int cb = result.getBlue(); + int ca = result.getAlpha(); cr *= ca; cg *= ca; cb *= ca; int na = 255 - ca; - result = new Color((bg.getRed() * na + cr) >> 8, (bg.getGreen() * na + cg) >> 8, (bg.getBlue() * na + cb) >> 8, - Math.min(255, bg.getAlpha()+c.getAlpha()) // Not really correct, but gets the job done without recursion while still looking ok. + result.setRGBA((c.getRed() * na + cr) >> 8, (c.getGreen() * na + cg) >> 8, (c.getBlue() * na + cb) >> 8, + Math.min(255, c.getAlpha()+ca) // Not really correct, but gets the job done without recursion while still looking ok. ); } } } } } - return result; } } From 04056572f8bca1c0efa31b25248dd7035d7da5e5 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 15 May 2011 23:50:50 -0500 Subject: [PATCH 9/9] Optimize the getRequiredChunks() to load only chunks that are needed - reduces chunks needed by 42% versus current implementation. --- .../dynmap/kzedmap/HighlightTileRenderer.java | 1 - src/main/java/org/dynmap/kzedmap/KzedMap.java | 50 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java index f30c73f6..826fed17 100644 --- a/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java +++ b/src/main/java/org/dynmap/kzedmap/HighlightTileRenderer.java @@ -25,7 +25,6 @@ public class HighlightTileRenderer extends DefaultTileRenderer { @Override protected void scan(World world, int x, int y, int z, int seq, boolean isnether, final Color result) { result.setTransparent(); - int top_nether_id = 0; for (;;) { if (y < 0) { break; diff --git a/src/main/java/org/dynmap/kzedmap/KzedMap.java b/src/main/java/org/dynmap/kzedmap/KzedMap.java index 09841714..8c44acf2 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMap.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMap.java @@ -138,6 +138,26 @@ public class KzedMap extends MapType { onTileInvalidated.trigger(tile); } + /** + * Test if point x,z is inside rectangle with corner at r0x,r0z and with + * size vectors s1x,s1z and s2x,s2z + * + */ + private boolean testPointInRectangle(int x, int z, int r0x, int r0z, int s1x, int s1z, + int s2x, int s2z) { + int xr = x - r0x; + int zr = z - r0z; /* Get position relative to rectangle corner */ + int dots1 = xr*s1x + zr*s1z; + int dots2 = xr*s2x + zr*s2z; + /* If dot product of relative point and each side is between zero and dot product + * of each side and itself, we're inside + */ + if((dots1 >= 0) && (dots1 <= (s1x*s1x+s1z*s1z)) && + (dots2 >= 0) && (dots2 <= (s2x*s2x+s2z*s2z))) { + return true; + } + return false; + } @Override public DynmapChunk[] getRequiredChunks(MapTile tile) { if (tile instanceof KzedMapTile) { @@ -155,13 +175,43 @@ public class KzedMap extends MapType { int x, z; + /* Actual pattern of chunks needed is create by the slanted + * square prism corresponding to the render path of the tile. + * Top of prism (corresponding to y=127) is diamond shape from + * ix, iz to ix+64,iz+64 to ix+128,iz to ix+64,iz-64 + * Bottom is same shape, offset by -64 on x, +64 on z (net + * render path to y=0), correspond to ix-64, iz+64 to + * ix,iz+128 to ix+64,iz+64 to ix,iz. Projection of + * the prism on to the x,z plane (which is all that matters for + * chunks) yields a diagonal rectangular area from ix-64(x1),iz+64 + * to ix,iz+128(z2) to ix+128(x2),iz to ix+64,iz-64(z1). + * Chunks outside this are not needed - we scan a simple rectangle + * (chunk grid aligned) and skip adding the ones that are outside. + * This results in 42% less chunks being loaded. + */ ArrayList chunks = new ArrayList(); + for (x = x1; x < x2; x += 16) { for (z = z1; z < z2; z += 16) { + /* If any of the chunk corners are inside the rectangle, we need it */ + if((!testPointInRectangle(x, z, x1, iz + KzedMap.tileWidth/2, + KzedMap.tileWidth/2, KzedMap.tileHeight/2, + KzedMap.tileWidth, -KzedMap.tileHeight)) && + (!testPointInRectangle(x+15, z, x1, iz + KzedMap.tileWidth/2, + KzedMap.tileWidth/2, KzedMap.tileHeight/2, + KzedMap.tileWidth, -KzedMap.tileHeight)) && + (!testPointInRectangle(x+15, z+15, x1, iz + KzedMap.tileWidth/2, + KzedMap.tileWidth/2, KzedMap.tileHeight/2, + KzedMap.tileWidth, -KzedMap.tileHeight)) && + (!testPointInRectangle(x, z+15, x1, iz + KzedMap.tileWidth/2, + KzedMap.tileWidth/2, KzedMap.tileHeight/2, + KzedMap.tileWidth, -KzedMap.tileHeight))) + continue; DynmapChunk chunk = new DynmapChunk(x / 16, z / 16); chunks.add(chunk); } } + DynmapChunk[] result = new DynmapChunk[chunks.size()]; chunks.toArray(result); return result;