From 586c41d23c1524bb92ecb043ea9d35a89a2104d4 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 24 Aug 2011 01:23:05 -0500 Subject: [PATCH 01/27] Fix accessory face mixing --- src/main/java/org/dynmap/PlayerFaces.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dynmap/PlayerFaces.java b/src/main/java/org/dynmap/PlayerFaces.java index ef66d128..470d1038 100644 --- a/src/main/java/org/dynmap/PlayerFaces.java +++ b/src/main/java/org/dynmap/PlayerFaces.java @@ -56,12 +56,16 @@ public class PlayerFaces { int[] faceaccessory = new int[64]; /* 8x8 of face accessory */ /* Get buffered image for face at 8x8 */ DynmapBufferedImage face8x8 = DynmapBufferedImage.allocateBufferedImage(8, 8); + int[] bgcolor = new int[1]; + img.getRGB(0, 0, 1, 1, bgcolor, 0, 1); /* Get BG color (for accessory face) */ img.getRGB(8, 8, 8, 8, face8x8.argb_buf, 0, 8); /* Read face from image */ img.getRGB(40, 8, 8, 8, faceaccessory, 0, 8); /* Read face accessory from image */ /* Apply accessory to face: first element is transparency color so only ones not matching it */ for(int i = 0; i < 64; i++) { - if(faceaccessory[i] != faceaccessory[0]) + if(faceaccessory[i] != bgcolor[0]) face8x8.argb_buf[i] = faceaccessory[i]; + else if(face8x8.argb_buf[i] == bgcolor[0]) + face8x8.argb_buf[i] = 0; } /* Write 8x8 file */ File img_8x8 = new File(faces8x8dir, playername + ".png"); From 627cef720c4e57409cf95a044f4528d8b7ebf0a7 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Wed, 24 Aug 2011 22:11:31 -0500 Subject: [PATCH 02/27] Towny fixes - multiple areas on multiple worlds per town --- .../dynmap/regions/TownyConfigHandler.java | 227 +++++++++++------- web/js/regions.js | 30 +-- web/js/regions_Towny.js | 31 +-- 3 files changed, 170 insertions(+), 118 deletions(-) diff --git a/src/main/java/org/dynmap/regions/TownyConfigHandler.java b/src/main/java/org/dynmap/regions/TownyConfigHandler.java index 5f4865d8..d5aab6f5 100644 --- a/src/main/java/org/dynmap/regions/TownyConfigHandler.java +++ b/src/main/java/org/dynmap/regions/TownyConfigHandler.java @@ -7,6 +7,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; @@ -61,6 +62,23 @@ public class TownyConfigHandler { private static final String FLAGS[] = { "hasUpkeep", "pvp", "mobs", "public", "explosion", "fire" }; + + /** + * Find all contiguous blocks, set in target and clear in source + */ + private int floodFillTarget(TileFlags src, TileFlags dest, int x, int y) { + int cnt = 0; + if(src.getFlag(x, y)) { /* Set in src */ + src.setFlag(x, y, false); /* Clear source */ + dest.setFlag(x, y, true); /* Set in destination */ + cnt++; + cnt += floodFillTarget(src, dest, x+1, y); /* Fill adjacent blocks */ + cnt += floodFillTarget(src, dest, x-1, y); + cnt += floodFillTarget(src, dest, x, y+1); + cnt += floodFillTarget(src, dest, x, y-1); + } + return cnt; + } /** * Process data from given town file */ @@ -80,113 +98,152 @@ public class TownyConfigHandler { } /* Get block list */ String blocks = p.getProperty("townBlocks"); - /* If it doesn't start with world, we're done (town on different world) */ - if((blocks == null) || (!blocks.startsWith(wname+":"))) - return null; String[] nodes = blocks.split(";"); /* Split into list */ TileFlags blks = new TileFlags(); - ArrayList nodevals = new ArrayList(); - int minx = Integer.MAX_VALUE; - int miny = Integer.MAX_VALUE; + LinkedList nodevals = new LinkedList(); + boolean worldmatch = false; + for(String n: nodes) { + /* Is world prefix? */ int idx = n.indexOf(':'); - if(idx >= 0) n = n.substring(idx+1); + if(idx >= 0) { + String w = n.substring(0, idx); + if(w.startsWith("|")) w = w.substring(1); + worldmatch = w.equals(wname); /* See if our world */ + n = n.substring(idx+1); /* Process remainder as coordinate */ + } + if(!worldmatch) continue; String[] v = n.split(","); if(v.length == 2) { try { int[] vv = new int[] { Integer.valueOf(v[0]), Integer.valueOf(v[1]) }; blks.setFlag(vv[0], vv[1], true); nodevals.add(vv); - if(vv[0] < minx) { - minx = vv[0]; - miny = vv[1]; - } - else if((vv[0] == minx) && (vv[1] < miny)) { - miny = vv[1]; - } } catch (NumberFormatException nfx) { Log.severe("Error parsing block list in Towny - " + townfile.getPath()); return null; } } } - /* Trace outline of blocks - start from minx, miny going to x+ */ - int init_x = minx; - int init_y = miny; - int cur_x = minx+1; - int cur_y = miny; - direction dir = direction.XPLUS; - ArrayList linelist = new ArrayList(); - linelist.add(new int[] { init_x, init_y } ); // Add start point - while((cur_x != init_x) || (cur_y != init_y)) { - switch(dir) { - case XPLUS: /* Segment in X+ direction */ - if(!blks.getFlag(cur_x+1, cur_y)) { /* Right turn? */ - linelist.add(new int[] { cur_x+1, cur_y }); /* Finish line */ - dir = direction.YPLUS; /* Change direction */ + /* If nothing in this world, skip */ + if(nodevals.size() == 0) + return null; + /* Loop through until we don't find more areas */ + ArrayList[]> polygons = new ArrayList[]>(); + while(nodevals != null) { + LinkedList ournodes = null; + LinkedList newlist = null; + TileFlags ourblks = null; + int minx = Integer.MAX_VALUE; + int miny = Integer.MAX_VALUE; + for(int[] node : nodevals) { + if((ourblks == null) && blks.getFlag(node[0], node[1])) { /* Node still in map? */ + ourblks = new TileFlags(); /* Create map for shape */ + ournodes = new LinkedList(); + floodFillTarget(blks, ourblks, node[0], node[1]); /* Copy shape */ + ournodes.add(node); /* Add it to our node list */ + minx = node[0]; miny = node[1]; + } + /* If shape found, and we're in it, add to our node list */ + else if((ourblks != null) && (ourblks.getFlag(node[0], node[1]))) { + ournodes.add(node); + if(node[0] < minx) { + minx = node[0]; miny = node[1]; } - else if(!blks.getFlag(cur_x+1, cur_y-1)) { /* Straight? */ - cur_x++; + else if((node[0] == minx) && (node[1] < miny)) { + miny = node[1]; } - else { /* Left turn */ - linelist.add(new int[] { cur_x+1, cur_y }); /* Finish line */ - dir = direction.YMINUS; - cur_x++; cur_y--; - } - break; - case YPLUS: /* Segment in Y+ direction */ - if(!blks.getFlag(cur_x, cur_y+1)) { /* Right turn? */ - linelist.add(new int[] { cur_x+1, cur_y+1 }); /* Finish line */ - dir = direction.XMINUS; /* Change direction */ - } - else if(!blks.getFlag(cur_x+1, cur_y+1)) { /* Straight? */ - cur_y++; - } - else { /* Left turn */ - linelist.add(new int[] { cur_x+1, cur_y+1 }); /* Finish line */ - dir = direction.XPLUS; - cur_x++; cur_y++; - } - break; - case XMINUS: /* Segment in X- direction */ - if(!blks.getFlag(cur_x-1, cur_y)) { /* Right turn? */ - linelist.add(new int[] { cur_x, cur_y+1 }); /* Finish line */ - dir = direction.YMINUS; /* Change direction */ - } - else if(!blks.getFlag(cur_x-1, cur_y+1)) { /* Straight? */ - cur_x--; - } - else { /* Left turn */ - linelist.add(new int[] { cur_x, cur_y+1 }); /* Finish line */ - dir = direction.YPLUS; - cur_x--; cur_y++; - } - break; - case YMINUS: /* Segment in Y- direction */ - if(!blks.getFlag(cur_x, cur_y-1)) { /* Right turn? */ - linelist.add(new int[] { cur_x, cur_y }); /* Finish line */ - dir = direction.XPLUS; /* Change direction */ - } - else if(!blks.getFlag(cur_x-1, cur_y-1)) { /* Straight? */ - cur_y--; - } - else { /* Left turn */ - linelist.add(new int[] { cur_x, cur_y }); /* Finish line */ - dir = direction.XMINUS; - cur_x--; cur_y--; - } - break; + } + else { /* Else, keep it in the list for the next polygon */ + if(newlist == null) newlist = new LinkedList(); + newlist.add(node); + } } + nodevals = newlist; /* Replace list (null if no more to process) */ + if(ourblks == null) continue; /* Nothing found, skip to end */ + /* Trace outline of blocks - start from minx, miny going to x+ */ + int init_x = minx; + int init_y = miny; + int cur_x = minx; + int cur_y = miny; + direction dir = direction.XPLUS; + ArrayList linelist = new ArrayList(); + linelist.add(new int[] { init_x, init_y } ); // Add start point + while((cur_x != init_x) || (cur_y != init_y) || (dir != direction.YMINUS)) { + switch(dir) { + case XPLUS: /* Segment in X+ direction */ + if(!ourblks.getFlag(cur_x+1, cur_y)) { /* Right turn? */ + linelist.add(new int[] { cur_x+1, cur_y }); /* Finish line */ + dir = direction.YPLUS; /* Change direction */ + } + else if(!ourblks.getFlag(cur_x+1, cur_y-1)) { /* Straight? */ + cur_x++; + } + else { /* Left turn */ + linelist.add(new int[] { cur_x+1, cur_y }); /* Finish line */ + dir = direction.YMINUS; + cur_x++; cur_y--; + } + break; + case YPLUS: /* Segment in Y+ direction */ + if(!ourblks.getFlag(cur_x, cur_y+1)) { /* Right turn? */ + linelist.add(new int[] { cur_x+1, cur_y+1 }); /* Finish line */ + dir = direction.XMINUS; /* Change direction */ + } + else if(!ourblks.getFlag(cur_x+1, cur_y+1)) { /* Straight? */ + cur_y++; + } + else { /* Left turn */ + linelist.add(new int[] { cur_x+1, cur_y+1 }); /* Finish line */ + dir = direction.XPLUS; + cur_x++; cur_y++; + } + break; + case XMINUS: /* Segment in X- direction */ + if(!ourblks.getFlag(cur_x-1, cur_y)) { /* Right turn? */ + linelist.add(new int[] { cur_x, cur_y+1 }); /* Finish line */ + dir = direction.YMINUS; /* Change direction */ + } + else if(!ourblks.getFlag(cur_x-1, cur_y+1)) { /* Straight? */ + cur_x--; + } + else { /* Left turn */ + linelist.add(new int[] { cur_x, cur_y+1 }); /* Finish line */ + dir = direction.YPLUS; + cur_x--; cur_y++; + } + break; + case YMINUS: /* Segment in Y- direction */ + if(!ourblks.getFlag(cur_x, cur_y-1)) { /* Right turn? */ + linelist.add(new int[] { cur_x, cur_y }); /* Finish line */ + dir = direction.XPLUS; /* Change direction */ + } + else if(!ourblks.getFlag(cur_x-1, cur_y-1)) { /* Straight? */ + cur_y--; + } + else { /* Left turn */ + linelist.add(new int[] { cur_x, cur_y }); /* Finish line */ + dir = direction.XMINUS; + cur_x--; cur_y--; + } + break; + } + } + @SuppressWarnings("unchecked") + Map[] coordlist = new Map[linelist.size()]; + for(int i = 0; i < linelist.size(); i++) { + coordlist[i] = new HashMap(); + coordlist[i].put("x", linelist.get(i)[0] * townblocksize); + coordlist[i].put("z", linelist.get(i)[1] * townblocksize); + } + polygons.add(coordlist); } @SuppressWarnings("unchecked") - Map[] coordlist = new Map[linelist.size()]; - for(int i = 0; i < linelist.size(); i++) { - coordlist[i] = new HashMap(); - coordlist[i].put("x", linelist.get(i)[0] * townblocksize); - coordlist[i].put("z", linelist.get(i)[1] * townblocksize); - } + Map[][] polylist = new Map[polygons.size()][]; + polygons.toArray(polylist); rslt = new HashMap(); - rslt.put("points", coordlist); + rslt.put("points", polylist); + /* Add other data */ String mayor = p.getProperty("mayor"); if(mayor != null) rslt.put("mayor", mayor); diff --git a/web/js/regions.js b/web/js/regions.js index 716b4de8..9170f97d 100644 --- a/web/js/regions.js +++ b/web/js/regions.js @@ -53,44 +53,38 @@ componentconstructors['regions'] = function(dynmap, configuration) { } function create3DBoxLayer(maxx, minx, maxy, miny, maxz, minz, style) { - return new L.FeatureGroup([ - new L.Polygon([ + return new L.MultiPolygon([ + [ latlng(minx,miny,minz), latlng(maxx,miny,minz), latlng(maxx,miny,maxz), latlng(minx,miny,maxz) - ], style), - new L.Polygon([ + ],[ latlng(minx,maxy,minz), latlng(maxx,maxy,minz), latlng(maxx,maxy,maxz), latlng(minx,maxy,maxz) - ], style), - new L.Polygon([ + ],[ latlng(minx,miny,minz), latlng(minx,maxy,minz), latlng(maxx,maxy,minz), latlng(maxx,miny,minz) - ], style), - new L.Polygon([ + ],[ latlng(maxx,miny,minz), latlng(maxx,maxy,minz), latlng(maxx,maxy,maxz), latlng(maxx,miny,maxz) - ], style), - new L.Polygon([ + ],[ latlng(minx,miny,maxz), latlng(minx,maxy,maxz), latlng(maxx,maxy,maxz), latlng(maxx,miny,maxz) - ], style), - new L.Polygon([ + ],[ latlng(minx,miny,minz), latlng(minx,maxy,minz), latlng(minx,maxy,maxz), latlng(minx,miny,maxz) - ], style) - ]); + ]], style); } function create2DBoxLayer(maxx, minx, maxy, miny, maxz, minz, style) { @@ -117,12 +111,12 @@ componentconstructors['regions'] = function(dynmap, configuration) { sidelist[1] = botlist[i]; sidelist[2] = botlist[(i+1)%xarray.length]; sidelist[3] = toplist[(i+1)%xarray.length]; - polylist[i] = new L.Polygon(sidelist, style); + polylist[i] = sidelist; } - polylist[xarray.length] = new L.Polygon(botlist, style); - polylist[xarray.length+1] = new L.Polygon(toplist, style); + polylist[xarray.length] = botlist; + polylist[xarray.length+1] = toplist; - return new L.FeatureGroup(polylist); + return new L.MultiPolygon(polylist, style); } function create2DOutlineLayer(xarray, maxy, miny, zarray, style) { diff --git a/web/js/regions_Towny.js b/web/js/regions_Towny.js index ea3a5357..641dc116 100644 --- a/web/js/regions_Towny.js +++ b/web/js/regions_Towny.js @@ -1,14 +1,12 @@ regionConstructors['Towny'] = function(dynmap, configuration) { // Helper function. - function createOutlineFromRegion(name, region, outCreator) { + function createOutlineFromRegion(name, region, points, outCreator) { var xarray = []; var zarray = []; - if(region.points) { - var i; - for(i = 0; i < region.points.length; i++) { - xarray[i] = region.points[i].x; - zarray[i] = region.points[i].z; - } + var i; + for(i = 0; i < points.length; i++) { + xarray[i] = points[i].x; + zarray[i] = points[i].z; } var ymin = 64; var ymax = 65; @@ -20,14 +18,17 @@ regionConstructors['Towny'] = function(dynmap, configuration) { $.getJSON('standalone/'+regionFile, function(data) { var boxLayers = []; $.each(data, function(name, region) { - var outLayer = createOutlineFromRegion(name, region, configuration.createOutlineLayer); - if (outLayer) { - outLayer.bindPopup(configuration.createPopupContent(name, - $.extend(region, { - owners: { players: [region.mayor] }, - members: { players: [ region.residents ] } - }))); - boxLayers.push(outLayer); + var i; + for(i = 0; i < region.points.length; i++) { + var outLayer = createOutlineFromRegion(name, region, region.points[i], configuration.createOutlineLayer); + if (outLayer) { + outLayer.bindPopup(configuration.createPopupContent(name, + $.extend(region, { + owners: { players: [region.mayor] }, + members: { players: [ region.residents ] } + }))); + boxLayers.push(outLayer); + } } }); configuration.result(new L.LayerGroup(boxLayers)); From c65b17d80ba28cb0940d59b101ddb6d78aaae13e Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Thu, 25 Aug 2011 14:48:42 -0500 Subject: [PATCH 03/27] Disable IOImage cache use for image loads --- src/main/java/org/dynmap/hdmap/TexturePack.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/org/dynmap/hdmap/TexturePack.java b/src/main/java/org/dynmap/hdmap/TexturePack.java index 63f7953c..d87c4230 100644 --- a/src/main/java/org/dynmap/hdmap/TexturePack.java +++ b/src/main/java/org/dynmap/hdmap/TexturePack.java @@ -369,6 +369,7 @@ public class TexturePack { private void loadTerrainPNG(InputStream is) throws IOException { int i, j; /* Load image */ + ImageIO.setUseCache(false); BufferedImage img = ImageIO.read(is); if(img == null) { throw new FileNotFoundException(); } terrain_width = img.getWidth(); @@ -429,6 +430,7 @@ public class TexturePack { /* Load image into image array */ private void loadImage(InputStream is, int idx) throws IOException { /* Load image */ + ImageIO.setUseCache(false); BufferedImage img = ImageIO.read(is); if(img == null) { throw new FileNotFoundException(); } imgs[idx] = new LoadedImage(); @@ -642,6 +644,7 @@ public class TexturePack { } } BufferedImage img = DynmapBufferedImage.createBufferedImage(outbuf, terrain_width, terrain_height); + ImageIO.setUseCache(false); ImageIO.write(img, "png", f); } From 625b62fa36228307ea0290d9fc47280b5d6b45fc Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Thu, 25 Aug 2011 14:49:32 -0500 Subject: [PATCH 04/27] Use towns list in Towny worlds/* files to get valid towns (old towns still in towns/* directory) --- .../dynmap/regions/TownyConfigHandler.java | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/dynmap/regions/TownyConfigHandler.java b/src/main/java/org/dynmap/regions/TownyConfigHandler.java index d5aab6f5..27813891 100644 --- a/src/main/java/org/dynmap/regions/TownyConfigHandler.java +++ b/src/main/java/org/dynmap/regions/TownyConfigHandler.java @@ -39,19 +39,30 @@ public class TownyConfigHandler { */ public Map getRegionData(String wname) { Map rslt = new HashMap(); - /* List towns directory - process all towns there */ - File towndir = new File("plugins/Towny/data/towns"); - File[] towns = towndir.listFiles(new FilenameFilter() { - @Override - public boolean accept(File dir, String name) { - return name.endsWith(".txt"); + Properties p = new Properties(); + FileInputStream fis = null; + /* Read world data for this world */ + try { + fis = new FileInputStream("plugins/Towny/data/worlds/" + wname + ".txt"); /* Open and load file */ + p.load(fis); + } catch (IOException iox) { + Log.severe("Error loading Towny world file " + wname + ".txt"); + } finally { + if(fis != null) { + try { fis.close(); } catch (IOException iox) {} } - }); - for(File town : towns) { - Map td = processTown(town, wname); + } + /* Get towns list for our world */ + String t = p.getProperty("towns", ""); + String towns[] = t.split(","); /* Split on commas */ + /* List towns directory - process all towns there */ + for(String town : towns) { + town = town.trim(); + if(town.length() == 0) continue; + File tfile = new File("plugins/Towny/data/towns/" + town + ".txt"); + Map td = processTown(tfile, wname); if(td != null) { - String fn = town.getName(); - rslt.put(fn.substring(0, fn.lastIndexOf('.')), td); + rslt.put(town, td); } } return rslt; From bfb12e4f8130b9fa774176e935951cbc3c748125 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Thu, 25 Aug 2011 17:07:00 -0500 Subject: [PATCH 05/27] Add control for hide/show of component layers (only regions for now) --- web/js/dynmaputils.js | 6 ++++++ web/js/map.js | 8 +++++++- web/js/regions.js | 2 ++ 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/web/js/dynmaputils.js b/web/js/dynmaputils.js index 14304afd..e4dfb893 100644 --- a/web/js/dynmaputils.js +++ b/web/js/dynmaputils.js @@ -43,6 +43,12 @@ if (!Array.prototype.indexOf) { } } +var DynmapLayerControl = L.Control.Layers.extend({ + getPosition: function() { + return L.Control.Position.TOP_LEFT; + } +}); + var DynmapTileLayer = L.TileLayer.extend({ _currentzoom: undefined, diff --git a/web/js/map.js b/web/js/map.js index 5f4e73b8..c558880e 100644 --- a/web/js/map.js +++ b/web/js/map.js @@ -41,6 +41,7 @@ DynMap.prototype = { inittime: new Date().getTime(), followingPlayer: '', missedupdates: 0, + layercontrol: undefined, formatUrl: function(name, options) { var url = this.options.url[name]; $.each(options, function(n,v) { @@ -137,6 +138,9 @@ DynMap.prototype = { me.followPlayer(null); });*/ + me.layercontrol = new DynmapLayerControl(); + map.addControl(me.layercontrol); + // Sidebar var panel; var sidebar; @@ -319,7 +323,8 @@ DynMap.prototype = { var prevzoom = me.map.getZoom(); if (me.maptype) { - me.map.removeLayer(me.maptype); + me.layercontrol.removeLayer(me.maptype); + //me.map.removeLayer(me.maptype); } var prevmap = me.maptype; @@ -356,6 +361,7 @@ DynMap.prototype = { me.map.setZoom(prevzoom); } me.map.addLayer(me.maptype); + //me.layercontrol.addBaseLayer(me.maptype, 'Tiles'); if (worldChanged) { $(me).trigger('worldchanged'); diff --git a/web/js/regions.js b/web/js/regions.js index 9170f97d..e3ea82fe 100644 --- a/web/js/regions.js +++ b/web/js/regions.js @@ -165,6 +165,7 @@ componentconstructors['regions'] = function(dynmap, configuration) { var activeLayer = undefined; function undraw() { if (activeLayer) { + dynmap.layercontrol.removeLayer(activeLayer); dynmap.map.removeLayer(activeLayer); activeLayer = undefined; } @@ -183,6 +184,7 @@ componentconstructors['regions'] = function(dynmap, configuration) { result: function(regionsLayer) { activeLayer = regionsLayer; dynmap.map.addLayer(activeLayer); + dynmap.layercontrol.addOverlay(activeLayer, regionType); } })); } From 1a128a6d745773e1787fc6185bcaf266b0b9da36 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Thu, 25 Aug 2011 17:31:34 -0500 Subject: [PATCH 06/27] Add playermarkers to hide/show-able layer group --- web/js/playermarkers.js | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/web/js/playermarkers.js b/web/js/playermarkers.js index fd621276..60268547 100644 --- a/web/js/playermarkers.js +++ b/web/js/playermarkers.js @@ -72,18 +72,18 @@ componentconstructors['playermarkers'] = function(dynmap, configuration) { return div; }}); if(dynmap.world === player.location.world) - dynmap.map.addLayer(player.marker); + dynmap.playermarkergroup.addLayer(player.marker); }); $(dynmap).bind('playerremoved', function(event, player) { // Remove the marker. if(dynmap.map.hasLayer(player.marker)) - dynmap.map.removeLayer(player.marker); + dynmap.playermarkergroup.removeLayer(player.marker); }); $(dynmap).bind('playerupdated', function(event, player) { if(dynmap.world === player.location.world) { // Add if needed if(dynmap.map.hasLayer(player.marker) == false) - dynmap.map.addLayer(player.marker); + dynmap.playermarkergroup.addLayer(player.marker); else { // Update the marker. var markerPosition = dynmap.getProjection().fromLocationToLatLng(player.location); @@ -100,7 +100,7 @@ componentconstructors['playermarkers'] = function(dynmap, configuration) { } } } else if(dynmap.map.hasLayer(player.marker)) { - dynmap.map.removeLayer(player.marker); + dynmap.playermarkergroup.removeLayer(player.marker); } }); // Remove marker on start of map change @@ -109,7 +109,7 @@ componentconstructors['playermarkers'] = function(dynmap, configuration) { for(name in dynmap.players) { var player = dynmap.players[name]; // Turn off marker - let update turn it back on - dynmap.map.removeLayer(player.marker); + dynmap.playermarkergroup.removeLayer(player.marker); } }); // Remove marker on map change - let update place it again @@ -119,12 +119,15 @@ componentconstructors['playermarkers'] = function(dynmap, configuration) { var player = dynmap.players[name]; if(dynmap.world === player.location.world) { if(dynmap.map.hasLayer(player.marker) == false) - dynmap.map.addLayer(player.marker); + dynmap.playermarkergroup.addLayer(player.marker); var markerPosition = dynmap.getProjection().fromLocationToLatLng(player.location); player.marker.setLatLng(markerPosition); } else if(dynmap.map.hasLayer(player.marker)) { - dynmap.map.removeLayer(player.marker); + dynmap.playermarkergroup.removeLayer(player.marker); } } }); + dynmap.playermarkergroup = new L.LayerGroup(); + dynmap.map.addLayer(dynmap.playermarkergroup); + dynmap.layercontrol.addOverlay(dynmap.playermarkergroup, 'Players'); }; From aee7ea2d491332fd68dc6ea9dc98f565b234a064 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Fri, 26 Aug 2011 00:13:03 -0500 Subject: [PATCH 07/27] Add hidebydefault option for regions layers --- src/main/resources/configuration.txt | 6 ++++++ web/css/dynmap_style.css | 8 ++++++++ web/js/regions.js | 25 ++++++++++++++++--------- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/main/resources/configuration.txt b/src/main/resources/configuration.txt index 6ea8aa54..c5f218a9 100644 --- a/src/main/resources/configuration.txt +++ b/src/main/resources/configuration.txt @@ -91,6 +91,8 @@ components: # customstyle: # homebase: # strokeColor: "#00FF00" + # # Optional - make layer hidden by default + # hidebydefault: true #- class: org.dynmap.regions.RegionsComponent # type: regions @@ -122,6 +124,8 @@ components: # groupstyle: # homebase: # strokeColor: "#007F00" + # # Optional - make layer hidden by default + # hidebydefault: true #- class: org.dynmap.regions.RegionsComponent # type: regions @@ -150,6 +154,8 @@ components: # groupstyle: # MyNation: # strokeColor: "#007F00" + # # Optional - make layer hidden by default + # hidebydefault: true #- class: org.dynmap.TestComponent # stuff: "This is some configuration-value" diff --git a/web/css/dynmap_style.css b/web/css/dynmap_style.css index c702a54a..2f7b9aa3 100644 --- a/web/css/dynmap_style.css +++ b/web/css/dynmap_style.css @@ -35,6 +35,14 @@ font-weight: bold; } +.leaflet-control-layers { + background-color: #bbb; +} + +.leaflet-control-layers:hover { + background-color: #fff; +} + .leaflet-control-zoom-in { background-color: #eee; } diff --git a/web/js/regions.js b/web/js/regions.js index e3ea82fe..5e45f49f 100644 --- a/web/js/regions.js +++ b/web/js/regions.js @@ -162,12 +162,10 @@ componentconstructors['regions'] = function(dynmap, configuration) { loadcss('css/regions.css'); var regionType = configuration.name; loadjs('js/regions_' + regionType + '.js', function() { - var activeLayer = undefined; + configuration.activeLayer = undefined; function undraw() { - if (activeLayer) { - dynmap.layercontrol.removeLayer(activeLayer); - dynmap.map.removeLayer(activeLayer); - activeLayer = undefined; + if (configuration.activeLayer) { + configuration.activeLayer.clearLayers(); } } function redraw() { @@ -182,15 +180,24 @@ componentconstructors['regions'] = function(dynmap, configuration) { createOutlineLayer: configuration.use3dregions ? create3DOutlineLayer : create2DOutlineLayer, getStyle: getStyle, result: function(regionsLayer) { - activeLayer = regionsLayer; - dynmap.map.addLayer(activeLayer); - dynmap.layercontrol.addOverlay(activeLayer, regionType); + if(configuration.activeLayer) { /* Not first time */ + for(var i in regionsLayer._layers) { + configuration.activeLayer.addLayer(regionsLayer._layers[i]); + } + regionsLayer.clearLayers(); + } + else { + configuration.activeLayer = regionsLayer; + if(!configuration.hidebydefault) + dynmap.map.addLayer(configuration.activeLayer); + dynmap.layercontrol.addOverlay(configuration.activeLayer, regionType); + } } })); } } $(dynmap).bind('mapchanged', redraw); $(dynmap).bind('mapchanging', undraw); - redraw(); + redraw(true); }); } \ No newline at end of file From ace7fbf8387fa1aea7a5fa61ab07be15f41d4b73 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Fri, 26 Aug 2011 00:55:53 -0500 Subject: [PATCH 08/27] Handle multiple instances of same component type (regions) --- web/js/map.js | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/web/js/map.js b/web/js/map.js index c558880e..6693021b 100644 --- a/web/js/map.js +++ b/web/js/map.js @@ -284,12 +284,21 @@ DynMap.prototype = { me.selectMap(me.defaultworld.defaultmap); - var componentstoload = me.options.components.length; + var configset = { }; $.each(me.options.components, function(index, configuration) { - loadjs('js/' + configuration.type + '.js', function() { - var componentconstructor = componentconstructors[configuration.type]; + if(!configset[configuration.type]) + configset[configuration.type] = []; + configset[configuration.type].push(configuration); + }); + var componentstoload = configset.length; + + $.each(configset, function(type, configlist) { + loadjs('js/' + type + '.js', function() { + var componentconstructor = componentconstructors[type]; if (componentconstructor) { - me.components.push(new componentconstructor(me, configuration)); + $.each(configlist, function(idx, configuration) { + me.components.push(new componentconstructor(me, configuration)); + }); } else { // Could not load component. We'll ignore this for the moment. } @@ -323,8 +332,7 @@ DynMap.prototype = { var prevzoom = me.map.getZoom(); if (me.maptype) { - me.layercontrol.removeLayer(me.maptype); - //me.map.removeLayer(me.maptype); + me.map.removeLayer(me.maptype); } var prevmap = me.maptype; @@ -361,7 +369,6 @@ DynMap.prototype = { me.map.setZoom(prevzoom); } me.map.addLayer(me.maptype); - //me.layercontrol.addBaseLayer(me.maptype, 'Tiles'); if (worldChanged) { $(me).trigger('worldchanged'); From 3d385106e9464d2a87a71b729003dd0d55ebe90b Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Fri, 26 Aug 2011 01:22:03 -0500 Subject: [PATCH 09/27] Add hidebydefault setting to playermarkers component too --- src/main/resources/configuration.txt | 2 ++ web/js/map.js | 6 ++++-- web/js/playermarkers.js | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/resources/configuration.txt b/src/main/resources/configuration.txt index c5f218a9..ca3ea984 100644 --- a/src/main/resources/configuration.txt +++ b/src/main/resources/configuration.txt @@ -55,6 +55,8 @@ components: showplayerhealth: true # Option to make player faces small - don't use with showplayerhealth smallplayerfaces: false + # # Optional - make player faces layer hidden by default + # hidebydefault: true #- class: org.dynmap.ClientComponent # type: digitalclock - class: org.dynmap.ClientComponent diff --git a/web/js/map.js b/web/js/map.js index 6693021b..a571cdec 100644 --- a/web/js/map.js +++ b/web/js/map.js @@ -284,13 +284,15 @@ DynMap.prototype = { me.selectMap(me.defaultworld.defaultmap); + var componentstoload = 0; var configset = { }; $.each(me.options.components, function(index, configuration) { - if(!configset[configuration.type]) + if(!configset[configuration.type]) { configset[configuration.type] = []; + componentstoload++; + } configset[configuration.type].push(configuration); }); - var componentstoload = configset.length; $.each(configset, function(type, configlist) { loadjs('js/' + type + '.js', function() { diff --git a/web/js/playermarkers.js b/web/js/playermarkers.js index 60268547..e4d2d77a 100644 --- a/web/js/playermarkers.js +++ b/web/js/playermarkers.js @@ -128,6 +128,7 @@ componentconstructors['playermarkers'] = function(dynmap, configuration) { } }); dynmap.playermarkergroup = new L.LayerGroup(); - dynmap.map.addLayer(dynmap.playermarkergroup); + if(!configuration.hidebydefault) + dynmap.map.addLayer(dynmap.playermarkergroup); dynmap.layercontrol.addOverlay(dynmap.playermarkergroup, 'Players'); }; From ac650656b49cc528e0f0871e652c525b4f18638f Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sat, 27 Aug 2011 19:13:34 -0500 Subject: [PATCH 10/27] Fix Towny support to handle 0.75.x and 0.74.x --- .../dynmap/regions/TownyConfigHandler.java | 33 +++++++------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/src/main/java/org/dynmap/regions/TownyConfigHandler.java b/src/main/java/org/dynmap/regions/TownyConfigHandler.java index a0a3b897..ff8b07fc 100644 --- a/src/main/java/org/dynmap/regions/TownyConfigHandler.java +++ b/src/main/java/org/dynmap/regions/TownyConfigHandler.java @@ -125,34 +125,25 @@ public class TownyConfigHandler { } if(!worldmatch) continue; - java.util.regex.Matcher towny075Match = towny075Pattern.matcher(n); - if (towny075Match.matches()) { - // Towny Advanced >= 0.75 + int bidx = n.indexOf(']'); + if(bidx >= 0) { /* If 0.75 block type present, skip it (we don't care yet) */ + n = n.substring(bidx+1); + } + String[] v = n.split(","); + if(v.length >= 2) { /* Price in 0.75 is third - don't care :) */ try { - //int plotType = Integer.valueOf(towny075Match.group(1)); - int[] vv = new int[] { Integer.valueOf(towny075Match.group(2)), Integer.valueOf(towny075Match.group(3)) }; + int[] vv = new int[] { Integer.valueOf(v[0]), Integer.valueOf(v[1]) }; blks.setFlag(vv[0], vv[1], true); nodevals.add(vv); - } catch(NumberFormatException nfx) { + } catch (NumberFormatException nfx) { Log.severe("Error parsing block list in Towny - " + townfile.getPath()); return null; } + } else if(n.startsWith("|")){ /* End of list? */ + } else { - // Towny Advanced < 0.75 - String[] v = n.split(","); - if(v.length == 2) { - try { - int[] vv = new int[] { Integer.valueOf(v[0]), Integer.valueOf(v[1]) }; - blks.setFlag(vv[0], vv[1], true); - nodevals.add(vv); - } catch (NumberFormatException nfx) { - Log.severe("Error parsing block list in Towny - " + townfile.getPath()); - return null; - } - } else { - Log.severe("Invalid block list format in Towny - " + townfile.getPath()); - return null; - } + Log.severe("Invalid block list format in Towny - " + townfile.getPath()); + return null; } } /* If nothing in this world, skip */ From b20b9e14369e2990e6e467b961b87ddd5a548a4a Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 28 Aug 2011 10:12:04 -0500 Subject: [PATCH 11/27] Fix handling of non-standard town_block_size values --- src/main/java/org/dynmap/regions/TownyConfigHandler.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dynmap/regions/TownyConfigHandler.java b/src/main/java/org/dynmap/regions/TownyConfigHandler.java index ff8b07fc..141b117c 100644 --- a/src/main/java/org/dynmap/regions/TownyConfigHandler.java +++ b/src/main/java/org/dynmap/regions/TownyConfigHandler.java @@ -32,7 +32,12 @@ public class TownyConfigHandler { } Configuration tcfg = new Configuration(cfgfile); tcfg.load(); - townblocksize = tcfg.getInt("town_block_size", 16); /* Get block size */ + String tbsize = tcfg.getNode("town").getString("town_block_size", "16"); + try { + townblocksize = Integer.valueOf(tbsize); + } catch (NumberFormatException nfx) { + townblocksize = 16; + } } /** * Get map of attributes for given world From af1f47df23cd49335331bf77f2c12bd368312062 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Sun, 28 Aug 2011 21:31:04 -0500 Subject: [PATCH 12/27] Add first pass of Factions support --- .../dynmap/regions/FactionsConfigHandler.java | 284 ++++++++++++++++++ .../org/dynmap/regions/RegionHandler.java | 7 + .../org/dynmap/regions/RegionsComponent.java | 11 + src/main/resources/configuration.txt | 25 ++ web/js/regions_Factions.js | 37 +++ 5 files changed, 364 insertions(+) create mode 100644 src/main/java/org/dynmap/regions/FactionsConfigHandler.java create mode 100644 web/js/regions_Factions.js diff --git a/src/main/java/org/dynmap/regions/FactionsConfigHandler.java b/src/main/java/org/dynmap/regions/FactionsConfigHandler.java new file mode 100644 index 00000000..be47e0fd --- /dev/null +++ b/src/main/java/org/dynmap/regions/FactionsConfigHandler.java @@ -0,0 +1,284 @@ +package org.dynmap.regions; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.bukkit.ChatColor; +import org.bukkit.World; +import org.bukkit.util.config.Configuration; +import org.dynmap.ConfigurationNode; +import org.dynmap.DynmapPlugin; +import org.dynmap.Log; +import org.dynmap.utils.TileFlags; +import org.json.simple.JSONArray; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; + +public class FactionsConfigHandler { + private int townblocksize = 16; + protected JSONParser parser = new JSONParser(); + private Charset cs_utf8 = Charset.forName("UTF-8"); + + public FactionsConfigHandler(ConfigurationNode cfg) { + } + /** + * Get map of attributes for given world + */ + public Map getRegionData(String wname) { + /* Load factions.json file */ + File faction = new File("plugins/Factions/factions.json"); + if(faction.canRead() == false) { /* Can't read config */ + Log.severe("Cannot find Faction file - " + faction.getPath()); + return null; + } + JSONObject fact = null; + try { + Reader inputFileReader = new InputStreamReader(new FileInputStream(faction), cs_utf8); + fact = (JSONObject) parser.parse(inputFileReader); + inputFileReader.close(); + } catch (IOException ex) { + Log.severe("Exception while reading factions.json.", ex); + } catch (ParseException ex) { + Log.severe("Exception while parsing factions.json.", ex); + } + if(fact == null) + return null; + /* Load board.json */ + File board = new File("plugins/Factions/board.json"); + if(board.canRead() == false) { /* Can't read config */ + Log.severe("Cannot find Faction file - " + board.getPath()); + return null; + } + JSONObject blocks = null; + try { + Reader inputFileReader = new InputStreamReader(new FileInputStream(board), cs_utf8); + blocks = (JSONObject) parser.parse(inputFileReader); + inputFileReader.close(); + } catch (IOException ex) { + Log.severe("Exception while reading board.json.", ex); + } catch (ParseException ex) { + Log.severe("Exception while parsing board.json.", ex); + } + if(blocks == null) + return null; + /* Get value from board.json for requested world */ + Object wb = blocks.get(wname); + if((wb == null) || (!(wb instanceof JSONObject))) { + return null; + } + JSONObject wblocks = (JSONObject)wb; + Map rslt = new HashMap(); + /* Now go through the factions list, and find outline */ + for(Object factid : fact.keySet()) { + int fid = 0; + try { + fid = Integer.valueOf(factid.toString()); + } catch (NumberFormatException nfx) { + continue; + } + JSONObject fobj = (JSONObject)fact.get(factid); /* Get faction info */ + String town = (String)fobj.get("tag"); + town = ChatColor.stripColor(town); /* Strip color */ + Map td = processFaction(wblocks, fobj, fid); + if(td != null) { + rslt.put(town, td); + } + } + return rslt; + } + + enum direction { XPLUS, YPLUS, XMINUS, YMINUS }; + + private static final String FLAGS[] = { + "open", "peaceful", "peacefulExplosionsEnabled" + }; + + /** + * Find all contiguous blocks, set in target and clear in source + */ + private int floodFillTarget(TileFlags src, TileFlags dest, int x, int y) { + int cnt = 0; + if(src.getFlag(x, y)) { /* Set in src */ + src.setFlag(x, y, false); /* Clear source */ + dest.setFlag(x, y, true); /* Set in destination */ + cnt++; + cnt += floodFillTarget(src, dest, x+1, y); /* Fill adjacent blocks */ + cnt += floodFillTarget(src, dest, x-1, y); + cnt += floodFillTarget(src, dest, x, y+1); + cnt += floodFillTarget(src, dest, x, y-1); + } + return cnt; + } + /** + * Process data from given town file + */ + public Map processFaction(JSONObject blocks, JSONObject faction, int factid) { + /* Build list of nodes matching our faction ID */ + LinkedList nodevals = new LinkedList(); + TileFlags blks = new TileFlags(); + for(Object k: blocks.keySet()) { + Object fid = blocks.get(k); + int bfid = 0; + try { + bfid = Integer.valueOf(fid.toString()); + } catch (NumberFormatException nfx) { + continue; + } + if(bfid == factid) { /* Our faction? */ + String[] coords = k.toString().split(","); + if(coords.length >= 2) { + try { + int[] vv = new int[] { Integer.valueOf(coords[0]), Integer.valueOf(coords[1]) }; + blks.setFlag(vv[0], vv[1], true); + nodevals.add(vv); + } catch (NumberFormatException nfx) { + Log.severe("Error parsing Factions blocks"); + return null; + } + } + } + } + /* If nothing found for this faction, skip */ + if(nodevals.size() == 0) + return null; + /* Loop through until we don't find more areas */ + ArrayList[]> polygons = new ArrayList[]>(); + while(nodevals != null) { + LinkedList ournodes = null; + LinkedList newlist = null; + TileFlags ourblks = null; + int minx = Integer.MAX_VALUE; + int miny = Integer.MAX_VALUE; + for(int[] node : nodevals) { + if((ourblks == null) && blks.getFlag(node[0], node[1])) { /* Node still in map? */ + ourblks = new TileFlags(); /* Create map for shape */ + ournodes = new LinkedList(); + floodFillTarget(blks, ourblks, node[0], node[1]); /* Copy shape */ + ournodes.add(node); /* Add it to our node list */ + minx = node[0]; miny = node[1]; + } + /* If shape found, and we're in it, add to our node list */ + else if((ourblks != null) && (ourblks.getFlag(node[0], node[1]))) { + ournodes.add(node); + if(node[0] < minx) { + minx = node[0]; miny = node[1]; + } + else if((node[0] == minx) && (node[1] < miny)) { + miny = node[1]; + } + } + else { /* Else, keep it in the list for the next polygon */ + if(newlist == null) newlist = new LinkedList(); + newlist.add(node); + } + } + nodevals = newlist; /* Replace list (null if no more to process) */ + if(ourblks == null) continue; /* Nothing found, skip to end */ + /* Trace outline of blocks - start from minx, miny going to x+ */ + int init_x = minx; + int init_y = miny; + int cur_x = minx; + int cur_y = miny; + direction dir = direction.XPLUS; + ArrayList linelist = new ArrayList(); + linelist.add(new int[] { init_x, init_y } ); // Add start point + while((cur_x != init_x) || (cur_y != init_y) || (dir != direction.YMINUS)) { + switch(dir) { + case XPLUS: /* Segment in X+ direction */ + if(!ourblks.getFlag(cur_x+1, cur_y)) { /* Right turn? */ + linelist.add(new int[] { cur_x+1, cur_y }); /* Finish line */ + dir = direction.YPLUS; /* Change direction */ + } + else if(!ourblks.getFlag(cur_x+1, cur_y-1)) { /* Straight? */ + cur_x++; + } + else { /* Left turn */ + linelist.add(new int[] { cur_x+1, cur_y }); /* Finish line */ + dir = direction.YMINUS; + cur_x++; cur_y--; + } + break; + case YPLUS: /* Segment in Y+ direction */ + if(!ourblks.getFlag(cur_x, cur_y+1)) { /* Right turn? */ + linelist.add(new int[] { cur_x+1, cur_y+1 }); /* Finish line */ + dir = direction.XMINUS; /* Change direction */ + } + else if(!ourblks.getFlag(cur_x+1, cur_y+1)) { /* Straight? */ + cur_y++; + } + else { /* Left turn */ + linelist.add(new int[] { cur_x+1, cur_y+1 }); /* Finish line */ + dir = direction.XPLUS; + cur_x++; cur_y++; + } + break; + case XMINUS: /* Segment in X- direction */ + if(!ourblks.getFlag(cur_x-1, cur_y)) { /* Right turn? */ + linelist.add(new int[] { cur_x, cur_y+1 }); /* Finish line */ + dir = direction.YMINUS; /* Change direction */ + } + else if(!ourblks.getFlag(cur_x-1, cur_y+1)) { /* Straight? */ + cur_x--; + } + else { /* Left turn */ + linelist.add(new int[] { cur_x, cur_y+1 }); /* Finish line */ + dir = direction.YPLUS; + cur_x--; cur_y++; + } + break; + case YMINUS: /* Segment in Y- direction */ + if(!ourblks.getFlag(cur_x, cur_y-1)) { /* Right turn? */ + linelist.add(new int[] { cur_x, cur_y }); /* Finish line */ + dir = direction.XPLUS; /* Change direction */ + } + else if(!ourblks.getFlag(cur_x-1, cur_y-1)) { /* Straight? */ + cur_y--; + } + else { /* Left turn */ + linelist.add(new int[] { cur_x, cur_y }); /* Finish line */ + dir = direction.XMINUS; + cur_x--; cur_y--; + } + break; + } + } + @SuppressWarnings("unchecked") + Map[] coordlist = new Map[linelist.size()]; + for(int i = 0; i < linelist.size(); i++) { + coordlist[i] = new HashMap(); + coordlist[i].put("x", linelist.get(i)[0] * townblocksize); + coordlist[i].put("z", linelist.get(i)[1] * townblocksize); + } + polygons.add(coordlist); + } + @SuppressWarnings("unchecked") + Map[][] polylist = new Map[polygons.size()][]; + polygons.toArray(polylist); + HashMap rslt = new HashMap(); + rslt.put("points", polylist); + + /* Add other data */ + Map flags = new HashMap(); + for(String f : FLAGS) { + Object fval = faction.get(f); + if(fval != null) flags.put(f, fval.toString()); + } + rslt.put("flags", flags); + + return rslt; + } +} diff --git a/src/main/java/org/dynmap/regions/RegionHandler.java b/src/main/java/org/dynmap/regions/RegionHandler.java index f260cd5d..43711d40 100644 --- a/src/main/java/org/dynmap/regions/RegionHandler.java +++ b/src/main/java/org/dynmap/regions/RegionHandler.java @@ -24,12 +24,16 @@ public class RegionHandler extends FileHandler { private ConfigurationNode regions; private String regiontype; private TownyConfigHandler towny; + private FactionsConfigHandler factions; public RegionHandler(ConfigurationNode regions) { this.regions = regions; regiontype = regions.getString("name", "WorldGuard"); if(regiontype.equals("Towny")) { towny = new TownyConfigHandler(regions); } + else if(regiontype.equals("Factions")) { + factions = new FactionsConfigHandler(regions); + } } @Override protected InputStream getFileInput(String path, HttpRequest request, HttpResponse response) { @@ -48,6 +52,9 @@ public class RegionHandler extends FileHandler { if(regiontype.equals("Towny")) { regionData = towny.getRegionData(worldname); } + else if(regiontype.equals("Factions")) { + regionData = factions.getRegionData(worldname); + } else { /* If using worldpath, format is either plugins/// OR * plugins//worlds// diff --git a/src/main/java/org/dynmap/regions/RegionsComponent.java b/src/main/java/org/dynmap/regions/RegionsComponent.java index e079d611..8817cef1 100644 --- a/src/main/java/org/dynmap/regions/RegionsComponent.java +++ b/src/main/java/org/dynmap/regions/RegionsComponent.java @@ -21,6 +21,7 @@ import org.dynmap.web.Json; public class RegionsComponent extends ClientComponent { private TownyConfigHandler towny; + private FactionsConfigHandler factions; private String regiontype; public RegionsComponent(final DynmapPlugin plugin, final ConfigurationNode configuration) { @@ -37,6 +38,11 @@ public class RegionsComponent extends ClientComponent { towny = new TownyConfigHandler(configuration); plugin.webServer.handlers.put("/standalone/towny_*", new RegionHandler(configuration)); } + /* Load special handler for Factions */ + else if(regiontype.equals("Factions")) { + factions = new FactionsConfigHandler(configuration); + plugin.webServer.handlers.put("/standalone/factions_*", new RegionHandler(configuration)); + } else { plugin.webServer.handlers.put("/standalone/" + fname.substring(0, fname.lastIndexOf('.')) + "_*", new RegionHandler(configuration)); @@ -75,6 +81,11 @@ public class RegionsComponent extends ClientComponent { outputFileName = "towny_" + wname + ".json"; webWorldPath = new File(plugin.getWebPath()+"/standalone/", outputFileName); } + else if(regiontype.equals("Factions")) { + regionData = factions.getRegionData(wname); + outputFileName = "factions_" + wname + ".json"; + webWorldPath = new File(plugin.getWebPath()+"/standalone/", outputFileName); + } else { if(configuration.getBoolean("useworldpath", false)) { diff --git a/src/main/resources/configuration.txt b/src/main/resources/configuration.txt index ca3ea984..fce4e341 100644 --- a/src/main/resources/configuration.txt +++ b/src/main/resources/configuration.txt @@ -158,6 +158,31 @@ components: # strokeColor: "#007F00" # # Optional - make layer hidden by default # hidebydefault: true + + #- class: org.dynmap.regions.RegionsComponent + # type: regions + # name: Factions + # use3dregions: false + # infowindow: '
%regionname%
Flags
%flags%
' + # regionstyle: + # strokeColor: "#FF0000" + # strokeOpacity: 0.8 + # strokeWeight: 3 + # fillColor: "#FF0000" + # fillOpacity: 0.35 + # # Optional setting to limit which regions to show, by name - if commented out, all regions are shown + # visibleregions: + # - faction1 + # - faction2 + # # Optional setting to hide specific regions, by name + # hiddenregions: + # - hiddenfaction + # # Optional per-faction overrides for regionstyle (any defined replace those in regionstyle) + # customstyle: + # faction1: + # strokeColor: "#00FF00" + # # Optional - make layer hidden by default + # hidebydefault: true #- class: org.dynmap.TestComponent # stuff: "This is some configuration-value" diff --git a/web/js/regions_Factions.js b/web/js/regions_Factions.js new file mode 100644 index 00000000..1631aee1 --- /dev/null +++ b/web/js/regions_Factions.js @@ -0,0 +1,37 @@ +regionConstructors['Factions'] = function(dynmap, configuration) { + // Helper function. + function createOutlineFromRegion(name, region, points, outCreator) { + var xarray = []; + var zarray = []; + var i; + for(i = 0; i < points.length; i++) { + xarray[i] = points[i].x; + zarray[i] = points[i].z; + } + var ymin = 64; + var ymax = 65; + + return outCreator(xarray, ymax, ymin, zarray, configuration.getStyle(name, region.nation)); + } + + var regionFile = 'factions_'+configuration.worldName+'.json'; + $.getJSON('standalone/'+regionFile, function(data) { + var boxLayers = []; + $.each(data, function(name, region) { + var i; + for(i = 0; i < region.points.length; i++) { + var outLayer = createOutlineFromRegion(name, region, region.points[i], configuration.createOutlineLayer); + if (outLayer) { + outLayer.bindPopup(configuration.createPopupContent(name, + $.extend(region, { + owners: { players: [region.mayor] }, + members: { players: [ region.residents ] } + }))); + boxLayers.push(outLayer); + } + } + }); + configuration.result(new L.LayerGroup(boxLayers)); + + }); +}; \ No newline at end of file From 13bf8d7733490127f2e2e248c5557bfd109da28d Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Mon, 29 Aug 2011 14:21:39 -0500 Subject: [PATCH 13/27] Split renderdata (generated data) from normal block data - to help with custom block work --- .../java/org/dynmap/hdmap/HDBlockModels.java | 4 +-- .../org/dynmap/hdmap/HDPerspectiveState.java | 4 +++ .../org/dynmap/hdmap/IsoHDPerspective.java | 18 +++++++---- .../java/org/dynmap/hdmap/TexturePack.java | 23 ++++++++++---- src/main/resources/texture.txt | 30 +++++++++---------- 5 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/dynmap/hdmap/HDBlockModels.java b/src/main/java/org/dynmap/hdmap/HDBlockModels.java index f1b78e63..c0b6edfd 100644 --- a/src/main/java/org/dynmap/hdmap/HDBlockModels.java +++ b/src/main/java/org/dynmap/hdmap/HDBlockModels.java @@ -31,11 +31,11 @@ public class HDBlockModels { public static class HDScaledBlockModels { private short[][][] modelvectors; - public final short[] getScaledModel(int blocktype, int blockdata) { + public final short[] getScaledModel(int blocktype, int blockdata, int blockrenderdata) { if(modelvectors[blocktype] == null) { return null; } - return modelvectors[blocktype][blockdata]; + return modelvectors[blocktype][(blockrenderdata>=0)?blockrenderdata:blockdata]; } } diff --git a/src/main/java/org/dynmap/hdmap/HDPerspectiveState.java b/src/main/java/org/dynmap/hdmap/HDPerspectiveState.java index b54c26eb..f9ee74cf 100644 --- a/src/main/java/org/dynmap/hdmap/HDPerspectiveState.java +++ b/src/main/java/org/dynmap/hdmap/HDPerspectiveState.java @@ -20,6 +20,10 @@ public interface HDPerspectiveState { * Get current block data */ int getBlockData(); + /** + * Get current block render data + */ + int getBlockRenderData(); /** * Get direction of last block step */ diff --git a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java index a3fd0235..21a0b64f 100644 --- a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java +++ b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java @@ -95,6 +95,7 @@ public class IsoHDPerspective implements HDPerspective { private class OurPerspectiveState implements HDPerspectiveState { int blocktypeid = 0; int blockdata = 0; + int blockrenderdata = -1; int lastblocktypeid = 0; Vector3D top, bottom; int px, py; @@ -193,6 +194,10 @@ public class IsoHDPerspective implements HDPerspective { * Get current block data */ public final int getBlockData() { return blockdata; } + /** + * Get current block render data + */ + public final int getBlockRenderData() { return blockrenderdata; } /** * Get direction of last block step */ @@ -473,22 +478,23 @@ public class IsoHDPerspective implements HDPerspective { skiptoair = false; } else if(nonairhit || (blocktypeid != 0)) { + blockdata = mapiter.getBlockData(); switch(blocktypeid) { case FENCE_BLKTYPEID: /* Special case for fence - need to fake data so we can render properly */ - blockdata = generateFenceBlockData(mapiter); + blockrenderdata = generateFenceBlockData(mapiter); break; case CHEST_BLKTYPEID: /* Special case for chest - need to fake data so we can render */ - blockdata = generateChestBlockData(mapiter); + blockrenderdata = generateChestBlockData(mapiter); break; case REDSTONE_BLKTYPEID: /* Special case for redstone - fake data for wire pattern */ - blockdata = generateRedstoneWireBlockData(mapiter); + blockrenderdata = generateRedstoneWireBlockData(mapiter); break; default: - blockdata = mapiter.getBlockData(); - break; + blockrenderdata = -1; + break; } /* Look up to see if block is modelled */ - short[] model = scalemodels.getScaledModel(blocktypeid, blockdata); + short[] model = scalemodels.getScaledModel(blocktypeid, blockdata, blockrenderdata); if(model != null) { return handleSubModel(model, shaderstate, shaderdone); } diff --git a/src/main/java/org/dynmap/hdmap/TexturePack.java b/src/main/java/org/dynmap/hdmap/TexturePack.java index d87c4230..bec264d8 100644 --- a/src/main/java/org/dynmap/hdmap/TexturePack.java +++ b/src/main/java/org/dynmap/hdmap/TexturePack.java @@ -120,12 +120,15 @@ public class TexturePack { private List blockids; private int databits; private BlockTransparency bt; + private boolean userender; private static HDTextureMap[] texmaps; private static BlockTransparency transp[]; + private static boolean userenderdata[]; private static void initializeTable() { texmaps = new HDTextureMap[16*BLOCKTABLELEN]; transp = new BlockTransparency[BLOCKTABLELEN]; + userenderdata = new boolean[BLOCKTABLELEN]; HDTextureMap blank = new HDTextureMap(); for(int i = 0; i < texmaps.length; i++) texmaps[i] = blank; @@ -136,6 +139,7 @@ public class TexturePack { private HDTextureMap() { blockids = Collections.singletonList(Integer.valueOf(0)); databits = 0xFFFF; + userender = false; faces = new int[] { BLOCKINDEX_BLANK, BLOCKINDEX_BLANK, BLOCKINDEX_BLANK, BLOCKINDEX_BLANK, BLOCKINDEX_BLANK, BLOCKINDEX_BLANK }; for(int i = 0; i < texmaps.length; i++) { @@ -143,11 +147,12 @@ public class TexturePack { } } - public HDTextureMap(List blockids, int databits, int[] faces, BlockTransparency trans) { + public HDTextureMap(List blockids, int databits, int[] faces, BlockTransparency trans, boolean userender) { this.faces = faces; this.blockids = blockids; this.databits = databits; this.bt = trans; + this.userender = userender; } public void addToTable() { @@ -159,11 +164,15 @@ public class TexturePack { } } transp[blkid] = bt; /* Transparency is only blocktype based right now */ + userenderdata[blkid] = userender; /* Ditto for using render data */ } } - public static HDTextureMap getMap(int blkid, int blkdata) { - return texmaps[(blkid<<4) + blkdata]; + public static HDTextureMap getMap(int blkid, int blkdata, int blkrenderdata) { + if(userenderdata[blkid]) + return texmaps[(blkid<<4) + blkrenderdata]; + else + return texmaps[(blkid<<4) + blkdata]; } public static BlockTransparency getTransparency(int blkid) { @@ -704,6 +713,7 @@ public class TexturePack { line = line.substring(6); BlockTransparency trans = BlockTransparency.OPAQUE; String[] args = line.split(","); + boolean userenderdata = false; for(String a : args) { String[] av = a.split("="); if(av.length < 2) continue; @@ -759,12 +769,15 @@ public class TexturePack { Log.severe("Texture mapping has invalid transparency setting - " + av[1] + " - line " + rdr.getLineNumber() + " of " + txtname); } } + else if(av[0].equals("userenderdata")) { + userenderdata = av[1].equals("true"); + } } /* If no data bits, assume all */ if(databits < 0) databits = 0xFFFF; /* If we have everything, build block */ if(blkids.size() > 0) { - HDTextureMap map = new HDTextureMap(blkids, databits, faces, trans); + HDTextureMap map = new HDTextureMap(blkids, databits, faces, trans, userenderdata); map.addToTable(); cnt++; } @@ -795,7 +808,7 @@ public class TexturePack { */ public final void readColor(final HDPerspectiveState ps, final MapIterator mapiter, final Color rslt, final int blkid, final int lastblocktype, final boolean biome_shaded) { int blkdata = ps.getBlockData(); - HDTextureMap map = HDTextureMap.getMap(blkid, blkdata); + HDTextureMap map = HDTextureMap.getMap(blkid, blkdata, ps.getBlockRenderData()); BlockStep laststep = ps.getLastBlockStep(); int textid = map.faces[laststep.ordinal()]; /* Get index of texture source */ if(textid < 0) { diff --git a/src/main/resources/texture.txt b/src/main/resources/texture.txt index 96a65db7..c17fb2b9 100644 --- a/src/main/resources/texture.txt +++ b/src/main/resources/texture.txt @@ -280,35 +280,35 @@ block:id=52,allfaces=65,transparency=TRANSPARENT # Wooden stairs block:id=53,allsides=4,topbottom=4004,transparency=SEMITRANSPARENT # Chest - single, facing west -block:id=54,data=0,topbottom=25,south=26,north=26,east=26,west=27 +block:id=54,data=0,topbottom=25,south=26,north=26,east=26,west=27,userenderdata=true # Chest - single, facing south -block:id=54,data=1,topbottom=25,south=27,north=26,east=26,west=26 +block:id=54,data=1,topbottom=25,south=27,north=26,east=26,west=26,userenderdata=true # Chest - single, facing east -block:id=54,data=2,topbottom=25,south=26,north=26,east=27,west=26 +block:id=54,data=2,topbottom=25,south=26,north=26,east=27,west=26,userenderdata=true # Chest - single, facing north -block:id=54,data=3,topbottom=25,south=26,north=27,east=26,west=26 +block:id=54,data=3,topbottom=25,south=26,north=27,east=26,west=26,userenderdata=true # Chest - left side of double, facing west -block:id=54,data=4,topbottom=25,south=26,north=26,east=58,west=41 +block:id=54,data=4,topbottom=25,south=26,north=26,east=58,west=41,userenderdata=true # Chest - left side of double, facing south -block:id=54,data=5,topbottom=25,south=41,north=58,east=26,west=26 +block:id=54,data=5,topbottom=25,south=41,north=58,east=26,west=26,userenderdata=true # Chest - left side of double, facing east -block:id=54,data=6,topbottom=25,south=26,north=26,east=41,west=58 +block:id=54,data=6,topbottom=25,south=26,north=26,east=41,west=58,userenderdata=true # Chest - left side of double, facing north -block:id=54,data=7,topbottom=25,south=58,north=41,east=26,west=26 +block:id=54,data=7,topbottom=25,south=58,north=41,east=26,west=26,userenderdata=true # Chest - right side of double, facing west -block:id=54,data=8,topbottom=25,south=26,north=26,east=57,west=42 +block:id=54,data=8,topbottom=25,south=26,north=26,east=57,west=42,userenderdata=true # Chest - right side of double, facing south -block:id=54,data=9,topbottom=25,south=42,north=57,east=26,west=26 +block:id=54,data=9,topbottom=25,south=42,north=57,east=26,west=26,userenderdata=true # Chest - right side of double, facing east -block:id=54,data=10,topbottom=25,south=26,north=26,east=42,west=57 +block:id=54,data=10,topbottom=25,south=26,north=26,east=42,west=57,userenderdata=true # Chest - right side of double, facing north -block:id=54,data=11,topbottom=25,south=57,north=42,east=26,west=26 +block:id=54,data=11,topbottom=25,south=57,north=42,east=26,west=26,userenderdata=true # Redstone wire (all but NS and EW) -block:id=55,data=0,data=3,data=4,data=5,data=6,data=7,data=8,data=9,data=10,allfaces=180,transparency=TRANSPARENT +block:id=55,data=0,data=3,data=4,data=5,data=6,data=7,data=8,data=9,data=10,allfaces=180,transparency=TRANSPARENT,userenderdata=true # Redstone wire (EW) -block:id=55,data=2,allfaces=181,transparency=TRANSPARENT +block:id=55,data=2,allfaces=181,transparency=TRANSPARENT,userenderdata=true # Redstone wire (NS) -block:id=55,data=1,allfaces=4181,transparency=TRANSPARENT +block:id=55,data=1,allfaces=4181,transparency=TRANSPARENT,userenderdata=true # Diamond ore block:id=56,allfaces=50 # Diamond block From 974cd33853812151168ae96a768bfec69bfb52aa Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Mon, 29 Aug 2011 15:53:48 -0500 Subject: [PATCH 14/27] Fix multi-map KzedMap rendering (default cave not rendered issue) --- src/main/java/org/dynmap/kzedmap/KzedMapTile.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java index dba01203..aabe4bad 100644 --- a/src/main/java/org/dynmap/kzedmap/KzedMapTile.java +++ b/src/main/java/org/dynmap/kzedmap/KzedMapTile.java @@ -76,7 +76,14 @@ public class KzedMapTile extends MapTile { } public boolean render(MapChunkCache cache, String mapname) { - return map.render(cache, this, MapManager.mapman.getTileFile(this)); + boolean rslt = false; + for(MapTileRenderer r : map.renderers) { + if((mapname == null) || (r.getName().equals(mapname))) { + KzedMapTile t = new KzedMapTile(world, map, r, px, py); + rslt |= map.render(cache, t, MapManager.mapman.getTileFile(t)); + } + } + return rslt; } public List getRequiredChunks() { From 7e37817b86f3cfb779667e517c6515a544fc7f40 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Mon, 29 Aug 2011 21:00:05 -0500 Subject: [PATCH 15/27] Add 'sidebaropened: pinned' option to allow sidebar to be pre-pinned but support unpinning --- .../org/dynmap/ClientConfigurationComponent.java | 2 +- src/main/resources/configuration.txt | 2 +- web/js/map.js | 13 +++++++++---- 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/dynmap/ClientConfigurationComponent.java b/src/main/java/org/dynmap/ClientConfigurationComponent.java index 82da1775..51f3f9f7 100644 --- a/src/main/java/org/dynmap/ClientConfigurationComponent.java +++ b/src/main/java/org/dynmap/ClientConfigurationComponent.java @@ -20,7 +20,7 @@ public class ClientConfigurationComponent extends Component { s(t, "spammessage", c.getString("spammessage", "You may only chat once every %interval% seconds.")); s(t, "webprefix", unescapeString(c.getString("webprefix", "[WEB] "))); s(t, "defaultzoom", c.getInteger("defaultzoom", 0)); - s(t, "sidebaropened", c.getBoolean("sidebaropened", false)); + s(t, "sidebaropened", c.getString("sidebaropened", "false")); DynmapWorld defaultWorld = null; String defmap = null; diff --git a/src/main/resources/configuration.txt b/src/main/resources/configuration.txt index fce4e341..daac4053 100644 --- a/src/main/resources/configuration.txt +++ b/src/main/resources/configuration.txt @@ -260,7 +260,7 @@ updaterate: 2000 showplayerfacesinmenu: true -# Set sidebaropened: true to pin menu sidebar opened +# Set sidebaropened: 'true' to pin menu sidebar opened permanently, 'pinned' to default the sidebar to pinned, but allow it to unpin #sidebaropened: true joinmessage: "%playername% joined" diff --git a/web/js/map.js b/web/js/map.js index a571cdec..23a1ba47 100644 --- a/web/js/map.js +++ b/web/js/map.js @@ -145,10 +145,15 @@ DynMap.prototype = { var panel; var sidebar; var pinbutton; - if(!me.options.sidebaropened) { + + if(me.options.sidebaropened != 'true') { // false or pinned + var pincls = 'pinned' + if(me.options.sidebaropened == 'false') + pincls = ''; + sidebar = me.sidebar = $('
') - .addClass('sidebar') - .appendTo(container); + .addClass('sidebar ' + pincls) + .appendTo(container); panel = $('
') .addClass('panel') @@ -271,7 +276,7 @@ DynMap.prototype = { .append(link=$('')) .data('link', link) .appendTo(container);*/ - if(!me.options.sidebaropened) { + if(me.options.sidebaropened != 'true') { $('
') .addClass('hitbar') .appendTo(panel); From 5088adb7eb7b6dacd12420ed22b7cdf302b7a053 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Mon, 29 Aug 2011 21:33:41 -0500 Subject: [PATCH 16/27] Add support for 'http-reponse-headers' attribute to add custom response headers --- src/main/java/org/dynmap/DynmapPlugin.java | 13 +++++++++++++ src/main/java/org/dynmap/web/HttpServer.java | 9 +++++++++ .../java/org/dynmap/web/HttpServerConnection.java | 6 ++++++ src/main/resources/configuration.txt | 5 +++++ 4 files changed, 33 insertions(+) diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 1a6b5d1d..804db87b 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -303,6 +303,19 @@ public class DynmapPlugin extends JavaPlugin { boolean checkbannedips = configuration.getBoolean("check-banned-ips", true); int maxconnections = configuration.getInteger("max-sessions", 30); if(maxconnections < 2) maxconnections = 2; + /* Load customized response headers, if any */ + ConfigurationNode custhttp = configuration.getNode("http-response-headers"); + HashMap custhdrs = new HashMap(); + if(custhttp != null) { + for(String k : custhttp.keySet()) { + String v = custhttp.getString(k); + if(v != null) { + custhdrs.put(k, v); + } + } + } + HttpServer.setCustomHeaders(custhdrs); + if(allow_symlinks) Log.verboseinfo("Web server is permitting symbolic links"); else diff --git a/src/main/java/org/dynmap/web/HttpServer.java b/src/main/java/org/dynmap/web/HttpServer.java index 841dc71c..1509c7ff 100644 --- a/src/main/java/org/dynmap/web/HttpServer.java +++ b/src/main/java/org/dynmap/web/HttpServer.java @@ -10,8 +10,10 @@ import java.net.ServerSocket; import java.net.Socket; import java.net.SocketAddress; import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.IdentityHashMap; +import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import java.util.logging.Logger; @@ -34,6 +36,7 @@ public class HttpServer extends Thread { private Object lock = new Object(); private HashSet active_connections = new HashSet(); private HashSet keepalive_connections = new HashSet(); + private static Map headers = new HashMap(); public HttpServer(InetAddress bindAddress, int port, boolean check_banned_ips, int max_sessions) { this.bindAddress = bindAddress; @@ -189,4 +192,10 @@ public class HttpServer extends Thread { } return false; } + public static Map getCustomHeaders() { + return headers; + } + public static void setCustomHeaders(Map hdrs) { + headers = hdrs; + } } diff --git a/src/main/java/org/dynmap/web/HttpServerConnection.java b/src/main/java/org/dynmap/web/HttpServerConnection.java index 5784fafe..a41e9763 100644 --- a/src/main/java/org/dynmap/web/HttpServerConnection.java +++ b/src/main/java/org/dynmap/web/HttpServerConnection.java @@ -111,6 +111,12 @@ public class HttpServerConnection extends Thread { out.append(field.getValue()); out.append("\r\n"); } + for(Entry custom : HttpServer.getCustomHeaders().entrySet()) { + out.append(custom.getKey()); + out.append(": "); + out.append(custom.getValue()); + out.append("\r\n"); + } out.append("\r\n"); out.flush(); } diff --git a/src/main/resources/configuration.txt b/src/main/resources/configuration.txt index daac4053..ee397ffd 100644 --- a/src/main/resources/configuration.txt +++ b/src/main/resources/configuration.txt @@ -263,6 +263,11 @@ showplayerfacesinmenu: true # Set sidebaropened: 'true' to pin menu sidebar opened permanently, 'pinned' to default the sidebar to pinned, but allow it to unpin #sidebaropened: true +# Customized HTTP response headers - add 'id: value' pairs to all HTTP response headers (internal web server only) +#http-response-headers: +# Access-Control-Allow-Origin: "my-domain.com" +# X-Custom-Header-Of-Mine: "MyHeaderValue" + joinmessage: "%playername% joined" quitmessage: "%playername% quit" spammessage: "You may only chat once every %interval% seconds." From 8ac000abec3d91e6e2af24c29a829a88630d633e Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Tue, 30 Aug 2011 00:02:52 -0500 Subject: [PATCH 17/27] Support applying background color to JPG tiles (since no transparency) --- src/main/java/org/dynmap/hdmap/HDMap.java | 49 ++++++++++++++++++- .../org/dynmap/hdmap/IsoHDPerspective.java | 22 ++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/dynmap/hdmap/HDMap.java b/src/main/java/org/dynmap/hdmap/HDMap.java index 73f1fbd5..e8f31440 100644 --- a/src/main/java/org/dynmap/hdmap/HDMap.java +++ b/src/main/java/org/dynmap/hdmap/HDMap.java @@ -28,6 +28,8 @@ public class HDMap extends MapType { private ConfigurationNode configuration; private int mapzoomout; private MapType.ImageFormat imgformat; + private int bgcolornight; + private int bgcolorday; public static final String IMGFORMAT_PNG = "png"; public static final String IMGFORMAT_JPG = "jpg"; @@ -100,7 +102,20 @@ public class HDMap extends MapType { if(imgformat == null) { Log.severe("HDMap '"+name+"' set invalid image-format: " + fmt); imgformat = ImageFormat.FORMAT_PNG; - } + } + /* Get color info */ + String c = configuration.getString("background"); + if(c != null) { + bgcolorday = bgcolornight = parseColor(c); + } + c = configuration.getString("backgroundday"); + if(c != null) { + bgcolorday = parseColor(c); + } + c = configuration.getString("backgroundnight"); + if(c != null) { + bgcolornight = parseColor(c); + } } public HDShader getShader() { return shader; } @@ -222,4 +237,36 @@ public class HDMap extends MapType { a(worldObject, "maps", o); } + + private static int parseColor(String c) { + int v = 0; + if(c.startsWith("#")) { + c = c.substring(1); + if(c.length() == 3) { /* #rgb */ + try { + v = Integer.valueOf(c, 16); + } catch (NumberFormatException nfx) { + return 0; + } + v = 0xFF000000 | ((v & 0xF00) << 12) | ((v & 0x0F0) << 8) | ((v & 0x00F) << 4); + } + else if(c.length() == 6) { /* #rrggbb */ + try { + v = Integer.valueOf(c, 16); + } catch (NumberFormatException nfx) { + return 0; + } + v = 0xFF000000 | (v & 0xFFFFFF); + } + } + return v; + } + + public int getBackgroundARGBDay() { + return bgcolorday; + } + + public int getBackgroundARGBNight() { + return bgcolornight; + } } diff --git a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java index 21a0b64f..01971e88 100644 --- a/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java +++ b/src/main/java/org/dynmap/hdmap/IsoHDPerspective.java @@ -22,6 +22,7 @@ import org.dynmap.Log; import org.dynmap.MapManager; import org.dynmap.MapTile; import org.dynmap.MapType; +import org.dynmap.MapType.ImageFormat; import org.dynmap.TileHashManager; import org.dynmap.debug.Debug; import org.dynmap.utils.MapIterator.BlockStep; @@ -971,6 +972,9 @@ public class IsoHDPerspective implements HDPerspective { DynmapBufferedImage dayim[] = new DynmapBufferedImage[numshaders]; int[][] argb_buf = new int[numshaders][]; int[][] day_argb_buf = new int[numshaders][]; + boolean isjpg[] = new boolean[numshaders]; + int bgday[] = new int[numshaders]; + int bgnight[] = new int[numshaders]; for(int i = 0; i < numshaders; i++) { HDShader shader = shaderstate[i].getShader(); @@ -985,6 +989,9 @@ public class IsoHDPerspective implements HDPerspective { dayim[i] = DynmapBufferedImage.allocateBufferedImage(tileWidth, tileHeight); day_argb_buf[i] = dayim[i].argb_buf; } + isjpg[i] = shaderstate[i].getMap().getImageFormat() != ImageFormat.FORMAT_PNG; + bgday[i] = shaderstate[i].getMap().getBackgroundARGBDay(); + bgnight[i] = shaderstate[i].getMap().getBackgroundARGBNight(); } /* Create perspective state object */ @@ -996,6 +1003,7 @@ public class IsoHDPerspective implements HDPerspective { double ybase = tile.ty * tileHeight; boolean shaderdone[] = new boolean[numshaders]; boolean rendered[] = new boolean[numshaders]; + for(int x = 0; x < tileWidth; x++) { ps.px = x; for(int y = 0; y < tileHeight; y++) { @@ -1018,10 +1026,20 @@ public class IsoHDPerspective implements HDPerspective { rendered[i] = true; } shaderstate[i].getRayColor(rslt, 0); - argb_buf[i][(tileHeight-y-1)*tileWidth + x] = rslt.getARGB(); + if(isjpg[i] && rslt.isTransparent()) { + argb_buf[i][(tileHeight-y-1)*tileWidth + x] = bgnight[i]; + } + else { + argb_buf[i][(tileHeight-y-1)*tileWidth + x] = rslt.getARGB(); + } if(day_argb_buf[i] != null) { shaderstate[i].getRayColor(rslt, 1); - day_argb_buf[i][(tileHeight-y-1)*tileWidth + x] = rslt.getARGB(); + if(isjpg[i] && rslt.isTransparent()) { + day_argb_buf[i][(tileHeight-y-1)*tileWidth + x] = bgday[i]; + } + else { + day_argb_buf[i][(tileHeight-y-1)*tileWidth + x] = rslt.getARGB(); + } } } } From 0e1158c77bcdff5dd076db9fa8f6e558e24ed556 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Tue, 30 Aug 2011 00:03:37 -0500 Subject: [PATCH 18/27] Handle Factions on worlds with no faction data properly --- .../org/dynmap/regions/FactionsConfigHandler.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/dynmap/regions/FactionsConfigHandler.java b/src/main/java/org/dynmap/regions/FactionsConfigHandler.java index be47e0fd..917cb807 100644 --- a/src/main/java/org/dynmap/regions/FactionsConfigHandler.java +++ b/src/main/java/org/dynmap/regions/FactionsConfigHandler.java @@ -39,11 +39,12 @@ public class FactionsConfigHandler { * Get map of attributes for given world */ public Map getRegionData(String wname) { + Map rslt = new HashMap(); /* Load factions.json file */ File faction = new File("plugins/Factions/factions.json"); if(faction.canRead() == false) { /* Can't read config */ Log.severe("Cannot find Faction file - " + faction.getPath()); - return null; + return rslt; } JSONObject fact = null; try { @@ -56,12 +57,12 @@ public class FactionsConfigHandler { Log.severe("Exception while parsing factions.json.", ex); } if(fact == null) - return null; + return rslt; /* Load board.json */ File board = new File("plugins/Factions/board.json"); if(board.canRead() == false) { /* Can't read config */ Log.severe("Cannot find Faction file - " + board.getPath()); - return null; + return rslt; } JSONObject blocks = null; try { @@ -74,14 +75,13 @@ public class FactionsConfigHandler { Log.severe("Exception while parsing board.json.", ex); } if(blocks == null) - return null; + return rslt; /* Get value from board.json for requested world */ Object wb = blocks.get(wname); if((wb == null) || (!(wb instanceof JSONObject))) { - return null; + return rslt; } JSONObject wblocks = (JSONObject)wb; - Map rslt = new HashMap(); /* Now go through the factions list, and find outline */ for(Object factid : fact.keySet()) { int fid = 0; From 9da3180e383470f0fca87178d89814ffe4af1eca Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Tue, 30 Aug 2011 00:28:28 -0500 Subject: [PATCH 19/27] Handle Towny 0.74.0 configuration format --- src/main/java/org/dynmap/regions/TownyConfigHandler.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/dynmap/regions/TownyConfigHandler.java b/src/main/java/org/dynmap/regions/TownyConfigHandler.java index 141b117c..195a26af 100644 --- a/src/main/java/org/dynmap/regions/TownyConfigHandler.java +++ b/src/main/java/org/dynmap/regions/TownyConfigHandler.java @@ -32,7 +32,12 @@ public class TownyConfigHandler { } Configuration tcfg = new Configuration(cfgfile); tcfg.load(); - String tbsize = tcfg.getNode("town").getString("town_block_size", "16"); + org.bukkit.util.config.ConfigurationNode townnode = tcfg.getNode("town"); + String tbsize = "16"; + if(townnode != null) + tbsize = townnode.getString("town_block_size", "16"); + else + tbsize = tcfg.getString("town_block_size", "16"); try { townblocksize = Integer.valueOf(tbsize); } catch (NumberFormatException nfx) { From 704fc7fb32010adae8c05aa83df377fa9cdf9a81 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Tue, 30 Aug 2011 18:13:59 -0500 Subject: [PATCH 20/27] Yet another face fix - hopefully got the rules on accessories right this time... --- src/main/java/org/dynmap/PlayerFaces.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/dynmap/PlayerFaces.java b/src/main/java/org/dynmap/PlayerFaces.java index 470d1038..19210b72 100644 --- a/src/main/java/org/dynmap/PlayerFaces.java +++ b/src/main/java/org/dynmap/PlayerFaces.java @@ -56,16 +56,21 @@ public class PlayerFaces { int[] faceaccessory = new int[64]; /* 8x8 of face accessory */ /* Get buffered image for face at 8x8 */ DynmapBufferedImage face8x8 = DynmapBufferedImage.allocateBufferedImage(8, 8); - int[] bgcolor = new int[1]; - img.getRGB(0, 0, 1, 1, bgcolor, 0, 1); /* Get BG color (for accessory face) */ img.getRGB(8, 8, 8, 8, face8x8.argb_buf, 0, 8); /* Read face from image */ img.getRGB(40, 8, 8, 8, faceaccessory, 0, 8); /* Read face accessory from image */ - /* Apply accessory to face: first element is transparency color so only ones not matching it */ + /* Apply accessory to face: see if anything is transparent (if so, apply accessory */ + boolean transp = false; for(int i = 0; i < 64; i++) { - if(faceaccessory[i] != bgcolor[0]) - face8x8.argb_buf[i] = faceaccessory[i]; - else if(face8x8.argb_buf[i] == bgcolor[0]) - face8x8.argb_buf[i] = 0; + if((faceaccessory[i] & 0xFF000000) == 0) { + transp = true; + break; + } + } + if(transp) { + for(int i = 0; i < 64; i++) { + if((faceaccessory[i] & 0xFF000000) != 0) + face8x8.argb_buf[i] = faceaccessory[i]; + } } /* Write 8x8 file */ File img_8x8 = new File(faces8x8dir, playername + ".png"); From fba69e3d19f56aca5a633afa2270e46a3179d6e3 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Thu, 1 Sep 2011 01:01:51 -0500 Subject: [PATCH 21/27] Frame out API and data handling for markers support --- src/main/java/org/dynmap/DynmapPlugin.java | 22 +- src/main/java/org/dynmap/markers/Marker.java | 74 +++++ .../java/org/dynmap/markers/MarkerAPI.java | 49 +++ .../java/org/dynmap/markers/MarkerIcon.java | 20 ++ .../java/org/dynmap/markers/MarkerSet.java | 83 ++++++ .../dynmap/markers/impl/MarkerAPIImpl.java | 278 ++++++++++++++++++ .../dynmap/markers/impl/MarkerIconImpl.java | 57 ++++ .../org/dynmap/markers/impl/MarkerImpl.java | 179 +++++++++++ .../dynmap/markers/impl/MarkerSetImpl.java | 228 ++++++++++++++ src/main/resources/plugin.yml | 10 + 10 files changed, 997 insertions(+), 3 deletions(-) create mode 100644 src/main/java/org/dynmap/markers/Marker.java create mode 100644 src/main/java/org/dynmap/markers/MarkerAPI.java create mode 100644 src/main/java/org/dynmap/markers/MarkerIcon.java create mode 100644 src/main/java/org/dynmap/markers/MarkerSet.java create mode 100644 src/main/java/org/dynmap/markers/impl/MarkerAPIImpl.java create mode 100644 src/main/java/org/dynmap/markers/impl/MarkerIconImpl.java create mode 100644 src/main/java/org/dynmap/markers/impl/MarkerImpl.java create mode 100644 src/main/java/org/dynmap/markers/impl/MarkerSetImpl.java diff --git a/src/main/java/org/dynmap/DynmapPlugin.java b/src/main/java/org/dynmap/DynmapPlugin.java index 804db87b..c0973188 100644 --- a/src/main/java/org/dynmap/DynmapPlugin.java +++ b/src/main/java/org/dynmap/DynmapPlugin.java @@ -59,6 +59,8 @@ import org.dynmap.debug.Debug; import org.dynmap.debug.Debugger; import org.dynmap.hdmap.HDBlockModels; import org.dynmap.hdmap.TexturePack; +import org.dynmap.markers.MarkerAPI; +import org.dynmap.markers.impl.MarkerAPIImpl; import org.dynmap.permissions.BukkitPermissions; import org.dynmap.permissions.NijikokunPermissions; import org.dynmap.permissions.OpPermissions; @@ -85,6 +87,8 @@ public class DynmapPlugin extends JavaPlugin { private HashMap> event_handlers = new HashMap>(); + private MarkerAPI markerapi; + public static File dataDirectory; public static File tilesDirectory; @@ -208,6 +212,9 @@ public class DynmapPlugin extends JavaPlugin { permissions = new OpPermissions(new String[] { "fullrender", "cancelrender", "radiusrender", "resetstats", "reload" }); dataDirectory = this.getDataFolder(); + + /* Initialize marker API, if not already done */ + MarkerAPI m_api = getMarkerAPI(); /* Load block models */ HDBlockModels.loadModels(dataDirectory); /* Load texture mappings */ @@ -278,11 +285,11 @@ public class DynmapPlugin extends JavaPlugin { if (!configuration.getBoolean("disable-webserver", false)) { startWebserver(); } - + /* Print version info */ PluginDescriptionFile pdfFile = this.getDescription(); Log.info("version " + pdfFile.getVersion() + " is enabled" ); - + events.trigger("initialized", null); } @@ -640,6 +647,9 @@ public class DynmapPlugin extends JavaPlugin { @Override public boolean onCommand(CommandSender sender, Command cmd, String commandLabel, String[] args) { + if(cmd.getName().equalsIgnoreCase("dmarker")) { + return MarkerAPIImpl.onCommand(this, sender, cmd, commandLabel, args); + } if (!cmd.getName().equalsIgnoreCase("dynmap")) return false; Player player = null; @@ -768,7 +778,7 @@ public class DynmapPlugin extends JavaPlugin { return false; } - private boolean checkPlayerPermission(CommandSender sender, String permission) { + public boolean checkPlayerPermission(CommandSender sender, String permission) { if (!(sender instanceof Player) || sender.isOp()) { return true; } else if (!permissions.has(sender, permission.toLowerCase())) { @@ -1163,4 +1173,10 @@ public class DynmapPlugin extends JavaPlugin { } ll.add(listener); } + + public MarkerAPI getMarkerAPI() { + if(markerapi == null) + markerapi = MarkerAPIImpl.initializeMarkerAPI(this); + return markerapi; + } } diff --git a/src/main/java/org/dynmap/markers/Marker.java b/src/main/java/org/dynmap/markers/Marker.java new file mode 100644 index 00000000..46f7b5b7 --- /dev/null +++ b/src/main/java/org/dynmap/markers/Marker.java @@ -0,0 +1,74 @@ +package org.dynmap.markers; + +import org.bukkit.Location; + +/** + * This defines the public interface to a marker object, for use with the MarkerAPI + */ +public interface Marker { + /** + * Get ID of the marker (unique string within the MarkerSet) + * @return id of marker + */ + public String getMarkerID(); + /** + * Get the marker set for the marker + * @return marker set + */ + public MarkerSet getMarkerSet(); + /** + * Delete the marker + */ + public void deleteMarker(); + /** + * Get marker's world ID + * @return world id + */ + public String getWorld(); + /** + * Get marker's X coordinate + * @return x coordinate + */ + public int getX(); + /** + * Get marker's Y coordinate + * @return y coordinate + */ + public int getY(); + /** + * Get marker's Z coordinate + * @return z coordinate + */ + public int getZ(); + /** + * Update the marker's location + * @param worldid - world ID + * @param x - x coord + * @param y - y coord + * @param z - z coord + */ + public void setLocation(String worldid, int x, int y, int z); + /** + * Get the marker's icon + * @return marker icon + */ + public MarkerIcon getMarkerIcon(); + /** + * Set the marker's icon + * @param icon - new marker icon + * @return true if new marker icon set, false if not allowed + */ + public boolean setMarkerIcon(MarkerIcon icon); + /** + * Test if marker is persistent + */ + public boolean isPersistentMarker(); + /** + * Get the marker's label + */ + public String getLabel(); + /** + * Update the marker's label + */ + public void setLabel(String lbl); +} diff --git a/src/main/java/org/dynmap/markers/MarkerAPI.java b/src/main/java/org/dynmap/markers/MarkerAPI.java new file mode 100644 index 00000000..a5a5c1d0 --- /dev/null +++ b/src/main/java/org/dynmap/markers/MarkerAPI.java @@ -0,0 +1,49 @@ +package org.dynmap.markers; + +import java.io.File; +import java.util.Set; + +/** + * This defines the public interface to the MarkerAPI (as retrieved by the getMarkerAPI() method in the DynmapPlugin class). + */ +public interface MarkerAPI { + /** + * Get set of defined marker sets + * @return set of marker sets + */ + public Set getMarkerSets(); + /** + * Find marker set by ID + * @param id - ID of marker set + * @return marker set, or null if not found + */ + public MarkerSet getMarkerSet(String id); + /** + * Create marker set + * @param id - ID for marker set (must be unique among marker set - limit to alphanumerics, periods, underscores) + * @param lbl - Label for marker set + * @param iconlimit - set of allowed marker icons (if null, any marker icon can be used in set) + * @param persistent - if true, set is persistent (and can contain persistent markers) + * @return marker set, or null if failed to be created + */ + public MarkerSet createMarkerSet(String id, String lbl, Set iconlimit, boolean persistent); + /** + * Get set of defined marker icons + * @return set of marker icons + */ + public Set getMarkerIcons(); + /** + * Find marker icon by ID + * @param id - ID of marker icon + * @return marker icon, or null if not found + */ + public MarkerIcon getMarkerIcon(String id); + /** + * Register a new marker icon + * @param id - ID of marker icon (must be unique among marker icons - letters, numbers, periods, underscores only) + * @param label - label for marker icon + * @param markerfile - file containing PNG encoded icon for marker (will be copied) + * @return marker icon object, or null if failed + */ + public MarkerIcon createMarkerIcon(String id, String label, File markerfile); +} diff --git a/src/main/java/org/dynmap/markers/MarkerIcon.java b/src/main/java/org/dynmap/markers/MarkerIcon.java new file mode 100644 index 00000000..2612ca9a --- /dev/null +++ b/src/main/java/org/dynmap/markers/MarkerIcon.java @@ -0,0 +1,20 @@ +package org.dynmap.markers; + +/** + * This defines the public interface to a marker icon, for use with the MarkerAPI + */ +public interface MarkerIcon { + /** Default marker icon - always exists */ + public static final String DEFAULT = "default"; + + /** + * Get ID of the marker icon (unique among marker icons) + * @return ID + */ + public String getMarkerIconID(); + /** + * Get label for marker icon (descriptive - for helping select icon, or for legend/key) + * @return icon label + */ + public String getMarkerIconLabel(); +} diff --git a/src/main/java/org/dynmap/markers/MarkerSet.java b/src/main/java/org/dynmap/markers/MarkerSet.java new file mode 100644 index 00000000..011527fd --- /dev/null +++ b/src/main/java/org/dynmap/markers/MarkerSet.java @@ -0,0 +1,83 @@ +package org.dynmap.markers; + +import java.util.Set; + +/** + * This defines the public interface to a marker set object, for use with the MarkerAPI. + * This represents a logical set of markers, which are presented as a labelled layer on the web UI. + * Marker sets can be created as persistent or non-persistent, but only persistent marker sets can contain persistent markers. + */ +public interface MarkerSet { + public static final String DEFAULT = "markers"; /* Default set - always exists */ + + /** + * Get set of all markers currently in the set + * @return set of markers (set is copy - safe to iterate) + */ + public Set getMarkers(); + /** + * Create a new marker in the marker set + * + * @param id - ID of the marker - must be unique within the set: if null, unique ID is generated + * @param label - Label for the marker + * @param world - world ID + * @param x - x coord + * @param y - y coord + * @param z - z coord + * @param icon - Icon for the marker + * @param is_persistent - if true, marker is persistent (saved and reloaded on restart). If set is not persistent, this must be false. + * @return created marker, or null if cannot be created. + */ + public Marker createMarker(String id, String label, String world, int x, int y, int z, MarkerIcon icon, boolean is_persistent); + /** + * Get marker by ID + * @param id - ID of the marker + * @return marker, or null if cannot be found + */ + public Marker findMarker(String id); + /** + * Get ID of marker set - unique among marker sets + * @return ID + */ + public String getMarkerSetID(); + /** + * Get label for marker set + * @return label + */ + public String getMarkerSetLabel(); + /** + * Update label for marker set + * @param lbl - label for marker set + */ + public void setMarketSetLabel(String lbl); + /** + * Test if marker set is persistent + * @return true if the set is persistent + */ + public boolean isMarkerSetPersistent(); + /** + * Get marker icons allowed in set (if restricted) + * @return set of allowed marker icons + */ + public Set getAllowedMarkerIcons(); + /** + * Add marker icon to allowed set (must have been created restricted) + * @param icon - icon to be added + */ + public void addAllowedMarkerIcon(MarkerIcon icon); + /** + * Test if marker icon is allowed + * @param icon - marker icon + * @return true if allowed, false if not + */ + public boolean isAllowedMarkerIcon(MarkerIcon icon); + /** + * Get distinct set of marker icons used by set (based on markers currently in set) + * @return set of marker icons + */ + public Set getMarkerIconsInUse(); + /** + * Delete marker set + */ + public void deleteMarkerSet(); +} diff --git a/src/main/java/org/dynmap/markers/impl/MarkerAPIImpl.java b/src/main/java/org/dynmap/markers/impl/MarkerAPIImpl.java new file mode 100644 index 00000000..a723f465 --- /dev/null +++ b/src/main/java/org/dynmap/markers/impl/MarkerAPIImpl.java @@ -0,0 +1,278 @@ +package org.dynmap.markers.impl; + +import java.io.File; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import org.bukkit.Location; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.util.config.Configuration; +import org.bukkit.util.config.ConfigurationNode; +import org.dynmap.DynmapPlugin; +import org.dynmap.Log; +import org.dynmap.markers.Marker; +import org.dynmap.markers.MarkerAPI; +import org.dynmap.markers.MarkerIcon; +import org.dynmap.markers.MarkerSet; + +/** + * Implementation class for MarkerAPI - should not be called directly + */ +public class MarkerAPIImpl implements MarkerAPI { + private File markerpersist; + private HashMap markericons = new HashMap(); + private HashMap markersets = new HashMap(); + + static MarkerAPIImpl api; + + /** + * Singleton initializer + */ + public static MarkerAPI initializeMarkerAPI(DynmapPlugin plugin) { + if(api != null) { + api.cleanup(); + } + api = new MarkerAPIImpl(); + /* Initialize persistence file name */ + api.markerpersist = new File(plugin.getDataFolder(), "markers.yml"); + /* Load persistence */ + api.loadMarkers(); + /* Fill in default icons and sets, if needed */ + if(api.getMarkerIcon(MarkerIcon.DEFAULT) == null) { + api.createMarkerIcon(MarkerIcon.DEFAULT, "Marker", null); + } + if(api.getMarkerSet(MarkerSet.DEFAULT) == null) { + api.createMarkerSet(MarkerSet.DEFAULT, "Markers", null, true); + } + return api; + } + + /** + * Cleanup + */ + private void cleanup() { + for(MarkerIconImpl icn : markericons.values()) + icn.cleanup(); + markericons.clear(); + for(MarkerSetImpl set : markersets.values()) + set.cleanup(); + markersets.clear(); + } + + @Override + public Set getMarkerSets() { + return new HashSet(markersets.values()); + } + + @Override + public MarkerSet getMarkerSet(String id) { + return markersets.get(id); + } + + @Override + public MarkerSet createMarkerSet(String id, String lbl, Set iconlimit, boolean persistent) { + if(markersets.containsKey(id)) return null; /* Exists? */ + + MarkerSetImpl set = new MarkerSetImpl(id, lbl, iconlimit, persistent); + + markersets.put(id, set); /* Add to list */ + if(persistent) { + saveMarkers(); + } + markerSetUpdated(set, MarkerUpdate.CREATED); /* Notify update */ + + return set; + } + + @Override + public Set getMarkerIcons() { + return new HashSet(markericons.values()); + } + + @Override + public MarkerIcon getMarkerIcon(String id) { + return markericons.get(id); + } + + @Override + public MarkerIcon createMarkerIcon(String id, String label, File markerfile) { + if(markericons.containsKey(id)) return null; /* Exists? */ + MarkerIconImpl ico = new MarkerIconImpl(id, label); + + markericons.put(id, ico); /* Add to set */ + + saveMarkers(); /* Save results */ + + return ico; + } + + static MarkerIconImpl getMarkerIconImpl(String id) { + if(api != null) { + return api.markericons.get(id); + } + return null; + } + + /** + * Save persistence for markers + */ + static void saveMarkers() { + if(api != null) { + Log.info("saveMarkers()"); + Configuration conf = new Configuration(api.markerpersist); /* Make configuration object */ + /* First, save icon definitions */ + HashMap icons = new HashMap(); + for(String id : api.markericons.keySet()) { + MarkerIconImpl ico = api.markericons.get(id); + Map dat = ico.getPersistentData(); + if(dat != null) { + icons.put(id, dat); + } + } + conf.setProperty("icons", icons); + /* Then, save persistent sets */ + HashMap sets = new HashMap(); + for(String id : api.markersets.keySet()) { + MarkerSetImpl set = api.markersets.get(id); + if(set.isMarkerSetPersistent()) { + Map dat = set.getPersistentData(); + if(dat != null) { + sets.put(id, dat); + } + } + } + conf.setProperty("sets", sets); + /* And write it out */ + if(!conf.save()) + Log.severe("Error writing markers - " + api.markerpersist.getPath()); + } + } + + /** + * Load persistence + */ + private boolean loadMarkers() { + cleanup(); + + Configuration conf = new Configuration(api.markerpersist); /* Make configuration object */ + conf.load(); /* Load persistence */ + /* Get icons */ + Map icons = conf.getNodes("icons"); + if(icons == null) return false; + for(String id : icons.keySet()) { + MarkerIconImpl ico = new MarkerIconImpl(id); + if(ico.loadPersistentData(icons.get(id))) { + markericons.put(id, ico); + } + } + /* Get marker sets */ + Map sets = conf.getNodes("sets"); + if(sets != null) { + for(String id: sets.keySet()) { + MarkerSetImpl set = new MarkerSetImpl(id); + if(set.loadPersistentData(sets.get(id))) { + markersets.put(id, set); + } + } + } + + return true; + } + + enum MarkerUpdate { CREATED, UPDATED, DELETED }; + + /** + * Signal marker update + * @param marker - updated marker + * @param update - type of update + */ + static void markerUpdated(MarkerImpl marker, MarkerUpdate update) { + Log.info("markerUpdated(" + marker.getMarkerID() + "," + update + ")"); + } + /** + * Signal marker set update + * @param markerset - updated marker set + * @param update - type of update + */ + static void markerSetUpdated(MarkerSetImpl markerset, MarkerUpdate update) { + Log.info("markerSetUpdated(" + markerset.getMarkerSetID() + "," + update + ")"); + } + + /** + * Remove marker set + */ + static void removeMarkerSet(MarkerSetImpl markerset) { + if(api != null) { + api.markersets.remove(markerset.getMarkerSetID()); /* Remove set from list */ + if(markerset.isMarkerSetPersistent()) { /* If persistent */ + MarkerAPIImpl.saveMarkers(); /* Drive save */ + } + markerSetUpdated(markerset, MarkerUpdate.DELETED); /* Signal delete of set */ + } + } + + private static final Set commands = new HashSet(Arrays.asList(new String[] { + "add", "update", "delete", "list" + })); + + public static boolean onCommand(DynmapPlugin plugin, CommandSender sender, Command cmd, String commandLabel, String[] args) { + if(args.length == 0) + return false; + Player player = null; + if (sender instanceof Player) + player = (Player) sender; + /* Check if valid command */ + String c = args[0]; + if (!commands.contains(c)) { + return false; + } + if(api == null) return false; + /* Process commands */ + if(c.equals("add") && plugin.checkPlayerPermission(sender, "marker.add")) { + if(player == null) { + sender.sendMessage("Command can only be used by player"); + } + else if(args.length > 1) { + String lbl = args[1]; + if(lbl.charAt(0) == '"') { /* Starts with doublequote */ + lbl = lbl.substring(1); /* Trim it off */ + int idx = 2; + while(lbl.indexOf('"') < 0) { + if(idx < args.length) { + lbl = lbl + " " + args[idx]; + idx++; + } + else { + break; + } + } + idx = lbl.indexOf('"'); + if(idx >= 0) lbl = lbl.substring(0, idx); + } + Location loc = player.getLocation(); + /* Add new marker (generic ID and default set) */ + MarkerSet set = api.getMarkerSet(MarkerSet.DEFAULT); + MarkerIcon ico = api.getMarkerIcon(MarkerIcon.DEFAULT); + Marker m = set.createMarker(null, lbl, loc.getWorld().getName(), loc.getBlockX(), loc.getBlockY(), loc.getBlockZ(), ico, true); + if(m == null) { + sender.sendMessage("Error creating marker"); + } + else { + sender.sendMessage("Added marker id='" + m.getMarkerID() + "' (" + m.getLabel() + ") to marker set " + set.getMarkerSetID()); + } + } + else { + sender.sendMessage("Marker label required"); + } + } + else { + return false; + } + return true; + } +} diff --git a/src/main/java/org/dynmap/markers/impl/MarkerIconImpl.java b/src/main/java/org/dynmap/markers/impl/MarkerIconImpl.java new file mode 100644 index 00000000..0b15f4f2 --- /dev/null +++ b/src/main/java/org/dynmap/markers/impl/MarkerIconImpl.java @@ -0,0 +1,57 @@ +package org.dynmap.markers.impl; + +import java.util.HashMap; +import java.util.Map; + +import org.bukkit.util.config.ConfigurationNode; +import org.dynmap.markers.MarkerIcon; + +class MarkerIconImpl implements MarkerIcon { + private String iconid; + private String label; + + MarkerIconImpl(String id) { + iconid = id; + label = id; + } + + MarkerIconImpl(String id, String lbl) { + iconid = id; + if(lbl != null) + label = lbl; + else + label = id; + } + + void cleanup() { + + } + + @Override + public String getMarkerIconID() { + return iconid; + } + + @Override + public String getMarkerIconLabel() { + return label; + } + + /** + * Get configuration node to be saved + * @return node + */ + Map getPersistentData() { + HashMap node = new HashMap(); + node.put("label", label); + + return node; + } + + boolean loadPersistentData(ConfigurationNode node) { + label = node.getString("label", iconid); + + return true; + } + +} diff --git a/src/main/java/org/dynmap/markers/impl/MarkerImpl.java b/src/main/java/org/dynmap/markers/impl/MarkerImpl.java new file mode 100644 index 00000000..0b88230b --- /dev/null +++ b/src/main/java/org/dynmap/markers/impl/MarkerImpl.java @@ -0,0 +1,179 @@ +package org.dynmap.markers.impl; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.bukkit.Location; +import org.bukkit.util.config.ConfigurationNode; +import org.dynmap.markers.Marker; +import org.dynmap.markers.MarkerIcon; +import org.dynmap.markers.MarkerSet; +import org.dynmap.markers.impl.MarkerAPIImpl.MarkerUpdate; + +class MarkerImpl implements Marker { + private String markerid; + private String label; + private MarkerSetImpl markerset; + private int x, y, z; + private String world; + private MarkerIconImpl icon; + private boolean ispersistent; + + /** + * Create marker + * @param id - marker ID + * @param lbl - label + * @param world - world id + * @param x - x coord + * @param y - y coord + * @param z - z coord + * @param icon - marker icon + * @param persistent - true if persistent + */ + MarkerImpl(String id, String lbl, String world, int x, int y, int z, MarkerIconImpl icon, boolean persistent, MarkerSetImpl set) { + markerid = id; + if(lbl != null) + label = lbl; + else + label = id; + this.x = x; this.y = y; this.z = z; + this.world = world; + this.icon = icon; + ispersistent = persistent; + markerset = set; + } + /** + * Make bare marker - used for persistence load + * @param id - marker ID + * @param set - marker set + */ + MarkerImpl(String id, MarkerSetImpl set) { + markerid = id; + markerset = set; + label = id; + x = z = 0; y = 64; world = "world"; + icon = MarkerAPIImpl.getMarkerIconImpl(MarkerIcon.DEFAULT); + } + /** + * Load marker from configuration node + * @param node - configuration node + */ + boolean loadPersistentData(ConfigurationNode node) { + label = node.getString("label", markerid); + x = node.getInt("x", 0); + y = node.getInt("y", 64); + z = node.getInt("z", 0); + world = node.getString("world", "world"); + icon = MarkerAPIImpl.getMarkerIconImpl(node.getString("icon", MarkerIcon.DEFAULT)); + ispersistent = true; /* Loaded from config, so must be */ + + return true; + } + + void cleanup() { + icon = null; + markerset = null; + } + + @Override + public String getMarkerID() { + return markerid; + } + + @Override + public MarkerSet getMarkerSet() { + return markerset; + } + + @Override + public void deleteMarker() { + markerset.removeMarker(this); /* Remove from our marker set (notified by set) */ + cleanup(); + } + + @Override + public MarkerIcon getMarkerIcon() { + return icon; + } + + @Override + public boolean setMarkerIcon(MarkerIcon icon) { + if(!(icon instanceof MarkerIconImpl)) { + return false; + } + /* Check if icons restricted for this set */ + Set icns = markerset.getAllowedMarkerIcons(); + if((icns != null) && (icns.contains(icon) == false)) { + return false; + } + this.icon = (MarkerIconImpl)icon; + MarkerAPIImpl.markerUpdated(this, MarkerUpdate.UPDATED); + if(ispersistent) + MarkerAPIImpl.saveMarkers(); + + return true; + } + + @Override + public boolean isPersistentMarker() { + return ispersistent; + } + + @Override + public String getLabel() { + return label; + } + + @Override + public void setLabel(String lbl) { + label = lbl; + MarkerAPIImpl.markerUpdated(this, MarkerUpdate.UPDATED); + if(ispersistent) + MarkerAPIImpl.saveMarkers(); + } + + /** + * Get configuration node to be saved + * @return node + */ + Map getPersistentData() { + if(!ispersistent) /* Nothing if not persistent */ + return null; + HashMap node = new HashMap(); + node.put("label", label); + node.put("x", Integer.valueOf(x)); + node.put("y", Integer.valueOf(y)); + node.put("z", Integer.valueOf(z)); + node.put("world", world); + node.put("icon", icon.getMarkerIconID()); + + return node; + } + @Override + public String getWorld() { + return world; + } + @Override + public int getX() { + return x; + } + @Override + public int getY() { + return y; + } + @Override + public int getZ() { + return z; + } + @Override + public void setLocation(String worldid, int x, int y, int z) { + this.world = worldid; + this.x = x; + this.y = y; + this.z = z; + MarkerAPIImpl.markerUpdated(this, MarkerUpdate.UPDATED); + if(ispersistent) + MarkerAPIImpl.saveMarkers(); + } +} diff --git a/src/main/java/org/dynmap/markers/impl/MarkerSetImpl.java b/src/main/java/org/dynmap/markers/impl/MarkerSetImpl.java new file mode 100644 index 00000000..9c248c1b --- /dev/null +++ b/src/main/java/org/dynmap/markers/impl/MarkerSetImpl.java @@ -0,0 +1,228 @@ +package org.dynmap.markers.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.bukkit.Location; +import org.bukkit.util.config.ConfigurationNode; +import org.dynmap.Log; +import org.dynmap.markers.Marker; +import org.dynmap.markers.MarkerIcon; +import org.dynmap.markers.MarkerSet; +import org.dynmap.markers.impl.MarkerAPIImpl.MarkerUpdate; + +class MarkerSetImpl implements MarkerSet { + private HashMap markers = new HashMap(); + private String setid; + private String label; + private HashMap allowedicons = null; + + private boolean ispersistent; + + MarkerSetImpl(String id) { + setid = id; + label = id; + } + + MarkerSetImpl(String id, String lbl, Set iconlimit, boolean persistent) { + setid = id; + if(lbl != null) + label = lbl; + else + label = id; + if(iconlimit != null) { + allowedicons = new HashMap(); + for(MarkerIcon ico : iconlimit) { + if(ico instanceof MarkerIconImpl) { + allowedicons.put(ico.getMarkerIconID(), (MarkerIconImpl)ico); + } + } + } + ispersistent = persistent; + } + + void cleanup() { + for(MarkerImpl m : markers.values()) + m.cleanup(); + markers.clear(); + } + + @Override + public Set getMarkers() { + return new HashSet(markers.values()); + } + + @Override + public Marker createMarker(String id, String label, String world, int x, int y, int z, MarkerIcon icon, boolean is_persistent) { + if(id == null) { /* If not defined, generate unique one */ + int i = 0; + do { + i++; + id = "marker_" + i; + } while(markers.containsKey(id)); + } + if(markers.containsKey(id)) return null; /* Duplicate ID? */ + if(!(icon instanceof MarkerIconImpl)) return null; + /* If limited icons, and this isn't valid one, quit */ + if((allowedicons != null) && (allowedicons.containsKey(icon.getMarkerIconID()) == false)) return null; + /* Create marker */ + is_persistent = is_persistent && this.ispersistent; + MarkerImpl marker = new MarkerImpl(id, label, world, x, y, z, (MarkerIconImpl)icon, is_persistent, this); + markers.put(id, marker); /* Add to set */ + if(is_persistent) + MarkerAPIImpl.saveMarkers(); + + MarkerAPIImpl.markerUpdated(marker, MarkerUpdate.CREATED); /* Signal create */ + + return marker; + } + + @Override + public Marker findMarker(String id) { + return markers.get(id); + } + + @Override + public String getMarkerSetID() { + return setid; + } + + @Override + public String getMarkerSetLabel() { + return label; + } + + @Override + public void setMarketSetLabel(String lbl) { + label = lbl; + MarkerAPIImpl.markerSetUpdated(this, MarkerUpdate.UPDATED); + if(ispersistent) + MarkerAPIImpl.saveMarkers(); + } + + @Override + public boolean isMarkerSetPersistent() { + return ispersistent; + } + + @Override + public Set getAllowedMarkerIcons() { + if(allowedicons != null) + return new HashSet(allowedicons.values()); + else + return null; + } + + @Override + public void addAllowedMarkerIcon(MarkerIcon icon) { + if(!(icon instanceof MarkerIconImpl)) return; + if(allowedicons == null) return; + allowedicons.put(icon.getMarkerIconID(), (MarkerIconImpl)icon); + MarkerAPIImpl.markerSetUpdated(this, MarkerUpdate.UPDATED); + if(ispersistent) + MarkerAPIImpl.saveMarkers(); + } + + @Override + public boolean isAllowedMarkerIcon(MarkerIcon icon) { + if(allowedicons == null) return true; + return allowedicons.containsKey(icon.getMarkerIconID()); + } + + @Override + public Set getMarkerIconsInUse() { + HashSet ids = new HashSet(); + HashSet icons = new HashSet(); + for(Marker m : markers.values()) { + MarkerIcon mi = m.getMarkerIcon(); + if(!ids.contains(mi.getMarkerIconID())) { + ids.add(mi.getMarkerIconID()); + icons.add(mi); + } + } + return icons; + } + + @Override + public void deleteMarkerSet() { + MarkerAPIImpl.removeMarkerSet(this); /* Remove from top level sets (notification from there) */ + if(ispersistent) + MarkerAPIImpl.saveMarkers(); + cleanup(); + } + /** + * Remove marker from set + * + * @param marker + */ + void removeMarker(MarkerImpl marker) { + markers.remove(marker.getMarkerID()); /* Remove from set */ + if(ispersistent && marker.isPersistentMarker()) { /* If persistent */ + MarkerAPIImpl.saveMarkers(); /* Drive save */ + } + MarkerAPIImpl.markerUpdated(marker, MarkerUpdate.DELETED); + } + + /** + * Get configuration node to be saved + * @return node + */ + Map getPersistentData() { + if(!ispersistent) /* Nothing if not persistent */ + return null; + HashMap node = new HashMap(); + for(String id : markers.keySet()) { + MarkerImpl m = markers.get(id); + if(m.isPersistentMarker()) { + node.put(id, m.getPersistentData()); + } + } + /* Make top level node */ + HashMap setnode = new HashMap(); + setnode.put("label", label); + if(allowedicons != null) { + ArrayList allowed = new ArrayList(allowedicons.keySet()); + setnode.put("allowedicons", allowed); + } + setnode.put("markers", node); + return setnode; + } + + /** + * Load marker from configuration node + * @param node - configuration node + */ + boolean loadPersistentData(ConfigurationNode node) { + label = node.getString("label", setid); /* Get label */ + ConfigurationNode markernode = node.getNode("markers"); + if(markernode != null) { + for(String id : markernode.getKeys()) { + MarkerImpl marker = new MarkerImpl(id, this); /* Make and load marker */ + if(marker.loadPersistentData(markernode.getNode(id))) { + markers.put(id, marker); + } + else { + Log.info("Error loading marker '" + id + "' for set '" + setid + "'"); + marker.cleanup(); + } + } + } + List allowed = node.getStringList("allowedicons", null); + if(allowed != null) { + for(String id : allowed) { + MarkerIconImpl icon = MarkerAPIImpl.getMarkerIconImpl(id); + if(icon != null) + allowedicons.put(id, icon); + else + Log.info("Error loading allowed icon '" + id + "' for set '" + setid + "'"); + } + } + ispersistent = true; + + return true; + } +} diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml index d3e825c1..39d88f46 100644 --- a/src/main/resources/plugin.yml +++ b/src/main/resources/plugin.yml @@ -23,6 +23,11 @@ commands: / stats world - Show render statistics for maps on world 'world'. / resetstats - Reset render statistics. / resetstats world - Reset render statistics for maps on world 'world'. + dmarker: + description: Manipulate map markers + usage: | + / add