package org.dynmap.regions; import java.io.File; import java.io.FileInputStream; import java.io.FilenameFilter; 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; import java.util.Set; 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; public class TownyConfigHandler { private int townblocksize; /* Size of each block - default is 16x16 */ public TownyConfigHandler(ConfigurationNode cfg) { /* Find path to Towny base configuration */ File cfgfile = new File("plugins/Towny/settings/config.yml"); if(cfgfile.canRead() == false) { /* Can't read config */ Log.severe("Cannot find Towny file - " + cfgfile.getPath()); return; } Configuration tcfg = new Configuration(cfgfile); tcfg.load(); townblocksize = tcfg.getInt("town_block_size", 16); /* Get block size */ } /** * Get map of attributes for given world */ 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"); } }); for(File town : towns) { Map td = processTown(town, wname); if(td != null) { String fn = town.getName(); rslt.put(fn.substring(0, fn.lastIndexOf('.')), td); } } return rslt; } enum direction { XPLUS, YPLUS, XMINUS, YMINUS }; 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 */ public Map processTown(File townfile, String wname) { Properties p = new Properties(); FileInputStream fis = null; Map rslt = null; try { fis = new FileInputStream(townfile); /* Open and load file */ p.load(fis); } catch (IOException iox) { Log.severe("Error loading Towny file " + townfile.getPath()); } finally { if(fis != null) { try { fis.close(); } catch (IOException iox) {} } } /* Get block list */ String blocks = p.getProperty("townBlocks"); String[] nodes = blocks.split(";"); /* Split into list */ TileFlags blks = new TileFlags(); LinkedList nodevals = new LinkedList(); boolean worldmatch = false; for(String n: nodes) { /* Is world prefix? */ int idx = n.indexOf(':'); 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); } catch (NumberFormatException nfx) { Log.severe("Error parsing block list in Towny - " + townfile.getPath()); return null; } } } /* 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((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); rslt = new HashMap(); rslt.put("points", polylist); /* Add other data */ String mayor = p.getProperty("mayor"); if(mayor != null) rslt.put("mayor", mayor); String nation = p.getProperty("nation"); if(nation != null) rslt.put("nation", nation); String assistants = p.getProperty("assistants"); if(assistants != null) rslt.put("assistants", assistants); String residents = p.getProperty("residents"); if(residents != null) rslt.put("residents", residents); Map flags = new HashMap(); for(String f : FLAGS) { String fval = p.getProperty(f); if(fval != null) flags.put(f, fval); } rslt.put("flags", flags); return rslt; } }