Refactor :D

This commit is contained in:
gomeow
2013-03-07 10:57:44 -08:00
parent cfb25b14c5
commit 873df5b929
10 changed files with 25 additions and 20 deletions
@@ -0,0 +1,119 @@
package com.drtshock.playervaults;
import java.io.IOException;
import org.bukkit.ChatColor;
import org.bukkit.Material;
import org.bukkit.block.Block;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.HumanEntity;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.block.Action;
import org.bukkit.event.entity.PlayerDeathEvent;
import org.bukkit.event.inventory.InventoryCloseEvent;
import org.bukkit.event.player.PlayerInteractEntityEvent;
import org.bukkit.event.player.PlayerInteractEvent;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.inventory.Inventory;
import com.drtshock.playervaults.commands.Commands;
import com.drtshock.playervaults.commands.VaultViewInfo;
import com.drtshock.playervaults.util.VaultManager;
public class Listeners implements Listener {
public Main plugin;
public Listeners(Main instance) {
this.plugin = instance;
}
VaultManager vm = new VaultManager(plugin);
public void doSaveStuff(Player p) {
if(Commands.inVault.containsKey(p.getName())) {
Inventory inv = p.getOpenInventory().getTopInventory();
VaultViewInfo info = Commands.inVault.get(p.getName());
try {
vm.saveVault(inv, info.getHolder(), info.getNumber());
} catch (IOException e) {
e.printStackTrace();
}
Commands.inVault.remove(p.getName());
}
}
@EventHandler
public void onQuit(PlayerQuitEvent event) {
Player player = event.getPlayer();
doSaveStuff(player);
}
@EventHandler
public void onJoin(PlayerJoinEvent event) {
Player player = event.getPlayer();
vm.playerVaultFile(player.getName());
if(player.isOp() && Main.update)
{
player.sendMessage(ChatColor.GREEN + "Version " + Main.name + " of PlayerVaults is up for download!");
player.sendMessage(ChatColor.GREEN + "http://dev.bukkit.org/server-mods/playervaults to view the changelog and download!");
}
}
@EventHandler
public void onDeath(PlayerDeathEvent event) {
Player player = event.getEntity();
doSaveStuff(player);
}
@EventHandler
public void onClose(InventoryCloseEvent event) {
HumanEntity he = event.getPlayer();
if(he instanceof Player) {
Player player = (Player) he;
doSaveStuff(player);
}
}
/**
* Check if a player is trying to do something while
* in a vault.
* Don't let them open up another chest.
* @param event
*/
@EventHandler
public void onInteract(PlayerInteractEvent event) {
Player player = event.getPlayer();
if(Commands.inVault.containsKey(player.getName()) && event.getAction() == Action.RIGHT_CLICK_BLOCK) {
Block block = event.getClickedBlock();
/**
* Different inventories that
* we don't want the player to open.
*/
if(block.getType() == Material.CHEST
|| block.getType() == Material.ENDER_CHEST
|| block.getType() == Material.FURNACE
|| block.getType() == Material.BURNING_FURNACE
|| block.getType() == Material.BREWING_STAND
|| block.getType() == Material.BEACON) {
event.setCancelled(true);
}
}
}
/**
* Don't let a player open a trading inventory OR a minecart
* while he has his vault open.
* @param event
*/
@EventHandler
public void onInteractEntity(PlayerInteractEntityEvent event) {
Player player = event.getPlayer();
EntityType type = event.getRightClicked().getType();
if((type == EntityType.VILLAGER||type==EntityType.MINECART) && Commands.inVault.containsKey(player.getName())) {
event.setCancelled(true);
}
}
}
+192
View File
@@ -0,0 +1,192 @@
package com.drtshock.playervaults;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.logging.Logger;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
import com.drtshock.playervaults.commands.Commands;
import com.drtshock.playervaults.util.Metrics;
import com.drtshock.playervaults.util.Updater;
public class Main extends JavaPlugin {
public Main plugin;
public Logger log;
public static boolean update = false;
public static String name = "";
Commands commands = new Commands();
@Override
public void onEnable() {
log = getServer().getLogger();
PluginManager pm = getServer().getPluginManager();
pm.registerEvents(new Listeners(this), this);
getCommand("pv").setExecutor(new Commands());
getCommand("pvdel").setExecutor(new Commands());
loadConfig();
loadLang();
startMetrics();
Updater u = new Updater();
try {
if(u.getUpdate()) {
update = true;
name = u.getNewVersion();
}
} catch (Exception e) {}
}
@Override
public void onDisable() {
//saveData();
}
public void startMetrics() {
try {
Metrics metrics = new Metrics(this);
metrics.start();
} catch (IOException localIOException) {
localIOException.printStackTrace();
}
}
public void loadConfig() {
/**
* Check to see if there's a config.
* If not then create a new one.
*/
File config = new File(getDataFolder() + File.separator + "config.yml");
if(!config.exists()) {
saveDefaultConfig();
}
}
public void loadLang() {
/**
* Check to see if there's a config.
* If not then create a new one.
*/
File lang = new File(getDataFolder() + File.separator + "lang.yml");
if(!lang.exists()) {
try{
getDataFolder().mkdir();
lang.createNewFile();
} catch (IOException e) {
log.warning("[PlayerVaults] Couldn't create language file.");
}
/**
* Write the config file here.
* New, genius way to write it :)
*/
try {
FileOutputStream fos = new FileOutputStream(lang);
InputStream is = getResource("lang.yml");
byte[] linebuffer = new byte[4096];
int lineLength = 0;
while((lineLength = is.read(linebuffer)) > 0)
{
fos.write(linebuffer, 0, lineLength);
}
fos.close();
} catch (IOException e) {
log.warning("[PlayerVaults] Couldn't write Language file: " + e);
}
}
}
private YamlConfiguration lang() {
File file = new File(getDataFolder() + File.separator + "lang.yml");
YamlConfiguration lang = YamlConfiguration.loadConfiguration(file);
return lang;
}
/**
* Methods to get values from the config.
* public so any class / plugin can get them.
*/
/**
*
* @return updateCheck
*/
public boolean updateCheck() {
return getConfig().getBoolean("check-update");
}
/**
*
* @return disabled worlds.
*/
public List<?> disabledWorlds() {
return getConfig().getList("disabled-worlds");
}
/**
* Values for the lang.yml
*/
/**
*
* @return title used in all messages.
*/
public String title() {
return lang().getString("title-name");
}
/**
*
* @return string for opening vault.
*/
public String openVault() {
return lang().getString("open-vault");
}
/**
*
* @return string for opening someone else's vault.
*/
public String openOtherVault() {
return lang().getString("open-other-vault");
}
/**
*
* @return string for invalid args.
*/
public String invalidArgs() {
return lang().getString("invalid-args");
}
/**
*
* @return string for deleting a vault.
*/
public String deleteVault() {
return lang().getString("delete-vault");
}
/**
*
* @return string for deleting someone else's vault.
*/
public String deleteOtherVault() {
return lang().getString("delete-other-vault");
}
public Logger getLog() {
return getServer().getLogger();
}
}
@@ -0,0 +1,54 @@
package com.drtshock.playervaults.commands;
import java.util.HashMap;
import org.bukkit.ChatColor;
import org.bukkit.command.Command;
import org.bukkit.command.CommandExecutor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class Commands implements CommandExecutor {
public static HashMap<String, VaultViewInfo> inVault = new HashMap<String, VaultViewInfo>();
private final String pv = ChatColor.DARK_RED + "[" + ChatColor.WHITE + "PlayerVaults" +
ChatColor.DARK_RED + "]" + ChatColor.WHITE + ": ";
@Override
public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
if(cmd.getName().equalsIgnoreCase("pv")) {
if(sender instanceof Player) {
Player p = (Player) sender;
switch(args.length) {
case 1:
if(VaultOperations.openOwnVault(p, args[0]))
inVault.put(sender.getName(), new VaultViewInfo(sender.getName(), Integer.parseInt(args[0])));
break;
case 2:
if(VaultOperations.openOtherVault(p,args[0], args[1])) {
inVault.put(sender.getName(), new VaultViewInfo(args[0], Integer.parseInt(args[1])));
}
break;
default:
Feedback.showHelp(sender, Feedback.Type.OPEN);
}
}
else sender.sendMessage(pv + "Sorry but that can only be run by a player!");
}
else if(cmd.getName().equalsIgnoreCase("pvdel")) {
if(sender instanceof Player) {
Player p = (Player) sender;
switch(args.length) {
case 1:
VaultOperations.deleteOwnVault(p, args[0]);
break;
default:
Feedback.showHelp(sender, Feedback.Type.DELETE);
}
}
else sender.sendMessage(pv + "Sorry but that can only be run by a player!");
}
return true;
}
}
@@ -0,0 +1,39 @@
package com.drtshock.playervaults.commands;
import org.bukkit.ChatColor;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
public class Feedback {
public enum Type {
OPEN,
DELETE,
WORKBENCH, //For later versions
FURNACE;
}
static String pv = ChatColor.DARK_RED + "[" + ChatColor.WHITE + "PlayerVaults" +
ChatColor.DARK_RED + "]" + ChatColor.WHITE + ": ";
public static void noPerms(CommandSender sender) {
sender.sendMessage(pv + "You don't have permission for that!");
return;
}
public static void showHelp(CommandSender sender, Type t) {
if(t == Type.OPEN) {
sender.sendMessage(pv + "/pv <number>");
sender.sendMessage(pv + "/pv <player> <number>");
}
else if(t == Type.DELETE) {
sender.sendMessage(pv + "/pvdel <number>");
sender.sendMessage(pv + "/pvdel <player> <number>");
}
}
public static void badWorld(Player sender) {
sender.sendMessage(pv + "You can't use this in that world!");
return;
}
}
@@ -0,0 +1,122 @@
package com.drtshock.playervaults.commands;
import java.io.IOException;
import org.bukkit.ChatColor;
import org.bukkit.World;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import com.drtshock.playervaults.Main;
import com.drtshock.playervaults.util.VaultManager;
public class VaultOperations {
private static Main plugin;
private static VaultManager vm = new VaultManager(plugin);
static String pv = ChatColor.DARK_RED + "[" + ChatColor.WHITE + "PlayerVaults" +
ChatColor.DARK_RED + "]" + ChatColor.WHITE + ": ";
public static boolean checkPerms(CommandSender cs, int number) {
if(cs.hasPermission("playervaults.amount."+String.valueOf(number))) return true;
for(int x = number; x <= 99;x++) {
if(cs.hasPermission("playervaults.amount."+String.valueOf(x))) return true;
}
return false;
}
/*
* TODO: Chenge how permissions are checked here.
*/
public static boolean openOwnVault(Player sender, String arg) {
if(allowedWorld(sender)) {
if(arg.matches("^[0-9]{1,2}$")) {
int number = 0;
try {
number = Integer.parseInt(arg);
}
catch(NumberFormatException nfe) {
sender.sendMessage(pv+ChatColor.RED+"You need to specify a number!");
return false;
}
if(checkPerms(sender, number)) {
vm.loadVault(sender, sender.getName(), number);
sender.sendMessage(pv + "Opening vault " + ChatColor.GREEN + number);
return true;
} else {
Feedback.noPerms(sender);
}
}
}
else {
Feedback.badWorld(sender);
}
return false;
}
public static boolean openOtherVault(Player sender, String user, String arg) {
if(sender.hasPermission("playervaults.admin")) {
if(arg.matches("^[0-9]{1,2}$")) {
int number = 0;
try {
number = Integer.parseInt(arg);
}
catch(NumberFormatException nfe) {
sender.sendMessage(pv+ChatColor.RED+"You need to specify a number!");
}
vm.loadVault(sender, user, number);
sender.sendMessage(pv + "Opening vault " + ChatColor.GREEN + number);
return true;
}
else {
Feedback.noPerms(sender);
}
}
return false;
}
public static void deleteOwnVault(Player sender, String arg) {
if(arg.matches("^[0-9]{1,2}$")) {
int number = 0;
try {
number = Integer.parseInt(arg);
}
catch(NumberFormatException nfe) {
sender.sendMessage(pv+ChatColor.RED+"You need to specify a number!");
}
try {
vm.deleteVault(sender, sender.getName(), number);
} catch (IOException e) {
sender.sendMessage(pv+"There was an error deleting that vault!");
}
}
}
public static void deleteOtherVault(Player sender, String user, String arg) {
if(sender.hasPermission("playervaults.delete")) {
if(arg.matches("^[0-9]{1,2}$")) {
int number = 0;
try {
number = Integer.parseInt(arg);
}
catch(NumberFormatException nfe) {
sender.sendMessage(pv+ChatColor.RED+"You need to specify a number!");
}
try {
vm.deleteVault(sender, user, number);
} catch (IOException e) {
sender.sendMessage(pv+"There was an error deleting that vault!");
}
}
}
else Feedback.noPerms(sender);
}
private static boolean allowedWorld(Player player) {
World world = player.getWorld();
if(plugin.disabledWorlds().contains(world))
return false;
return true;
}
}
@@ -0,0 +1,21 @@
package com.drtshock.playervaults.commands;
public class VaultViewInfo {
String s;
int i;
public VaultViewInfo(String s, int i) {
this.s = s;
this.i = i;
}
public String getHolder() {
return this.s;
}
public int getNumber() {
return this.i;
}
}
@@ -0,0 +1,634 @@
package com.drtshock.playervaults.util;
/*
* 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.
*/
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.configuration.InvalidConfigurationException;
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.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
/**
* <p>
* The metrics class obtains data about a plugin and submits statistics about it to the metrics backend.
* </p>
* <p>
* Public methods provided by this class:
* </p>
* <code>
* Graph createGraph(String name); <br/>
* void addCustomData(Metrics.Plotter plotter); <br/>
* void start(); <br/>
* </code>
*/
public class Metrics {
/**
* 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";
/**
* The separator to use for custom data. This MUST NOT change unless you are hosting your own
* version of metrics and want to change it.
*/
private static final String CUSTOM_DATA_SEPARATOR = "~~";
/**
* Interval of time to ping (in minutes)
*/
private static final int PING_INTERVAL = 10;
/**
* The plugin this metrics submits for
*/
private final Plugin plugin;
/**
* All of the custom graphs to submit to metrics
*/
private final Set<Graph> graphs = Collections.synchronizedSet(new HashSet<Graph>());
/**
* The default graph, used for addCustomData when you don't want a specific graph
*/
private final Graph defaultGraph = new Graph("Default");
/**
* 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 Metrics(final 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");
}
/**
* Construct and create a Graph that can be used to separate specific plotters to their own graphs
* on the metrics website. Plotters can be added to the graph object returned.
*
* @param name The name of the graph
* @return Graph object created. Will never return NULL under normal circumstances unless bad parameters are given
*/
public Graph createGraph(final String name) {
if (name == null) {
throw new IllegalArgumentException("Graph name cannot be null");
}
// Construct the graph object
final Graph graph = new Graph(name);
// Now we can add our graph
graphs.add(graph);
// and return back
return graph;
}
/**
* Add a Graph object to Metrics that represents data for the plugin that should be sent to the backend
*
* @param graph The name of the graph
*/
public void addGraph(final Graph graph) {
if (graph == null) {
throw new IllegalArgumentException("Graph cannot be null");
}
graphs.add(graph);
}
/**
* Adds a custom data plotter to the default graph
*
* @param plotter The plotter to use to plot custom data
*/
public void addCustomData(final Plotter plotter) {
if (plotter == null) {
throw new IllegalArgumentException("Plotter cannot be null");
}
// Add the plotter to the graph o/
defaultGraph.addPlotter(plotter);
// Ensure the default graph is included in the submitted graphs
graphs.add(defaultGraph);
}
/**
* 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.
*/
@SuppressWarnings("deprecation")
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;
// Tell all plotters to stop gathering information.
for (Graph graph : graphs){
graph.onOptOut();
}
}
}
// 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(final 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");
}
// Acquire a lock on the graphs, which lets us make the assumption we also lock everything
// inside of the graph (e.g plotters)
synchronized (graphs) {
final Iterator<Graph> iter = graphs.iterator();
while (iter.hasNext()) {
final Graph graph = iter.next();
for (Plotter plotter : graph.getPlotters()) {
// The key name to send to the metrics server
// The format is C-GRAPHNAME-PLOTTERNAME where separator - is defined at the top
// Legacy (R4) submitters use the format Custom%s, or CustomPLOTTERNAME
final String key = String.format("C%s%s%s%s", CUSTOM_DATA_SEPARATOR, graph.getName(), CUSTOM_DATA_SEPARATOR, plotter.getColumnName());
// The value to send, which for the foreseeable future is just the string
// value of plotter.getValue()
final String value = Integer.toString(plotter.getValue());
// Add it to the http post data :)
encodeDataPair(data, key, value);
}
}
}
// 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
} else {
// Is this the first update this hour?
if (response.contains("OK This is your first update this hour")) {
synchronized (graphs) {
final Iterator<Graph> iter = graphs.iterator();
while (iter.hasNext()) {
final Graph graph = iter.next();
for (Plotter plotter : graph.getPlotters()) {
plotter.reset();
}
}
}
}
}
}
/**
* 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;
}
}
/**
* <p>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:</p>
* <code>
* StringBuffer data = new StringBuffer();
* data.append(encode("guid")).append('=').append(encode(guid));
* encodeDataPair(data, "version", description.getVersion());
* </code>
*
* @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");
}
/**
* Represents a custom graph on the website
*/
public static class Graph {
/**
* The graph's name, alphanumeric and spaces only :)
* If it does not comply to the above when submitted, it is rejected
*/
private final String name;
/**
* The set of plotters that are contained within this graph
*/
private final Set<Plotter> plotters = new LinkedHashSet<Plotter>();
private Graph(final String name) {
this.name = name;
}
/**
* Gets the graph's name
*
* @return the Graph's name
*/
public String getName() {
return name;
}
/**
* Add a plotter to the graph, which will be used to plot entries
*
* @param plotter the plotter to add to the graph
*/
public void addPlotter(final Plotter plotter) {
plotters.add(plotter);
}
/**
* Remove a plotter from the graph
*
* @param plotter the plotter to remove from the graph
*/
public void removePlotter(final Plotter plotter) {
plotters.remove(plotter);
}
/**
* Gets an <b>unmodifiable</b> set of the plotter objects in the graph
*
* @return an unmodifiable {@link Set} of the plotter objects
*/
public Set<Plotter> getPlotters() {
return Collections.unmodifiableSet(plotters);
}
@Override
public int hashCode() {
return name.hashCode();
}
@Override
public boolean equals(final Object object) {
if (!(object instanceof Graph)) {
return false;
}
final Graph graph = (Graph) object;
return graph.name.equals(name);
}
/**
* Called when the server owner decides to opt-out of Metrics while the server is running.
*/
protected void onOptOut() {
}
}
/**
* Interface used to collect custom data for a plugin
*/
public static abstract class Plotter {
/**
* The plot's name
*/
private final String name;
/**
* Construct a plotter with the default plot name
*/
public Plotter() {
this("Default");
}
/**
* Construct a plotter with a specific plot name
*
* @param name the name of the plotter to use, which will show up on the website
*/
public Plotter(final String name) {
this.name = name;
}
/**
* Get the current value for the plotted point. Since this function defers to an external function
* it may or may not return immediately thus cannot be guaranteed to be thread friendly or safe.
* This function can be called from any thread so care should be taken when accessing resources
* that need to be synchronized.
*
* @return the current value for the point to be plotted.
*/
public abstract int getValue();
/**
* Get the column name for the plotted point
*
* @return the plotted point's column name
*/
public String getColumnName() {
return name;
}
/**
* Called after the website graphs have been updated
*/
public void reset() {
}
@Override
public int hashCode() {
return getColumnName().hashCode();
}
@Override
public boolean equals(final Object object) {
if (!(object instanceof Plotter)) {
return false;
}
final Plotter plotter = (Plotter) object;
return plotter.name.equals(name) && plotter.getValue() == getValue();
}
}
}
@@ -0,0 +1,69 @@
package com.drtshock.playervaults.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import net.minecraft.server.v1_4_R1.NBTBase;
import net.minecraft.server.v1_4_R1.NBTTagCompound;
import net.minecraft.server.v1_4_R1.NBTTagList;
import org.bukkit.ChatColor;
import org.bukkit.craftbukkit.v1_4_R1.inventory.CraftInventoryCustom;
import org.bukkit.craftbukkit.v1_4_R1.inventory.CraftItemStack;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.yaml.snakeyaml.external.biz.base64Coder.Base64Coder;
public class Serialization {
public static String toBase64(Inventory inventory) {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
DataOutputStream dataOutput = new DataOutputStream(outputStream);
NBTTagList itemList = new NBTTagList();
// Save every element in the list
for (int i = 0; i < inventory.getSize(); i++) {
NBTTagCompound outputObject = new NBTTagCompound();
CraftItemStack craft = getCraftVersion(inventory.getItem(i));
// Convert the item stack to a NBT compound
if (craft != null)
CraftItemStack.asNMSCopy(craft).save(outputObject);
itemList.add(outputObject);
}
// Now save the list
NBTBase.a(itemList, dataOutput);
// Serialize that array
return Base64Coder.encodeLines(outputStream.toByteArray());
}
public static Inventory fromBase64(String data) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(Base64Coder.decodeLines(data));
NBTTagList itemList = (NBTTagList) NBTBase.b(new DataInputStream(inputStream));
Inventory inventory = new CraftInventoryCustom(null, itemList.size(), ChatColor.DARK_RED + "Vault");
for (int i = 0; i < itemList.size(); i++) {
NBTTagCompound inputObject = (NBTTagCompound) itemList.get(i);
if (!inputObject.isEmpty()) {
inventory.setItem(i, CraftItemStack.asCraftMirror(net.minecraft.server.v1_4_R1.ItemStack.createStack(inputObject)));
}
}
// Serialize that array
return inventory;
}
private static CraftItemStack getCraftVersion(ItemStack stack) {
if (stack instanceof CraftItemStack)
return (CraftItemStack) stack;
else if (stack != null)
return CraftItemStack.asCraftCopy(stack);
else
return null;
}
}
@@ -0,0 +1,55 @@
package com.drtshock.playervaults.util;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.UnknownHostException;
import com.drtshock.playervaults.Main;
public class Updater extends Main {
String newVersion = "";
public String getNewVersion() {
return this.newVersion;
}
public boolean getUpdate() throws Exception {
String version = getDescription().getVersion();
URL url = new URL("http://dev.bukkit.org/server-mods/playervaults/files.rss");
InputStreamReader isr = null;
try {
isr = new InputStreamReader(url.openStream());
}
catch(UnknownHostException e) {
return false; //Cannot connect
}
BufferedReader in = new BufferedReader(isr);
String line;
int lineNum = 0;
while((line = in.readLine()) != null) {
if(line.length() != line.replace("<title>", "").length()) {
line = line.replaceAll("<title>", "").replaceAll("</title>", "").replaceAll(" ", "").substring(1); //Substring 1 for me, takes off the beginning v on my file name "v1.3.2"
if(lineNum == 1) {
this.newVersion = line;
Integer newVer = Integer.parseInt(line.replace(".", ""));
Integer oldVer = Integer.parseInt(version.replace(".", ""));
if(oldVer < newVer) {
return true; //They are using an old version
}
else if(oldVer > newVer) {
return false; //They are using a FUTURE version!
}
else {
return false; //They are up to date!
}
}
lineNum = lineNum + 1;
}
}
in.close();
return false;
}
}
@@ -0,0 +1,97 @@
package com.drtshock.playervaults.util;
import java.io.File;
import java.io.IOException;
import org.bukkit.Bukkit;
import org.bukkit.ChatColor;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.inventory.Inventory;
import com.drtshock.playervaults.Main;
public class VaultManager {
public Main plugin;
public VaultManager(Main instance) {
this.plugin = instance;
}
String title;
private final String directory = "plugins" + File.separator + "PlayerVaults" + File.separator + "vaults";
/**
* Method to save player's vault.
* Serialize his inventory.
* Save the vaults.yml
* @param player
* @throws IOException
*/
public void saveVault(Inventory inv, String player, int number) throws IOException {
// Get the player's file and serialize the inventory.
String ser = Serialization.toBase64(inv);
YamlConfiguration yaml = playerVaultFile(player);
yaml.set("vault" + number + "", ser);
saveFile(player, yaml);
}
/**
* Method to load player's vault.
* Deserialize his inventory
*
* TODO: Check to see if the path exists before we get it!
*/
public void loadVault(Player sender, String holder, int number) {
YamlConfiguration playerFile = playerVaultFile(holder);
String data = playerFile.getString("vault"+number);
Player player = (Player) sender;
if(data == null) {
Inventory inv = Bukkit.createInventory(player, 54, ChatColor.DARK_RED + "Vault #"+String.valueOf(number));
player.openInventory(inv);
} else {
Inventory inv = Serialization.fromBase64(data);
player.openInventory(inv);
}
}
public void deleteVault(Player sender, String target, int number) throws IOException {
String name = target.toLowerCase();
File file = new File(directory + name + ".yml");
FileConfiguration playerFile = YamlConfiguration.loadConfiguration(file);
if(file.exists()) {
ConfigurationSection section = playerFile.getConfigurationSection("vault" + number);
section.set(null, null);
sender.sendMessage(title + "Deleting " + ChatColor.GREEN + " " + number);
playerFile.save(file);
}
else {
sender.sendMessage(title + " That doesn't exist!");
}
}
public YamlConfiguration playerVaultFile(String player) {
File folder = new File(directory);
if(!folder.exists()) {
folder.mkdir();
}
File file = new File(directory + File.separator + player.toLowerCase() + ".yml");
if(!file.exists()) {
try {
file.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
YamlConfiguration playerFile = YamlConfiguration.loadConfiguration(file);
return playerFile;
}
public void saveFile(String name, YamlConfiguration yaml) throws IOException {
File file = new File(directory + File.separator + name.toLowerCase() + ".yml");
yaml.save(file);
}
}