├── src └── main │ ├── resources │ └── plugin.yml │ ├── assembly │ └── package.xml │ └── java │ └── org │ └── dynmap │ └── towny │ ├── events │ ├── BuildTownFlagsEvent.java │ ├── TownRenderEvent.java │ ├── TownSetMarkerIconEvent.java │ └── BuildTownMarkerDescriptionEvent.java │ ├── mapupdate │ ├── AreaStyleHolder.java │ ├── TileFlags.java │ ├── TownInfoWindow.java │ ├── AreaStyle.java │ └── UpdateTowns.java │ ├── listeners │ └── DynmapTownyListener.java │ ├── settings │ ├── ConfigNodes.java │ └── Settings.java │ └── DynmapTownyPlugin.java ├── .gitignore ├── README.md └── pom.xml /src/main/resources/plugin.yml: -------------------------------------------------------------------------------- 1 | name: Dynmap-Towny 2 | main: org.dynmap.towny.DynmapTownyPlugin 3 | version: ${project.version} 4 | author: LlmDl 5 | api-version: 1.14 6 | depend: [ dynmap, Towny ] 7 | softdepend: [ TownyChat ] 8 | folia-supported: true 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Eclipse stuff 2 | /.classpath 3 | /.project 4 | /.settings 5 | /.vscode 6 | 7 | # Intellij 8 | .idea 9 | *.iml 10 | 11 | #dependencies 12 | /deps 13 | 14 | # netbeans 15 | /nbproject 16 | 17 | # we use maven! 18 | /build.xml 19 | 20 | # maven 21 | /target 22 | 23 | # vim 24 | .*.sw[a-p] 25 | 26 | # various other potential build files 27 | /build 28 | /bin 29 | /dist 30 | /manifest.mf 31 | 32 | # Mac filesystem dust 33 | /.DS_Store 34 | /Dynmap-Towny.iml 35 | -------------------------------------------------------------------------------- /src/main/assembly/package.xml: -------------------------------------------------------------------------------- 1 | 2 | bin 3 | false 4 | 5 | zip 6 | 7 | 8 | 9 | 10 | 11 | ${project.build.directory}/${artifactId}-${version}.jar 12 | / 13 | ${artifactId}.jar 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/events/BuildTownFlagsEvent.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.events; 2 | 3 | import com.palmergames.bukkit.towny.object.Town; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.event.Event; 6 | import org.bukkit.event.HandlerList; 7 | import org.jetbrains.annotations.NotNull; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Called when 13 | */ 14 | public class BuildTownFlagsEvent extends Event { 15 | private static HandlerList handlers = new HandlerList(); 16 | private final Town town; 17 | private final List flags; 18 | 19 | public BuildTownFlagsEvent(Town town, List flags) { 20 | super(!Bukkit.getServer().isPrimaryThread()); 21 | this.town = town; 22 | this.flags = flags; 23 | } 24 | 25 | public Town getTown() { 26 | return town; 27 | } 28 | 29 | public List getFlags() { 30 | return flags; 31 | } 32 | 33 | @NotNull 34 | public HandlerList getHandlers() { 35 | return handlers; 36 | } 37 | 38 | public static HandlerList getHandlerList() { 39 | return handlers; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/events/TownRenderEvent.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.events; 2 | 3 | import com.palmergames.bukkit.towny.object.Town; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.event.Event; 6 | import org.bukkit.event.HandlerList; 7 | import org.dynmap.markers.AreaMarker; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | /** 11 | * Called when Dynmap-Towny has made a town which will be rendered. 12 | */ 13 | public class TownRenderEvent extends Event { 14 | private static HandlerList handlers = new HandlerList(); 15 | private final Town town; 16 | private final AreaMarker areaMarker; 17 | 18 | public TownRenderEvent(Town town, AreaMarker areaMarker) { 19 | super(!Bukkit.getServer().isPrimaryThread()); 20 | this.town = town; 21 | this.areaMarker = areaMarker; 22 | } 23 | 24 | public Town getTown() { 25 | return town; 26 | } 27 | 28 | public AreaMarker getAreaMarker() { 29 | return areaMarker; 30 | } 31 | 32 | @NotNull 33 | public HandlerList getHandlers() { 34 | return handlers; 35 | } 36 | 37 | public static HandlerList getHandlerList() { 38 | return handlers; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/events/TownSetMarkerIconEvent.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.events; 2 | 3 | import com.palmergames.bukkit.towny.object.Town; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.event.Event; 6 | import org.bukkit.event.HandlerList; 7 | import org.dynmap.markers.MarkerIcon; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | /** 11 | * Event called when the marker icon for a town is chosen. 12 | */ 13 | public class TownSetMarkerIconEvent extends Event { 14 | 15 | private static final HandlerList handlers = new HandlerList(); 16 | private final Town town; 17 | private MarkerIcon icon; 18 | 19 | public TownSetMarkerIconEvent(Town town, MarkerIcon icon) { 20 | super(!Bukkit.getServer().isPrimaryThread()); 21 | this.town = town; 22 | this.icon = icon; 23 | } 24 | 25 | public Town getTown() { 26 | return town; 27 | } 28 | 29 | public void setIcon(MarkerIcon icon) { 30 | this.icon = icon; 31 | } 32 | 33 | public MarkerIcon getIcon() { 34 | return icon; 35 | } 36 | 37 | @NotNull 38 | @Override 39 | public HandlerList getHandlers() { 40 | return handlers; 41 | } 42 | 43 | public static HandlerList getHandlerList() { 44 | return handlers; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/events/BuildTownMarkerDescriptionEvent.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.events; 2 | 3 | import com.palmergames.bukkit.towny.object.Town; 4 | import org.bukkit.Bukkit; 5 | import org.bukkit.event.Event; 6 | import org.bukkit.event.HandlerList; 7 | import org.dynmap.towny.mapupdate.TownInfoWindow; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | public class BuildTownMarkerDescriptionEvent extends Event { 11 | 12 | private static final HandlerList handlers = new HandlerList(); 13 | private final Town town; 14 | private String description; 15 | 16 | public BuildTownMarkerDescriptionEvent(Town town) { 17 | super(!Bukkit.getServer().isPrimaryThread()); 18 | this.town = town; 19 | this.description = TownInfoWindow.formatInfoWindow(town); 20 | } 21 | 22 | public Town getTown() { 23 | return town; 24 | } 25 | 26 | public String getDescription() { 27 | return description; 28 | } 29 | 30 | public void setDescription(String description) { 31 | this.description = description; 32 | } 33 | 34 | @NotNull 35 | @Override 36 | public HandlerList getHandlers() { 37 | return handlers; 38 | } 39 | 40 | public static HandlerList getHandlerList() { 41 | return handlers; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/mapupdate/AreaStyleHolder.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.mapupdate; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Set; 6 | 7 | import org.bukkit.configuration.ConfigurationSection; 8 | import org.bukkit.configuration.file.FileConfiguration; 9 | import org.dynmap.towny.DynmapTownyPlugin; 10 | 11 | public class AreaStyleHolder { 12 | 13 | private static AreaStyle defstyle = new AreaStyle(); 14 | private static Map cusstyle = new HashMap(); 15 | private static Map nationstyle = new HashMap(); 16 | 17 | public static void initialize() { 18 | DynmapTownyPlugin plugin = DynmapTownyPlugin.getPlugin(); 19 | FileConfiguration cfg = plugin.getConfig(); 20 | ConfigurationSection sect = cfg.getConfigurationSection("custstyle"); 21 | if (sect != null) { 22 | Set ids = sect.getKeys(false); 23 | 24 | for (String id : ids) { 25 | cusstyle.put(id, new AreaStyle("custstyle." + id, plugin.getDynmapAPI().getMarkerAPI())); 26 | } 27 | } 28 | sect = cfg.getConfigurationSection("nationstyle"); 29 | if (sect != null) { 30 | Set ids = sect.getKeys(false); 31 | 32 | for (String id : ids) { 33 | nationstyle.put(id, new AreaStyle("nationstyle." + id, plugin.getDynmapAPI().getMarkerAPI())); 34 | } 35 | } 36 | } 37 | 38 | public static AreaStyle getDefaultStyle() { 39 | return defstyle; 40 | } 41 | 42 | public static Map getCustomStyles() { 43 | return cusstyle; 44 | } 45 | 46 | public static Map getNationStyles() { 47 | return nationstyle; 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/mapupdate/TileFlags.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.mapupdate; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * scalable flags primitive - used for keeping track of potentially huge number of tiles 7 | * 8 | * Represents a flag for each tile, with 2D coordinates based on 0,0 origin. Flags are grouped 9 | * 64 x 64, represented by an array of 64 longs. Each set is stored in a hashmap, keyed by a long 10 | * computed by ((x/64)<<32)+(y/64). 11 | * 12 | */ 13 | public class TileFlags { 14 | private HashMap chunkmap = new HashMap(); 15 | private long last_key = Long.MAX_VALUE; 16 | private long[] last_row; 17 | 18 | public TileFlags() { 19 | } 20 | 21 | public boolean getFlag(int x, int y) { 22 | long k = (((long)(x >> 6)) << 32) | (0xFFFFFFFFL & (long)(y >> 6)); 23 | long[] row; 24 | if(k == last_key) { 25 | row = last_row; 26 | } 27 | else { 28 | row = chunkmap.get(k); 29 | last_key = k; 30 | last_row = row; 31 | } 32 | if(row == null) 33 | return false; 34 | else 35 | return (row[y & 0x3F] & (1L << (x & 0x3F))) != 0; 36 | } 37 | 38 | public void setFlag(int x, int y, boolean f) { 39 | long k = (((long)(x >> 6)) << 32) | (0xFFFFFFFFL & (long)(y >> 6)); 40 | long[] row; 41 | if(k == last_key) { 42 | row = last_row; 43 | } 44 | else { 45 | row = chunkmap.get(k); 46 | last_key = k; 47 | last_row = row; 48 | } 49 | if(f) { 50 | if(row == null) { 51 | row = new long[64]; 52 | chunkmap.put(k, row); 53 | last_row = row; 54 | } 55 | row[y & 0x3F] |= (1L << (x & 0x3F)); 56 | } 57 | else { 58 | if(row != null) 59 | row[y & 0x3F] &= ~(1L << (x & 0x3F)); 60 | } 61 | } 62 | public void clear() { 63 | chunkmap.clear(); 64 | last_row = null; 65 | last_key = Long.MAX_VALUE; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/listeners/DynmapTownyListener.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.listeners; 2 | 3 | import org.bukkit.event.EventHandler; 4 | import org.bukkit.event.EventPriority; 5 | import org.bukkit.event.Listener; 6 | import org.bukkit.event.player.PlayerLoginEvent; 7 | import org.bukkit.event.player.PlayerQuitEvent; 8 | import org.bukkit.event.server.PluginEnableEvent; 9 | import org.bukkit.plugin.PluginManager; 10 | import org.dynmap.DynmapWebChatEvent; 11 | import org.dynmap.towny.DynmapTownyPlugin; 12 | import org.dynmap.towny.settings.Settings; 13 | 14 | public class DynmapTownyListener implements Listener { 15 | final DynmapTownyPlugin plugin; 16 | public DynmapTownyListener(PluginManager pm, DynmapTownyPlugin plugin) { 17 | this.plugin = plugin; 18 | pm.registerEvents(this, plugin); 19 | } 20 | 21 | @EventHandler(priority = EventPriority.MONITOR) 22 | public void onPlayerLogin(PlayerLoginEvent event) { 23 | if (!Settings.sendLoginMessage() || !Settings.usingTownyChat() || event.getResult() != PlayerLoginEvent.Result.ALLOWED) 24 | return; 25 | 26 | plugin.getDynmapAPI().postPlayerJoinQuitToWeb(event.getPlayer(), true); 27 | } 28 | 29 | @EventHandler(priority = EventPriority.MONITOR) 30 | public void onPlayerQuit(PlayerQuitEvent event) { 31 | if (!Settings.sendQuitMessage() || !Settings.usingTownyChat()) 32 | return; 33 | 34 | plugin.getDynmapAPI().postPlayerJoinQuitToWeb(event.getPlayer(), false); 35 | } 36 | 37 | @EventHandler(priority = EventPriority.MONITOR) 38 | public void onWebchatEvent(DynmapWebChatEvent event) { 39 | if (!Settings.usingTownyChat() || Settings.getChatFormat().isEmpty() || event.isCancelled() || event.isProcessed()) 40 | return; 41 | 42 | event.setProcessed(); 43 | String msg = Settings.getChatFormat() 44 | .replace("&color;", "\u00A7") 45 | .replace("%playername%", event.getName()) 46 | .replace("%message%", event.getMessage()); 47 | plugin.getServer().broadcastMessage(msg); 48 | } 49 | 50 | @EventHandler(priority = EventPriority.NORMAL) 51 | private void onDynMapReload(PluginEnableEvent event) { 52 | if (!event.getPlugin().getName().equals("dynmap")) 53 | return; 54 | 55 | PluginManager pm = plugin.getServer().getPluginManager(); 56 | if (pm.isPluginEnabled("Dynmap-Towny")) { 57 | pm.disablePlugin(plugin); 58 | pm.enablePlugin(plugin); 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dynmap-Towny 2 | 3 | Dynmap-Towny provides a simple way to add visibility of Towny towns and nations on Dynmap's maps. The plugin depends on the presence of both Dynmap and Towny Advanced, and interacts directly with the Towny API. Updates to zones are automatically processed (on a settable period - default is once per 5 minutes (300 seconds)). By default, the plugin will be active after simply installing it (by unzipping the distribution into the plugins/ directory and restarting the server). 4 | 5 | Towns of any shape are supported, and a proper outline border is computed and displayed that will encompass all the contiguous blocks of a given town (**limitation - 'holes' in the middle of a town may still be shaded to look like part of the town, this is a limitation of Dynmap itself.**). Outposts, including outposts on other worlds, are supported. Clicking on the town will display a popup with a configurable set of data on the town. 6 | 7 | Dynmap-Towny will also show configurable icons for the home block of each town, including distinctive icons when the town is a capital of its country. 8 | 9 | Display style, including the color and opacity of the outlines and fill, as well as icons used for home markers, can be tailored. This can be done at the global default level, the per-nation level, or the per town level. The Y coordinate (altitude) of the town outlines can also be set (default is 64 - standard sea level) - typically this would be done using 'custstyle', to set the value for each town needing to be adjusted individually. 10 | 11 | Visibility of towns can be controlled via the 'visibleregions' and 'hiddenregions' settings. Besides listing the names of the towns to be made visible or hidden, entries with the format 'world:' can be used to make all towns on a given world visible or hidden. 12 | 13 | Also, the display of the town outlines can be restricted to a minimum zoom-in level, via the 'minzoom' setting. When non-zero, this setting causes the town outlines to only be displayed at or beyond the given zoom-in level. 14 | 15 | ## Plugin Configuration 16 | After the first load, there will be a config.yml file in the plugins/Dynmap-Towny directory. Details of the default configuration, and all the provided settings, can be found [here](https://github.com/TownyAdvanced/Dynmap-Towny/wiki) 17 | 18 | ## Acknowledgements 19 | This is a fork of [Dynmap-Towny](https://github.com/webbukkit/Dynmap-Towny). 20 | 21 | LlmDl forked this from Hank Jordan who forked it from the original repo. 22 | 23 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | org.dynmap 4 | Dynmap-Towny 5 | 1.3.2 6 | 7 | 8 | clean package 9 | 10 | 11 | src/main/resources 12 | true 13 | 14 | *.yml 15 | *.txt 16 | 17 | 18 | 19 | src/main/resources 20 | false 21 | 22 | *.yml 23 | *.txt 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.apache.maven.plugins 31 | maven-compiler-plugin 32 | 3.11.0 33 | 34 | ${java.version} 35 | ${java.version} 36 | 37 | 38 | 39 | 40 | 41 | 42 | spigot-repo 43 | https://hub.spigotmc.org/nexus/content/repositories/snapshots/ 44 | 45 | 46 | dynmap-repo 47 | https://repo.mikeprimm.com/ 48 | 49 | 50 | glaremasters repo 51 | https://repo.glaremasters.me/repository/towny/ 52 | 53 | 54 | jitpack.io 55 | https://jitpack.io 56 | 57 | 58 | 59 | 60 | 61 | org.spigotmc 62 | spigot-api 63 | 1.16.5-R0.1-SNAPSHOT 64 | provided 65 | 66 | 67 | us.dynmap 68 | dynmap-api 69 | 2.5 70 | provided 71 | 72 | 73 | com.palmergames.bukkit.towny 74 | towny 75 | 0.100.4.0 76 | provided 77 | 78 | 79 | com.github.TownyAdvanced 80 | TownyChat 81 | 0.90 82 | provided 83 | 84 | 85 | 86 | 17 87 | UTF-8 88 | 89 | 90 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/mapupdate/TownInfoWindow.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.mapupdate; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.stream.Collectors; 6 | 7 | import org.bukkit.Bukkit; 8 | import org.dynmap.towny.events.BuildTownFlagsEvent; 9 | import org.dynmap.towny.settings.Settings; 10 | 11 | import com.palmergames.bukkit.towny.TownyEconomyHandler; 12 | import com.palmergames.bukkit.towny.TownyFormatter; 13 | import com.palmergames.bukkit.towny.TownySettings; 14 | import com.palmergames.bukkit.towny.object.Resident; 15 | import com.palmergames.bukkit.towny.object.Town; 16 | import com.palmergames.bukkit.towny.object.Translation; 17 | import com.palmergames.bukkit.towny.utils.TownRuinUtil; 18 | import com.palmergames.util.StringMgmt; 19 | 20 | public class TownInfoWindow { 21 | 22 | public static String formatInfoWindow(Town town) { 23 | String v = "
" + Settings.getTownInfoWindow() + "
"; 24 | v = v.replace("%regionname%", town.getName()); 25 | v = v.replace("%playerowners%", town.hasMayor()?town.getMayor().getName():""); 26 | String[] residents = town.getResidents().stream().map(obj -> obj.getName()).collect(Collectors.toList()).toArray(new String[0]); 27 | if (residents.length > 34) { 28 | String[] entire = residents; 29 | residents = new String[35 + 1]; 30 | System.arraycopy(entire, 0, residents, 0, 35); 31 | residents[35] = Translation.of("status_town_reslist_overlength"); 32 | } 33 | 34 | String res = String.join(", ", residents); 35 | v = v.replace("%playermembers%", res); 36 | String mgrs = ""; 37 | for(Resident r : town.getRank("assistant")) { 38 | if(mgrs.length()>0) mgrs += ", "; 39 | mgrs += r.getName(); 40 | } 41 | v = v.replace("%playermanagers%", res); 42 | 43 | String dispNames = ""; 44 | for (Resident r: town.getResidents()) { 45 | if(dispNames.length()>0) dispNames += ", "; 46 | dispNames += r.isOnline() ? r.getPlayer().getDisplayName() : r.getFormattedName(); 47 | } 48 | v = v.replace("%residentdisplaynames%", dispNames); 49 | 50 | v = v.replace("%residentcount%", town.getResidents().size() + ""); 51 | v = v.replace("%founded%", town.getRegistered() != 0 ? TownyFormatter.registeredFormat.format(town.getRegistered()) : "Not set"); 52 | v = v.replace("%board%", town.getBoard()); 53 | v = v.replace("%towntrusted%", town.getTrustedResidents().isEmpty() ? Translation.of("status_no_town") //Translation is "None" 54 | : StringMgmt.join(town.getTrustedResidents().stream().map(trustedRes-> trustedRes.getName()).collect(Collectors.toList()), ", ")); 55 | 56 | if (TownySettings.isUsingEconomy() && TownyEconomyHandler.isActive()) { 57 | if (town.isTaxPercentage()) { 58 | v = v.replace("%tax%", town.getTaxes() + "%"); 59 | } else { 60 | v = v.replace("%tax%", TownyEconomyHandler.getFormattedBalance(town.getTaxes())); 61 | } 62 | 63 | v = v.replace("%bank%", TownyEconomyHandler.getFormattedBalance(town.getAccount().getCachedBalance())); 64 | v = v.replace("%upkeep%", TownyEconomyHandler.getFormattedBalance(TownySettings.getTownUpkeepCost(town))); 65 | } 66 | String nation = town.hasNation() ? town.getNationOrNull().getName() : Settings.noNationSlug(); 67 | 68 | v = v.replace("%nation%", nation); 69 | 70 | String natStatus = ""; 71 | if (town.isCapital()) { 72 | natStatus = "Capital of " + nation; 73 | } else if (town.hasNation()) { 74 | natStatus = "Member of " + nation; 75 | } 76 | 77 | v = v.replace("%nationstatus%", natStatus); 78 | 79 | v = v.replace("%public%", getEnabledDisabled(town.isPublic())); 80 | v = v.replace("%peaceful%", getEnabledDisabled(town.isNeutral())); 81 | v = v.replace("%conquered%", getEnabledDisabled(town.isConquered())); 82 | 83 | 84 | /* Build flags */ 85 | List flags = new ArrayList<>(); 86 | flags.add(Translation.of("msg_perm_hud_pvp") + getEnabledDisabled(town.isPVP())); 87 | flags.add(Translation.of("msg_perm_hud_mobspawns") + getEnabledDisabled(town.hasMobs())); 88 | flags.add(Translation.of("msg_perm_hud_explosions") + getEnabledDisabled(town.isExplosion())); 89 | flags.add(Translation.of("msg_perm_hud_firespread") + getEnabledDisabled(town.isFire())); 90 | 91 | if (TownySettings.getTownRuinsEnabled() && town.isRuined()) 92 | flags.add(Translation.of("msg_comptype_ruined") + " " + Translation.of("msg_time_remaining_before_full_removal", 93 | TownySettings.getTownRuinsMaxDurationHours() - TownRuinUtil.getTimeSinceRuining(town))); 94 | 95 | BuildTownFlagsEvent buildTownFlagsEvent = new BuildTownFlagsEvent(town, flags); 96 | Bukkit.getPluginManager().callEvent(buildTownFlagsEvent); 97 | 98 | v = v.replace("%flags%", String.join("
", buildTownFlagsEvent.getFlags())); 99 | 100 | return v; 101 | } 102 | 103 | private static String getEnabledDisabled(boolean b) { 104 | return b ? Translation.of("enabled") : Translation.of("disabled"); 105 | } 106 | } -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/settings/ConfigNodes.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.settings; 2 | 3 | public enum ConfigNodes { 4 | 5 | VERSION_HEADER("version", "", ""), 6 | VERSION( 7 | "version.version", 8 | "", 9 | "# This is the current version. Please do not edit."), 10 | UPDATE_ROOT("update","","",""), 11 | UPDATE_PERIOD("update.period", 12 | "300", 13 | "# Seconds between updating Towny information on the dynmap."), 14 | 15 | LAYER_ROOT("layer","","",""), 16 | LAYER_NAME("layer.name", 17 | "Towny", 18 | "","# The name of the Towny layer on dynmap."), 19 | LAYER_HIDE_BY_DEFAULT("layer.hidebydefault", 20 | "false", 21 | "","# Is the Towny layer hidden by default?"), 22 | LAYER_PRIORITY("layer.layerprio", 23 | "2", 24 | "", "# Ordering priority in layer menu (low goes before high - default is 0)"), 25 | LAYER_MIN_ZOOM("layer.minzoom", 26 | "0", 27 | "", "# (optional) set minimum zoom level before layer is visible (0 = defalt, always visible)"), 28 | 29 | VISIBILITY_ROOT("player_visibility","","","# Requires Player-Info-Is-Protected enabled in dynmap."), 30 | VISIBILITY_BY_TOWN("player_visibility.visibility-by-town", 31 | "true", 32 | "","# Allow all residents of a given town to see one another."), 33 | VISIBILITY_BY_NATION("player_visibility.visibility-by-nation", 34 | "true", 35 | "","# Allow all residents of a given nation to see one another."), 36 | 37 | TOWNBLOCK_COLOURS_ROOT("townblock_colours","","",""), 38 | TOWNBLOCK_SHOW_SHOPS("townblock_colours.shops.showShops", 39 | "false", 40 | "", "# Show shop plots with their own fill colour."), 41 | TOWNBLOCK_SHOP_FILL_COLOUR("townblock_colours.shops.fillColour", 42 | "#0000FF", 43 | "", "# The colour shops plots are filled with."), 44 | TOWNBLOCK_SHOW_ARENAS("townblock_colours.arenas.showArenas", 45 | "false", 46 | "", "# Show arena plots with their own fill colour."), 47 | TOWNBLOCK_ARENA_FILL_COLOUR("townblock_colours.arenas.fillColour", 48 | "#FF00FF", 49 | "", "# The colour arena plots are filled with."), 50 | TOWNBLOCK_SHOW_EMBASSIES("townblock_colours.embassies.showEmbassies", 51 | "false", 52 | "", "# Show embassy plots with their own fill colour."), 53 | TOWNBLOCK_EMBASSY_FILL_COLOUR("townblock_colours.embassies.fillColour", 54 | "#00FFFF", 55 | "", "# The colour embassy plots are filled with."), 56 | TOWNBLOCK_SHOW_WILDSPLOTS("townblock_colours.wilds.showWilds", 57 | "false", 58 | "", "# Show wilds plots with their own fill colour."), 59 | TOWNBLOCK_WILDS_FILL_COLOUR("townblock_colours.wilds.fillColour", 60 | "#00FF00", 61 | "", "# The colour wilds plots are filled with."), 62 | 63 | DYNAMIC_COLOURS_ROOT("dynamic_colours","","",""), 64 | DYNAMIC_COLOURS_TOWN("dynamic_colours.town_colours", 65 | "true", 66 | "","# Use dynamic nation colors, which are set in-game by individual nations (using /t set mapcolor )"), 67 | DYNAMIC_COLOURS_NATION("dynamic_colours.nation_colours", 68 | "true", 69 | "","# Use dynamic nation colors, which are set in-game by individual nations (using /n set mapcolor )"), 70 | 71 | INFOWINDOW_ROOT("infowindow","","",""), 72 | INFOWINDOW_TOWN_POPUP("infowindow.town_info_window", 73 | "
%regionname% (%nation%)
Mayor %playerowners%
Associates %playermanagers%
Flags
%flags%
", 74 | "","# Format for town popup."), 75 | INFOWINDOW_NO_NATION_SLUG("infowindow.noNationSlug", 76 | "", 77 | "","# What is shown in the info window's %nation% when a town has no nation."), 78 | 79 | REGIONSTYLE_ROOT("regionstyle","","",""), 80 | REGIONSTYLE_STROKE_COLOUR("regionstyle.strokeColor", "#FF0000"), 81 | REGIONSTYLE_STROKE_OPACITY("regionstyle.strokeOpacity", "0.8"), 82 | REGIONSTYLE_STROKE_WEIGHT("regionstyle.strokeWeight", "3"), 83 | REGIONSTYLE_FILL_COLOUR("regionstyle.fillColor", "#FF0000"), 84 | REGIONSTYLE_FILL_OPACITY("regionstyle.fillOpacity", "0.35"), 85 | REGIONSTYLE_BOOST("regionstyle.boost", "false"), 86 | REGIONSTYLE_ICONS_ROOT("regionstyle.icons","","", 87 | "# Allowed icon names found here: https://github.com/webbukkit/dynmap/wiki/Using-Markers", 88 | "# If you want to not show outposts, put in a invalid marker name ie: 'outpostIcon: boot'."), 89 | REGIONSTYLE_HOME_ICON("regionstyle.icons.homeIcon", 90 | "blueflag"), 91 | REGIONSTYLE_CAPITAL_ICON("regionstyle.icons.capitalIcon", 92 | "king"), 93 | REGIONSTYLE_OUTPOST_ICON("regionstyle.icons.outpostIcon", 94 | "tower"), 95 | REGIONSTYLE_RUIN_ICON("regionstyle.icons.ruinIcon", 96 | "warning"), 97 | 98 | VISIBLE_ROOT("visibleregions","","", 99 | "# Optional setting to limit which regions to show, by name - if commented out, all regions are shown.", 100 | "# To show all regions on a given world, add 'world:' to the list."), 101 | HIDDEN_ROOT("hiddenregions","","", 102 | "# Optional setting to hide specific regions, by name.", 103 | "# To hide all regions on a given world, add 'world:' to the list."), 104 | 105 | CUSTOMSTYLE_ROOT("custstyle","","",""), 106 | NATIONSTYLE_ROOT("nationstyle","","",""), 107 | 108 | CHAT_ROOT("chat","","","# Chat settings for use with TownyChat."), 109 | CHAT_SEND_LOGIN("chat.sendlogin","true"), 110 | CHAT_SEND_QUIT("chat.sendquit","true"), 111 | CHAT_FORMAT("chat.format","&color;2[WEB] %playername%: &color;f%message%"), 112 | 113 | LANGUAGE("language", 114 | "english.yml", 115 | "# The language file you wish to use"); 116 | 117 | private final String Root; 118 | private final String Default; 119 | private String[] comments; 120 | 121 | ConfigNodes(String root, String def, String... comments) { 122 | 123 | this.Root = root; 124 | this.Default = def; 125 | this.comments = comments; 126 | } 127 | 128 | /** 129 | * Retrieves the root for a config option 130 | * 131 | * @return The root for a config option 132 | */ 133 | public String getRoot() { 134 | 135 | return Root; 136 | } 137 | 138 | /** 139 | * Retrieves the default value for a config path 140 | * 141 | * @return The default value for a config path 142 | */ 143 | public String getDefault() { 144 | 145 | return Default; 146 | } 147 | 148 | /** 149 | * Retrieves the comment for a config path 150 | * 151 | * @return The comments for a config path 152 | */ 153 | public String[] getComments() { 154 | 155 | if (comments != null) { 156 | return comments; 157 | } 158 | 159 | String[] comments = new String[1]; 160 | comments[0] = ""; 161 | return comments; 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/DynmapTownyPlugin.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.Set; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | import java.util.stream.Collectors; 9 | 10 | import org.bukkit.plugin.Plugin; 11 | import org.bukkit.plugin.PluginManager; 12 | import org.bukkit.plugin.java.JavaPlugin; 13 | import org.dynmap.DynmapAPI; 14 | import org.dynmap.markers.AreaMarker; 15 | import org.dynmap.markers.Marker; 16 | import org.dynmap.markers.MarkerAPI; 17 | import org.dynmap.markers.MarkerSet; 18 | import org.dynmap.markers.PlayerSet; 19 | import org.dynmap.towny.listeners.DynmapTownyListener; 20 | import org.dynmap.towny.mapupdate.AreaStyleHolder; 21 | import org.dynmap.towny.mapupdate.UpdateTowns; 22 | import org.dynmap.towny.settings.Settings; 23 | 24 | import com.palmergames.bukkit.towny.Towny; 25 | import com.palmergames.bukkit.towny.TownyAPI; 26 | import com.palmergames.bukkit.towny.TownyUniverse; 27 | import com.palmergames.bukkit.towny.exceptions.initialization.TownyInitException; 28 | import com.palmergames.bukkit.towny.object.Nation; 29 | import com.palmergames.bukkit.towny.object.Town; 30 | import com.palmergames.bukkit.towny.scheduling.ScheduledTask; 31 | import com.palmergames.bukkit.towny.scheduling.TaskScheduler; 32 | import com.palmergames.bukkit.towny.scheduling.impl.BukkitTaskScheduler; 33 | import com.palmergames.bukkit.towny.scheduling.impl.FoliaTaskScheduler; 34 | 35 | public class DynmapTownyPlugin extends JavaPlugin { 36 | 37 | private static final String requiredTownyVersion = "0.100.4.0"; 38 | private static Logger log; 39 | private static DynmapTownyPlugin plugin; 40 | private final TaskScheduler scheduler; 41 | private ScheduledTask task; 42 | private UpdateTowns townUpdater; 43 | private DynmapAPI dynmapAPI; 44 | private MarkerAPI markerAPI; 45 | private MarkerSet markerSet; 46 | private Map areaMarkers = new HashMap(); 47 | private Map markers = new HashMap(); 48 | 49 | public DynmapTownyPlugin() { 50 | this.scheduler = isFoliaClassPresent() ? new FoliaTaskScheduler(this) : new BukkitTaskScheduler(this); 51 | plugin = this; 52 | } 53 | 54 | @Override 55 | public void onLoad() { 56 | log = this.getLogger(); 57 | } 58 | 59 | public void onEnable() { 60 | info("Initializing DynmapTowny."); 61 | 62 | Plugin dynmap = null; 63 | Plugin towny = null; 64 | Plugin townychat = null; 65 | PluginManager pm = getServer().getPluginManager(); 66 | 67 | /* Get dynmap */ 68 | dynmap = pm.getPlugin("dynmap"); 69 | if (dynmap == null || !dynmap.isEnabled()) { 70 | severe("Cannot find dynmap, check your logs to see if it enabled properly?!"); 71 | return; 72 | } 73 | 74 | /* Get Towny */ 75 | towny = pm.getPlugin("Towny"); 76 | if (towny == null || !towny.isEnabled()) { 77 | severe("Cannot find Towny, check your logs to see if it enabled properly?!"); 78 | return; 79 | } 80 | 81 | /* Get DynmapAPI */ 82 | dynmapAPI = (DynmapAPI) dynmap; 83 | 84 | // Make sure the Towny version is new enough. 85 | if (!townyVersionCheck()) { 86 | getLogger().severe("Towny version does not meet required minimum version: " + requiredTownyVersion); 87 | this.getServer().getPluginManager().disablePlugin(this); 88 | return; 89 | } else { 90 | getLogger().info("Towny version " + towny.getDescription().getVersion() + " found."); 91 | } 92 | 93 | if (!loadConfig()) { 94 | onDisable(); 95 | return; 96 | } 97 | 98 | /* Register the event listener. */ 99 | new DynmapTownyListener(pm, this); 100 | 101 | /* Kick off the rest of the plugin. */ 102 | activate(); 103 | 104 | /* Check if TownyChat is present. */ 105 | townychat = pm.getPlugin("TownyChat"); 106 | if (townychat != null && townychat.isEnabled()) { 107 | dynmapAPI.setDisableChatToWebProcessing(true); 108 | Settings.setUsingTownyChat(true); 109 | info("TownyChat detect: disabling normal chat-to-web processing in Dynmap"); 110 | } 111 | } 112 | 113 | private boolean loadConfig() { 114 | try { 115 | Settings.loadConfig(); 116 | } catch (TownyInitException e) { 117 | e.printStackTrace(); 118 | severe("Config.yml failed to load! Disabling!"); 119 | return false; 120 | } 121 | info("Config.yml loaded successfully."); 122 | return true; 123 | } 124 | 125 | private void activate() { 126 | markerAPI = dynmapAPI.getMarkerAPI(); 127 | if (markerAPI == null) { 128 | severe("Error loading dynmap marker API!"); 129 | return; 130 | } 131 | 132 | /* We might be reloading and have a set already in use. */ 133 | if (markerSet != null) { 134 | markerSet.deleteMarkerSet(); 135 | markerSet = null; 136 | } 137 | 138 | /* Initialize the critical Towny MarkerSet. */ 139 | if (!initializeMarkerSet()) { 140 | severe("Error creating Towny marker set!"); 141 | return; 142 | } 143 | 144 | /* Initialize the Styles from the config for use by the TownyUpdate task. */ 145 | AreaStyleHolder.initialize(); 146 | 147 | /* Create our UpdateTowns instance, used to actually draw the towns. */ 148 | townUpdater = new UpdateTowns(); 149 | 150 | /* Set up update job - based on period */ 151 | long per = Math.max(15, Settings.getUpdatePeriod()) * 20L; 152 | task = scheduler.runAsyncRepeating(new TownyUpdate(), 40, per); 153 | 154 | info("version " + this.getDescription().getVersion() + " is activated"); 155 | } 156 | 157 | private boolean initializeMarkerSet() { 158 | markerSet = markerAPI.getMarkerSet("towny.markerset"); 159 | if (markerSet == null) 160 | markerSet = markerAPI.createMarkerSet("towny.markerset", Settings.getLayerName(), null, false); 161 | else 162 | markerSet.setMarkerSetLabel(Settings.getLayerName()); 163 | 164 | if (markerSet == null) 165 | return false; 166 | 167 | int minzoom = Settings.getMinZoom(); 168 | if (minzoom > 0) 169 | markerSet.setMinZoom(minzoom); 170 | markerSet.setLayerPriority(Settings.getLayerPriority()); 171 | markerSet.setHideByDefault(Settings.getLayerHiddenByDefault()); 172 | return true; 173 | } 174 | 175 | public void onDisable() { 176 | if (markerSet != null) { 177 | markerSet.deleteMarkerSet(); 178 | markerSet = null; 179 | } 180 | task.cancel(); 181 | } 182 | 183 | private static boolean isFoliaClassPresent() { 184 | try { 185 | Class.forName("io.papermc.paper.threadedregions.RegionizedServer"); 186 | return true; 187 | } catch (ClassNotFoundException e) { 188 | return false; 189 | } 190 | } 191 | 192 | public static DynmapTownyPlugin getPlugin() { 193 | return plugin; 194 | } 195 | 196 | public static String getPrefix() { 197 | return "[" + plugin.getName() + "]"; 198 | } 199 | 200 | private boolean townyVersionCheck() { 201 | try { 202 | return Towny.isTownyVersionSupported(requiredTownyVersion); 203 | } catch (NoSuchMethodError e) { 204 | return false; 205 | } 206 | } 207 | 208 | public String getVersion() { 209 | return this.getDescription().getVersion(); 210 | } 211 | 212 | public static void info(String msg) { 213 | log.log(Level.INFO, msg); 214 | } 215 | 216 | public static void severe(String msg) { 217 | log.log(Level.SEVERE, msg); 218 | } 219 | 220 | public DynmapAPI getDynmapAPI() { 221 | return dynmapAPI; 222 | } 223 | 224 | public MarkerSet getMarkerSet() { 225 | return markerSet; 226 | } 227 | 228 | public Map getAreaMarkers() { 229 | return areaMarkers; 230 | } 231 | 232 | public Map getMarkers() { 233 | return markers; 234 | } 235 | 236 | private class TownyUpdate implements Runnable { 237 | public void run() { 238 | if (TownyUniverse.getInstance().getDataSource() != null) { 239 | scheduler.runAsync(townUpdater); 240 | 241 | if (Settings.getPlayerVisibilityByTown()) 242 | TownyAPI.getInstance().getTowns().forEach(t -> updateTown(t)); 243 | 244 | if (Settings.getPlayerVisibilityByNation()) 245 | TownyAPI.getInstance().getNations().forEach(n -> updateNation(n)); 246 | } 247 | } 248 | } 249 | 250 | private void updateTown(Town town) { 251 | Set plids = town.getResidents().stream().map(r -> r.getName()).collect(Collectors.toSet()); 252 | String setid = "towny.town." + town.getName(); 253 | PlayerSet set = markerAPI.getPlayerSet(setid); /* See if set exists */ 254 | if (set == null) { 255 | set = markerAPI.createPlayerSet(setid, true, plids, false); 256 | info("Added player visibility set '" + setid + "' for town " + town.getName()); 257 | return; 258 | } 259 | set.setPlayers(plids); 260 | } 261 | 262 | private void updateNation(Nation nat) { 263 | Set plids = nat.getResidents().stream().map(r -> r.getName()).collect(Collectors.toSet()); 264 | String setid = "towny.nation." + nat.getName(); 265 | PlayerSet set = markerAPI.getPlayerSet(setid); /* See if set exists */ 266 | if (set == null) { 267 | set = markerAPI.createPlayerSet(setid, true, plids, false); 268 | info("Added player visibility set '" + setid + "' for nation " + nat.getName()); 269 | return; 270 | } 271 | set.setPlayers(plids); 272 | } 273 | } 274 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/mapupdate/AreaStyle.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.mapupdate; 2 | 3 | import org.bukkit.configuration.file.FileConfiguration; 4 | import org.dynmap.markers.MarkerAPI; 5 | import org.dynmap.markers.MarkerIcon; 6 | import org.dynmap.towny.DynmapTownyPlugin; 7 | import org.dynmap.towny.settings.Settings; 8 | 9 | import com.palmergames.bukkit.towny.object.TownBlockType; 10 | 11 | public class AreaStyle { 12 | int strokecolor; 13 | double strokeopacity; 14 | int strokeweight; 15 | int fillcolor; 16 | double fillopacity; 17 | int fillcolor_shops; 18 | int fillcolor_embassies; 19 | int fillcolor_arenas; 20 | int fillcolor_wilds; 21 | MarkerIcon homeicon; 22 | MarkerIcon capitalicon; 23 | MarkerIcon ruinicon; 24 | MarkerIcon outposticon; 25 | int yc; 26 | boolean boost; 27 | 28 | /** 29 | * Used to create AreaStyles based off of what is in the config used on all 30 | * non-custom and non-nation styles. 31 | */ 32 | public AreaStyle() { 33 | String sc = Settings.getStrokeColour(); 34 | strokeopacity = Settings.getStrokeOpacity(); 35 | strokeweight = Settings.getStrokeWeight(); 36 | String fc = Settings.getFillColour(); 37 | String fcs = Settings.getShopsFillColour(); 38 | String fca = Settings.getArenaFillColour(); 39 | String fce = Settings.getEmbassyFillColour(); 40 | String fcw = Settings.getWildsFillColour(); 41 | yc = 64; 42 | boost = Settings.getBoost(); 43 | 44 | strokecolor = -1; 45 | fillcolor = -1; 46 | fillcolor_shops = -1; 47 | fillcolor_arenas = -1; 48 | fillcolor_embassies = -1; 49 | fillcolor_wilds = -1; 50 | try { 51 | if (sc != null) 52 | strokecolor = Integer.parseInt(sc.substring(1), 16); 53 | if (fc != null) 54 | fillcolor = Integer.parseInt(fc.substring(1), 16); 55 | if (fcs != null) 56 | fillcolor_shops = Integer.parseInt(fcs.substring(1), 16); 57 | if (fca != null) 58 | fillcolor_arenas = Integer.parseInt(fca.substring(1), 16); 59 | if (fce != null) 60 | fillcolor_embassies = Integer.parseInt(fce.substring(1), 16); 61 | if (fcw != null) 62 | fillcolor_wilds = Integer.parseInt(fcw.substring(1), 16); 63 | } catch (NumberFormatException nfx) { 64 | } 65 | 66 | fillopacity = Settings.getFillOpacity(); 67 | homeicon = Settings.getHomeIcon(); 68 | capitalicon = Settings.getCapitalIcon(); 69 | outposticon = Settings.getOutpostIcon(); 70 | ruinicon = Settings.getRuinIcon(); 71 | } 72 | 73 | /** 74 | * Used to contain non-default style AreaStyles, used for CustomeStyles and 75 | * NationStyles configured in the config. 76 | * 77 | * @param path The path of the config containing nationstyle + id or 78 | * custstyle + id. 79 | * @param markerapi Instance of the MarkerAPI we are using. 80 | */ 81 | public AreaStyle(String path, MarkerAPI markerapi) { 82 | FileConfiguration cfg = DynmapTownyPlugin.getPlugin().getConfig(); 83 | String sc = cfg.getString(path + ".strokecolor", null); 84 | strokeopacity = cfg.getDouble(path + ".strokeopacity", -1); 85 | strokeweight = cfg.getInt(path + ".strokeweight", -1); 86 | String fc = cfg.getString(path + ".fillcolor", null); 87 | String fcs = cfg.getString(path + ".fillcolorshops", null); 88 | String fca = cfg.getString(path + ".fillcolorarenas", null); 89 | String fce = cfg.getString(path + ".fillcolorembassies", null); 90 | String fcw = cfg.getString(path + ".fillColorwilds", null); 91 | yc = cfg.getInt(path + ".y", -1); 92 | boost = cfg.getBoolean(path + ".boost", false); 93 | 94 | strokecolor = -1; 95 | fillcolor = -1; 96 | fillcolor_shops = -1; 97 | fillcolor_arenas = -1; 98 | fillcolor_embassies = -1; 99 | fillcolor_wilds = -1; 100 | try { 101 | if (sc != null) 102 | strokecolor = Integer.parseInt(sc.substring(1), 16); 103 | if (fc != null) 104 | fillcolor = Integer.parseInt(fc.substring(1), 16); 105 | if (fcs != null) 106 | fillcolor_shops = Integer.parseInt(fcs.substring(1), 16); 107 | if (fca != null) 108 | fillcolor_arenas = Integer.parseInt(fca.substring(1), 16); 109 | if (fce != null) 110 | fillcolor_embassies = Integer.parseInt(fce.substring(1), 16); 111 | if (fcw != null) 112 | fillcolor_wilds = Integer.parseInt(fcw.substring(1), 16); 113 | } catch (NumberFormatException nfx) { 114 | } 115 | 116 | fillopacity = cfg.getDouble(path + ".fillopacity", -1); 117 | String homemarker = cfg.getString(path + ".homeicon", null); 118 | if (homemarker != null) { 119 | homeicon = markerapi.getMarkerIcon(homemarker); 120 | if (homeicon == null) { 121 | homeicon = markerapi.getMarkerIcon("blueicon"); 122 | } 123 | } 124 | String capitalmarker = cfg.getString(path + ".capitalicon", null); 125 | if (capitalmarker != null) { 126 | capitalicon = markerapi.getMarkerIcon(capitalmarker); 127 | if (capitalicon == null) { 128 | capitalicon = markerapi.getMarkerIcon("king"); 129 | } 130 | } 131 | ruinicon = markerapi.getMarkerIcon("warning"); 132 | 133 | String outpostmarker = cfg.getString(path + ".outposticon", null); 134 | if (outpostmarker != null) { 135 | outposticon = markerapi.getMarkerIcon(outpostmarker); 136 | if (outposticon == null) 137 | outposticon = markerapi.getMarkerIcon("tower"); 138 | } 139 | } 140 | 141 | public int getStrokeColor(AreaStyle cust, AreaStyle nat) { 142 | if ((cust != null) && (cust.strokecolor >= 0)) 143 | return cust.strokecolor; 144 | else if ((nat != null) && (nat.strokecolor >= 0)) 145 | return nat.strokecolor; 146 | else if (strokecolor >= 0) 147 | return strokecolor; 148 | else 149 | return 0xFF0000; 150 | } 151 | 152 | public double getStrokeOpacity(AreaStyle cust, AreaStyle nat) { 153 | if ((cust != null) && (cust.strokeopacity >= 0)) 154 | return cust.strokeopacity; 155 | else if ((nat != null) && (nat.strokeopacity >= 0)) 156 | return nat.strokeopacity; 157 | else if (strokeopacity >= 0) 158 | return strokeopacity; 159 | else 160 | return 0.8; 161 | } 162 | 163 | public int getStrokeWeight(AreaStyle cust, AreaStyle nat) { 164 | if ((cust != null) && (cust.strokeweight >= 0)) 165 | return cust.strokeweight; 166 | else if ((nat != null) && (nat.strokeweight >= 0)) 167 | return nat.strokeweight; 168 | else if (strokeweight >= 0) 169 | return strokeweight; 170 | else 171 | return 3; 172 | } 173 | 174 | public int getFillColor(TownBlockType btype) { 175 | if (btype == TownBlockType.COMMERCIAL) 176 | return fillcolor_shops; 177 | if (btype == TownBlockType.ARENA) 178 | return fillcolor_arenas; 179 | if (btype == TownBlockType.EMBASSY) 180 | return fillcolor_embassies; 181 | if (btype == TownBlockType.WILDS) 182 | return fillcolor_wilds; 183 | return -1; 184 | } 185 | 186 | public int getFillColor(AreaStyle cust, AreaStyle nat, TownBlockType btype) { 187 | if (btype == TownBlockType.COMMERCIAL) { 188 | if ((cust != null) && (cust.fillcolor_shops >= 0)) 189 | return cust.fillcolor_shops; 190 | else if ((nat != null) && (nat.fillcolor_shops >= 0)) 191 | return nat.fillcolor_shops; 192 | else if (fillcolor_shops >= 0) 193 | return fillcolor_shops; 194 | else 195 | return 0xFF0000; 196 | } else if (btype == TownBlockType.ARENA) { 197 | if ((cust != null) && (cust.fillcolor_arenas >= 0)) 198 | return cust.fillcolor_shops; 199 | else if ((nat != null) && (nat.fillcolor_arenas >= 0)) 200 | return nat.fillcolor_arenas; 201 | else if (fillcolor_arenas >= 0) 202 | return fillcolor_arenas; 203 | else 204 | return 0xFF0000; 205 | } else if (btype == TownBlockType.EMBASSY) { 206 | if ((cust != null) && (cust.fillcolor_embassies >= 0)) 207 | return cust.fillcolor_embassies; 208 | else if ((nat != null) && (nat.fillcolor_embassies >= 0)) 209 | return nat.fillcolor_embassies; 210 | else if (fillcolor_embassies >= 0) 211 | return fillcolor_embassies; 212 | else 213 | return 0xFF0000; 214 | } else if (btype == TownBlockType.WILDS) { 215 | if ((cust != null) && (cust.fillcolor_wilds >= 0)) 216 | return cust.fillcolor_wilds; 217 | else if ((nat != null) && (nat.fillcolor_wilds >= 0)) 218 | return nat.fillcolor_wilds; 219 | else if (fillcolor_wilds >= 0) 220 | return fillcolor_wilds; 221 | else 222 | return 0xFF0000; 223 | } 224 | if ((cust != null) && (cust.fillcolor >= 0)) 225 | return cust.fillcolor; 226 | else if ((nat != null) && (nat.fillcolor >= 0)) 227 | return nat.fillcolor; 228 | else if (fillcolor >= 0) 229 | return fillcolor; 230 | else 231 | return 0xFF0000; 232 | } 233 | 234 | public double getFillOpacity(AreaStyle cust, AreaStyle nat) { 235 | if ((cust != null) && (cust.fillopacity >= 0)) 236 | return cust.fillopacity; 237 | else if ((nat != null) && (nat.fillopacity >= 0)) 238 | return nat.fillopacity; 239 | else if (fillopacity >= 0) 240 | return fillopacity; 241 | else 242 | return 0.35; 243 | } 244 | 245 | public MarkerIcon getHomeMarker(AreaStyle cust, AreaStyle nat) { 246 | if ((cust != null) && (cust.homeicon != null)) 247 | return cust.homeicon; 248 | else if ((nat != null) && (nat.homeicon != null)) 249 | return nat.homeicon; 250 | else 251 | return homeicon; 252 | } 253 | 254 | public MarkerIcon getCapitalMarker(AreaStyle cust, AreaStyle nat) { 255 | if ((cust != null) && (cust.capitalicon != null)) 256 | return cust.capitalicon; 257 | else if ((nat != null) && (nat.capitalicon != null)) 258 | return nat.capitalicon; 259 | else if (capitalicon != null) 260 | return capitalicon; 261 | else 262 | return getHomeMarker(cust, nat); 263 | } 264 | 265 | public MarkerIcon getOutpostMarker(AreaStyle cust, AreaStyle nat) { 266 | if ((cust != null) && (cust.outposticon != null)) 267 | return cust.outposticon; 268 | else if ((nat != null) && (nat.outposticon != null)) 269 | return nat.outposticon; 270 | else if (outposticon != null) 271 | return outposticon; 272 | else 273 | return Settings.getOutpostIcon(); 274 | } 275 | 276 | public MarkerIcon getRuinIcon() { 277 | return ruinicon; 278 | } 279 | 280 | public int getY(AreaStyle cust, AreaStyle nat) { 281 | if ((cust != null) && (cust.yc >= 0)) 282 | return cust.yc; 283 | else if ((nat != null) && (nat.yc >= 0)) 284 | return nat.yc; 285 | else if (yc >= 0) 286 | return yc; 287 | else 288 | return 64; 289 | } 290 | 291 | public boolean getBoost(AreaStyle cust, AreaStyle nat) { 292 | if ((cust != null) && cust.boost) 293 | return cust.boost; 294 | else if ((nat != null) && nat.boost) 295 | return nat.boost; 296 | else 297 | return boost; 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/settings/Settings.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.settings; 2 | 3 | import java.nio.file.Path; 4 | import java.util.ArrayList; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Locale; 8 | import java.util.Set; 9 | 10 | import org.bukkit.configuration.ConfigurationSection; 11 | import org.dynmap.markers.MarkerIcon; 12 | import org.dynmap.towny.DynmapTownyPlugin; 13 | 14 | import com.palmergames.bukkit.config.CommentedConfiguration; 15 | import com.palmergames.bukkit.towny.exceptions.initialization.TownyInitException; 16 | import com.palmergames.util.FileMgmt; 17 | 18 | public class Settings { 19 | private static final String NATIONSTYLE_ROOT = "nationstyle"; 20 | private static final String CUSTSTYLE_ROOT = "custstyle"; 21 | private static CommentedConfiguration config, newConfig; 22 | private static Path configPath = DynmapTownyPlugin.getPlugin().getDataFolder().toPath().resolve("config.yml"); 23 | private static boolean usingTownyChat; 24 | private static MarkerIcon outpostIcon = null; 25 | private static MarkerIcon homeIcon = null; 26 | private static MarkerIcon capitalIcon = null; 27 | private static MarkerIcon ruinIcon = null; 28 | private static boolean visByTownSet; 29 | private static boolean visByNationSet; 30 | private static boolean visByNation; 31 | private static boolean visByTown; 32 | private static Set visibleRegions = null; 33 | private static Set hiddenRegions = null; 34 | 35 | public static void loadConfig() { 36 | if (FileMgmt.checkOrCreateFile(configPath.toString())) { 37 | 38 | // read the config.yml into memory 39 | config = new CommentedConfiguration(configPath); 40 | if (!config.load()) 41 | throw new TownyInitException("Failed to load config.yml.", TownyInitException.TownyError.MAIN_CONFIG); 42 | 43 | setDefaults(DynmapTownyPlugin.getPlugin().getVersion(), configPath); 44 | config.save(); 45 | } 46 | } 47 | 48 | public static void addComment(String root, String... comments) { 49 | 50 | newConfig.addComment(root.toLowerCase(), comments); 51 | } 52 | 53 | private static void setNewProperty(String root, Object value) { 54 | 55 | if (value == null) { 56 | value = ""; 57 | } 58 | newConfig.set(root.toLowerCase(), value.toString()); 59 | } 60 | 61 | @SuppressWarnings("unused") 62 | private static void setProperty(String root, Object value) { 63 | 64 | config.set(root.toLowerCase(), value.toString()); 65 | } 66 | 67 | /** 68 | * Builds a new config reading old config data. 69 | */ 70 | private static void setDefaults(String version, Path configPath) { 71 | 72 | newConfig = new CommentedConfiguration(configPath); 73 | newConfig.load(); 74 | 75 | for (ConfigNodes root : ConfigNodes.values()) { 76 | if (root.getComments().length > 0) 77 | addComment(root.getRoot(), root.getComments()); 78 | if (root.getRoot() == ConfigNodes.VERSION.getRoot()) 79 | setNewProperty(root.getRoot(), version); 80 | else 81 | setNewProperty(root.getRoot(), (config.get(root.getRoot().toLowerCase()) != null) ? config.get(root.getRoot().toLowerCase()) : root.getDefault()); 82 | } 83 | 84 | trySetDefaultCustomAndNationStyles(); 85 | config = newConfig; 86 | newConfig = null; 87 | } 88 | 89 | private static void trySetDefaultCustomAndNationStyles() { 90 | if(config.contains(CUSTSTYLE_ROOT)) // Custstyle already exists. 91 | newConfig.set(CUSTSTYLE_ROOT, config.get(CUSTSTYLE_ROOT)); 92 | else { // Make a new custstyle. 93 | DynmapTownyPlugin.getPlugin().getLogger().info("Config: Creating default custom styles."); 94 | 95 | newConfig.createSection(CUSTSTYLE_ROOT); 96 | ConfigurationSection configurationSection = newConfig.getConfigurationSection(CUSTSTYLE_ROOT); 97 | configurationSection.set(".customregion1.strokecolor", "#00FF00"); 98 | configurationSection.set(".customregion1.y", "64"); 99 | configurationSection.set(".customregion2.strokecolor", "#007F00"); 100 | configurationSection.set(".customregion2.y", "64"); 101 | configurationSection.set(".customregion2.boost", "false"); 102 | } 103 | 104 | if(config.contains(NATIONSTYLE_ROOT)) // Custstyle already exists. 105 | newConfig.set(NATIONSTYLE_ROOT, config.get(NATIONSTYLE_ROOT)); 106 | else { // Make a new nationstyle. 107 | DynmapTownyPlugin.getPlugin().getLogger().info("Config: Creating default nation styles."); 108 | 109 | newConfig.createSection(NATIONSTYLE_ROOT); 110 | ConfigurationSection configurationSection = newConfig.getConfigurationSection(NATIONSTYLE_ROOT); 111 | configurationSection.set(".NationOfBlue.strokecolor", "#0000FF"); 112 | configurationSection.set(".NationOfBlue.fillcolor", "#0000FF"); 113 | configurationSection.set(".NationOfBlue.boost", "false"); 114 | configurationSection.set("._none_.homeicon", "greenflag"); 115 | } 116 | } 117 | 118 | public static String getString(String root, String def) { 119 | 120 | String data = config.getString(root.toLowerCase(), def); 121 | if (data == null) { 122 | sendError(root.toLowerCase() + " from config.yml"); 123 | return ""; 124 | } 125 | return data; 126 | } 127 | 128 | private static void sendError(String msg) { 129 | 130 | DynmapTownyPlugin.severe("Error could not read " + msg); 131 | } 132 | 133 | public static boolean getBoolean(ConfigNodes node) { 134 | 135 | return Boolean.parseBoolean(config.getString(node.getRoot().toLowerCase(), node.getDefault())); 136 | } 137 | 138 | public static double getDouble(ConfigNodes node) { 139 | 140 | try { 141 | return Double.parseDouble(config.getString(node.getRoot().toLowerCase(), node.getDefault()).trim()); 142 | } catch (NumberFormatException e) { 143 | sendError(node.getRoot().toLowerCase() + " from config.yml"); 144 | return 0.0; 145 | } 146 | } 147 | 148 | public static int getInt(ConfigNodes node) { 149 | 150 | try { 151 | return Integer.parseInt(config.getString(node.getRoot().toLowerCase(), node.getDefault()).trim()); 152 | } catch (NumberFormatException e) { 153 | sendError(node.getRoot().toLowerCase() + " from config.yml"); 154 | return 0; 155 | } 156 | } 157 | 158 | public static String getString(ConfigNodes node) { 159 | 160 | return config.getString(node.getRoot().toLowerCase(), node.getDefault()); 161 | } 162 | 163 | public static List getStrArr(ConfigNodes node) { 164 | 165 | String[] strArray = getString(node.getRoot().toLowerCase(Locale.ROOT), node.getDefault()).split(","); 166 | List list = new ArrayList<>(); 167 | 168 | for (String string : strArray) 169 | if (string != null && !string.isEmpty()) 170 | list.add(string.trim()); 171 | 172 | return list; 173 | } 174 | 175 | public static void setUsingTownyChat(boolean value) { 176 | usingTownyChat = value; 177 | } 178 | 179 | public static boolean usingTownyChat() { 180 | return usingTownyChat; 181 | } 182 | 183 | public static boolean sendLoginMessage() { 184 | return getBoolean(ConfigNodes.CHAT_SEND_LOGIN); 185 | } 186 | 187 | public static boolean sendQuitMessage() { 188 | return getBoolean(ConfigNodes.CHAT_SEND_QUIT); 189 | } 190 | 191 | public static String getChatFormat() { 192 | return getString(ConfigNodes.CHAT_FORMAT); 193 | } 194 | 195 | public static String getTownInfoWindow() { 196 | return getString(ConfigNodes.INFOWINDOW_TOWN_POPUP); 197 | } 198 | 199 | public static String noNationSlug() { 200 | return getString(ConfigNodes.INFOWINDOW_NO_NATION_SLUG); 201 | } 202 | 203 | public static MarkerIcon getOutpostIcon() { 204 | if (outpostIcon == null) 205 | outpostIcon = DynmapTownyPlugin.getPlugin().getDynmapAPI().getMarkerAPI().getMarkerIcon(getString(ConfigNodes.REGIONSTYLE_OUTPOST_ICON)); 206 | return outpostIcon; 207 | } 208 | 209 | public static MarkerIcon getHomeIcon() { 210 | if (homeIcon == null) 211 | homeIcon = DynmapTownyPlugin.getPlugin().getDynmapAPI().getMarkerAPI().getMarkerIcon(getString(ConfigNodes.REGIONSTYLE_HOME_ICON)); 212 | return homeIcon; 213 | } 214 | 215 | public static MarkerIcon getCapitalIcon() { 216 | if (capitalIcon == null) 217 | capitalIcon = DynmapTownyPlugin.getPlugin().getDynmapAPI().getMarkerAPI().getMarkerIcon(getString(ConfigNodes.REGIONSTYLE_CAPITAL_ICON)); 218 | return capitalIcon; 219 | } 220 | 221 | public static MarkerIcon getRuinIcon() { 222 | if (ruinIcon == null) 223 | ruinIcon = DynmapTownyPlugin.getPlugin().getDynmapAPI().getMarkerAPI().getMarkerIcon(getString(ConfigNodes.REGIONSTYLE_RUIN_ICON)); 224 | return ruinIcon; 225 | } 226 | 227 | public static String getStrokeColour() { 228 | return getString(ConfigNodes.REGIONSTYLE_STROKE_COLOUR); 229 | } 230 | 231 | public static double getStrokeOpacity() { 232 | return getDouble(ConfigNodes.REGIONSTYLE_STROKE_OPACITY); 233 | } 234 | 235 | public static int getStrokeWeight() { 236 | return getInt(ConfigNodes.REGIONSTYLE_STROKE_WEIGHT); 237 | } 238 | 239 | public static String getFillColour() { 240 | return getString(ConfigNodes.REGIONSTYLE_FILL_COLOUR); 241 | } 242 | 243 | public static String getShopsFillColour() { 244 | return getString(ConfigNodes.TOWNBLOCK_SHOP_FILL_COLOUR); 245 | } 246 | 247 | public static String getArenaFillColour() { 248 | return getString(ConfigNodes.TOWNBLOCK_SHOP_FILL_COLOUR); 249 | } 250 | 251 | public static String getEmbassyFillColour() { 252 | return getString(ConfigNodes.TOWNBLOCK_SHOP_FILL_COLOUR); 253 | } 254 | 255 | public static String getWildsFillColour() { 256 | return getString(ConfigNodes.TOWNBLOCK_SHOP_FILL_COLOUR); 257 | } 258 | 259 | public static double getFillOpacity() { 260 | return getDouble(ConfigNodes.REGIONSTYLE_FILL_OPACITY); 261 | } 262 | 263 | 264 | public static boolean getBoost() { 265 | return getBoolean(ConfigNodes.REGIONSTYLE_BOOST); 266 | } 267 | 268 | public static String getLayerName() { 269 | return getString(ConfigNodes.LAYER_NAME); 270 | } 271 | 272 | public static int getMinZoom() { 273 | return getInt(ConfigNodes.LAYER_MIN_ZOOM); 274 | } 275 | 276 | public static int getLayerPriority() { 277 | return getInt(ConfigNodes.LAYER_PRIORITY); 278 | } 279 | 280 | public static boolean getLayerHiddenByDefault() { 281 | return getBoolean(ConfigNodes.LAYER_HIDE_BY_DEFAULT); 282 | } 283 | 284 | public static boolean getPlayerVisibilityByTown() { 285 | if (visByTownSet) 286 | return visByTown; 287 | 288 | visByTownSet = true; 289 | if (getBoolean(ConfigNodes.VISIBILITY_BY_TOWN)) { 290 | try { 291 | if (!DynmapTownyPlugin.getPlugin().getDynmapAPI().testIfPlayerInfoProtected()) { 292 | visByTown = false; 293 | DynmapTownyPlugin.getPlugin().getLogger().info("Dynmap does not have player-info-protected enabled - visibility-by-nation will have no effect"); 294 | return visByTown; 295 | } 296 | } catch (NoSuchMethodError x) { 297 | visByTown = false; 298 | DynmapTownyPlugin.getPlugin().getLogger().info("Dynmap does not support function needed for 'visibility-by-nation' - need to upgrade to 0.60 or later"); 299 | return visByTown; 300 | } 301 | visByTown = true; 302 | } 303 | return visByTown; 304 | } 305 | 306 | public static boolean getPlayerVisibilityByNation() { 307 | if (visByNationSet) 308 | return visByNation; 309 | 310 | visByNationSet = true; 311 | if (getBoolean(ConfigNodes.VISIBILITY_BY_NATION)) { 312 | try { 313 | if (!DynmapTownyPlugin.getPlugin().getDynmapAPI().testIfPlayerInfoProtected()) { 314 | visByNation = false; 315 | DynmapTownyPlugin.getPlugin().getLogger().info("Dynmap does not have player-info-protected enabled - visibility-by-nation will have no effect"); 316 | return visByNation; 317 | } 318 | } catch (NoSuchMethodError x) { 319 | visByNation = false; 320 | DynmapTownyPlugin.getPlugin().getLogger().info("Dynmap does not support function needed for 'visibility-by-nation' - need to upgrade to 0.60 or later"); 321 | return visByNation; 322 | } 323 | visByNation = true; 324 | } 325 | return visByNation; 326 | } 327 | 328 | public static boolean usingDynamicNationColours() { 329 | return getBoolean(ConfigNodes.DYNAMIC_COLOURS_NATION); 330 | } 331 | 332 | public static boolean usingDynamicTownColours() { 333 | return getBoolean(ConfigNodes.DYNAMIC_COLOURS_TOWN); 334 | } 335 | 336 | public static int getUpdatePeriod() { 337 | return getInt(ConfigNodes.UPDATE_PERIOD); 338 | } 339 | 340 | public static boolean showingShops() { 341 | return getBoolean(ConfigNodes.TOWNBLOCK_SHOW_SHOPS); 342 | } 343 | 344 | public static boolean showingArenas() { 345 | return getBoolean(ConfigNodes.TOWNBLOCK_SHOW_ARENAS); 346 | } 347 | 348 | public static boolean showingEmbassies() { 349 | return getBoolean(ConfigNodes.TOWNBLOCK_SHOW_EMBASSIES); 350 | } 351 | 352 | public static boolean showingWilds() { 353 | return getBoolean(ConfigNodes.TOWNBLOCK_SHOW_WILDSPLOTS); 354 | } 355 | 356 | public static Set getVisibleRegions() { 357 | if (visibleRegions == null) 358 | visibleRegions = new HashSet<>(getStrArr(ConfigNodes.VISIBLE_ROOT)); 359 | return visibleRegions; 360 | } 361 | 362 | public static boolean visibleRegionsAreSet() { 363 | Set visibleRegions = getVisibleRegions(); 364 | // Pre-CommentedConfiguration configs will have their region set to [], which throws off the results. 365 | return visibleRegions.size() > 0 && !(visibleRegions.size() == 1 && visibleRegions.contains("[]")); 366 | } 367 | 368 | public static Set getHiddenRegions() { 369 | if (hiddenRegions == null) 370 | hiddenRegions = new HashSet<>(getStrArr(ConfigNodes.HIDDEN_ROOT)); 371 | return hiddenRegions; 372 | } 373 | 374 | public static boolean hiddenRegionsAreSet() { 375 | Set hiddenRegions = getHiddenRegions(); 376 | // Pre-CommentedConfiguration configs will have their region set to [], which throws off the results. 377 | return hiddenRegions.size() > 0 && !(hiddenRegions.size() == 1 && hiddenRegions.contains("[]")); 378 | } 379 | } 380 | -------------------------------------------------------------------------------- /src/main/java/org/dynmap/towny/mapupdate/UpdateTowns.java: -------------------------------------------------------------------------------- 1 | package org.dynmap.towny.mapupdate; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.ArrayList; 5 | import java.util.Collection; 6 | import java.util.HashMap; 7 | import java.util.LinkedList; 8 | import java.util.Map; 9 | import java.util.stream.Collectors; 10 | 11 | import org.bukkit.Bukkit; 12 | import org.bukkit.Location; 13 | import org.dynmap.markers.AreaMarker; 14 | import org.dynmap.markers.Marker; 15 | import org.dynmap.markers.MarkerIcon; 16 | import org.dynmap.markers.MarkerSet; 17 | import org.dynmap.towny.DynmapTownyPlugin; 18 | import org.dynmap.towny.events.BuildTownMarkerDescriptionEvent; 19 | import org.dynmap.towny.events.TownRenderEvent; 20 | import org.dynmap.towny.events.TownSetMarkerIconEvent; 21 | import org.dynmap.towny.settings.Settings; 22 | 23 | import com.palmergames.bukkit.towny.TownyAPI; 24 | import com.palmergames.bukkit.towny.TownySettings; 25 | import com.palmergames.bukkit.towny.object.Town; 26 | import com.palmergames.bukkit.towny.object.TownBlock; 27 | import com.palmergames.bukkit.towny.object.TownBlockType; 28 | import com.palmergames.bukkit.towny.object.TownBlockTypeCache.CacheType; 29 | import com.palmergames.bukkit.towny.object.TownyWorld; 30 | import com.palmergames.bukkit.towny.object.Translation; 31 | 32 | public class UpdateTowns implements Runnable { 33 | 34 | private final int TOWNBLOCKSIZE = TownySettings.getTownBlockSize(); 35 | private final DynmapTownyPlugin plugin = DynmapTownyPlugin.getPlugin(); 36 | private final MarkerSet set = plugin.getMarkerSet(); 37 | private Map existingAreaMarkers = plugin.getAreaMarkers(); 38 | private Map existingMarkers = plugin.getMarkers(); 39 | 40 | private final AreaStyle defstyle = AreaStyleHolder.getDefaultStyle(); 41 | private final Map cusstyle = AreaStyleHolder.getCustomStyles(); 42 | private final Map nationstyle = AreaStyleHolder.getNationStyles(); 43 | 44 | enum direction {XPLUS, ZPLUS, XMINUS, ZMINUS}; 45 | 46 | @Override 47 | public void run() { 48 | Map newmap = new HashMap(); /* Build new map */ 49 | Map newmark = new HashMap(); /* Build new map */ 50 | 51 | /* Loop through towns */ 52 | for (Town t : TownyAPI.getInstance().getTowns()) { 53 | try { 54 | handleTown(t, newmap, newmark, null); 55 | if (Settings.showingShops() && townHasTBsOfType(t, TownBlockType.COMMERCIAL)) { 56 | handleTown(t, newmap, newmark, TownBlockType.COMMERCIAL); 57 | } 58 | if (Settings.showingArenas() && townHasTBsOfType(t, TownBlockType.ARENA)) { 59 | handleTown(t, newmap, newmark, TownBlockType.ARENA); 60 | } 61 | if (Settings.showingEmbassies() && townHasTBsOfType(t, TownBlockType.EMBASSY)) { 62 | handleTown(t, newmap, newmark, TownBlockType.EMBASSY); 63 | } 64 | if (Settings.showingWilds() && townHasTBsOfType(t, TownBlockType.WILDS)) { 65 | handleTown(t, newmap, newmark, TownBlockType.WILDS); 66 | } 67 | } catch (Exception e) { 68 | plugin.getLogger().info(e.getMessage()); 69 | } 70 | } 71 | /* Now, review old maps - anything left is removed */ 72 | existingAreaMarkers.values().forEach(a -> a.deleteMarker()); 73 | existingMarkers.values().forEach(m -> m.deleteMarker()); 74 | 75 | /* And replace with new map */ 76 | existingAreaMarkers = newmap; 77 | existingMarkers = newmark; 78 | } 79 | 80 | private boolean townHasTBsOfType(Town t, TownBlockType tbType) { 81 | return t.getTownBlockTypeCache().getNumTownBlocks(tbType, CacheType.ALL) > 0; 82 | } 83 | 84 | /* Handle specific town */ 85 | private void handleTown(Town town, Map newWorldNameAreaMarkerMap, Map newWorldNameMarkerMap, TownBlockType btype) throws Exception { 86 | String townName = town.getName(); 87 | int poly_index = 0; /* Index of polygon for when a town has multiple shapes. */ 88 | 89 | /* Get the Town's Townblocks or the Towns' Townblocks of one TownBlockType. */ 90 | Collection townBlocks = filterTownBlocks(town, btype); 91 | if (townBlocks.isEmpty()) 92 | return; 93 | 94 | /* Build popup */ 95 | BuildTownMarkerDescriptionEvent event = new BuildTownMarkerDescriptionEvent(town); 96 | Bukkit.getPluginManager().callEvent(event); 97 | String infoWindowPopup = event.getDescription(); 98 | 99 | HashMap worldNameShapeMap = new HashMap(); 100 | LinkedList townBlocksToDraw = new LinkedList(); 101 | TownyWorld currentWorld = null; 102 | TileFlags currentShape = null; 103 | boolean vis = false; 104 | /* Loop through blocks: set flags on blockmaps for worlds */ 105 | for(TownBlock townBlock : townBlocks) { 106 | if(townBlock.getWorld() != currentWorld) { /* Not same world */ 107 | String worldName = townBlock.getWorld().getName(); 108 | vis = isVisible(townName, worldName); /* See if visible */ 109 | if(vis) { /* Only accumulate for visible areas */ 110 | currentShape = worldNameShapeMap.get(worldName); /* Find existing */ 111 | if(currentShape == null) { 112 | currentShape = new TileFlags(); 113 | worldNameShapeMap.put(worldName, currentShape); /* Add fresh one */ 114 | } 115 | } 116 | currentWorld = townBlock.getWorld(); 117 | } 118 | if(vis) { 119 | currentShape.setFlag(townBlock.getX(), townBlock.getZ(), true); /* Set flag for block */ 120 | townBlocksToDraw.addLast(townBlock); 121 | } 122 | } 123 | /* Loop through until we don't find more areas */ 124 | while(townBlocksToDraw != null) { 125 | LinkedList ourTownBlocks = null; 126 | LinkedList townBlockLeftToDraw = null; 127 | TileFlags ourShape = null; 128 | int minx = Integer.MAX_VALUE; 129 | int minz = Integer.MAX_VALUE; 130 | for(TownBlock tb : townBlocksToDraw) { 131 | int tbX = tb.getX(); 132 | int tbZ = tb.getZ(); 133 | if(ourShape == null) { /* If not started, switch to world for this block first */ 134 | if(tb.getWorld() != currentWorld) { 135 | currentWorld = tb.getWorld(); 136 | currentShape = worldNameShapeMap.get(currentWorld.getName()); 137 | } 138 | } 139 | /* If we need to start shape, and this block is not part of one yet */ 140 | if((ourShape == null) && currentShape.getFlag(tbX, tbZ)) { 141 | ourShape = new TileFlags(); /* Create map for shape */ 142 | ourTownBlocks = new LinkedList(); 143 | floodFillTarget(currentShape, ourShape, tbX, tbZ); /* Copy shape */ 144 | ourTownBlocks.add(tb); /* Add it to our node list */ 145 | minx = tbX; minz = tbZ; 146 | } 147 | /* If shape found, and we're in it, add to our node list */ 148 | else if((ourShape != null) && (tb.getWorld() == currentWorld) && 149 | (ourShape.getFlag(tbX, tbZ))) { 150 | ourTownBlocks.add(tb); 151 | if(tbX < minx) { 152 | minx = tbX; minz = tbZ; 153 | } 154 | else if((tbX == minx) && (tbZ < minz)) { 155 | minz = tbZ; 156 | } 157 | } 158 | else { /* Else, keep it in the list for the next polygon */ 159 | if(townBlockLeftToDraw == null) 160 | townBlockLeftToDraw = new LinkedList(); 161 | townBlockLeftToDraw.add(tb); 162 | } 163 | } 164 | townBlocksToDraw = townBlockLeftToDraw; /* Replace list (null if no more to process) */ 165 | if(ourShape != null) { 166 | poly_index = traceTownOutline(town, newWorldNameAreaMarkerMap, btype, poly_index, infoWindowPopup, currentWorld.getName(), ourShape, minx, minz); 167 | } 168 | } 169 | 170 | /* We're showing only specific TownBlockTypes on this pass, dont render townspawns and outposts.*/ 171 | if (btype != null) 172 | return; 173 | 174 | drawTownMarkers(town, newWorldNameMarkerMap, townName, infoWindowPopup); 175 | } 176 | 177 | private Collection filterTownBlocks(Town town, TownBlockType btype) { 178 | if (btype == null) 179 | return town.getTownBlocks(); 180 | return town.getTownBlocks().stream() 181 | .filter(tb -> tb.getTypeName().equalsIgnoreCase(btype.getName())) 182 | .collect(Collectors.toList()); 183 | } 184 | 185 | private int traceTownOutline(Town town, Map newWorldNameMarkerMap, TownBlockType btype, int poly_index, 186 | String infoWindowPopup, String worldName, TileFlags ourShape, int minx, int minz) throws Exception { 187 | 188 | double[] x; 189 | double[] z; 190 | /* Trace outline of blocks - start from minx, minz going to x+ */ 191 | int init_x = minx; 192 | int init_z = minz; 193 | int cur_x = minx; 194 | int cur_z = minz; 195 | direction dir = direction.XPLUS; 196 | ArrayList linelist = new ArrayList(); 197 | linelist.add(new int[] { init_x, init_z } ); // Add start point 198 | while((cur_x != init_x) || (cur_z != init_z) || (dir != direction.ZMINUS)) { 199 | switch(dir) { 200 | case XPLUS: /* Segment in X+ direction */ 201 | if(!ourShape.getFlag(cur_x+1, cur_z)) { /* Right turn? */ 202 | linelist.add(new int[] { cur_x+1, cur_z }); /* Finish line */ 203 | dir = direction.ZPLUS; /* Change direction */ 204 | } 205 | else if(!ourShape.getFlag(cur_x+1, cur_z-1)) { /* Straight? */ 206 | cur_x++; 207 | } 208 | else { /* Left turn */ 209 | linelist.add(new int[] { cur_x+1, cur_z }); /* Finish line */ 210 | dir = direction.ZMINUS; 211 | cur_x++; cur_z--; 212 | } 213 | break; 214 | case ZPLUS: /* Segment in Z+ direction */ 215 | if(!ourShape.getFlag(cur_x, cur_z+1)) { /* Right turn? */ 216 | linelist.add(new int[] { cur_x+1, cur_z+1 }); /* Finish line */ 217 | dir = direction.XMINUS; /* Change direction */ 218 | } 219 | else if(!ourShape.getFlag(cur_x+1, cur_z+1)) { /* Straight? */ 220 | cur_z++; 221 | } 222 | else { /* Left turn */ 223 | linelist.add(new int[] { cur_x+1, cur_z+1 }); /* Finish line */ 224 | dir = direction.XPLUS; 225 | cur_x++; cur_z++; 226 | } 227 | break; 228 | case XMINUS: /* Segment in X- direction */ 229 | if(!ourShape.getFlag(cur_x-1, cur_z)) { /* Right turn? */ 230 | linelist.add(new int[] { cur_x, cur_z+1 }); /* Finish line */ 231 | dir = direction.ZMINUS; /* Change direction */ 232 | } 233 | else if(!ourShape.getFlag(cur_x-1, cur_z+1)) { /* Straight? */ 234 | cur_x--; 235 | } 236 | else { /* Left turn */ 237 | linelist.add(new int[] { cur_x, cur_z+1 }); /* Finish line */ 238 | dir = direction.ZPLUS; 239 | cur_x--; cur_z++; 240 | } 241 | break; 242 | case ZMINUS: /* Segment in Z- direction */ 243 | if(!ourShape.getFlag(cur_x, cur_z-1)) { /* Right turn? */ 244 | linelist.add(new int[] { cur_x, cur_z }); /* Finish line */ 245 | dir = direction.XPLUS; /* Change direction */ 246 | } 247 | else if(!ourShape.getFlag(cur_x-1, cur_z-1)) { /* Straight? */ 248 | cur_z--; 249 | } 250 | else { /* Left turn */ 251 | linelist.add(new int[] { cur_x, cur_z }); /* Finish line */ 252 | dir = direction.XMINUS; 253 | cur_x--; cur_z--; 254 | } 255 | break; 256 | } 257 | } 258 | /* Build information for specific area */ 259 | String polyid = town.getName() + "__" + poly_index; 260 | if(btype != null) { 261 | polyid += "_" + btype.getName(); 262 | } 263 | int sz = linelist.size(); 264 | x = new double[sz]; 265 | z = new double[sz]; 266 | for(int i = 0; i < sz; i++) { 267 | int[] line = linelist.get(i); 268 | x[i] = (double)line[0] * (double)TOWNBLOCKSIZE; 269 | z[i] = (double)line[1] * (double)TOWNBLOCKSIZE; 270 | } 271 | /* Find existing one */ 272 | AreaMarker areaMarker = existingAreaMarkers.remove(polyid); /* Existing area? */ 273 | if(areaMarker == null) { 274 | areaMarker = set.createAreaMarker(polyid, town.getName(), false, worldName, x, z, false); 275 | if(areaMarker == null) { 276 | areaMarker = set.findAreaMarker(polyid); 277 | if (areaMarker == null) { 278 | throw new Exception("Error adding area marker " + polyid); 279 | } 280 | } 281 | } 282 | else { 283 | areaMarker.setCornerLocations(x, z); /* Replace corner locations */ 284 | areaMarker.setLabel(town.getName()); /* Update label */ 285 | } 286 | /* Set popup */ 287 | areaMarker.setDescription(infoWindowPopup); 288 | /* Set line and fill properties */ 289 | addStyle(town, areaMarker, btype); 290 | 291 | /* Fire an event allowing other plugins to alter the AreaMarker */ 292 | TownRenderEvent renderEvent = new TownRenderEvent(town, areaMarker); 293 | Bukkit.getPluginManager().callEvent(renderEvent); 294 | areaMarker = renderEvent.getAreaMarker(); 295 | 296 | /* Add to map */ 297 | newWorldNameMarkerMap.put(polyid, areaMarker); 298 | poly_index++; 299 | return poly_index; 300 | } 301 | 302 | /** 303 | * Find all contiguous blocks, set in target and clear in source 304 | */ 305 | private static int floodFillTarget(TileFlags src, TileFlags dest, int x, int y) { 306 | int cnt = 0; 307 | ArrayDeque stack = new ArrayDeque(); 308 | stack.push(new int[] { x, y }); 309 | 310 | while (stack.isEmpty() == false) { 311 | int[] nxt = stack.pop(); 312 | x = nxt[0]; 313 | y = nxt[1]; 314 | if (src.getFlag(x, y)) { /* Set in src */ 315 | src.setFlag(x, y, false); /* Clear source */ 316 | dest.setFlag(x, y, true); /* Set in destination */ 317 | cnt++; 318 | if (src.getFlag(x + 1, y)) 319 | stack.push(new int[] { x + 1, y }); 320 | if (src.getFlag(x - 1, y)) 321 | stack.push(new int[] { x - 1, y }); 322 | if (src.getFlag(x, y + 1)) 323 | stack.push(new int[] { x, y + 1 }); 324 | if (src.getFlag(x, y - 1)) 325 | stack.push(new int[] { x, y - 1 }); 326 | } 327 | } 328 | return cnt; 329 | } 330 | 331 | private static boolean isVisible(String id, String worldname) { 332 | 333 | if (Settings.visibleRegionsAreSet() && 334 | (Settings.getVisibleRegions().contains("world:" + worldname) == false || Settings.getVisibleRegions().contains(id) == false)) 335 | return false; 336 | 337 | if (Settings.hiddenRegionsAreSet() && 338 | (Settings.getHiddenRegions().contains(id) || Settings.getHiddenRegions().contains("world:" + worldname))) 339 | return false; 340 | return true; 341 | } 342 | 343 | private void addStyle(Town town, AreaMarker m, TownBlockType btype) { 344 | AreaStyle as = cusstyle.get(town.getName()); /* Look up custom style for town, if any */ 345 | AreaStyle ns = nationstyle.get(getNationNameOrNone(town)); /* Look up nation style, if any */ 346 | 347 | if(btype == null) { 348 | m.setLineStyle(defstyle.getStrokeWeight(as, ns), defstyle.getStrokeOpacity(as, ns), defstyle.getStrokeColor(as, ns)); 349 | } 350 | else { 351 | m.setLineStyle(1, 0, 0); 352 | } 353 | m.setFillStyle(defstyle.getFillOpacity(as, ns), defstyle.getFillColor(as, ns, btype)); 354 | double y = defstyle.getY(as, ns); 355 | m.setRangeY(y, y); 356 | m.setBoostFlag(defstyle.getBoost(as, ns)); 357 | 358 | // We're dealing with something that has a custom AreaStyle applied via the 359 | // custstyle or nationstyle in the config.yml, do not apply dynamic colours. 360 | if (as != null || (ns != null && ns.fillcolor > -1 && ns.strokecolor > -1)) 361 | return; 362 | 363 | //Read dynamic colors from town/nation objects 364 | if(Settings.usingDynamicTownColours() || Settings.usingDynamicNationColours()) { 365 | try { 366 | String colorHexCode; 367 | Integer townFillColorInteger = null; 368 | Integer townBorderColorInteger = null; 369 | 370 | //CALCULATE FILL COLOUR 371 | if (Settings.usingDynamicTownColours()) { 372 | //Here we know the server is using town colors. This takes top priority for fill 373 | colorHexCode = town.getMapColorHexCode(); 374 | if(!colorHexCode.isEmpty()) { 375 | //If town has a color, use it 376 | townFillColorInteger = Integer.parseInt(colorHexCode, 16); 377 | townBorderColorInteger = townFillColorInteger; 378 | } 379 | } else { 380 | //Here we know the server is using nation colors 381 | colorHexCode = town.getNationMapColorHexCode(); 382 | if(colorHexCode != null && !colorHexCode.isEmpty()) { 383 | //If nation has a color, use it 384 | townFillColorInteger = Integer.parseInt(colorHexCode, 16); 385 | } 386 | } 387 | 388 | //CALCULATE BORDER COLOR 389 | if (Settings.usingDynamicNationColours()) { 390 | //Here we know the server is using nation colors. This takes top priority for border 391 | colorHexCode = town.getNationMapColorHexCode(); 392 | if(colorHexCode != null && !colorHexCode.isEmpty()) { 393 | //If nation has a color, use it 394 | townBorderColorInteger = Integer.parseInt(colorHexCode, 16); 395 | } 396 | } else { 397 | //Here we know the server is using town colors 398 | colorHexCode = town.getMapColorHexCode(); 399 | if(!colorHexCode.isEmpty()) { 400 | //If town has a color, use it 401 | townBorderColorInteger = Integer.parseInt(colorHexCode, 16); 402 | } 403 | } 404 | 405 | //SET FILL COLOR 406 | if(townFillColorInteger != null) { 407 | //Set fill style 408 | double fillOpacity = m.getFillOpacity(); 409 | //Allow special fills for some townblock types 410 | int townblockcolor = defstyle.getFillColor(btype); 411 | m.setFillStyle(fillOpacity, townblockcolor >= 0 ? townblockcolor : townFillColorInteger); 412 | } 413 | 414 | //SET BORDER COLOR 415 | if(townBorderColorInteger != null) { 416 | //Set stroke style 417 | double strokeOpacity = m.getLineOpacity(); 418 | int strokeWeight = m.getLineWeight(); 419 | m.setLineStyle(strokeWeight, strokeOpacity, townBorderColorInteger); 420 | } 421 | 422 | } catch (Exception ex) {} 423 | } 424 | } 425 | 426 | 427 | /* 428 | * Town Marker Drawing Methods 429 | */ 430 | 431 | private void drawTownMarkers(Town town, Map newWorldNameMarkerMap, String townName, String desc) { 432 | /* Now, add marker for home block */ 433 | TownBlock homeBlock = town.getHomeBlockOrNull(); 434 | 435 | if (homeBlock != null && isVisible(townName, homeBlock.getWorld().getName())) { 436 | MarkerIcon townHomeBlockIcon = getMarkerIcon(town); 437 | 438 | /* Fire an event allowing other plugins to alter the MarkerIcon */ 439 | TownSetMarkerIconEvent iconEvent = new TownSetMarkerIconEvent(town, townHomeBlockIcon); 440 | Bukkit.getPluginManager().callEvent(iconEvent); 441 | townHomeBlockIcon = iconEvent.getIcon(); 442 | 443 | if (townHomeBlockIcon != null) 444 | drawHomeBlockSpawn(newWorldNameMarkerMap, townName, desc, homeBlock, townHomeBlockIcon); 445 | } 446 | 447 | if (town.hasOutpostSpawn() && Settings.getOutpostIcon() != null) 448 | drawOutpostIcons(town, newWorldNameMarkerMap, desc); 449 | } 450 | 451 | private MarkerIcon getMarkerIcon(Town town) { 452 | if (town.isRuined()) 453 | return defstyle.getRuinIcon();; 454 | 455 | AreaStyle as = cusstyle.get(town.getName()); 456 | AreaStyle ns = nationstyle.get(getNationNameOrNone(town)); 457 | 458 | return town.isCapital() ? defstyle.getCapitalMarker(as, ns) : defstyle.getHomeMarker(as, ns); 459 | } 460 | 461 | private String getNationNameOrNone(Town town) { 462 | return town.hasNation() ? town.getNationOrNull().getName() : "_none_"; 463 | } 464 | 465 | private void drawHomeBlockSpawn(Map newWorldNameMarkerMap, String townName, String desc, TownBlock townBlock, MarkerIcon ico) { 466 | String markid = townName + "__home"; 467 | Marker home = existingMarkers.remove(markid); 468 | double xx = TOWNBLOCKSIZE * townBlock.getX() + (TOWNBLOCKSIZE / 2); 469 | double zz = TOWNBLOCKSIZE * townBlock.getZ() + (TOWNBLOCKSIZE / 2); 470 | if(home == null) { 471 | home = set.createMarker(markid, townName, townBlock.getWorld().getName(), xx, 64, zz, ico, false); 472 | if (home == null) 473 | return; 474 | } else { 475 | home.setLocation(townBlock.getWorld().getName(), xx, 64, zz); 476 | home.setLabel(townName); /* Update label */ 477 | home.setMarkerIcon(ico); 478 | } 479 | /* Set popup */ 480 | home.setDescription(desc); 481 | 482 | newWorldNameMarkerMap.put(markid, home); 483 | } 484 | 485 | private void drawOutpostIcons(Town town, Map newWorldNameMarkerMap, String desc) { 486 | AreaStyle as = cusstyle.get(town.getName()); 487 | AreaStyle ns = nationstyle.get(getNationNameOrNone(town)); 488 | MarkerIcon outpostIco = defstyle.getOutpostMarker(as, ns); 489 | 490 | int i = 0; 491 | for (Location loc : town.getAllOutpostSpawns()) { 492 | i++; 493 | TownBlock townBlock = TownyAPI.getInstance().getTownBlock(loc); 494 | if (townBlock == null || !isVisible(town.getName(), townBlock.getWorld().getName())) 495 | continue; 496 | 497 | double xx = TOWNBLOCKSIZE * townBlock.getX() + (TOWNBLOCKSIZE / 2); 498 | double zz = TOWNBLOCKSIZE * townBlock.getZ() + (TOWNBLOCKSIZE / 2); 499 | String outpostMarkerID = town.getName() + "_Outpost_" + i; 500 | String outpostName = "%s %s %s".formatted(town.getName(), 501 | Translation.of("outpost"), 502 | townBlock.getFormattedName().isEmpty() ? i : townBlock.getFormattedName()); 503 | Marker outpostMarker = existingMarkers.remove(outpostMarkerID); 504 | if (outpostMarker == null) { 505 | outpostMarker = set.createMarker(outpostMarkerID, outpostName, townBlock.getWorld().getName(), xx, 64, zz, outpostIco, true); 506 | if (outpostMarker == null) 507 | continue; 508 | } else { 509 | outpostMarker.setLocation(townBlock.getWorld().getName(), xx, 64, zz); 510 | outpostMarker.setLabel(outpostName); 511 | outpostMarker.setMarkerIcon(outpostIco); 512 | } 513 | outpostMarker.setDescription(desc); 514 | newWorldNameMarkerMap.put(outpostMarkerID, outpostMarker); 515 | } 516 | } 517 | 518 | } 519 | --------------------------------------------------------------------------------