From e8e4aa67cefa631d94340cd70b2d10e3bcfc3628 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sat, 20 Aug 2011 11:38:08 +0800 Subject: [PATCH 1/7] Fix boundary condition that was causing some missed tiles on chunk triggers --- src/main/java/org/dynmap/hdmap/IsoHDPerspective.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java index 6b6ad990..a3fd0235 100644 --- a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java +++ b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java @@ -781,7 +781,7 @@ public class IsoHDPerspective implements HDPerspective { } /* Now, add the tiles for the ranges - not perfect, but it works (some extra tiles on corners possible) */ for(int i = mintilex; i <= maxtilex; i++) { - for(int j = mintiley; j < maxtiley; j++) { + for(int j = mintiley; j <= maxtiley; j++) { addTile(tiles, world, i, j); } } From 43df59a88c54defcb1789f1e74216ed8c404b649 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 21 Aug 2011 04:21:39 +0800 Subject: [PATCH 2/7] Make sure we can clean up event listeners during reload (make /dynmap reload work) --- src/main/java/org/dynmap/DynmapPlugin.java | 628 ++++++++++++------ .../org/dynmap/SimpleWebChatComponent.java | 9 +- .../org/dynmap/herochat/HeroChatHandler.java | 6 +- .../dynmap/herochat/HeroWebChatComponent.java | 7 +- 4 files changed, 452 insertions(+), 198 deletions(-) diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 22ce6cd8..414f59f4 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -12,6 +12,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -24,6 +25,9 @@ import org.bukkit.block.BlockFace; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; +import org.bukkit.event.CustomEventListener; +import org.bukkit.event.Event; +import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBurnEvent; import org.bukkit.event.block.BlockFadeEvent; @@ -34,12 +38,15 @@ import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.event.block.LeavesDecayEvent; +import org.bukkit.event.player.PlayerChatEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerListener; import org.bukkit.event.player.PlayerMoveEvent; +import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.world.ChunkLoadEvent; import org.bukkit.event.world.WorldListener; import org.bukkit.event.world.WorldLoadEvent; +import org.bukkit.plugin.Plugin; import org.bukkit.plugin.PluginDescriptionFile; import org.bukkit.plugin.PluginManager; import org.bukkit.plugin.java.JavaPlugin; @@ -69,6 +76,8 @@ public class DynmapPlugin extends JavaPlugin { private boolean generate_only = false; private static boolean ignore_chunk_loads = false; /* Flag keep us from processing our own chunk loads */ + private HashMap> event_handlers = new HashMap>(); + public static File dataDirectory; public static File tilesDirectory; @@ -321,6 +330,11 @@ public class DynmapPlugin extends JavaPlugin { webServer.shutdown(); webServer = null; } + /* Clean up all registered handlers */ + for(Event.Type t : event_handlers.keySet()) { + List ll = event_handlers.get(t); + ll.clear(); /* Empty list - we use presence of list to remember that we've registered with Bukkit */ + } Debug.clearDebuggers(); } @@ -337,212 +351,214 @@ public class DynmapPlugin extends JavaPlugin { private boolean onleaves; private boolean onburn; private boolean onpiston; - + private boolean onplayerjoin; + private boolean onplayermove; + private boolean ongeneratechunk; + private boolean onloadchunk; + + public void registerEvents() { - final PluginManager pm = getServer().getPluginManager(); - final MapManager mm = mapManager; + + BlockListener blockTrigger = new BlockListener() { + @Override + public void onBlockPlace(BlockPlaceEvent event) { + Location loc = event.getBlock().getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + if(onplace) { + mapManager.touch(loc); + } + } + + @Override + public void onBlockBreak(BlockBreakEvent event) { + if(event.isCancelled()) + return; + Location loc = event.getBlock().getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + if(onbreak) { + mapManager.touch(loc); + } + } + + @Override + public void onLeavesDecay(LeavesDecayEvent event) { + if(event.isCancelled()) + return; + Location loc = event.getBlock().getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + if(onleaves) { + mapManager.touch(loc); + } + } + + @Override + public void onBlockBurn(BlockBurnEvent event) { + if(event.isCancelled()) + return; + Location loc = event.getBlock().getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + if(onburn) { + mapManager.touch(loc); + } + } + + @Override + public void onBlockForm(BlockFormEvent event) { + if(event.isCancelled()) + return; + Location loc = event.getBlock().getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + if(onblockform) { + mapManager.touch(loc); + } + } + + @Override + public void onBlockFade(BlockFadeEvent event) { + if(event.isCancelled()) + return; + Location loc = event.getBlock().getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + if(onblockfade) { + mapManager.touch(loc); + } + } + + @Override + public void onBlockSpread(BlockSpreadEvent event) { + if(event.isCancelled()) + return; + Location loc = event.getBlock().getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + if(onblockspread) { + mapManager.touch(loc); + } + } + + @Override + public void onBlockPistonRetract(BlockPistonRetractEvent event) { + if(event.isCancelled()) + return; + Block b = event.getBlock(); + Location loc = b.getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + BlockFace dir = event.getDirection(); + if(onpiston) { + mapManager.touchVolume(loc, b.getRelative(dir, 2).getLocation()); + } + for(int i = 0; i < 2; i++) { + b = b.getRelative(dir, 1); + mapManager.sscache.invalidateSnapshot(b.getLocation()); + } + } + @Override + public void onBlockPistonExtend(BlockPistonExtendEvent event) { + if(event.isCancelled()) + return; + Block b = event.getBlock(); + Location loc = b.getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + BlockFace dir = event.getDirection(); + if(onpiston) { + mapManager.touchVolume(loc, b.getRelative(dir, 1+event.getLength()).getLocation()); + } + for(int i = 0; i < 1+event.getLength(); i++) { + b = b.getRelative(dir, 1); + mapManager.sscache.invalidateSnapshot(b.getLocation()); + } + } + }; + // To trigger rendering. - { - BlockListener renderTrigger = new BlockListener() { - - @Override - public void onBlockPlace(BlockPlaceEvent event) { - if(event.isCancelled()) - return; - Location loc = event.getBlock().getLocation(); - mm.sscache.invalidateSnapshot(loc); - if(onplace) { - mm.touch(loc); - } - } + onplace = isTrigger("blockplaced"); + registerEvent(Event.Type.BLOCK_PLACE, blockTrigger); + + onbreak = isTrigger("blockbreak"); + registerEvent(Event.Type.BLOCK_BREAK, blockTrigger); + + if(isTrigger("snowform")) Log.info("The 'snowform' trigger has been deprecated due to Bukkit changes - use 'blockformed'"); + + onleaves = isTrigger("leavesdecay"); + registerEvent(Event.Type.LEAVES_DECAY, blockTrigger); + + onburn = isTrigger("blockburn"); + registerEvent(Event.Type.BLOCK_BURN, blockTrigger); - @Override - public void onBlockBreak(BlockBreakEvent event) { - if(event.isCancelled()) - return; - Location loc = event.getBlock().getLocation(); - mm.sscache.invalidateSnapshot(loc); - if(onbreak) { - mm.touch(loc); - } - } + onblockform = isTrigger("blockformed"); + registerEvent(Event.Type.BLOCK_FORM, blockTrigger); + + onblockfade = isTrigger("blockfaded"); + registerEvent(Event.Type.BLOCK_FADE, blockTrigger); + + onblockspread = isTrigger("blockspread"); + registerEvent(Event.Type.BLOCK_SPREAD, blockTrigger); - @Override - public void onLeavesDecay(LeavesDecayEvent event) { - if(event.isCancelled()) - return; - Location loc = event.getBlock().getLocation(); - mm.sscache.invalidateSnapshot(loc); - if(onleaves) { - mm.touch(loc); - } - } - - @Override - public void onBlockBurn(BlockBurnEvent event) { - if(event.isCancelled()) - return; - Location loc = event.getBlock().getLocation(); - mm.sscache.invalidateSnapshot(loc); - if(onburn) { - mm.touch(loc); - } - } - - @Override - public void onBlockForm(BlockFormEvent event) { - if(event.isCancelled()) - return; - Location loc = event.getBlock().getLocation(); - mm.sscache.invalidateSnapshot(loc); - if(onblockform) { - mm.touch(loc); - } - } - @Override - public void onBlockFade(BlockFadeEvent event) { - if(event.isCancelled()) - return; - Location loc = event.getBlock().getLocation(); - mm.sscache.invalidateSnapshot(loc); - if(onblockfade) { - mm.touch(loc); - } - } - @Override - public void onBlockSpread(BlockSpreadEvent event) { - if(event.isCancelled()) - return; - Location loc = event.getBlock().getLocation(); - mm.sscache.invalidateSnapshot(loc); - if(onblockspread) { - mm.touch(loc); - } - } - @Override - public void onBlockPistonRetract(BlockPistonRetractEvent event) { - if(event.isCancelled()) - return; - Block b = event.getBlock(); - Location loc = b.getLocation(); - mm.sscache.invalidateSnapshot(loc); - BlockFace dir = event.getDirection(); - if(onpiston) { - mm.touchVolume(loc, b.getRelative(dir, 2).getLocation()); - } - for(int i = 0; i < 2; i++) { - b = b.getRelative(dir, 1); - mm.sscache.invalidateSnapshot(b.getLocation()); - } - } - @Override - public void onBlockPistonExtend(BlockPistonExtendEvent event) { - if(event.isCancelled()) - return; - Block b = event.getBlock(); - Location loc = b.getLocation(); - mm.sscache.invalidateSnapshot(loc); - BlockFace dir = event.getDirection(); - if(onpiston) { - mm.touchVolume(loc, b.getRelative(dir, 1+event.getLength()).getLocation()); - } - for(int i = 0; i < 1+event.getLength(); i++) { - b = b.getRelative(dir, 1); - mm.sscache.invalidateSnapshot(b.getLocation()); - } - } - }; - onplace = isTrigger("blockplaced"); - pm.registerEvent(org.bukkit.event.Event.Type.BLOCK_PLACE, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); - - onbreak = isTrigger("blockbreak"); - pm.registerEvent(org.bukkit.event.Event.Type.BLOCK_BREAK, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); - - if(isTrigger("snowform")) Log.info("The 'snowform' trigger has been deprecated due to Bukkit changes - use 'blockformed'"); - - onleaves = isTrigger("leavesdecay"); - pm.registerEvent(org.bukkit.event.Event.Type.LEAVES_DECAY, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); - - onburn = isTrigger("blockburn"); - pm.registerEvent(org.bukkit.event.Event.Type.BLOCK_BURN, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); + onpiston = isTrigger("pistonmoved"); + registerEvent(Event.Type.BLOCK_PISTON_EXTEND, blockTrigger); + registerEvent(Event.Type.BLOCK_PISTON_RETRACT, blockTrigger); + /* Register player event trigger handlers */ + PlayerListener playerTrigger = new PlayerListener() { + @Override + public void onPlayerJoin(PlayerJoinEvent event) { + mapManager.touch(event.getPlayer().getLocation()); + } - onblockform = isTrigger("blockformed"); - pm.registerEvent(org.bukkit.event.Event.Type.BLOCK_FORM, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); - - onblockfade = isTrigger("blockfaded"); - pm.registerEvent(org.bukkit.event.Event.Type.BLOCK_FADE, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); - - onblockspread = isTrigger("blockspread"); - pm.registerEvent(org.bukkit.event.Event.Type.BLOCK_SPREAD, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); + @Override + public void onPlayerMove(PlayerMoveEvent event) { + mapManager.touch(event.getPlayer().getLocation()); + } + }; - onpiston = isTrigger("pistonmoved"); - pm.registerEvent(org.bukkit.event.Event.Type.BLOCK_PISTON_EXTEND, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); - pm.registerEvent(org.bukkit.event.Event.Type.BLOCK_PISTON_RETRACT, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); + onplayerjoin = isTrigger("playerjoin"); + onplayermove = isTrigger("playermove"); + if(onplayerjoin) + registerEvent(Event.Type.PLAYER_JOIN, playerTrigger); + if(onplayermove) + registerEvent(Event.Type.PLAYER_MOVE, playerTrigger); + + /* Register world event triggers */ + ongeneratechunk = isTrigger("chunkgenerated"); + if(ongeneratechunk) { + try { /* Test if new enough bukkit to allow this */ + ChunkLoadEvent.class.getDeclaredMethod("isNewChunk", new Class[0]); + } catch (NoSuchMethodException nsmx) { + Log.info("Warning: CraftBukkit build does not support function needed for 'chunkgenerated' trigger - disabling"); + ongeneratechunk = false; + } } - { - PlayerListener renderTrigger = new PlayerListener() { - @Override - public void onPlayerJoin(PlayerJoinEvent event) { - mm.touch(event.getPlayer().getLocation()); - } - - @Override - public void onPlayerMove(PlayerMoveEvent event) { - mm.touch(event.getPlayer().getLocation()); - } - }; - if (isTrigger("playerjoin")) - pm.registerEvent(org.bukkit.event.Event.Type.PLAYER_JOIN, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); - if (isTrigger("playermove")) - pm.registerEvent(org.bukkit.event.Event.Type.PLAYER_MOVE, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); + onloadchunk = isTrigger("chunkloaded"); + if(onloadchunk) { + generate_only = false; } - { - WorldListener renderTrigger = new WorldListener() { - @Override - public void onChunkLoad(ChunkLoadEvent event) { - if(ignore_chunk_loads) - return; + else if (ongeneratechunk) { + generate_only = true; + } + registerEvent(Event.Type.CHUNK_LOAD, ourWorldEventHandler); + + // To link configuration to real loaded worlds. + WorldListener worldTrigger = new WorldListener() { + @Override + public void onChunkLoad(ChunkLoadEvent event) { + if(ignore_chunk_loads) + return; + if(onloadchunk || ongeneratechunk) { if(generate_only) { - if(!isNewChunk(event)) + if(!event.isNewChunk()) return; } /* Touch extreme corners */ int x = event.getChunk().getX() << 4; int z = event.getChunk().getZ() << 4; - mm.touchVolume(new Location(event.getWorld(), x, 0, z), new Location(event.getWorld(), x+15, 127, z+15)); - } - private boolean isNewChunk(ChunkLoadEvent event) { - return event.isNewChunk(); - } - }; - boolean ongenerate = isTrigger("chunkgenerated"); - if(ongenerate) { - try { /* Test if new enough bukkit to allow this */ - ChunkLoadEvent.class.getDeclaredMethod("isNewChunk", new Class[0]); - } catch (NoSuchMethodException nsmx) { - Log.info("Warning: CraftBukkit build does not support function needed for 'chunkgenerated' trigger - disabling"); - ongenerate = false; + mapManager.touchVolume(new Location(event.getWorld(), x, 0, z), new Location(event.getWorld(), x+15, 127, z+15)); } } - if(isTrigger("chunkloaded")) { - generate_only = false; - pm.registerEvent(org.bukkit.event.Event.Type.CHUNK_LOAD, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); - } - else if(ongenerate) { - generate_only = true; - pm.registerEvent(org.bukkit.event.Event.Type.CHUNK_LOAD, renderTrigger, org.bukkit.event.Event.Priority.Monitor, this); - } - } - - // To link configuration to real loaded worlds. - WorldListener worldListener = new WorldListener() { @Override public void onWorldLoad(WorldLoadEvent event) { - mm.activateWorld(event.getWorld()); + mapManager.activateWorld(event.getWorld()); } }; - pm.registerEvent(org.bukkit.event.Event.Type.WORLD_LOAD, worldListener, org.bukkit.event.Event.Priority.Monitor, this); + registerEvent(Event.Type.WORLD_LOAD, worldTrigger); } private static File combinePaths(File parent, String path) { @@ -831,4 +847,246 @@ public class DynmapPlugin extends JavaPlugin { return true; } } + + + private BlockListener ourBlockEventHandler = new BlockListener() { + + @Override + public void onBlockPlace(BlockPlaceEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((BlockListener)l).onBlockPlace(event); + } + } + } + + @Override + public void onBlockBreak(BlockBreakEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((BlockListener)l).onBlockBreak(event); + } + } + } + + @Override + public void onLeavesDecay(LeavesDecayEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((BlockListener)l).onLeavesDecay(event); + } + } + } + + @Override + public void onBlockBurn(BlockBurnEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((BlockListener)l).onBlockBurn(event); + } + } + } + + @Override + public void onBlockForm(BlockFormEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((BlockListener)l).onBlockForm(event); + } + } + } + @Override + public void onBlockFade(BlockFadeEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((BlockListener)l).onBlockFade(event); + } + } + } + @Override + public void onBlockSpread(BlockSpreadEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((BlockListener)l).onBlockSpread(event); + } + } + } + @Override + public void onBlockPistonRetract(BlockPistonRetractEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((BlockListener)l).onBlockPistonRetract(event); + } + } + } + @Override + public void onBlockPistonExtend(BlockPistonExtendEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((BlockListener)l).onBlockPistonExtend(event); + } + } + } + }; + private PlayerListener ourPlayerEventHandler = new PlayerListener() { + @Override + public void onPlayerJoin(PlayerJoinEvent event) { + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((PlayerListener)l).onPlayerJoin(event); + } + } + } + + @Override + public void onPlayerMove(PlayerMoveEvent event) { + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((PlayerListener)l).onPlayerMove(event); + } + } + } + + @Override + public void onPlayerQuit(PlayerQuitEvent event) { + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((PlayerListener)l).onPlayerQuit(event); + } + } + } + + @Override + public void onPlayerChat(PlayerChatEvent event) { + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((PlayerListener)l).onPlayerChat(event); + } + } + } + }; + + private WorldListener ourWorldEventHandler = new WorldListener() { + @Override + public void onWorldLoad(WorldLoadEvent event) { + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((WorldListener)l).onWorldLoad(event); + } + } + } + @Override + public void onChunkLoad(ChunkLoadEvent event) { + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((WorldListener)l).onChunkLoad(event); + } + } + } + }; + + private CustomEventListener ourCustomEventHandler = new CustomEventListener() { + @Override + public void onCustomEvent(Event event) { + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((CustomEventListener)l).onCustomEvent(event); + } + } + } + }; + + /** + * Register event listener - this will be cleaned up properly on a /dynmap reload, unlike + * registering with Bukkit directly + */ + public void registerEvent(Event.Type type, Listener listener) { + List ll = event_handlers.get(type); + PluginManager pm = getServer().getPluginManager(); + if(ll == null) { + switch(type) { /* See if it is a type we're brokering */ + case PLAYER_LOGIN: + case PLAYER_CHAT: + case PLAYER_JOIN: + case PLAYER_QUIT: + case PLAYER_MOVE: + pm.registerEvent(type, ourPlayerEventHandler, Event.Priority.Monitor, this); + break; + case BLOCK_PLACE: + case BLOCK_BREAK: + case LEAVES_DECAY: + case BLOCK_BURN: + case BLOCK_FORM: + case BLOCK_FADE: + case BLOCK_SPREAD: + case BLOCK_PISTON_EXTEND: + case BLOCK_PISTON_RETRACT: + pm.registerEvent(type, ourBlockEventHandler, Event.Priority.Monitor, this); + break; + case WORLD_LOAD: + case CHUNK_LOAD: + pm.registerEvent(type, ourWorldEventHandler, Event.Priority.Monitor, this); + break; + case CUSTOM_EVENT: + pm.registerEvent(type, ourCustomEventHandler, Event.Priority.Monitor, this); + break; + default: + Log.severe("registerEvent() in DynmapPlugin does not handle " + type); + return; + } + ll = new ArrayList(); + event_handlers.put(type, ll); /* Add list for this event */ + } + ll.add(listener); + } } diff --git a/src/main/java/org/dynmap/SimpleWebChatComponent.java b/src/main/java/org/dynmap/SimpleWebChatComponent.java index 241f5425..cec84327 100644 --- a/src/main/java/org/dynmap/SimpleWebChatComponent.java +++ b/src/main/java/org/dynmap/SimpleWebChatComponent.java @@ -32,11 +32,10 @@ public class SimpleWebChatComponent extends Component { if (configuration.getBoolean("allowchat", false)) { PlayerChatListener playerListener = new PlayerChatListener(); - PluginManager pm = plugin.getServer().getPluginManager(); - pm.registerEvent(org.bukkit.event.Event.Type.PLAYER_CHAT, playerListener, org.bukkit.event.Event.Priority.Monitor, plugin); - pm.registerEvent(org.bukkit.event.Event.Type.PLAYER_LOGIN, playerListener, org.bukkit.event.Event.Priority.Monitor, plugin); - pm.registerEvent(org.bukkit.event.Event.Type.PLAYER_JOIN, playerListener, org.bukkit.event.Event.Priority.Monitor, plugin); - pm.registerEvent(org.bukkit.event.Event.Type.PLAYER_QUIT, playerListener, org.bukkit.event.Event.Priority.Monitor, plugin); + plugin.registerEvent(org.bukkit.event.Event.Type.PLAYER_CHAT, playerListener); + plugin.registerEvent(org.bukkit.event.Event.Type.PLAYER_LOGIN, playerListener); + plugin.registerEvent(org.bukkit.event.Event.Type.PLAYER_JOIN, playerListener); + plugin.registerEvent(org.bukkit.event.Event.Type.PLAYER_QUIT, playerListener); } } diff --git a/src/main/java/org/dynmap/herochat/HeroChatHandler.java b/src/main/java/org/dynmap/herochat/HeroChatHandler.java index 7e2afd23..cdc6fe87 100644 --- a/src/main/java/org/dynmap/herochat/HeroChatHandler.java +++ b/src/main/java/org/dynmap/herochat/HeroChatHandler.java @@ -258,8 +258,7 @@ public class HeroChatHandler { } else { /* Set up to hear when HeroChat is enabled */ - server.getPluginManager().registerEvent(Event.Type.PLUGIN_ENABLE, - new OurPluginListener(), Event.Priority.Normal, plugin); + plugin.registerEvent(Event.Type.PLUGIN_ENABLE, new OurPluginListener()); } } @@ -277,8 +276,7 @@ public class HeroChatHandler { return; } /* Register event handler */ - plugin.getServer().getPluginManager().registerEvent(Event.Type.CUSTOM_EVENT, - new OurEventListener(), Event.Priority.Monitor, plugin); + plugin.registerEvent(Event.Type.CUSTOM_EVENT, new OurEventListener()); Log.verboseinfo("HeroChat integration active"); } /** diff --git a/src/main/java/org/dynmap/herochat/HeroWebChatComponent.java b/src/main/java/org/dynmap/herochat/HeroWebChatComponent.java index ef9bc410..8847029d 100644 --- a/src/main/java/org/dynmap/herochat/HeroWebChatComponent.java +++ b/src/main/java/org/dynmap/herochat/HeroWebChatComponent.java @@ -43,10 +43,9 @@ public class HeroWebChatComponent extends Component { // Also make HeroChat announce joins and quits. PlayerChatListener playerListener = new PlayerChatListener(); - PluginManager pm = plugin.getServer().getPluginManager(); - pm.registerEvent(org.bukkit.event.Event.Type.PLAYER_LOGIN, playerListener, org.bukkit.event.Event.Priority.Monitor, plugin); - pm.registerEvent(org.bukkit.event.Event.Type.PLAYER_JOIN, playerListener, org.bukkit.event.Event.Priority.Monitor, plugin); - pm.registerEvent(org.bukkit.event.Event.Type.PLAYER_QUIT, playerListener, org.bukkit.event.Event.Priority.Monitor, plugin); + plugin.registerEvent(org.bukkit.event.Event.Type.PLAYER_LOGIN, playerListener); + plugin.registerEvent(org.bukkit.event.Event.Type.PLAYER_JOIN, playerListener); + plugin.registerEvent(org.bukkit.event.Event.Type.PLAYER_QUIT, playerListener); } protected class PlayerChatListener extends PlayerListener { From 9b191bb3ab785a1c401ccba04ba1d58d0294b3af Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 21 Aug 2011 05:28:37 +0800 Subject: [PATCH 3/7] Add 'explosion' update trigger, add updated tile queue accelerator (to speed up processing if queue gets above a given threshold) --- .../java/org/dynmap/AsynchronousQueue.java | 13 ++- src/main/java/org/dynmap/DynmapPlugin.java | 80 ++++++++++++++----- src/main/java/org/dynmap/MapManager.java | 16 ++-- src/main/resources/configuration.txt | 9 ++- 4 files changed, 88 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/dynmap/AsynchronousQueue.java b/src/main/java/org/dynmap/AsynchronousQueue.java index 30e6781d..d4c54757 100644 --- a/src/main/java/org/dynmap/AsynchronousQueue.java +++ b/src/main/java/org/dynmap/AsynchronousQueue.java @@ -12,10 +12,14 @@ public class AsynchronousQueue { private Set set = new HashSet(); private Handler handler; private int dequeueTime; - - public AsynchronousQueue(Handler handler, int dequeueTime) { + private int accelDequeueTime; + private int accelDequeueThresh; + + public AsynchronousQueue(Handler handler, int dequeueTime, int accelDequeueThresh, int accelDequeueTime) { this.handler = handler; this.dequeueTime = dequeueTime; + this.accelDequeueTime = accelDequeueTime; + this.accelDequeueThresh = accelDequeueThresh; } public boolean push(T t) { @@ -83,7 +87,10 @@ public class AsynchronousQueue { if (t != null) { handler.handle(t); } - sleep(dequeueTime); + if(set.size() >= accelDequeueThresh) + sleep(accelDequeueTime); + else + sleep(dequeueTime); } } catch (Exception ex) { diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 414f59f4..e61211c5 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -27,6 +27,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.event.CustomEventListener; import org.bukkit.event.Event; +import org.bukkit.event.Event.Type; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockBreakEvent; import org.bukkit.event.block.BlockBurnEvent; @@ -38,6 +39,8 @@ import org.bukkit.event.block.BlockPistonRetractEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.event.block.BlockSpreadEvent; import org.bukkit.event.block.LeavesDecayEvent; +import org.bukkit.event.entity.EntityExplodeEvent; +import org.bukkit.event.entity.EntityListener; import org.bukkit.event.player.PlayerChatEvent; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerListener; @@ -355,7 +358,7 @@ public class DynmapPlugin extends JavaPlugin { private boolean onplayermove; private boolean ongeneratechunk; private boolean onloadchunk; - + private boolean onexplosion; public void registerEvents() { @@ -517,26 +520,25 @@ public class DynmapPlugin extends JavaPlugin { if(onplayermove) registerEvent(Event.Type.PLAYER_MOVE, playerTrigger); - /* Register world event triggers */ - ongeneratechunk = isTrigger("chunkgenerated"); - if(ongeneratechunk) { - try { /* Test if new enough bukkit to allow this */ - ChunkLoadEvent.class.getDeclaredMethod("isNewChunk", new Class[0]); - } catch (NoSuchMethodException nsmx) { - Log.info("Warning: CraftBukkit build does not support function needed for 'chunkgenerated' trigger - disabling"); - ongeneratechunk = false; + /* Register entity event triggers */ + EntityListener entityTrigger = new EntityListener() { + @Override + public void onEntityExplode(EntityExplodeEvent event) { + List blocks = event.blockList(); + for(Block b: blocks) { + Location loc = b.getLocation(); + mapManager.sscache.invalidateSnapshot(loc); + if(onexplosion) { + mapManager.touch(loc); + } + } } - } - onloadchunk = isTrigger("chunkloaded"); - if(onloadchunk) { - generate_only = false; - } - else if (ongeneratechunk) { - generate_only = true; - } - registerEvent(Event.Type.CHUNK_LOAD, ourWorldEventHandler); - - // To link configuration to real loaded worlds. + }; + onexplosion = isTrigger("explosion"); + registerEvent(Event.Type.ENTITY_EXPLODE, entityTrigger); + + + /* Register world event triggers */ WorldListener worldTrigger = new WorldListener() { @Override public void onChunkLoad(ChunkLoadEvent event) { @@ -558,6 +560,26 @@ public class DynmapPlugin extends JavaPlugin { mapManager.activateWorld(event.getWorld()); } }; + + ongeneratechunk = isTrigger("chunkgenerated"); + if(ongeneratechunk) { + try { /* Test if new enough bukkit to allow this */ + ChunkLoadEvent.class.getDeclaredMethod("isNewChunk", new Class[0]); + } catch (NoSuchMethodException nsmx) { + Log.info("Warning: CraftBukkit build does not support function needed for 'chunkgenerated' trigger - disabling"); + ongeneratechunk = false; + } + } + onloadchunk = isTrigger("chunkloaded"); + if(onloadchunk) { + generate_only = false; + } + else if (ongeneratechunk) { + generate_only = true; + } + registerEvent(Event.Type.CHUNK_LOAD, worldTrigger); + + // To link configuration to real loaded worlds. registerEvent(Event.Type.WORLD_LOAD, worldTrigger); } @@ -1046,6 +1068,21 @@ public class DynmapPlugin extends JavaPlugin { } }; + private EntityListener ourEntityEventHandler = new EntityListener() { + @Override + public void onEntityExplode(EntityExplodeEvent event) { + if(event.isCancelled()) + return; + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((EntityListener)l).onEntityExplode(event); + } + } + } + }; + /** * Register event listener - this will be cleaned up properly on a /dynmap reload, unlike * registering with Bukkit directly @@ -1080,6 +1117,9 @@ public class DynmapPlugin extends JavaPlugin { case CUSTOM_EVENT: pm.registerEvent(type, ourCustomEventHandler, Event.Priority.Monitor, this); break; + case ENTITY_EXPLODE: + pm.registerEvent(type, ourEntityEventHandler, Event.Priority.Monitor, this); + break; default: Log.severe("registerEvent() in DynmapPlugin does not handle " + type); return; diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index 0d55ee34..195e8ed3 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -488,12 +488,16 @@ public class MapManager { sscache = new SnapshotCache(configuration.getInteger("snapshotcachesize", 500)); parallelrendercnt = configuration.getInteger("parallelrendercnt", 0); - this.tileQueue = new AsynchronousQueue(new Handler() { - @Override - public void handle(MapTile t) { - scheduleDelayedJob(new FullWorldRenderState(t), 0); - } - }, (int) (configuration.getDouble("renderinterval", 0.5) * 1000)); + this.tileQueue = new AsynchronousQueue( + new Handler() { + @Override + public void handle(MapTile t) { + scheduleDelayedJob(new FullWorldRenderState(t), 0); + } + }, + (int) (configuration.getDouble("renderinterval", 0.5) * 1000), + configuration.getInteger("renderacceleratethreshold", 30), + (int)(configuration.getDouble("renderaccelerateinterval", 0.2) * 1000)); /* On dedicated thread, so default to no delays */ timeslice_int = (long)(configuration.getDouble("timesliceinterval", 0.0) * 1000); diff --git a/src/main/resources/configuration.txt b/src/main/resources/configuration.txt index 6e85ad7d..5822b528 100644 --- a/src/main/resources/configuration.txt +++ b/src/main/resources/configuration.txt @@ -111,8 +111,14 @@ display-whitelist: false # How often a tile gets rendered (in seconds). renderinterval: 1 +# How many tiles on update queue before accelerate render interval +renderacceleratethreshold: 60 + +# How often to render tiles when backlog is above renderacceleratethreshold +renderaccelerateinterval: 0.2 + # Zoom-out tile update period - how often to scan for and process tile updates into zoom-out tiles (in seconds) -zoomoutperiod: 60 +zoomoutperiod: 30 # Tile hashing is used to minimize tile file updates when no changes have occurred - set to false to disable enabletilehash: true @@ -130,6 +136,7 @@ render-triggers: - blockfaded - blockspread - pistonmoved + - explosion # The path where the tile-files are placed. tilespath: web/tiles From c327b98b97fd5d0043db7cc58b89ddcbc412195e Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 21 Aug 2011 05:43:18 +0800 Subject: [PATCH 4/7] Add progressloginterval setting - control how often progress messages done --- src/main/java/org/dynmap/MapManager.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dynmap/MapManager.java b/src/main/java/org/dynmap/MapManager.java index 195e8ed3..42c3d851 100644 --- a/src/main/java/org/dynmap/MapManager.java +++ b/src/main/java/org/dynmap/MapManager.java @@ -44,6 +44,7 @@ public class MapManager { private long timeslice_int = 0; /* In milliseconds */ private int max_chunk_loads_per_tick = DEFAULT_CHUNKS_PER_TICK; private int parallelrendercnt = 0; + private int progressinterval = 100; private int zoomout_period = DEFAULT_ZOOMOUT_PERIOD; /* Zoom-out tile processing period, in seconds */ /* Which fullrenders are active */ @@ -414,7 +415,7 @@ public class MapManager { if(!cache.isEmpty()) { rendercnt++; timeaccum += System.currentTimeMillis() - tstart; - if((rendercnt % 100) == 0) { + if((rendercnt % progressinterval) == 0) { double msecpertile = (double)timeaccum / (double)rendercnt / (double)activemaplist.size(); if(activemaplist.size() > 1) sender.sendMessage(rendertype + " of maps [" + activemaps + "] of '" + @@ -487,6 +488,8 @@ public class MapManager { hdmapman.loadHDLightings(plugin); sscache = new SnapshotCache(configuration.getInteger("snapshotcachesize", 500)); parallelrendercnt = configuration.getInteger("parallelrendercnt", 0); + progressinterval = configuration.getInteger("progressloginterval", 100); + if(progressinterval < 100) progressinterval = 100; this.tileQueue = new AsynchronousQueue( new Handler() { From 58d359fc1d647c3318ccabf1e4a898cde4c41405 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 21 Aug 2011 05:45:21 +0800 Subject: [PATCH 5/7] Add setting for progressloginterval --- src/main/resources/configuration.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/resources/configuration.txt b/src/main/resources/configuration.txt index 5822b528..588b1801 100644 --- a/src/main/resources/configuration.txt +++ b/src/main/resources/configuration.txt @@ -165,6 +165,9 @@ timesliceinterval: 0.0 # Maximum chunk loads per server tick (1/20th of a second) - reducing this below 90 will impact render performance, but also will reduce server thread load maxchunkspertick: 200 +# Progress report interval for fullrender/radiusrender, in tiles. Must be 100 or greater +progressloginterval: 100 + # EXPERIMENTAL - parallel fullrender: if defined, number of concurrent threads used for fullrender or radiusrender # Note: setting this will result in much more intensive CPU use, some additional memory use. Caution should be used when # setting this to equal or exceed the number of physical cores on the system. From 6e5482466dbf3aff72aaaeb156494a7cd39a556f Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 21 Aug 2011 10:31:14 +0800 Subject: [PATCH 6/7] Switch chunkgenerate to onChunkPopulate event: chunk is better "cooked" by then --- src/main/java/org/dynmap/DynmapPlugin.java | 45 ++++++++++++---------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index e61211c5..2c116d70 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -47,6 +47,7 @@ import org.bukkit.event.player.PlayerListener; import org.bukkit.event.player.PlayerMoveEvent; import org.bukkit.event.player.PlayerQuitEvent; import org.bukkit.event.world.ChunkLoadEvent; +import org.bukkit.event.world.ChunkPopulateEvent; import org.bukkit.event.world.WorldListener; import org.bukkit.event.world.WorldLoadEvent; import org.bukkit.plugin.Plugin; @@ -544,16 +545,16 @@ public class DynmapPlugin extends JavaPlugin { public void onChunkLoad(ChunkLoadEvent event) { if(ignore_chunk_loads) return; - if(onloadchunk || ongeneratechunk) { - if(generate_only) { - if(!event.isNewChunk()) - return; - } - /* Touch extreme corners */ - int x = event.getChunk().getX() << 4; - int z = event.getChunk().getZ() << 4; - mapManager.touchVolume(new Location(event.getWorld(), x, 0, z), new Location(event.getWorld(), x+15, 127, z+15)); - } + /* Touch extreme corners */ + int x = event.getChunk().getX() << 4; + int z = event.getChunk().getZ() << 4; + mapManager.touchVolume(new Location(event.getWorld(), x, 0, z), new Location(event.getWorld(), x+15, 127, z+15)); + } + @Override + public void onChunkPopulate(ChunkPopulateEvent event) { + int x = event.getChunk().getX() << 4; + int z = event.getChunk().getZ() << 4; + mapManager.touchVolume(new Location(event.getWorld(), x, 0, z), new Location(event.getWorld(), x+15, 127, z+15)); } @Override public void onWorldLoad(WorldLoadEvent event) { @@ -563,21 +564,12 @@ public class DynmapPlugin extends JavaPlugin { ongeneratechunk = isTrigger("chunkgenerated"); if(ongeneratechunk) { - try { /* Test if new enough bukkit to allow this */ - ChunkLoadEvent.class.getDeclaredMethod("isNewChunk", new Class[0]); - } catch (NoSuchMethodException nsmx) { - Log.info("Warning: CraftBukkit build does not support function needed for 'chunkgenerated' trigger - disabling"); - ongeneratechunk = false; - } + registerEvent(Event.Type.CHUNK_POPULATED, worldTrigger); } onloadchunk = isTrigger("chunkloaded"); if(onloadchunk) { - generate_only = false; + registerEvent(Event.Type.CHUNK_LOAD, worldTrigger); } - else if (ongeneratechunk) { - generate_only = true; - } - registerEvent(Event.Type.CHUNK_LOAD, worldTrigger); // To link configuration to real loaded worlds. registerEvent(Event.Type.WORLD_LOAD, worldTrigger); @@ -1053,6 +1045,16 @@ public class DynmapPlugin extends JavaPlugin { } } } + @Override + public void onChunkPopulate(ChunkPopulateEvent event) { + /* Call listeners */ + List ll = event_handlers.get(event.getType()); + if(ll != null) { + for(Listener l : ll) { + ((WorldListener)l).onChunkPopulate(event); + } + } + } }; private CustomEventListener ourCustomEventHandler = new CustomEventListener() { @@ -1112,6 +1114,7 @@ public class DynmapPlugin extends JavaPlugin { break; case WORLD_LOAD: case CHUNK_LOAD: + case CHUNK_POPULATED: pm.registerEvent(type, ourWorldEventHandler, Event.Priority.Monitor, this); break; case CUSTOM_EVENT: From 929ca8911ecebe2cadb95239d3d023db0ba4a31c Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Mon, 22 Aug 2011 04:57:21 +0800 Subject: [PATCH 7/7] Fix naming on Residence regions, make infowindow setting control popup content again --- web/js/regions.js | 34 ++++++++++++++-------------------- web/js/regions_Residence.js | 4 ++-- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/web/js/regions.js b/web/js/regions.js index aa3639a8..6cac8ce0 100644 --- a/web/js/regions.js +++ b/web/js/regions.js @@ -106,30 +106,24 @@ componentconstructors['regions'] = function(dynmap, configuration) { return a.join(', '); } else if (typeof a === 'string') { return a; + } else { + return ""; } - return null; } var members = region.members || {}; + var popup = this.infowindow || '
%regionname%
Owner %playerowners%
Flags
%flags%
'; + popup = popup.replace('%regionname%', name); + popup = popup.replace('%playerowners%', join(region.owners.players)); + popup = popup.replace('%groupowners%', join(region.owners.groups)); + popup = popup.replace('%playermembers%', join(members.players)); + popup = popup.replace('%groupmembers%', join(members.groups)); + var regionflags = ""; + $.each(region.flags, function(name, value) { + regionflags = regionflags + "" + name + ": " + value + "
"; + }); + popup = popup.replace('%flags%', regionflags); return $('
').addClass('regioninfo') - .append($('').addClass('regionname').text(name)) - .append($('').addClass('owners') - .append(region.owners.players && $('').addClass('playerowners').text(join(region.owners.players))) - .append(region.owners.groups && $('').addClass('groupowners').text(join(region.owners.groups))) - ) - .append($('').addClass('members') - .append(members.players && $('').addClass('playermembers').text(join(members.players))) - .append(members.groups && $('').addClass('groupmembers').text(join(members.groups))) - ) - .append(region.parent && $('').addClass('regionparent').text(region.parent)) - .append(region.flags && function() { - var regionflags = $('').addClass('regionflags'); - $.each(region.flags, function(name, value) { - regionflags.append($('').addClass('regionflag').text(name + ': ' + value)); - }); - return regionflags; - }()) - .append($('').addClass('regionpriority').text(region.priority)) - [0]; + .append(popup)[0]; } var self = this; diff --git a/web/js/regions_Residence.js b/web/js/regions_Residence.js index a8b5c0cd..7a8db228 100644 --- a/web/js/regions_Residence.js +++ b/web/js/regions_Residence.js @@ -8,7 +8,7 @@ regionConstructors['Residence'] = function(dynmap, configuration) { var boxLayers = []; $.each(data, function(name, residence) { if(configuration.worldName == residence.Permissions.World) { - $.each(residence.Areas, function(name, area) { + $.each(residence.Areas, function(aname, area) { var boxLayer = configuration.createBoxLayer(area.X1, area.X2, area.Y1, area.Y2, area.Z1, area.Z2); boxLayer.bindPopup(configuration.createPopupContent(name, $.extend(residence, { @@ -21,7 +21,7 @@ regionConstructors['Residence'] = function(dynmap, configuration) { $.each(residence.Subzones, function(szname, subzone) { $.each(subzone.Areas, function(name2, area2) { var subzoneLayer = configuration.createBoxLayer(area2.X1, area2.X2, area2.Y1, area2.Y2, area2.Z1, area2.Z2); - subzoneLayer.bindPopup(configuration.createPopupContent(name2, $.extend(subzone, { + subzoneLayer.bindPopup(configuration.createPopupContent(name + '.' + szname, $.extend(subzone, { owners: { players: [subzone.Permissions.Owner] }, flags: subzone.Permissions.AreaFlags })));