From f4de63e85e0c450906e917c0dc0fcceef0857061 Mon Sep 17 00:00:00 2001 From: Mike Primm Date: Mon, 29 Aug 2011 10:31:04 +0800 Subject: [PATCH] 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