diff --git a/WorldEdit.jar b/WorldEdit.jar deleted file mode 100644 index 242b3d8..0000000 Binary files a/WorldEdit.jar and /dev/null differ diff --git a/WorldGuard-5.5.3.jar b/WorldGuard-5.5.3.jar new file mode 100755 index 0000000..e8bb6d1 Binary files /dev/null and b/WorldGuard-5.5.3.jar differ diff --git a/WorldGuard.jar b/WorldGuard.jar deleted file mode 100755 index d7bca46..0000000 Binary files a/WorldGuard.jar and /dev/null differ diff --git a/pom.xml b/pom.xml index cb0fb3a..86e216f 100644 --- a/pom.xml +++ b/pom.xml @@ -56,28 +56,26 @@ org.dynmap dynmap-api - [0.28,) - compile + [0.80,) org.bukkit bukkit - [1.2.4-R1.0,1.7) - compile + [1.3.1-R1.0,1.7) com.sk89q WorldGuard - 5.5.2 + 5.5.3 system - ${project.basedir}/WorldGuard.jar + ${project.basedir}/WorldGuard-5.5.3.jar com.sk89q WorldEdit - 4.7 + 5.4.2 system - ${project.basedir}/WorldEdit.jar + ${project.basedir}/worldedit-5.4.2.jar \ No newline at end of file diff --git a/src/main/java/org/dynmap/worldguard/DynmapWorldGuardPlugin.java b/src/main/java/org/dynmap/worldguard/DynmapWorldGuardPlugin.java index 02ab9e7..b29f4fe 100644 --- a/src/main/java/org/dynmap/worldguard/DynmapWorldGuardPlugin.java +++ b/src/main/java/org/dynmap/worldguard/DynmapWorldGuardPlugin.java @@ -1,5 +1,6 @@ package org.dynmap.worldguard; +import java.io.IOException; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -57,6 +58,7 @@ public class DynmapWorldGuardPlugin extends JavaPlugin { private static class AreaStyle { String strokecolor; + String unownedstrokecolor; double strokeopacity; int strokeweight; String fillcolor; @@ -65,6 +67,7 @@ public class DynmapWorldGuardPlugin extends JavaPlugin { AreaStyle(FileConfiguration cfg, String path, AreaStyle def) { strokecolor = cfg.getString(path+".strokeColor", def.strokecolor); + unownedstrokecolor = cfg.getString(path+".unownedStrokeColor", def.unownedstrokecolor); strokeopacity = cfg.getDouble(path+".strokeOpacity", def.strokeopacity); strokeweight = cfg.getInt(path+".strokeWeight", def.strokeweight); fillcolor = cfg.getString(path+".fillColor", def.fillcolor); @@ -74,6 +77,7 @@ public class DynmapWorldGuardPlugin extends JavaPlugin { AreaStyle(FileConfiguration cfg, String path) { strokecolor = cfg.getString(path+".strokeColor", "#FF0000"); + unownedstrokecolor = cfg.getString(path+".unownedStrokeColor", "#00FF00"); strokeopacity = cfg.getDouble(path+".strokeOpacity", 0.8); strokeweight = cfg.getInt(path+".strokeWeight", 3); fillcolor = cfg.getString(path+".fillColor", "#FF0000"); @@ -169,11 +173,19 @@ public class DynmapWorldGuardPlugin extends JavaPlugin { if(as == null) as = defstyle; + boolean unowned = false; + if((region.getOwners().getPlayers().size() == 0) && + (region.getOwners().getGroups().size() == 0)) { + unowned = true; + } int sc = 0xFF0000; int fc = 0xFF0000; try { - sc = Integer.parseInt(as.strokecolor.substring(1), 16); - fc = Integer.parseInt(as.fillcolor.substring(1), 16); + if(unowned) + sc = Integer.parseInt(as.unownedstrokecolor.substring(1), 16); + else + sc = Integer.parseInt(as.strokecolor.substring(1), 16); + fc = Integer.parseInt(as.fillcolor.substring(1), 16); } catch (NumberFormatException nfx) { } m.setLineStyle(as.strokeweight, as.strokeopacity, sc); @@ -313,8 +325,17 @@ public class DynmapWorldGuardPlugin extends JavaPlugin { /* If both enabled, activate */ if(dynmap.isEnabled() && wg.isEnabled()) activate(); + /* Start up metrics */ + try { + MetricsLite ml = new MetricsLite(this); + ml.start(); + } catch (IOException iox) { + + } } + private boolean reload = false; + private void activate() { /* Now, get markers API */ markerapi = api.getMarkerAPI(); @@ -323,6 +344,12 @@ public class DynmapWorldGuardPlugin extends JavaPlugin { return; } /* Load configuration */ + if(reload) { + this.reloadConfig(); + } + else { + reload = true; + } FileConfiguration cfg = getConfig(); cfg.options().copyDefaults(true); /* Load defaults, if needed */ this.saveConfig(); /* Save updates, if needed */ diff --git a/src/main/java/org/dynmap/worldguard/MetricsLite.java b/src/main/java/org/dynmap/worldguard/MetricsLite.java new file mode 100644 index 0000000..9e20258 --- /dev/null +++ b/src/main/java/org/dynmap/worldguard/MetricsLite.java @@ -0,0 +1,357 @@ +/* + * Copyright 2011 Tyler Blair. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and contributors and should not be interpreted as representing official policies, + * either expressed or implied, of anybody else. + */ + +package org.dynmap.worldguard; + +import org.bukkit.Bukkit; +import org.bukkit.configuration.InvalidConfigurationException; +import org.bukkit.configuration.file.YamlConfiguration; +import org.bukkit.plugin.Plugin; +import org.bukkit.plugin.PluginDescriptionFile; + +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.io.UnsupportedEncodingException; +import java.net.Proxy; +import java.net.URL; +import java.net.URLConnection; +import java.net.URLEncoder; +import java.util.UUID; +import java.util.logging.Level; + +public class MetricsLite { + + /** + * The current revision number + */ + private final static int REVISION = 5; + + /** + * The base url of the metrics domain + */ + private static final String BASE_URL = "http://mcstats.org"; + + /** + * The url used to report a server's status + */ + private static final String REPORT_URL = "/report/%s"; + + /** + * Interval of time to ping (in minutes) + */ + private final static int PING_INTERVAL = 10; + + /** + * The plugin this metrics submits for + */ + private final Plugin plugin; + + /** + * The plugin configuration file + */ + private final YamlConfiguration configuration; + + /** + * The plugin configuration file + */ + private final File configurationFile; + + /** + * Unique server id + */ + private final String guid; + + /** + * Lock for synchronization + */ + private final Object optOutLock = new Object(); + + /** + * Id of the scheduled task + */ + private volatile int taskId = -1; + + public MetricsLite(Plugin plugin) throws IOException { + if (plugin == null) { + throw new IllegalArgumentException("Plugin cannot be null"); + } + + this.plugin = plugin; + + // load the config + configurationFile = getConfigFile(); + configuration = YamlConfiguration.loadConfiguration(configurationFile); + + // add some defaults + configuration.addDefault("opt-out", false); + configuration.addDefault("guid", UUID.randomUUID().toString()); + + // Do we need to create the file? + if (configuration.get("guid", null) == null) { + configuration.options().header("http://mcstats.org").copyDefaults(true); + configuration.save(configurationFile); + } + + // Load the guid then + guid = configuration.getString("guid"); + } + + /** + * Start measuring statistics. This will immediately create an async repeating task as the plugin and send + * the initial data to the metrics backend, and then after that it will post in increments of + * PING_INTERVAL * 1200 ticks. + * + * @return True if statistics measuring is running, otherwise false. + */ + public boolean start() { + synchronized (optOutLock) { + // Did we opt out? + if (isOptOut()) { + return false; + } + + // Is metrics already running? + if (taskId >= 0) { + return true; + } + + // Begin hitting the server with glorious data + taskId = plugin.getServer().getScheduler().scheduleAsyncRepeatingTask(plugin, new Runnable() { + + private boolean firstPost = true; + + public void run() { + try { + // This has to be synchronized or it can collide with the disable method. + synchronized (optOutLock) { + // Disable Task, if it is running and the server owner decided to opt-out + if (isOptOut() && taskId > 0) { + plugin.getServer().getScheduler().cancelTask(taskId); + taskId = -1; + } + } + + // We use the inverse of firstPost because if it is the first time we are posting, + // it is not a interval ping, so it evaluates to FALSE + // Each time thereafter it will evaluate to TRUE, i.e PING! + postPlugin(!firstPost); + + // After the first post we set firstPost to false + // Each post thereafter will be a ping + firstPost = false; + } catch (IOException e) { + //Bukkit.getLogger().log(Level.INFO, "[Metrics] " + e.getMessage()); + } + } + }, 0, PING_INTERVAL * 1200); + + return true; + } + } + + /** + * Has the server owner denied plugin metrics? + * + * @return true if metrics should be opted out of it + */ + public boolean isOptOut() { + synchronized(optOutLock) { + try { + // Reload the metrics file + configuration.load(getConfigFile()); + } catch (IOException ex) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + return true; + } catch (InvalidConfigurationException ex) { + Bukkit.getLogger().log(Level.INFO, "[Metrics] " + ex.getMessage()); + return true; + } + return configuration.getBoolean("opt-out", false); + } + } + + /** + * Enables metrics for the server by setting "opt-out" to false in the config file and starting the metrics task. + * + * @throws IOException + */ + public void enable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (isOptOut()) { + configuration.set("opt-out", false); + configuration.save(configurationFile); + } + + // Enable Task, if it is not running + if (taskId < 0) { + start(); + } + } + } + + /** + * Disables metrics for the server by setting "opt-out" to true in the config file and canceling the metrics task. + * + * @throws IOException + */ + public void disable() throws IOException { + // This has to be synchronized or it can collide with the check in the task. + synchronized (optOutLock) { + // Check if the server owner has already set opt-out, if not, set it. + if (!isOptOut()) { + configuration.set("opt-out", true); + configuration.save(configurationFile); + } + + // Disable Task, if it is running + if (taskId > 0) { + this.plugin.getServer().getScheduler().cancelTask(taskId); + taskId = -1; + } + } + } + + /** + * Gets the File object of the config file that should be used to store data such as the GUID and opt-out status + * + * @return the File object for the config file + */ + public File getConfigFile() { + // I believe the easiest way to get the base folder (e.g craftbukkit set via -P) for plugins to use + // is to abuse the plugin object we already have + // plugin.getDataFolder() => base/plugins/PluginA/ + // pluginsFolder => base/plugins/ + // The base is not necessarily relative to the startup directory. + File pluginsFolder = plugin.getDataFolder().getParentFile(); + + // return => base/plugins/PluginMetrics/config.yml + return new File(new File(pluginsFolder, "PluginMetrics"), "config.yml"); + } + + /** + * Generic method that posts a plugin to the metrics website + */ + private void postPlugin(boolean isPing) throws IOException { + // The plugin's description file containg all of the plugin data such as name, version, author, etc + final PluginDescriptionFile description = plugin.getDescription(); + + // Construct the post data + final StringBuilder data = new StringBuilder(); + data.append(encode("guid")).append('=').append(encode(guid)); + encodeDataPair(data, "version", description.getVersion()); + encodeDataPair(data, "server", Bukkit.getVersion()); + encodeDataPair(data, "players", Integer.toString(Bukkit.getServer().getOnlinePlayers().length)); + encodeDataPair(data, "revision", String.valueOf(REVISION)); + + // If we're pinging, append it + if (isPing) { + encodeDataPair(data, "ping", "true"); + } + + // Create the url + URL url = new URL(BASE_URL + String.format(REPORT_URL, encode(plugin.getDescription().getName()))); + + // Connect to the website + URLConnection connection; + + // Mineshafter creates a socks proxy, so we can safely bypass it + // It does not reroute POST requests so we need to go around it + if (isMineshafterPresent()) { + connection = url.openConnection(Proxy.NO_PROXY); + } else { + connection = url.openConnection(); + } + + connection.setDoOutput(true); + + // Write the data + final OutputStreamWriter writer = new OutputStreamWriter(connection.getOutputStream()); + writer.write(data.toString()); + writer.flush(); + + // Now read the response + final BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); + final String response = reader.readLine(); + + // close resources + writer.close(); + reader.close(); + + if (response == null || response.startsWith("ERR")) { + throw new IOException(response); //Throw the exception + } + //if (response.startsWith("OK")) - We should get "OK" followed by an optional description if everything goes right + } + + /** + * Check if mineshafter is present. If it is, we need to bypass it to send POST requests + * + * @return true if mineshafter is installed on the server + */ + private boolean isMineshafterPresent() { + try { + Class.forName("mineshafter.MineServer"); + return true; + } catch (Exception e) { + return false; + } + } + + /** + *

Encode a key/value data pair to be used in a HTTP post request. This INCLUDES a & so the first + * key/value pair MUST be included manually, e.g:

+ * + * StringBuffer data = new StringBuffer(); + * data.append(encode("guid")).append('=').append(encode(guid)); + * encodeDataPair(data, "version", description.getVersion()); + * + * + * @param buffer the stringbuilder to append the data pair onto + * @param key the key value + * @param value the value + */ + private static void encodeDataPair(final StringBuilder buffer, final String key, final String value) throws UnsupportedEncodingException { + buffer.append('&').append(encode(key)).append('=').append(encode(value)); + } + + /** + * Encode text as UTF-8 + * + * @param text the text to encode + * @return the encoded text, as UTF-8 + */ + private static String encode(final String text) throws UnsupportedEncodingException { + return URLEncoder.encode(text, "UTF-8"); + } + +} \ No newline at end of file diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 493e0d2..34ce4f2 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -25,6 +25,7 @@ regionstyle: strokeWeight: 3 fillColor: "#FF0000" fillOpacity: 0.35 + unownedStrokeColor: "#00FF00" # Optional setting to limit which regions to show, by name - if commented out, all regions are shown # To show all regions on a given world, add 'world:' to the list diff --git a/worldedit-5.4.2.jar b/worldedit-5.4.2.jar new file mode 100644 index 0000000..127f103 Binary files /dev/null and b/worldedit-5.4.2.jar differ