public boolean useNewMoneySystem = false;
+ // -------------------------------------------- //
+ // INTEGRATION: DYNMAP
+ // -------------------------------------------- //
+
+ // Should the dynmap intagration be used?
+ public boolean dynmapEnabled = true;
+
+ // Should the dynmap updates be logged to console output?
+ public boolean dynmapLogTimeSpent = false;
+
+ // Name of the Factions layer
+ public String dynmapLayerName = "Factions";
+
+ // Should the layer be visible per default
+ public boolean dynmapLayerHiddenByDefault = false;
+
+ // Ordering priority in layer menu (low goes before high - default is 0)
+ public int dynmapLayerPriority = 2;
+
+ // (optional) set minimum zoom level before layer is visible (0 = defalt, always visible)
+ public int dynmapLayerMinimumZoom = 0;
+
+ // Format for popup - substitute values for macros
+ //public String dynmapInfowindowFormat = "%regionname%
Flags
%flags%
";
+ public String dynmapFactionDescription =
+ "\n" +
+ "%name%\n" +
+ "%description%\n" +
+ "\n" +
+ "Leader: %players.leader%\n" +
+ "Members: %players%\n" +
+ "\n" +
+ "Age: %age%\n" +
+ "Bank: %money%\n" +
+ "\n" +
+ "Flags:\n" +
+ "%flags.table3%\n" +
+ "
";
+
+ // Enable the %money% macro. Only do this if you know your economy manager is thread safe.
+ public boolean dynmapShowMoneyInDescription = false;
+
+ // Allow players in faction to see one another on Dynmap (only relevant if Dynmap has 'player-info-protected' enabled)
+ //public boolean dynmapVisibilityByFaction = true;
+
+ // Optional setting to limit which regions to show.
+ // If empty all regions are shown.
+ // Specify Faction either by name or UUID.
+ // To show all regions on a given world, add 'world:' to the list.
+ public Set dynmapVisibleFactions = new MassiveSet<>();
+
+ // Optional setting to hide specific Factions.
+ // Specify Faction either by name or UUID.
+ // To hide all regions on a given world, add 'world:' to the list.
+ public Set dynmapHiddenFactions = new MassiveSet<>();
+
+ @EditorVisible(false)
+ public DynmapStyle dynmapDefaultStyle = new DynmapStyle(
+ IntegrationDynmap.DYNMAP_STYLE_LINE_COLOR,
+ IntegrationDynmap.DYNMAP_STYLE_LINE_OPACITY,
+ IntegrationDynmap.DYNMAP_STYLE_LINE_WEIGHT,
+ IntegrationDynmap.DYNMAP_STYLE_FILL_COLOR,
+ IntegrationDynmap.DYNMAP_STYLE_FILL_OPACITY,
+ IntegrationDynmap.DYNMAP_STYLE_HOME_MARKER,
+ IntegrationDynmap.DYNMAP_STYLE_BOOST
+ );
+
+ // Optional per Faction style overrides. Any defined replace those in dynmapDefaultStyle.
+ // Specify Faction either by name or UUID.
+ @EditorVisible(false)
+ public Map dynmapFactionStyles = MUtil.map(
+ "SafeZone", new DynmapStyle().withLineColor("#FF00FF").withFillColor("#FF00FF").withBoost(false),
+ "WarZone", new DynmapStyle().withLineColor("#FF0000").withFillColor("#FF0000").withBoost(false)
+ );
}
diff --git a/src/com/massivecraft/factions/integration/dynmap/AreaMarkerValues.java b/src/com/massivecraft/factions/integration/dynmap/AreaMarkerValues.java
new file mode 100644
index 00000000..b85df912
--- /dev/null
+++ b/src/com/massivecraft/factions/integration/dynmap/AreaMarkerValues.java
@@ -0,0 +1,211 @@
+package com.massivecraft.factions.integration.dynmap;
+
+import com.massivecraft.massivecore.util.MUtil;
+import org.dynmap.markers.AreaMarker;
+import org.dynmap.markers.MarkerAPI;
+import org.dynmap.markers.MarkerSet;
+
+public class AreaMarkerValues
+{
+ // -------------------------------------------- //
+ // FIELDS
+ // -------------------------------------------- //
+
+ private final String label;
+ public String getLabel() { return label; }
+ public AreaMarkerValues withLabel(String label) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final String world;
+ public String getWorld() { return world; }
+ public AreaMarkerValues withWorld(String world) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final double[] x;
+ public double[] getX() { return x; }
+ public AreaMarkerValues withX(double[] x) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final double[] z;
+ public double[] getZ() { return z; }
+ public AreaMarkerValues withZ(double[] z) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final String description;
+ public String getDescription() { return description; }
+ public AreaMarkerValues withDescription(String description) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final int lineColor;
+ public int getLineColor() { return lineColor; }
+ public AreaMarkerValues withLineColor(int lineColor) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final double lineOpacity;
+ public double getLineOpacity() { return lineOpacity; }
+ public AreaMarkerValues withLineOpacity(double lineOpacity) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final int lineWeight;
+ public int getLineWeight() { return lineWeight; }
+ public AreaMarkerValues withLineWright(int lineWeight) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final int fillColor;
+ public int getFillColor() { return fillColor; }
+ public AreaMarkerValues withFillColor(int fillColor) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final double fillOpacity;
+ public double getFillOpacity() { return fillOpacity; }
+ public AreaMarkerValues withFillOpacity(double fillOpacity) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ private final boolean boost;
+ public boolean isBoost() { return boost; }
+ public AreaMarkerValues withBoost(boolean boost) { return new AreaMarkerValues(label, world, x, z, description, lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, boost); }
+
+ public AreaMarkerValues withStyle(DynmapStyle style)
+ {
+ return new AreaMarkerValues(label, world, x, z, description, style);
+ }
+
+ // -------------------------------------------- //
+ // CONSTRUCTOR
+ // -------------------------------------------- //
+
+ public AreaMarkerValues(String label, String world, double[] x, double[] z, String description, DynmapStyle style)
+ {
+ this(label, world, x, z, description, style.getLineColor(), style.getLineOpacity(), style.getLineWeight(), style.getFillColor(), style.getFillOpacity(), style.getBoost());
+ }
+
+ public AreaMarkerValues(String label, String world, double[] x, double[] z, String description, int lineColor, double lineOpacity, int lineWeight, int fillColor, double fillOpacity, boolean boost)
+ {
+ this.label = label;
+ this.world = world;
+ this.x = x;
+ this.z = z;
+ this.description = description;
+ this.lineColor = lineColor;
+ this.lineOpacity = lineOpacity;
+ this.lineWeight = lineWeight;
+ this.fillColor = fillColor;
+ this.fillOpacity = fillOpacity;
+ this.boost = boost;
+ }
+
+ // -------------------------------------------- //
+ // MASTER
+ // -------------------------------------------- //
+
+ public AreaMarker ensureExistsAndUpdated(AreaMarker areaMarker, MarkerAPI markerApi, MarkerSet markerset, String markerId)
+ {
+ // NOTE: I remove from the map created just in the beginning of this method.
+ // NOTE: That way what is left at the end will be outdated markers to remove.
+ if (areaMarker == null)
+ {
+ areaMarker = create(markerApi, markerset, markerId);
+ }
+ else
+ {
+ update(markerApi, markerset, areaMarker);
+ }
+
+ if (areaMarker == null)
+ {
+ EngineDynmap.logSevere("Could not get/create the area marker " + markerId);
+ }
+
+ return areaMarker;
+ }
+
+ // -------------------------------------------- //
+ // CREATE
+ // -------------------------------------------- //
+
+ public AreaMarker create(MarkerAPI markerApi, MarkerSet markerset, String markerId)
+ {
+ AreaMarker ret = markerset.createAreaMarker(
+ markerId,
+ this.getLabel(),
+ false,
+ this.getWorld(),
+ this.getX(),
+ this.getZ(),
+ false // not persistent
+ );
+
+ if (ret == null) return null;
+
+ // Description
+ ret.setDescription(this.getDescription());
+
+ // Line Style
+ ret.setLineStyle(this.getLineWeight(), this.getLineOpacity(), this.getLineColor());
+
+ // Fill Style
+ ret.setFillStyle(this.getFillOpacity(), this.getFillColor());
+
+ // Boost Flag
+ ret.setBoostFlag(this.isBoost());
+
+ return ret;
+ }
+
+ // -------------------------------------------- //
+ // UPDATE
+ // -------------------------------------------- //
+
+ public void update(MarkerAPI markerApi, MarkerSet markerset, AreaMarker marker)
+ {
+ // Corner Locations
+ if (!equals(marker, this.x, this.z))
+ {
+ marker.setCornerLocations(this.x, this.z);
+ }
+
+ // Label
+ MUtil.setIfDifferent(this.getLabel(), marker::getLabel, marker::setLabel);
+
+ // Description
+ MUtil.setIfDifferent(this.getDescription(), marker::getDescription, marker::setDescription);
+
+ // Line Style
+ if
+ (
+ !MUtil.equals(marker.getLineWeight(), this.lineWeight)
+ ||
+ !MUtil.equals(marker.getLineOpacity(), this.lineOpacity)
+ ||
+ !MUtil.equals(marker.getLineColor(), this.lineColor)
+ )
+ {
+ marker.setLineStyle(this.lineWeight, this.lineOpacity, this.lineColor);
+ }
+
+ // Fill Style
+ if
+ (
+ !MUtil.equals(marker.getFillOpacity(), this.fillOpacity)
+ ||
+ !MUtil.equals(marker.getFillColor(), this.fillColor)
+ )
+ {
+ marker.setFillStyle(this.fillOpacity, this.fillColor);
+ }
+
+ // Boost Flag
+ MUtil.setIfDifferent(this.isBoost(), marker::getBoostFlag, marker::setBoostFlag);
+ }
+
+ // -------------------------------------------- //
+ // UTIL
+ // -------------------------------------------- //
+
+ public static boolean equals(AreaMarker marker, double[] x, double[] z)
+ {
+ int length = marker.getCornerCount();
+
+ if (x.length != length) return false;
+ if (z.length != length) return false;
+
+ for (int i = 0; i < length; i++)
+ {
+ if (marker.getCornerX(i) != x[i]) return false;
+ if (marker.getCornerZ(i) != z[i]) return false;
+ }
+
+ return true;
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/massivecraft/factions/integration/dynmap/DynmapStyle.java b/src/com/massivecraft/factions/integration/dynmap/DynmapStyle.java
new file mode 100644
index 00000000..743224c0
--- /dev/null
+++ b/src/com/massivecraft/factions/integration/dynmap/DynmapStyle.java
@@ -0,0 +1,90 @@
+package com.massivecraft.factions.integration.dynmap;
+
+import com.massivecraft.factions.entity.MConf;
+
+public class DynmapStyle
+{
+ // -------------------------------------------- //
+ // FIELDS
+ // -------------------------------------------- //
+
+ public final String lineColor;
+ public int getLineColor() { return getColor(coalesce(this.lineColor, MConf.get().dynmapDefaultStyle.lineColor, IntegrationDynmap.DYNMAP_STYLE_LINE_COLOR)); }
+ public DynmapStyle withLineColor(String lineColor) { return new DynmapStyle(lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, homeMarker, boost); }
+
+ public final Double lineOpacity;
+ public double getLineOpacity() { return coalesce(this.lineOpacity, MConf.get().dynmapDefaultStyle.lineOpacity, IntegrationDynmap.DYNMAP_STYLE_LINE_OPACITY); }
+ public DynmapStyle withLineOpacity(Double lineOpacity) { return new DynmapStyle(lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, homeMarker, boost); }
+
+ public final Integer lineWeight;
+ public int getLineWeight() { return coalesce(this.lineWeight, MConf.get().dynmapDefaultStyle.lineWeight, IntegrationDynmap.DYNMAP_STYLE_LINE_WEIGHT); }
+ public DynmapStyle withLineWeight(Integer lineWeight) { return new DynmapStyle(lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, homeMarker, boost); }
+
+ public final String fillColor;
+ public int getFillColor() { return getColor(coalesce(this.fillColor, MConf.get().dynmapDefaultStyle.fillColor, IntegrationDynmap.DYNMAP_STYLE_FILL_COLOR)); }
+ public DynmapStyle withFillColor(String fillColor) { return new DynmapStyle(lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, homeMarker, boost); }
+
+ public final Double fillOpacity;
+ public double getFillOpacity() { return coalesce(this.fillOpacity, MConf.get().dynmapDefaultStyle.fillOpacity, IntegrationDynmap.DYNMAP_STYLE_FILL_OPACITY); }
+ public DynmapStyle withFillOpacity(Double fillOpacity) { return new DynmapStyle(lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, homeMarker, boost); }
+
+ // NOTE: We just return the string here. We do not return the resolved Dynmap MarkerIcon object.
+ // The reason is we use this class in the MConf. For serialization to work Dynmap would have to be loaded and we can't require that.
+ // Using dynmap is optional.
+ public final String homeMarker;
+ public String getHomeMarker() { return coalesce(this.homeMarker, MConf.get().dynmapDefaultStyle.homeMarker, IntegrationDynmap.DYNMAP_STYLE_HOME_MARKER); }
+ public DynmapStyle withHomeMarker(String homeMarker) { return new DynmapStyle(lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, homeMarker, boost); }
+
+ public final Boolean boost;
+ public boolean getBoost() { return coalesce(this.boost, MConf.get().dynmapDefaultStyle.boost, IntegrationDynmap.DYNMAP_STYLE_BOOST); }
+ public DynmapStyle withBoost(Boolean boost) { return new DynmapStyle(lineColor, lineOpacity, lineWeight, fillColor, fillOpacity, homeMarker, boost); }
+
+ // -------------------------------------------- //
+ // CONSTRUCTOR
+ // -------------------------------------------- //
+
+ public DynmapStyle()
+ {
+ this(null, null, null, null, null, null, null);
+ }
+
+ public DynmapStyle(String lineColor, Double lineOpacity, Integer lineWeight, String fillColor, Double fillOpacity, String homeMarker, Boolean boost)
+ {
+ this.lineColor = lineColor;
+ this.lineOpacity = lineOpacity;
+ this.lineWeight = lineWeight;
+ this.fillColor = fillColor;
+ this.fillOpacity = fillOpacity;
+ this.homeMarker = homeMarker;
+ this.boost = boost;
+ }
+
+ // -------------------------------------------- //
+ // UTIL
+ // -------------------------------------------- //
+
+ @SafeVarargs
+ public static T coalesce(T... items)
+ {
+ for (T item : items)
+ {
+ if (item != null) return item;
+ }
+ return null;
+ }
+
+ public static int getColor(String string)
+ {
+ int ret = 0x00FF00;
+ try
+ {
+ ret = Integer.parseInt(string.substring(1), 16);
+ }
+ catch (NumberFormatException nfx)
+ {
+
+ }
+ return ret;
+ }
+
+}
\ No newline at end of file
diff --git a/src/com/massivecraft/factions/integration/dynmap/EngineDynmap.java b/src/com/massivecraft/factions/integration/dynmap/EngineDynmap.java
new file mode 100644
index 00000000..47e41f5c
--- /dev/null
+++ b/src/com/massivecraft/factions/integration/dynmap/EngineDynmap.java
@@ -0,0 +1,691 @@
+package com.massivecraft.factions.integration.dynmap;
+
+import com.massivecraft.factions.Factions;
+import com.massivecraft.factions.entity.BoardColl;
+import com.massivecraft.factions.entity.Faction;
+import com.massivecraft.factions.entity.MConf;
+import com.massivecraft.factions.entity.MFlag;
+import com.massivecraft.factions.entity.MPlayer;
+import com.massivecraft.factions.integration.Econ;
+import com.massivecraft.massivecore.Engine;
+import com.massivecraft.massivecore.collections.MassiveList;
+import com.massivecraft.massivecore.collections.MassiveMap;
+import com.massivecraft.massivecore.collections.MassiveSet;
+import com.massivecraft.massivecore.money.Money;
+import com.massivecraft.massivecore.ps.PS;
+import com.massivecraft.massivecore.util.MUtil;
+import com.massivecraft.massivecore.util.TimeDiffUtil;
+import com.massivecraft.massivecore.util.TimeUnit;
+import com.massivecraft.massivecore.util.Txt;
+import org.apache.commons.lang.StringEscapeUtils;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.dynmap.DynmapAPI;
+import org.dynmap.markers.AreaMarker;
+import org.dynmap.markers.MarkerAPI;
+import org.dynmap.markers.MarkerSet;
+
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+public class EngineDynmap extends Engine
+{
+ // -------------------------------------------- //
+ // INSTANCE & CONSTRUCT
+ // -------------------------------------------- //
+
+ private static EngineDynmap i = new EngineDynmap();
+ public static EngineDynmap get() { return i; }
+ private EngineDynmap()
+ {
+ // Async
+ this.setSync(false);
+
+ // Every 15 seconds
+ this.setPeriod(15 * 20L);
+ }
+
+ // -------------------------------------------- //
+ // FIELDS
+ // -------------------------------------------- //
+
+ private DynmapAPI dynmapApi;
+ private MarkerAPI markerApi;
+ private MarkerSet markerset;
+
+ // -------------------------------------------- //
+ // RUN: UPDATE
+ // -------------------------------------------- //
+
+ // Thread Safe / Asynchronous: Yes
+ @Override
+ public void run()
+ {
+ // Is Dynmap enabled?
+ if (MConf.get().dynmapEnabled)
+ {
+ this.perform();
+ }
+ else
+ {
+ this.disable();
+ }
+ }
+
+ public void perform()
+ {
+ long before = System.currentTimeMillis();
+
+ // We do what we can here.
+ // You /can/ run this method from the main server thread but it's not recommended at all.
+ // This method is supposed to be run async to avoid locking the main server thread.
+ //final Map homes = createHomes();
+ final Map areas = createAreas();
+
+ logTimeSpent("Async", before);
+
+ // Shedule non thread safe sync at the end!
+ Bukkit.getScheduler().scheduleSyncDelayedTask(Factions.get(), () -> this.updateFactionsDynmap(areas));
+ }
+
+ public void updateFactionsDynmap(Map areas)
+ {
+ long before = System.currentTimeMillis();
+
+ if (!Bukkit.isPrimaryThread()) throw new IllegalStateException("async");
+
+ if (!fetchDynmapAPI()) return;
+
+ // createLayer() is thread safe but it makes use of fields set in fetchDynmapAPI() so we must have it after.
+ if (!updateLayer(createLayer())) return;
+
+ updateAreas(areas);
+
+ logTimeSpent("Sync", before);
+ }
+
+ public void disable()
+ {
+ if (this.markerset != null)
+ {
+ this.markerset.deleteMarkerSet();
+ this.markerset = null;
+ }
+ }
+
+ // Thread Safe / Asynchronous: Yes
+ public static void logTimeSpent(String name, long start)
+ {
+ if (!MConf.get().dynmapLogTimeSpent) return;
+ long end = System.currentTimeMillis();
+ long duration = end-start;
+
+ String message = Txt.parse("Dynmap %s took %dms.", name, duration);
+ Factions.get().log(message);
+ }
+
+ // -------------------------------------------- //
+ // API
+ // -------------------------------------------- //
+
+ // Thread Safe / Asynchronous: No
+ public boolean fetchDynmapAPI()
+ {
+ // Get DynmapAPI
+ this.dynmapApi = (DynmapAPI) Bukkit.getPluginManager().getPlugin("dynmap");
+ if (this.dynmapApi == null)
+ {
+ logSevere("Could not access the DynmapAPI.");
+ return false;
+ }
+
+ // Get MarkerAPI
+ this.markerApi = this.dynmapApi.getMarkerAPI();
+ if (this.markerApi == null)
+ {
+ logSevere("Could not access the MarkerAPI.");
+ return false;
+ }
+
+ return true;
+ }
+
+ // -------------------------------------------- //
+ // UPDATE: Layer
+ // -------------------------------------------- //
+
+ // Thread Safe / Asynchronous: Yes
+ public LayerValues createLayer()
+ {
+ return new LayerValues(
+ MConf.get().dynmapLayerName,
+ MConf.get().dynmapLayerMinimumZoom,
+ MConf.get().dynmapLayerPriority,
+ MConf.get().dynmapLayerHiddenByDefault
+ );
+ }
+
+ // Thread Safe / Asynchronous: No
+ public boolean updateLayer(LayerValues temp)
+ {
+ this.markerset = temp.ensureExistsAndUpdated(this.markerApi, IntegrationDynmap.FACTIONS_MARKERSET);
+ return this.markerset != null;
+ }
+
+ // -------------------------------------------- //
+ // UPDATE: AREAS
+ // -------------------------------------------- //
+
+ // Thread Safe: YES
+ public Map createAreas()
+ {
+ Map>> worldFactionChunks = BoardColl.get().getWorldToFactionToChunks(false);
+ return createAreas(worldFactionChunks);
+
+ }
+
+ // Thread Safe: YES
+ public Map createAreas(Map>> worldFactionChunks)
+ {
+ // For each world create the areas
+ return worldFactionChunks.entrySet().stream()
+ .map(this::createAreas)
+ // And combine all of those into a single map:
+ .map(Map::entrySet)
+ .flatMap(Set::stream)
+ .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
+ }
+
+ public Map createAreas(Entry>> superEntry)
+ {
+ return createAreas(superEntry.getKey(), superEntry.getValue());
+ }
+
+ public Map createAreas(String world, Map> map)
+ {
+ // For each entry convert it into the appropriate map (with method below)
+ return map.entrySet().stream()
+ .map(e -> createAreas(world, e))
+ // And combine all of those into a single map:
+ .map(Map::entrySet)
+ .flatMap(Set::stream)
+ .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
+ }
+
+ public Map createAreas(String world, Entry> entry)
+ {
+ return createAreas(world, entry.getKey(), entry.getValue());
+ }
+
+ public Map createAreas(String world, Faction faction, Set chunks)
+ {
+ // If the faction is visible ...
+ if (!isVisible(faction, world)) return Collections.emptyMap();
+
+ // ... and has any chunks ...
+ if (chunks.isEmpty()) return Collections.emptyMap();
+
+ Map ret = new MassiveMap<>();
+
+ // Get info
+ String description = getDescription(faction);
+ DynmapStyle style = this.getStyle(faction);
+
+ // Here we start of with all chunks
+ // This field is slowly cleared when the chunks are grouped into polygons
+ Set allChunksSource = new MassiveSet<>(chunks);
+
+ while (!allChunksSource.isEmpty())
+ {
+ PS somePs = allChunksSource.iterator().next();
+
+ // Create the polygon
+ Set polygonChunks = new MassiveSet<>();
+ floodFillTarget(allChunksSource, polygonChunks, somePs);
+ List linelist = getLineList(polygonChunks);
+
+ // Calc the x and y arrays
+ int sz = linelist.size();
+ double[] x = new double[sz];
+ double[] z = new double[sz];
+
+ int i = 0;
+ for (PS ps : linelist)
+ {
+ x[i] = ps.getLocationX(true);
+ z[i] = ps.getLocationZ(true);
+ i++;
+ }
+
+ // Build information for specific area
+ String markerId = calcMarkerId(world, faction);
+ AreaMarkerValues values = new AreaMarkerValues(faction.getName(), world, x, z, description, style);
+ ret.put(markerId, values);
+ }
+
+ return ret;
+ }
+
+ private static PS getMinimum(Collection pss)
+ {
+ int minimumX = Integer.MAX_VALUE;
+ int minimumZ = Integer.MAX_VALUE;
+
+ for (PS chunk : pss)
+ {
+ int chunkX = chunk.getChunkX();
+ int chunkZ = chunk.getChunkZ();
+
+ if (chunkX < minimumX)
+ {
+ minimumX = chunkX;
+ minimumZ = chunkZ;
+ }
+ else if (chunkX == minimumX && chunkZ < minimumZ)
+ {
+ minimumZ = chunkZ;
+ }
+ }
+ return PS.valueOf(minimumX, minimumZ);
+ }
+
+ // XPLUS, ZPLUS, XMINUS, ZMINUS
+ private static List getLineList(Set polygonChunks)
+ {
+ PS minimumChunk = getMinimum(polygonChunks);
+
+ //final int initialX = minimumChunk.getChunkX();
+ //final int initialZ = minimumChunk.getChunkZ();
+ //int currentX = initialX;
+ //int currentZ = initialZ;
+
+ PS currentChunk = minimumChunk;
+
+ Direction direction = Direction.XPLUS;
+ List linelist = new MassiveList<>();
+
+ linelist.add(minimumChunk); // Add start point
+ while ((!currentChunk.equals(minimumChunk)) || (direction != Direction.ZMINUS))
+ {
+ PS adjacent = direction.adjacent(currentChunk);
+ PS corner = direction.getCorner(currentChunk);
+ // If the adjacent chunk is not present
+
+ if (!polygonChunks.contains(adjacent))
+ { // Right turn?
+ linelist.add(corner); // Finish line
+ direction = direction.turnRight(); // Change direction
+ }
+
+ // If the chunk left of the adjacent is not present
+ else if (!polygonChunks.contains(direction.turnLeft().adjacent(adjacent)))
+ { // Straight?
+ currentChunk = adjacent;
+ }
+
+ else
+ { // Left turn
+ linelist.add(corner); // Finish line
+ direction = direction.turnLeft();
+
+ // Left turn of adjacent
+ currentChunk = direction.adjacent(adjacent);
+ }
+ }
+
+ return linelist;
+ }
+
+ // IS CLAIMED
+
+ private static boolean isSoutheastClaimed(PS ps, Collection polygon)
+ {
+ return polygon.contains(PS.valueOf(ps.getChunkX() - 1, ps.getChunkZ() + 1));
+ }
+
+ private static boolean isNortheastClaimed(PS ps, Collection polygon)
+ {
+ return polygon.contains(PS.valueOf(ps.getChunkX() + 1, ps.getChunkZ() + 1));
+ }
+
+ private static boolean isSouthwestClaimed(PS ps, Collection polygon)
+ {
+ return polygon.contains(PS.valueOf(ps.getChunkX() - 1, ps.getChunkZ() - 1));
+ }
+
+ private static boolean isNorthwestClaimed(PS ps, Collection polygon)
+ {
+ return polygon.contains(PS.valueOf(ps.getChunkX() + 1, ps.getChunkZ() - 1));
+ }
+
+ // GET CHUNKS
+
+ private static PS getNortheastPS(PS ps)
+ {
+ return PS.valueOf(ps.getChunkX() + 1, ps.getChunkZ() + 1);
+ }
+
+ private static PS getSoutheastPS(PS ps)
+ {
+ return PS.valueOf(ps.getChunkX(), ps.getChunkZ() + 1);
+ }
+
+ private static PS getSouthwestPS(PS ps)
+ {
+ return PS.valueOf(ps.getChunkX(), ps.getChunkZ());
+ }
+
+ private static PS getNorthwestPS(PS ps)
+ {
+ return PS.valueOf(ps.getChunkX() + 1, ps.getChunkZ());
+ }
+
+ // This markerIndex, is if a faction has several claims in a single world
+ private int markerIdx = 0;
+ private String lastPartialMarkerId = "";
+ public String calcMarkerId(String world, Faction faction)
+ {
+ // Calc current partial
+ String partial = IntegrationDynmap.FACTIONS_AREA_ + world + "__" + faction.getId() + "__";
+
+ // If different than last time, then reset the counter
+ if (!partial.equals(lastPartialMarkerId)) markerIdx = 0;
+
+ this.lastPartialMarkerId = partial;
+
+ return partial + markerIdx++;
+ }
+
+ // Thread Safe: NO
+ public void updateAreas(Map values)
+ {
+ // Cleanup old markers
+ this.markerset.getAreaMarkers().stream() // Get current markers
+ .filter(am -> !values.containsKey(am.getMarkerID())) // That are not in the new map
+ .forEach(AreaMarker::deleteMarker); // and delete them
+
+
+ // Map Current
+ Map markers = getMarkerMap(this.markerset);
+
+ // Loop New
+ values.forEach((markerId, value) ->
+ value.ensureExistsAndUpdated(markers.get(markerId), this.markerApi, this.markerset, markerId));
+
+ }
+
+ private static Map getMarkerMap(MarkerSet markerSet)
+ {
+ return markerSet.getAreaMarkers().stream().collect(Collectors.toMap(AreaMarker::getMarkerID, m->m));
+ }
+
+ // -------------------------------------------- //
+ // UTIL & SHARED
+ // -------------------------------------------- //
+
+ // Thread Safe / Asynchronous: Yes
+ private String getDescription(Faction faction)
+ {
+ String ret = "" + MConf.get().dynmapFactionDescription + "
";
+
+ // Name
+ String name = faction.getName();
+ ret = addToHtml(ret, "name", name);
+
+ // Description
+ String description = faction.getDescriptionDesc();
+ ret = addToHtml(ret, "description", description);
+
+ // MOTD (probably shouldn't be shown but if the server owner specifies it, I don't care)
+ String motd = faction.getMotd();
+ if (motd != null) ret = addToHtml(ret, "motd", motd);
+
+ // Age
+ long ageMillis = faction.getAge();
+ LinkedHashMap ageUnitcounts = TimeDiffUtil.limit(TimeDiffUtil.unitcounts(ageMillis, TimeUnit.getAllButMillisSecondsAndMinutes()), 3);
+ String age = TimeDiffUtil.formatedVerboose(ageUnitcounts, "");
+ ret = addToHtml(ret, "age", age);
+
+ // Money
+ String money = "unavailable";
+ if (Econ.isEnabled() && MConf.get().dynmapShowMoneyInDescription)
+ {
+ money = Money.format(Econ.getMoney(faction));
+ }
+ ret = addToHtml(ret, "money", money);
+
+ // Flags
+ Map flags = MFlag.getAll().stream()
+ .filter(MFlag::isVisible)
+ .collect(Collectors.toMap(m -> m, faction::getFlag));
+
+ List flagMapParts = new MassiveList<>();
+ List flagTableParts = new MassiveList<>();
+
+ for (Entry entry : flags.entrySet())
+ {
+ String flagName = entry.getKey().getName();
+ boolean value = entry.getValue();
+
+ String bool = String.valueOf(value);
+ String color = calcBoolcolor(flagName, value);
+ String boolcolor = calcBoolcolor(String.valueOf(value), value);
+
+ ret = ret.replace("%" + flagName + ".bool%", bool); // true
+ ret = ret.replace("%" + flagName + ".color%", color); // monsters (red or green)
+ ret = ret.replace("%" + flagName + ".boolcolor%", boolcolor); // true (red or green)
+
+ flagMapParts.add(flagName + ": " + boolcolor);
+ flagTableParts.add(color);
+ }
+
+ String flagMap = Txt.implode(flagMapParts, "
\n");
+ ret = ret.replace("%flags.map%", flagMap);
+
+ // The server can specify the wished number of columns
+ // So we loop over the possibilities
+ for (int cols = 1; cols <= 10; cols++)
+ {
+ String flagTable = getHtmlAsciTable(flagTableParts, cols);
+ ret = ret.replace("%flags.table" + cols + "%", flagTable);
+ }
+
+ // Players
+ List playersList = faction.getMPlayers();
+ String playersCount = String.valueOf(playersList.size());
+ String players = getHtmlPlayerString(playersList);
+
+ MPlayer playersLeaderObject = faction.getLeader();
+ String playersLeader = getHtmlPlayerName(playersLeaderObject);
+
+ ret = ret.replace("%players%", players);
+ ret = ret.replace("%players.count%", playersCount);
+ ret = ret.replace("%players.leader%", playersLeader);
+
+ return ret;
+ }
+
+ public static String getHtmlAsciTable(Collection strings, final int cols)
+ {
+ StringBuilder ret = new StringBuilder();
+
+ int count = 0;
+ for (Iterator iter = strings.iterator(); iter.hasNext();)
+ {
+ String string = iter.next();
+ count++;
+
+ ret.append(string);
+
+ if (iter.hasNext())
+ {
+ boolean lineBreak = count % cols == 0;
+ ret.append(lineBreak ? "
" : " | ");
+ }
+ }
+
+ return ret.toString();
+ }
+
+ public static String getHtmlPlayerString(List mplayers)
+ {
+ List names = mplayers.stream().map(EngineDynmap::getHtmlPlayerName).collect(Collectors.toList());
+ return Txt.implodeCommaAndDot(names);
+ }
+
+ public static String getHtmlPlayerName(MPlayer mplayer)
+ {
+ if (mplayer == null) return "none";
+ return StringEscapeUtils.escapeHtml(mplayer.getName());
+ }
+
+ public static String calcBoolcolor(String string, boolean bool)
+ {
+ return "" + string + "";
+ }
+
+ public static String addToHtml(String ret, String target, String replace)
+ {
+ if (ret == null) throw new NullPointerException("ret");
+ if (target == null) throw new NullPointerException("target");
+ if (replace == null) throw new NullPointerException("replace");
+
+ target = "%" + target + "%";
+ replace = ChatColor.stripColor(replace);
+ replace = StringEscapeUtils.escapeHtml(replace);
+ return ret.replace(target, replace);
+ }
+
+ // Thread Safe / Asynchronous: Yes
+ private boolean isVisible(Faction faction, String world)
+ {
+ if (faction == null) throw new NullPointerException("faction");
+ if (world == null) throw new NullPointerException("world");
+
+ final String factionId = faction.getId();
+ final String factionName = faction.getName();
+ final String worldId = "world:" + world;
+
+ Set ids = MUtil.set(factionId, factionName, worldId);
+
+ if (factionId == null) throw new NullPointerException("faction id");
+ if (factionName == null) throw new NullPointerException("faction name");
+
+ Set visible = MConf.get().dynmapVisibleFactions;
+ Set hidden = MConf.get().dynmapHiddenFactions;
+
+
+ if (!visible.isEmpty() && visible.stream().noneMatch(ids::contains))
+ {
+ return false;
+ }
+
+ if (!hidden.isEmpty() && hidden.stream().anyMatch(ids::contains))
+ {
+ return false;
+ }
+
+
+ return true;
+ }
+
+ // Thread Safe / Asynchronous: Yes
+ public DynmapStyle getStyle(Faction faction)
+ {
+ Map styles = MConf.get().dynmapFactionStyles;
+
+ return DynmapStyle.coalesce(
+ styles.get(faction.getId()),
+ styles.get(faction.getName()),
+ MConf.get().dynmapDefaultStyle
+ );
+ }
+
+ public static void logSevere(String msg)
+ {
+ String message = ChatColor.RED.toString() + msg;
+ Factions.get().log(message);
+ }
+
+ enum Direction
+ {
+ XPLUS, ZPLUS, XMINUS, ZMINUS
+
+ ;
+
+ public PS adjacent(PS ps)
+ {
+ switch (this)
+ {
+ case XPLUS: return PS.valueOf(ps.getChunkX() + 1, ps.getChunkZ());
+ case ZPLUS: return PS.valueOf(ps.getChunkX(), ps.getChunkZ() + 1);
+ case XMINUS: return PS.valueOf(ps.getChunkX() - 1, ps.getChunkZ());
+ case ZMINUS: return PS.valueOf(ps.getChunkX(), ps.getChunkZ() - 1);
+ }
+ throw new RuntimeException("say what");
+ }
+
+ public PS getCorner(PS ps)
+ {
+ switch (this)
+ {
+ case XPLUS: return PS.valueOf(ps.getChunkX() + 1, ps.getChunkZ());
+ case ZPLUS: return PS.valueOf(ps.getChunkX() + 1, ps.getChunkZ() + 1);
+ case XMINUS: return PS.valueOf(ps.getChunkX(), ps.getChunkZ() + 1);
+ case ZMINUS: return PS.valueOf(ps.getChunkX(), ps.getChunkZ());
+ }
+ throw new RuntimeException("say what");
+ }
+
+ public Direction turnRight()
+ {
+ return values()[(this.ordinal() + 1) % values().length];
+ }
+
+ public Direction turnAround()
+ {
+ return this.turnRight().turnRight();
+ }
+
+ public Direction turnLeft()
+ {
+ return this.turnRight().turnRight().turnRight();
+ }
+ }
+
+ private void floodFillTarget(Collection source, Collection destination, PS startChunk)
+ {
+ // Create the deque
+ ArrayDeque stack = new ArrayDeque<>();
+ stack.push(startChunk);
+
+ // And for each item in the queue
+ while (!stack.isEmpty())
+ {
+ PS next = stack.pop();
+
+ // If it is in the source
+ // Remove it from there to avoid double-counting (and endless recursion)
+ if (!source.remove(next)) continue;
+
+ // Add to destination
+ destination.add(next);
+
+ // And look in adjacent chunks that are within the source
+ Stream.of(Direction.values())
+ .map(d -> d.adjacent(next))
+ .filter(source::contains)
+ .forEach(stack::push);
+ }
+ }
+
+}
diff --git a/src/com/massivecraft/factions/integration/dynmap/IntegrationDynmap.java b/src/com/massivecraft/factions/integration/dynmap/IntegrationDynmap.java
new file mode 100644
index 00000000..68f7b126
--- /dev/null
+++ b/src/com/massivecraft/factions/integration/dynmap/IntegrationDynmap.java
@@ -0,0 +1,55 @@
+package com.massivecraft.factions.integration.dynmap;
+
+import com.massivecraft.factions.integration.worldguard.EngineWorldGuard;
+import com.massivecraft.massivecore.Engine;
+import com.massivecraft.massivecore.Integration;
+
+public class IntegrationDynmap extends Integration
+{
+ // -------------------------------------------- //
+ // CONSTANTS
+ // -------------------------------------------- //
+
+ // Constants must be here rather than in EngineDynmap.
+ // MConf relies on DynmapStyle which relies on these constants
+ // and we must be able to load MConf without EngineDynmap.
+ public final static int BLOCKS_PER_CHUNK = 16;
+
+ public final static String FACTIONS = "factions";
+ public final static String FACTIONS_ = FACTIONS + "_";
+
+ public final static String FACTIONS_MARKERSET = FACTIONS_ + "markerset";
+
+ public final static String FACTIONS_AREA = FACTIONS_ + "area";
+ public final static String FACTIONS_AREA_ = FACTIONS_AREA + "_";
+
+ public final static transient String DYNMAP_STYLE_LINE_COLOR = "#00FF00";
+ public final static transient double DYNMAP_STYLE_LINE_OPACITY = 0.8D;
+ public final static transient int DYNMAP_STYLE_LINE_WEIGHT = 3;
+ public final static transient String DYNMAP_STYLE_FILL_COLOR = "#00FF00";
+ public final static transient double DYNMAP_STYLE_FILL_OPACITY = 0.35D;
+ public final static transient String DYNMAP_STYLE_HOME_MARKER = "greenflag";
+ public final static transient boolean DYNMAP_STYLE_BOOST = false;
+
+ // -------------------------------------------- //
+ // INSTANCE & CONSTRUCT
+ // -------------------------------------------- //
+
+ private static IntegrationDynmap i = new IntegrationDynmap();
+ public static IntegrationDynmap get() { return i; }
+ private IntegrationDynmap()
+ {
+ this.setPluginName("dynmap");
+ }
+
+ // -------------------------------------------- //
+ // OVERRIDE
+ // -------------------------------------------- //
+
+ @Override
+ public Engine getEngine()
+ {
+ return EngineDynmap.get();
+ }
+
+}
diff --git a/src/com/massivecraft/factions/integration/dynmap/LayerValues.java b/src/com/massivecraft/factions/integration/dynmap/LayerValues.java
new file mode 100644
index 00000000..5392c224
--- /dev/null
+++ b/src/com/massivecraft/factions/integration/dynmap/LayerValues.java
@@ -0,0 +1,107 @@
+package com.massivecraft.factions.integration.dynmap;
+
+import com.massivecraft.massivecore.util.MUtil;
+import org.dynmap.markers.MarkerAPI;
+import org.dynmap.markers.MarkerSet;
+
+public class LayerValues
+{
+
+ // -------------------------------------------- //
+ // FIELDS
+ // -------------------------------------------- //
+
+ private final String label;
+ public String getLabel() { return label; }
+ public LayerValues withLabel(String label) { return new LayerValues(label, minimumZoom, priority, hiddenByDefault); }
+
+ private final int minimumZoom;
+ public int getMinimumZoom() { return minimumZoom; }
+ public LayerValues withMinimumZoom(int minimumZoom) { return new LayerValues(label, minimumZoom, priority, hiddenByDefault); }
+
+ private final int priority;
+ public int getPriority() { return priority; }
+ public LayerValues withPriority(int priority) { return new LayerValues(label, minimumZoom, priority, hiddenByDefault); }
+
+ private final boolean hiddenByDefault;
+ public boolean isHiddenByDefault() { return hiddenByDefault; }
+ public LayerValues withHidenByDefault(boolean hideByDefault) { return new LayerValues(label, minimumZoom, priority, hideByDefault); }
+
+ // -------------------------------------------- //
+ // CONSTRUCTOR
+ // -------------------------------------------- //
+
+ public LayerValues(String label, int minimumZoom, int priority, boolean hideByDefault)
+ {
+ this.label = label;
+ this.minimumZoom = minimumZoom;
+ this.priority = priority;
+ this.hiddenByDefault = hideByDefault;
+ }
+
+ // -------------------------------------------- //
+ // MASTER
+ // -------------------------------------------- //
+
+ public MarkerSet ensureExistsAndUpdated(MarkerAPI api, String id)
+ {
+ MarkerSet set = api.getMarkerSet(id);
+ if (set == null)
+ {
+ set = this.create(api, id);
+ }
+ else
+ {
+ this.update(set);
+ }
+
+ if (set == null)
+ {
+ EngineDynmap.logSevere("Could not create the Faction Markerset/Layer");
+ }
+
+ return set;
+ }
+
+ // -------------------------------------------- //
+ // CREATE
+ // -------------------------------------------- //
+
+ public MarkerSet create(MarkerAPI markerApi, String id)
+ {
+ MarkerSet ret = markerApi.createMarkerSet(id, this.label, null, false); // ("null, false" at the end means "all icons allowed, not perisistent")
+ if (ret == null) return null;
+
+ // Minimum Zoom
+ if (this.minimumZoom > 0)
+ {
+ ret.setMinZoom(this.getMinimumZoom());
+ }
+
+ // Priority
+ ret.setLayerPriority(this.getPriority());
+
+ // Hide by Default
+ ret.setHideByDefault(this.isHiddenByDefault());
+ return ret;
+ }
+
+ // -------------------------------------------- //
+ // UPDATE
+ // -------------------------------------------- //
+
+ public void update(MarkerSet markerset)
+ {
+ // Minimum Zoom
+ if (this.minimumZoom > 0)
+ {
+ MUtil.setIfDifferent(this.getMinimumZoom(), markerset::getMinZoom, markerset::setMinZoom);
+ }
+
+ // Set other values
+ MUtil.setIfDifferent(this.getLabel(), markerset::getMarkerSetLabel, markerset::setMarkerSetLabel);
+ MUtil.setIfDifferent(this.getPriority(), markerset::getLayerPriority, markerset::setLayerPriority);
+ MUtil.setIfDifferent(this.isHiddenByDefault(), markerset::getHideByDefault, markerset::setHideByDefault);
+ }
+
+}
diff --git a/src/com/massivecraft/factions/integration/dynmap/MarkerValues.java b/src/com/massivecraft/factions/integration/dynmap/MarkerValues.java
new file mode 100644
index 00000000..50c747f6
--- /dev/null
+++ b/src/com/massivecraft/factions/integration/dynmap/MarkerValues.java
@@ -0,0 +1,135 @@
+package com.massivecraft.factions.integration.dynmap;
+
+import com.massivecraft.massivecore.util.MUtil;
+import org.dynmap.markers.Marker;
+import org.dynmap.markers.MarkerAPI;
+import org.dynmap.markers.MarkerIcon;
+import org.dynmap.markers.MarkerSet;
+
+public class MarkerValues
+{
+ // -------------------------------------------- //
+ // FIELDS
+ // -------------------------------------------- //
+
+ private final String label;
+ public String getLabel() { return label; }
+ public MarkerValues withLabel(String label) { return new MarkerValues(label, world, x, y, z, iconName, description); }
+
+ private final String world;
+ public String getWorld() { return world; }
+ public MarkerValues withWorld(String world) { return new MarkerValues(label, world, x, y, z, iconName, description); }
+
+ private final double x;
+ public double getX() { return x; }
+ public MarkerValues withX(double x) { return new MarkerValues(label, world, x, y, z, iconName, description); }
+
+ private final double y;
+ public double getY() { return y; }
+ public MarkerValues withY(double y) { return new MarkerValues(label, world, x, y, z, iconName, description); }
+
+ private final double z;
+ public double getZ() { return z; }
+ public MarkerValues withZ(double z) { return new MarkerValues(label, world, x, y, z, iconName, description); }
+
+ private final String iconName;
+ public String getIconName() { return iconName; }
+ public MarkerValues withIconName(String iconName) { return new MarkerValues(label, world, x, y, z, iconName, description); }
+
+ private final String description;
+ public String getDescription() { return description; }
+ public MarkerValues withDescription(String description) { return new MarkerValues(label, world, x, y, z, iconName, description); }
+
+ // -------------------------------------------- //
+ // CONSTRUCTOR
+ // -------------------------------------------- //
+
+ public MarkerValues(String label, String world, double x, double y, double z, String iconName, String description)
+ {
+ this.label = label;
+ this.world = world;
+ this.x = x;
+ this.y = y;
+ this.z = z;
+ this.iconName = iconName;
+ this.description = description;
+ }
+
+ // -------------------------------------------- //
+ // MAKE SURE EXISTS
+ // -------------------------------------------- //
+
+ public Marker ensureExistsAndUpdated(MarkerAPI markerApi, MarkerSet markerset, String id)
+ {
+ throw new UnsupportedOperationException("todo");
+ }
+
+ // -------------------------------------------- //
+ // CREATE
+ // -------------------------------------------- //
+
+ public Marker create(MarkerAPI markerApi, MarkerSet markerset, String markerId)
+ {
+ Marker ret = markerset.createMarker(
+ markerId,
+ this.getLabel(),
+ this.getWorld(),
+ this.getX(),
+ this.getY(),
+ this.getZ(),
+ getMarkerIcon(markerApi, this.getIconName()),
+ false // not persistent
+ );
+
+ if (ret == null) return null;
+
+ ret.setDescription(this.getDescription());
+
+ return ret;
+ }
+
+ // -------------------------------------------- //
+ // UPDATE
+ // -------------------------------------------- //
+
+ public void update(MarkerAPI markerApi, MarkerSet markerset, Marker marker)
+ {
+ if
+ (
+ !MUtil.equals(marker.getWorld(), this.getWorld())
+ ||
+ marker.getX() != this.getX()
+ ||
+ marker.getY() != this.getY()
+ ||
+ marker.getZ() != this.getZ()
+ )
+ {
+ marker.setLocation(
+ this.getWorld(),
+ this.getX(),
+ this.getY(),
+ this.getZ()
+ );
+ }
+
+ MUtil.setIfDifferent(this.getLabel(), marker::getLabel, marker::setLabel);
+
+ MarkerIcon icon = getMarkerIcon(markerApi, this.iconName);
+ MUtil.setIfDifferent(icon, marker::getMarkerIcon, marker::setMarkerIcon);
+
+ MUtil.setIfDifferent(this.getDescription(), marker::getDescription, marker::setDescription);
+ }
+
+ // -------------------------------------------- //
+ // UTIL
+ // -------------------------------------------- //
+
+ public static MarkerIcon getMarkerIcon(MarkerAPI markerApi, String name)
+ {
+ MarkerIcon ret = markerApi.getMarkerIcon(name);
+ if (ret == null) ret = markerApi.getMarkerIcon(IntegrationDynmap.DYNMAP_STYLE_HOME_MARKER);
+ return ret;
+ }
+
+}
\ No newline at end of file